From 4def7035cac133607256fd91352ce54ac4548a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 2 Mar 2013 19:31:03 +0100 Subject: [PATCH 1/9] refs: introduce an iterator This allows us to get a list of reference names in a loop instead of callbacks. --- include/git2/refs.h | 25 ++++++ include/git2/sys/refdb_backend.h | 35 +++++++++ include/git2/types.h | 4 + src/refdb.c | 24 ++++++ src/refdb.h | 4 + src/refdb_fs.c | 126 +++++++++++++++++++++++++++++++ src/refs.c | 20 +++++ tests-clar/refs/iterator.c | 76 +++++++++++++++++++ 8 files changed, 314 insertions(+) create mode 100644 tests-clar/refs/iterator.c diff --git a/include/git2/refs.h b/include/git2/refs.h index e1d425352..48ecc96e0 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -346,6 +346,31 @@ GIT_EXTERN(void) git_reference_free(git_reference *ref); */ GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2); +/** + * Create an iterator for the repo's references + * + * @param out pointer in which to store the iterator + * @param repo the repository + * @return 0 or an error code + */ +GIT_EXTERN(int) git_reference_iterator_new(git_reference_iterator **out, git_repository *repo); + +/** + * Get the next reference name + * + * @param out pointer in which to store the string + * @param iter the iterator + * @param 0, GIT_ITEROVER if there are no more; or an error code + */ +GIT_EXTERN(int) git_reference_next(const char **out, git_reference_iterator *iter); + +/** + * Free the iterator and its associated resources + * + * @param iter the iterator to free + */ +GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter); + /** * Perform a callback on each reference in the repository whose name * matches the given pattern. diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index d5f599fec..4c882b5cd 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -20,6 +20,22 @@ */ GIT_BEGIN_DECL + +/** + * Every backend's iterator must have a pointer to itself as the first + * element, so the API can talk to it. You'd define your iterator as + * + * struct my_iterator { + * git_reference_iterator parent; + * ... + * } + * + * and assing `iter->parent.backend` to your `git_refdb_backend`. + */ +struct git_reference_iterator { + git_refdb_backend *backend; +}; + /** An instance for a custom backend */ struct git_refdb_backend { unsigned int version; @@ -66,6 +82,25 @@ struct git_refdb_backend { void *payload); /** + * Allocate an iterator object for the backend + */ + int (*iterator)( + git_reference_iterator **iter, + struct git_refdb_backend *backend); + + /** + * Return the current value and advance the iterator. + */ + int (*next)( + const char **name, + git_reference_iterator *iter); + + /** + * Free the iterator + */ + void (*iterator_free)( + git_reference_iterator *iter); + /* * Writes the given reference to the refdb. A refdb implementation * must provide this function. */ diff --git a/include/git2/types.h b/include/git2/types.h index aca9ed927..43751d3b0 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -165,6 +165,10 @@ typedef struct git_signature { /** In-memory representation of a reference. */ typedef struct git_reference git_reference; +/** Iterator for references */ +typedef struct git_reference_iterator git_reference_iterator; + + /** Basic type of any Git reference. */ typedef enum { GIT_REF_INVALID = 0, /** Invalid reference */ diff --git a/src/refdb.c b/src/refdb.c index 33a1934d1..73882e807 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -124,6 +124,30 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name) return error; } +int git_refdb_iterator(git_reference_iterator **out, git_refdb *db) +{ + git_reference_iterator *iter; + + /* FIXME: don't segfault when there is no backends */ + if (db->backend->iterator(&iter, db->backend) < 0) { + git__free(iter); + return -1; + } + + *out = iter; + return 0; +} + +int git_refdb_next(const char **out, git_reference_iterator *iter) +{ + return iter->backend->next(out, iter); +} + +void git_refdb_iterator_free(git_reference_iterator *iter) +{ + iter->backend->iterator_free(iter); +} + int git_refdb_foreach( git_refdb *db, unsigned int list_flags, diff --git a/src/refdb.h b/src/refdb.h index 047113ac8..a243f627c 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -39,6 +39,10 @@ int git_refdb_foreach_glob( git_reference_foreach_cb callback, void *payload); +int git_refdb_iterator(git_reference_iterator **out, git_refdb *db); +int git_refdb_next(const char **out, git_reference_iterator *iter); +void git_refdb_iterator_free(git_reference_iterator *iter); + int git_refdb_write(git_refdb *refdb, const git_reference *ref); int git_refdb_delete(git_refdb *refdb, const git_reference *ref); diff --git a/src/refdb_fs.c b/src/refdb_fs.c index c0a32bae7..5c8e59f52 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -13,6 +13,7 @@ #include "reflog.h" #include "refdb.h" #include "refdb_fs.h" +#include "iterator.h" #include #include @@ -652,6 +653,128 @@ static int refdb_fs_backend__foreach( return data.callback_error ? GIT_EUSER : result; } +typedef struct { + git_reference_iterator parent; + unsigned int loose; + /* packed */ + git_strmap *h; + khiter_t k; + /* loose */ + git_iterator *fsiter; + git_buf buf; +} refdb_fs_iter; + +static int refdb_fs_backend__iterator(git_reference_iterator **out, git_refdb_backend *_backend) +{ + refdb_fs_iter *iter; + refdb_fs_backend *backend; + + assert(_backend); + backend = (refdb_fs_backend *)_backend; + + if (packed_load(backend) < 0) + return -1; + + iter = git__calloc(1, sizeof(refdb_fs_iter)); + GITERR_CHECK_ALLOC(iter); + + iter->parent.backend = _backend; + iter->h = backend->refcache.packfile; + iter->k = kh_begin(backend->refcache.packfile); + + *out = (git_reference_iterator *)iter; + + return 0; +} + +static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter) +{ + refdb_fs_iter *iter = (refdb_fs_iter *) _iter; + + git_buf_free(&iter->buf); + git_iterator_free(iter->fsiter); + git__free(iter); +} + +static int iter_packed(const char **out, refdb_fs_iter *iter) +{ + /* Move forward to the next entry */ + while (!kh_exist(iter->h, iter->k)) { + iter->k++; + if (iter->k == kh_end(iter->h)) + return GIT_ITEROVER; + } + + *out = kh_key(iter->h, iter->k); + iter->k++; + + return 0; +} + +static int iter_loose(const char **out, refdb_fs_iter *iter) +{ + const git_index_entry *entry; + int retry; + git_strmap *packfile_refs; + refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend; + + packfile_refs = backend->refcache.packfile; + + do { + khiter_t pos; + if (git_iterator_current(&entry, iter->fsiter) < 0) + return -1; + + git_buf_clear(&iter->buf); + if (!entry) + return GIT_ITEROVER; + + if (git_buf_printf(&iter->buf, "refs/%s", entry->path) < 0) + return -1; + + git_iterator_advance(NULL, iter->fsiter); + + /* Skip this one if we already listed it in packed */ + pos = git_strmap_lookup_index(packfile_refs, git_buf_cstr(&iter->buf)); + retry = 0; + if (git_strmap_valid_index(packfile_refs, pos) || + !git_reference_is_valid_name(git_buf_cstr(&iter->buf))) + retry = 1; + + *out = git_buf_cstr(&iter->buf); + } while (retry); + + return 0; +} + +static int iter_loose_setup(refdb_fs_iter *iter) +{ + refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend; + + git_buf_clear(&iter->buf); + if (git_buf_printf(&iter->buf, "%s/refs", backend->path) < 0) + return -1; + + return git_iterator_for_filesystem(&iter->fsiter, git_buf_cstr(&iter->buf), 0, NULL, NULL); +} + +static int refdb_fs_backend__next(const char **out, git_reference_iterator *_iter) +{ + refdb_fs_iter *iter = (refdb_fs_iter *)_iter; + + /* First round of checks to make sure where we are */ + if (!iter->loose && iter->k == kh_end(iter->h)) { + if (iter_loose_setup(iter) < 0) + return -1; + iter->loose = 1; + } + + if (!iter->loose) + return iter_packed(out, iter); + else + return iter_loose(out, iter); +} + static int loose_write(refdb_fs_backend *backend, const git_reference *ref) { git_filebuf file = GIT_FILEBUF_INIT; @@ -1082,6 +1205,9 @@ int git_refdb_backend_fs( backend->parent.exists = &refdb_fs_backend__exists; backend->parent.lookup = &refdb_fs_backend__lookup; backend->parent.foreach = &refdb_fs_backend__foreach; + backend->parent.iterator = &refdb_fs_backend__iterator; + backend->parent.next = &refdb_fs_backend__next; + backend->parent.iterator_free = &refdb_fs_backend__iterator_free; backend->parent.write = &refdb_fs_backend__write; backend->parent.delete = &refdb_fs_backend__delete; backend->parent.compress = &refdb_fs_backend__compress; diff --git a/src/refs.c b/src/refs.c index b85a2e828..547bd570c 100644 --- a/src/refs.c +++ b/src/refs.c @@ -568,6 +568,26 @@ int git_reference_foreach( return git_refdb_foreach(refdb, list_flags, callback, payload); } +int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo) +{ + git_refdb *refdb; + + if (git_repository_refdb__weakptr(&refdb, repo) < 0) + return -1; + + return git_refdb_iterator(out, refdb); +} + +int git_reference_next(const char **out, git_reference_iterator *iter) +{ + return git_refdb_next(out, iter); +} + +void git_reference_iterator_free(git_reference_iterator *iter) +{ + git_refdb_iterator_free(iter); +} + static int cb__reflist_add(const char *ref, void *data) { return git_vector_insert((git_vector *)data, git__strdup(ref)); diff --git a/tests-clar/refs/iterator.c b/tests-clar/refs/iterator.c new file mode 100644 index 000000000..aef0453c8 --- /dev/null +++ b/tests-clar/refs/iterator.c @@ -0,0 +1,76 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "vector.h" + +static git_repository *repo; + +void test_refs_iterator__initialize(void) +{ + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); +} + +void test_refs_iterator__cleanup(void) +{ + git_repository_free(repo); +} + +static const char *refnames[] = { + "refs/heads/br2", + "refs/heads/cannot-fetch", + "refs/heads/chomped", + "refs/heads/haacked", + "refs/heads/master", + "refs/heads/not-good", + "refs/heads/packed", + "refs/heads/packed-test", + "refs/heads/subtrees", + "refs/heads/test", + "refs/heads/track-local", + "refs/heads/trailing", + "refs/notes/fanout", + "refs/remotes/test/master", + "refs/tags/annotated_tag_to_blob", + "refs/tags/e90810b", + "refs/tags/hard_tag", + "refs/tags/point_to_blob", + "refs/tags/taggerless", + "refs/tags/test", + "refs/tags/wrapped_tag", +}; + +void test_refs_iterator__list(void) +{ + git_reference_iterator *iter; + git_vector output; + char *refname; + int error; + size_t i; + + cl_git_pass(git_vector_init(&output, 32, git__strcmp_cb)); + cl_git_pass(git_reference_iterator_new(&iter, repo)); + + do { + const char *name; + error = git_reference_next(&name, iter); + cl_assert(error == 0 || error == GIT_ITEROVER); + if (error != GIT_ITEROVER) { + char *dup = git__strdup(name); + cl_assert(dup != NULL); + cl_git_pass(git_vector_insert(&output, dup)); + } + } while (!error); + + cl_assert_equal_i(output.length, ARRAY_SIZE(refnames)); + + git_vector_sort(&output); + git_vector_foreach(&output, i, refname) { + cl_assert_equal_s(refname, refnames[i]); + } + + git_reference_iterator_free(iter); + + git_vector_foreach(&output, i, refname) { + git__free(refname); + } + git_vector_free(&output); +} From 69a3c766b6c6b9bbe2859bf8572c12b93a9d37c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 15:03:32 +0200 Subject: [PATCH 2/9] refdb_fs: don't crash when the repo doesn't have a path --- src/refdb_fs.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 5c8e59f52..62965a66b 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -181,6 +181,9 @@ static int packed_load(refdb_fs_backend *backend) GITERR_CHECK_ALLOC(ref_cache->packfile); } + if (backend->path == NULL) + return 0; + result = reference_read(&packfile, &ref_cache->packfile_time, backend->path, GIT_PACKEDREFS_FILE, &updated); @@ -1150,6 +1153,10 @@ static int setup_namespace(git_buf *path, git_repository *repo) { char *parts, *start, *end; + /* Not all repositories have a path */ + if (repo->path_repository == NULL) + return 0; + /* Load the path to the repo first */ git_buf_puts(path, repo->path_repository); From 51fc5e895d7655611431a0bdb76b9fd8499a9e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 15:16:55 +0200 Subject: [PATCH 3/9] Make sure the ref iterator works in an repo without physical presence --- src/refdb.c | 6 +++++- tests-clar/refs/iterator.c | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/refdb.c b/src/refdb.c index 73882e807..269234bfa 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -128,7 +128,11 @@ int git_refdb_iterator(git_reference_iterator **out, git_refdb *db) { git_reference_iterator *iter; - /* FIXME: don't segfault when there is no backends */ + if (!db->backend || !db->backend->iterator) { + giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators"); + return -1; + } + if (db->backend->iterator(&iter, db->backend) < 0) { git__free(iter); return -1; diff --git a/tests-clar/refs/iterator.c b/tests-clar/refs/iterator.c index aef0453c8..d5555c657 100644 --- a/tests-clar/refs/iterator.c +++ b/tests-clar/refs/iterator.c @@ -74,3 +74,21 @@ void test_refs_iterator__list(void) } git_vector_free(&output); } + +void test_refs_iterator__empty(void) +{ + git_reference_iterator *iter; + git_odb *odb; + const char *name; + git_repository *empty; + + cl_git_pass(git_odb_new(&odb)); + cl_git_pass(git_repository_wrap_odb(&empty, odb)); + + cl_git_pass(git_reference_iterator_new(&iter, empty)); + cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&name, iter)); + + git_reference_iterator_free(iter); + git_odb_free(odb); + git_repository_free(empty); +} From fb592a96ebac46f0e176fbbe849cf9b52b9f4727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 15:54:57 +0200 Subject: [PATCH 4/9] Remove outdated test Selecting wether to list loose or packed references is not something we want to support anymore, so remove a test for this. --- tests-clar/refs/list.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests-clar/refs/list.c b/tests-clar/refs/list.c index 3948b2b7a..8a386fdfd 100644 --- a/tests-clar/refs/list.c +++ b/tests-clar/refs/list.c @@ -41,17 +41,6 @@ void test_refs_list__all(void) git_strarray_free(&ref_list); } -void test_refs_list__symbolic_only(void) -{ - // try to list only the symbolic references - git_strarray ref_list; - - cl_git_pass(git_reference_list(&ref_list, g_repo, GIT_REF_SYMBOLIC)); - cl_assert(ref_list.count == 0); /* no symrefs in the test repo */ - - git_strarray_free(&ref_list); -} - void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_extension(void) { git_strarray ref_list; From 932af0e9eb35e37b2cf993c0806d6ea46476cf39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 15:57:44 +0200 Subject: [PATCH 5/9] Add iterator support to the testdb backend --- tests-clar/refdb/testdb.c | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c index 627254e44..eee60ac90 100644 --- a/tests-clar/refdb/testdb.c +++ b/tests-clar/refdb/testdb.c @@ -112,6 +112,49 @@ static int refdb_test_backend__lookup( return GIT_ENOTFOUND; } +typedef struct { + git_reference_iterator parent; + size_t i; +} refdb_test_iter; + +static int refdb_test_backend__iterator(git_reference_iterator **out, git_refdb_backend *_backend) +{ + refdb_test_iter *iter; + + GIT_UNUSED(_backend); + + iter = git__malloc(sizeof(refdb_test_iter)); + GITERR_CHECK_ALLOC(iter); + + iter->parent.backend = _backend; + iter->i = 0; + + *out = (git_reference_iterator *) iter; + + return 0; +} + +static int refdb_test_backend__next(const char **name, git_reference_iterator *_iter) +{ + refdb_test_entry *entry; + refdb_test_backend *backend = (refdb_test_backend *) _iter->backend; + refdb_test_iter *iter = (refdb_test_iter *) _iter; + + entry = git_vector_get(&backend->refs, iter->i); + if (!entry) + return GIT_ITEROVER; + + *name = entry->name; + iter->i++; + + return 0; +} + +static void refdb_test_backend__iterator_free(git_reference_iterator *iter) +{ + git__free(iter); +} + static int refdb_test_backend__foreach( git_refdb_backend *_backend, unsigned int list_flags, @@ -200,6 +243,9 @@ int refdb_backend_test( backend->parent.exists = &refdb_test_backend__exists; backend->parent.lookup = &refdb_test_backend__lookup; + backend->parent.iterator = &refdb_test_backend__iterator; + backend->parent.next = &refdb_test_backend__next; + backend->parent.iterator_free = &refdb_test_backend__iterator_free; backend->parent.foreach = &refdb_test_backend__foreach; backend->parent.write = &refdb_test_backend__write; backend->parent.delete = &refdb_test_backend__delete; From 95727245fd4f08aa77d1077901976ab3bd7472e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 16:03:17 +0200 Subject: [PATCH 6/9] refs: implement _foreach with the iterator --- src/refs.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/refs.c b/src/refs.c index 547bd570c..4ee6235cf 100644 --- a/src/refs.c +++ b/src/refs.c @@ -562,10 +562,28 @@ int git_reference_foreach( git_reference_foreach_cb callback, void *payload) { - git_refdb *refdb; - git_repository_refdb__weakptr(&refdb, repo); + git_reference_iterator *iter; + const char *name; + int error; - return git_refdb_foreach(refdb, list_flags, callback, payload); + GIT_UNUSED(list_flags); + + if (git_reference_iterator_new(&iter, repo) < 0) + return -1; + + while ((error = git_reference_next(&name, iter)) == 0) { + if (callback(name, payload)) { + error = GIT_EUSER; + goto out; + } + } + + if (error == GIT_ITEROVER) + error = 0; + +out: + git_reference_iterator_free(iter); + return error; } int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo) From 2b562c3a1edf0f521bdb7adf23e524d5a8389b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 16:32:58 +0200 Subject: [PATCH 7/9] refs: remove the OID/SYMBOLIC filtering Nobody should ever be using anything other than ALL at this level, so remove the option altogether. As part of this, git_reference_foreach_glob is now implemented in the frontend using an iterator. Backends will later regain the ability of doing the glob filtering in the backend. --- examples/general.c | 2 +- include/git2/refs.h | 19 +------ include/git2/sys/refdb_backend.h | 31 +++-------- src/branch.c | 2 +- src/clone.c | 1 - src/refdb.c | 48 ---------------- src/refdb.h | 13 ----- src/refdb_fs.c | 95 -------------------------------- src/refs.c | 39 ++++++++----- src/remote.c | 1 - src/repository.c | 2 +- src/revwalk.c | 2 +- src/tag.c | 2 +- src/transports/local.c | 2 +- src/transports/smart_protocol.c | 2 +- tests-clar/network/fetchlocal.c | 6 +- tests-clar/refdb/inmemory.c | 4 +- tests-clar/refdb/testdb.c | 28 ---------- tests-clar/refs/foreachglob.c | 14 ++--- tests-clar/refs/list.c | 4 +- tests-clar/refs/listall.c | 4 +- 21 files changed, 55 insertions(+), 266 deletions(-) diff --git a/examples/general.c b/examples/general.c index adc7ed8d2..d7a58479c 100644 --- a/examples/general.c +++ b/examples/general.c @@ -453,7 +453,7 @@ int main (int argc, char** argv) // Here we will implement something like `git for-each-ref` simply listing // out all available references and the object SHA they resolve to. git_strarray ref_list; - git_reference_list(&ref_list, repo, GIT_REF_LISTALL); + git_reference_list(&ref_list, repo); const char *refname; git_reference *ref; diff --git a/include/git2/refs.h b/include/git2/refs.h index 48ecc96e0..9bb78ccd6 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -284,12 +284,6 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref); /** * Fill a list with all the references that can be found in a repository. * - * Using the `list_flags` parameter, the listed references may be filtered - * by type (`GIT_REF_OID` or `GIT_REF_SYMBOLIC`) or using a bitwise OR of - * `git_ref_t` values. To include packed refs, include `GIT_REF_PACKED`. - * For convenience, use the value `GIT_REF_LISTALL` to obtain all - * references, including packed ones. - * * The string array will be filled with the names of all references; these * values are owned by the user and should be free'd manually when no * longer needed, using `git_strarray_free()`. @@ -297,36 +291,27 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref); * @param array Pointer to a git_strarray structure where * the reference names will be stored * @param repo Repository where to find the refs - * @param list_flags Filtering flags for the reference listing * @return 0 or an error code */ -GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo, unsigned int list_flags); +GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo); typedef int (*git_reference_foreach_cb)(const char *refname, void *payload); /** * Perform a callback on each reference in the repository. * - * Using the `list_flags` parameter, the references may be filtered by - * type (`GIT_REF_OID` or `GIT_REF_SYMBOLIC`) or using a bitwise OR of - * `git_ref_t` values. To include packed refs, include `GIT_REF_PACKED`. - * For convenience, use the value `GIT_REF_LISTALL` to obtain all - * references, including packed ones. - * * The `callback` function will be called for each reference in the * repository, receiving the name of the reference and the `payload` value * passed to this method. Returning a non-zero value from the callback * will terminate the iteration. * * @param repo Repository where to find the refs - * @param list_flags Filtering flags for the reference listing. * @param callback Function which will be called for every listed ref * @param payload Additional data to pass to the callback * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_reference_foreach( git_repository *repo, - unsigned int list_flags, git_reference_foreach_cb callback, void *payload); @@ -385,7 +370,6 @@ GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter); * * @param repo Repository where to find the refs * @param glob Pattern to match (fnmatch-style) against reference name. - * @param list_flags Filtering flags for the reference listing. * @param callback Function which will be called for every listed ref * @param payload Additional data to pass to the callback * @return 0 on success, GIT_EUSER on non-zero callback, or error code @@ -393,7 +377,6 @@ GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter); GIT_EXTERN(int) git_reference_foreach_glob( git_repository *repo, const char *glob, - unsigned int list_flags, git_reference_foreach_cb callback, void *payload); diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 4c882b5cd..f5eacd105 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -59,30 +59,9 @@ struct git_refdb_backend { const char *ref_name); /** - * Enumerates each reference in the refdb. A refdb implementation must - * provide this function. - */ - int (*foreach)( - git_refdb_backend *backend, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload); - - /** - * Enumerates each reference in the refdb that matches the given - * glob string. A refdb implementation may provide this function; - * if it is not provided, foreach will be used and the results filtered - * against the glob. - */ - int (*foreach_glob)( - git_refdb_backend *backend, - const char *glob, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload); - - /** - * Allocate an iterator object for the backend + * Allocate an iterator object for the backend. + * + * A refdb implementation must provide this function. */ int (*iterator)( git_reference_iterator **iter, @@ -90,6 +69,8 @@ struct git_refdb_backend { /** * Return the current value and advance the iterator. + * + * A refdb implementation must provide this function. */ int (*next)( const char **name, @@ -97,6 +78,8 @@ struct git_refdb_backend { /** * Free the iterator + * + * A refdb implementation must provide this function. */ void (*iterator_free)( git_reference_iterator *iter); diff --git a/src/branch.c b/src/branch.c index 88f052529..74e8d843e 100644 --- a/src/branch.c +++ b/src/branch.c @@ -157,7 +157,7 @@ int git_branch_foreach( filter.branch_type = list_flags; filter.callback_payload = payload; - return git_reference_foreach(repo, GIT_REF_LISTALL, &branch_foreach_cb, (void *)&filter); + return git_reference_foreach(repo, &branch_foreach_cb, (void *)&filter); } int git_branch_move( diff --git a/src/clone.c b/src/clone.c index aeb7bbf5c..49af9a1e2 100644 --- a/src/clone.c +++ b/src/clone.c @@ -243,7 +243,6 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote) /* Not master. Check all the other refs. */ if (git_reference_foreach( repo, - GIT_REF_LISTALL, reference_matches_remote_head, &head_info) < 0) goto cleanup; diff --git a/src/refdb.c b/src/refdb.c index 269234bfa..5e33c2e38 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -152,60 +152,12 @@ void git_refdb_iterator_free(git_reference_iterator *iter) iter->backend->iterator_free(iter); } -int git_refdb_foreach( - git_refdb *db, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload) -{ - assert(db && db->backend); - - return db->backend->foreach(db->backend, list_flags, callback, payload); -} - struct glob_cb_data { const char *glob; git_reference_foreach_cb callback; void *payload; }; -static int fromglob_cb(const char *reference_name, void *payload) -{ - struct glob_cb_data *data = (struct glob_cb_data *)payload; - - if (!p_fnmatch(data->glob, reference_name, 0)) - return data->callback(reference_name, data->payload); - - return 0; -} - -int git_refdb_foreach_glob( - git_refdb *db, - const char *glob, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload) -{ - int error; - struct glob_cb_data data; - - assert(db && db->backend && glob && callback); - - if(db->backend->foreach_glob != NULL) - error = db->backend->foreach_glob(db->backend, - glob, list_flags, callback, payload); - else { - data.glob = glob; - data.callback = callback; - data.payload = payload; - - error = db->backend->foreach(db->backend, - list_flags, fromglob_cb, &data); - } - - return error; -} - int git_refdb_write(git_refdb *db, const git_reference *ref) { assert(db && db->backend); diff --git a/src/refdb.h b/src/refdb.h index a243f627c..e88dead7a 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -26,19 +26,6 @@ int git_refdb_lookup( git_refdb *refdb, const char *ref_name); -int git_refdb_foreach( - git_refdb *refdb, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload); - -int git_refdb_foreach_glob( - git_refdb *refdb, - const char *glob, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload); - int git_refdb_iterator(git_reference_iterator **out, git_refdb *db); int git_refdb_next(const char **out, git_reference_iterator *iter); void git_refdb_iterator_free(git_reference_iterator *iter); diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 62965a66b..ab04e453e 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -562,100 +562,6 @@ struct dirent_list_data { int callback_error; }; -static git_ref_t loose_guess_rtype(const git_buf *full_path) -{ - git_buf ref_file = GIT_BUF_INIT; - git_ref_t type; - - type = GIT_REF_INVALID; - - if (git_futils_readbuffer(&ref_file, full_path->ptr) == 0) { - if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) - type = GIT_REF_SYMBOLIC; - else - type = GIT_REF_OID; - } - - git_buf_free(&ref_file); - return type; -} - -static int _dirent_loose_listall(void *_data, git_buf *full_path) -{ - struct dirent_list_data *data = (struct dirent_list_data *)_data; - const char *file_path = full_path->ptr + data->repo_path_len; - - if (git_path_isdir(full_path->ptr) == true) - return git_path_direach(full_path, _dirent_loose_listall, _data); - - /* do not add twice a reference that exists already in the packfile */ - if (git_strmap_exists(data->backend->refcache.packfile, file_path)) - return 0; - - if (data->list_type != GIT_REF_LISTALL) { - if ((data->list_type & loose_guess_rtype(full_path)) == 0) - return 0; /* we are filtering out this reference */ - } - - /* Locked references aren't returned */ - if (!git__suffixcmp(file_path, GIT_FILELOCK_EXTENSION)) - return 0; - - if (data->callback(file_path, data->callback_payload)) - data->callback_error = GIT_EUSER; - - return data->callback_error; -} - -static int refdb_fs_backend__foreach( - git_refdb_backend *_backend, - unsigned int list_type, - git_reference_foreach_cb callback, - void *payload) -{ - refdb_fs_backend *backend; - int result; - struct dirent_list_data data; - git_buf refs_path = GIT_BUF_INIT; - const char *ref_name; - void *ref = NULL; - - GIT_UNUSED(ref); - - assert(_backend); - backend = (refdb_fs_backend *)_backend; - - if (packed_load(backend) < 0) - return -1; - - /* list all the packed references first */ - if (list_type & GIT_REF_OID) { - git_strmap_foreach(backend->refcache.packfile, ref_name, ref, { - if (callback(ref_name, payload)) - return GIT_EUSER; - }); - } - - /* now list the loose references, trying not to - * duplicate the ref names already in the packed-refs file */ - - data.repo_path_len = strlen(backend->path); - data.list_type = list_type; - data.backend = backend; - data.callback = callback; - data.callback_payload = payload; - data.callback_error = 0; - - if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0) - return -1; - - result = git_path_direach(&refs_path, _dirent_loose_listall, &data); - - git_buf_free(&refs_path); - - return data.callback_error ? GIT_EUSER : result; -} - typedef struct { git_reference_iterator parent; unsigned int loose; @@ -1211,7 +1117,6 @@ int git_refdb_backend_fs( backend->parent.exists = &refdb_fs_backend__exists; backend->parent.lookup = &refdb_fs_backend__lookup; - backend->parent.foreach = &refdb_fs_backend__foreach; backend->parent.iterator = &refdb_fs_backend__iterator; backend->parent.next = &refdb_fs_backend__next; backend->parent.iterator_free = &refdb_fs_backend__iterator_free; diff --git a/src/refs.c b/src/refs.c index 4ee6235cf..a7be117ad 100644 --- a/src/refs.c +++ b/src/refs.c @@ -139,7 +139,7 @@ static int reference_path_available( data.available = 1; error = git_reference_foreach( - repo, GIT_REF_LISTALL, _reference_available_cb, (void *)&data); + repo, _reference_available_cb, (void *)&data); if (error < 0) return error; @@ -558,7 +558,6 @@ int git_reference_resolve(git_reference **ref_out, const git_reference *ref) int git_reference_foreach( git_repository *repo, - unsigned int list_flags, git_reference_foreach_cb callback, void *payload) { @@ -566,8 +565,6 @@ int git_reference_foreach( const char *name; int error; - GIT_UNUSED(list_flags); - if (git_reference_iterator_new(&iter, repo) < 0) return -1; @@ -613,8 +610,7 @@ static int cb__reflist_add(const char *ref, void *data) int git_reference_list( git_strarray *array, - git_repository *repo, - unsigned int list_flags) + git_repository *repo) { git_vector ref_list; @@ -627,7 +623,7 @@ int git_reference_list( return -1; if (git_reference_foreach( - repo, list_flags, &cb__reflist_add, (void *)&ref_list) < 0) { + repo, &cb__reflist_add, (void *)&ref_list) < 0) { git_vector_free(&ref_list); return -1; } @@ -925,19 +921,32 @@ int git_reference__update_terminal( int git_reference_foreach_glob( git_repository *repo, const char *glob, - unsigned int list_flags, - int (*callback)( - const char *reference_name, - void *payload), + git_reference_foreach_cb callback, void *payload) { - git_refdb *refdb; + git_reference_iterator *iter; + const char *name; + int error; - assert(repo && glob && callback); + if (git_reference_iterator_new(&iter, repo) < 0) + return -1; - git_repository_refdb__weakptr(&refdb, repo); + while ((error = git_reference_next(&name, iter)) == 0) { + if (p_fnmatch(glob, name, 0)) + continue; - return git_refdb_foreach_glob(refdb, glob, list_flags, callback, payload); + if (callback(name, payload)) { + error = GIT_EUSER; + goto out; + } + } + + if (error == GIT_ITEROVER) + error = 0; + +out: + git_reference_iterator_free(iter); + return error; } int git_reference_has_log( diff --git a/src/remote.c b/src/remote.c index 6eaaf8b49..10bffec44 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1297,7 +1297,6 @@ static int rename_remote_references( if (git_reference_foreach( repo, - GIT_REF_LISTALL, rename_cb, &refnames) < 0) goto cleanup; diff --git a/src/repository.c b/src/repository.c index e6eaf753c..80118a41f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1469,7 +1469,7 @@ static int at_least_one_cb(const char *refname, void *payload) static int repo_contains_no_reference(git_repository *repo) { - int error = git_reference_foreach(repo, GIT_REF_LISTALL, at_least_one_cb, NULL); + int error = git_reference_foreach(repo, at_least_one_cb, NULL); if (error == GIT_EUSER) return 0; diff --git a/src/revwalk.c b/src/revwalk.c index 16f06624d..528d02b20 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -186,7 +186,7 @@ static int push_glob(git_revwalk *walk, const char *glob, int hide) data.hide = hide; if (git_reference_foreach_glob( - walk->repo, git_buf_cstr(&buf), GIT_REF_LISTALL, push_glob_cb, &data) < 0) + walk->repo, git_buf_cstr(&buf), push_glob_cb, &data) < 0) goto on_error; regfree(&preg); diff --git a/src/tag.c b/src/tag.c index a4f2e2581..f81956de7 100644 --- a/src/tag.c +++ b/src/tag.c @@ -427,7 +427,7 @@ int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data) data.cb_data = cb_data; data.repo = repo; - return git_reference_foreach(repo, GIT_REF_OID, &tags_cb, &data); + return git_reference_foreach(repo, &tags_cb, &data); } typedef struct { diff --git a/src/transports/local.c b/src/transports/local.c index 8b4d50c14..bd3bf93bf 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -124,7 +124,7 @@ static int store_refs(transport_local *t) assert(t); - if (git_reference_list(&ref_names, t->repo, GIT_REF_LISTALL) < 0 || + if (git_reference_list(&ref_names, t->repo) < 0 || git_vector_init(&t->refs, ref_names.count, NULL) < 0) goto on_error; diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 765b914b7..09bef1ae3 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -193,7 +193,7 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo) unsigned int i; git_reference *ref; - if (git_reference_list(&refs, repo, GIT_REF_LISTALL) < 0) + if (git_reference_list(&refs, repo) < 0) return -1; if (git_revwalk_new(&walk, repo) < 0) diff --git a/tests-clar/network/fetchlocal.c b/tests-clar/network/fetchlocal.c index bcf298cde..09335b3df 100644 --- a/tests-clar/network/fetchlocal.c +++ b/tests-clar/network/fetchlocal.c @@ -34,7 +34,7 @@ void test_network_fetchlocal__complete(void) cl_git_pass(git_remote_download(origin, transfer_cb, &callcount)); cl_git_pass(git_remote_update_tips(origin)); - cl_git_pass(git_reference_list(&refnames, repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&refnames, repo)); cl_assert_equal_i(19, (int)refnames.count); cl_assert(callcount > 0); @@ -58,7 +58,7 @@ void test_network_fetchlocal__partial(void) const char *url; cl_set_cleanup(&cleanup_sandbox, NULL); - cl_git_pass(git_reference_list(&refnames, repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&refnames, repo)); cl_assert_equal_i(1, (int)refnames.count); url = cl_git_fixture_url("testrepo.git"); @@ -69,7 +69,7 @@ void test_network_fetchlocal__partial(void) git_strarray_free(&refnames); - cl_git_pass(git_reference_list(&refnames, repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&refnames, repo)); cl_assert_equal_i(20, (int)refnames.count); /* 18 remote + 1 local */ cl_assert(callcount > 0); diff --git a/tests-clar/refdb/inmemory.c b/tests-clar/refdb/inmemory.c index 243b5bb37..d2594cd6d 100644 --- a/tests-clar/refdb/inmemory.c +++ b/tests-clar/refdb/inmemory.c @@ -163,7 +163,7 @@ void test_refdb_inmemory__foreach(void) cl_git_pass(git_oid_fromstr(&oid3, "763d71aadf09a7951596c9746c024e7eece7c7af")); cl_git_pass(git_reference_create(&write3, repo, GIT_REFS_HEADS_DIR "test3", &oid3, 0)); - cl_git_pass(git_reference_foreach(repo, GIT_REF_LISTALL, foreach_test, &i)); + cl_git_pass(git_reference_foreach(repo,foreach_test, &i)); cl_assert_equal_i(3, (int)i); git_reference_free(write1); @@ -210,7 +210,7 @@ void test_refdb_inmemory__delete(void) git_reference_delete(write3); git_reference_free(write3); - cl_git_pass(git_reference_foreach(repo, GIT_REF_LISTALL, delete_test, &i)); + cl_git_pass(git_reference_foreach(repo, delete_test, &i)); cl_assert_equal_i(1, (int)i); git_reference_free(write2); diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c index eee60ac90..4d118562a 100644 --- a/tests-clar/refdb/testdb.c +++ b/tests-clar/refdb/testdb.c @@ -155,33 +155,6 @@ static void refdb_test_backend__iterator_free(git_reference_iterator *iter) git__free(iter); } -static int refdb_test_backend__foreach( - git_refdb_backend *_backend, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload) -{ - refdb_test_backend *backend; - refdb_test_entry *entry; - size_t i; - - assert(_backend); - backend = (refdb_test_backend *)_backend; - - git_vector_foreach(&backend->refs, i, entry) { - if (entry->type == GIT_REF_OID && (list_flags & GIT_REF_OID) == 0) - continue; - - if (entry->type == GIT_REF_SYMBOLIC && (list_flags & GIT_REF_SYMBOLIC) == 0) - continue; - - if (callback(entry->name, payload) != 0) - return GIT_EUSER; - } - - return 0; -} - static void refdb_test_entry_free(refdb_test_entry *entry) { if (entry->type == GIT_REF_SYMBOLIC) @@ -246,7 +219,6 @@ int refdb_backend_test( backend->parent.iterator = &refdb_test_backend__iterator; backend->parent.next = &refdb_test_backend__next; backend->parent.iterator_free = &refdb_test_backend__iterator_free; - backend->parent.foreach = &refdb_test_backend__foreach; backend->parent.write = &refdb_test_backend__write; backend->parent.delete = &refdb_test_backend__delete; backend->parent.free = &refdb_test_backend__free; diff --git a/tests-clar/refs/foreachglob.c b/tests-clar/refs/foreachglob.c index 4da1a15dd..2c458082f 100644 --- a/tests-clar/refs/foreachglob.c +++ b/tests-clar/refs/foreachglob.c @@ -37,11 +37,11 @@ static int count_cb(const char *reference_name, void *payload) return 0; } -static void assert_retrieval(const char *glob, unsigned int flags, int expected_count) +static void assert_retrieval(const char *glob, int expected_count) { int count = 0; - cl_git_pass(git_reference_foreach_glob(repo, glob, flags, count_cb, &count)); + cl_git_pass(git_reference_foreach_glob(repo, glob, count_cb, &count)); cl_assert_equal_i(expected_count, count); } @@ -49,17 +49,17 @@ static void assert_retrieval(const char *glob, unsigned int flags, int expected_ void test_refs_foreachglob__retrieve_all_refs(void) { /* 12 heads (including one packed head) + 1 note + 2 remotes + 7 tags */ - assert_retrieval("*", GIT_REF_LISTALL, 22); + assert_retrieval("*", 22); } void test_refs_foreachglob__retrieve_remote_branches(void) { - assert_retrieval("refs/remotes/*", GIT_REF_LISTALL, 2); + assert_retrieval("refs/remotes/*", 2); } void test_refs_foreachglob__retrieve_local_branches(void) { - assert_retrieval("refs/heads/*", GIT_REF_LISTALL, 12); + assert_retrieval("refs/heads/*", 12); } void test_refs_foreachglob__retrieve_partially_named_references(void) @@ -69,7 +69,7 @@ void test_refs_foreachglob__retrieve_partially_named_references(void) * refs/remotes/test/master, refs/tags/test */ - assert_retrieval("*test*", GIT_REF_LISTALL, 4); + assert_retrieval("*test*", 4); } @@ -89,7 +89,7 @@ void test_refs_foreachglob__can_cancel(void) int count = 0; cl_assert_equal_i(GIT_EUSER, git_reference_foreach_glob( - repo, "*", GIT_REF_LISTALL, interrupt_cb, &count) ); + repo, "*", interrupt_cb, &count) ); cl_assert_equal_i(11, count); } diff --git a/tests-clar/refs/list.c b/tests-clar/refs/list.c index 8a386fdfd..c9c2af4a7 100644 --- a/tests-clar/refs/list.c +++ b/tests-clar/refs/list.c @@ -25,7 +25,7 @@ void test_refs_list__all(void) // try to list all the references in our test repo git_strarray ref_list; - cl_git_pass(git_reference_list(&ref_list, g_repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&ref_list, g_repo)); /*{ unsigned short i; @@ -50,7 +50,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten "./testrepo/.git/refs/heads/hanwen.lock", "144344043ba4d4a405da03de3844aa829ae8be0e\n"); - cl_git_pass(git_reference_list(&ref_list, g_repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&ref_list, g_repo)); cl_assert_equal_i((int)ref_list.count, 13); git_strarray_free(&ref_list); diff --git a/tests-clar/refs/listall.c b/tests-clar/refs/listall.c index 8f4c3746b..c696fbb2e 100644 --- a/tests-clar/refs/listall.c +++ b/tests-clar/refs/listall.c @@ -9,7 +9,7 @@ static void ensure_no_refname_starts_with_a_forward_slash(const char *path) size_t i; cl_git_pass(git_repository_open(&repo, path)); - cl_git_pass(git_reference_list(&ref_list, repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&ref_list, repo)); cl_assert(ref_list.count > 0); @@ -38,7 +38,7 @@ void test_refs_listall__from_repository_opened_through_gitdir_path(void) void test_refs_listall__from_repository_with_no_trailing_newline(void) { cl_git_pass(git_repository_open(&repo, cl_fixture("bad_tag.git"))); - cl_git_pass(git_reference_list(&ref_list, repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&ref_list, repo)); cl_assert(ref_list.count > 0); From 9bd89d962265b399b537e41058d40a6aeab00e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 16:49:39 +0200 Subject: [PATCH 8/9] Move a couple more functions to use iterators --- src/branch.c | 57 +++++++++++++++++++++++++++------------------------- src/remote.c | 32 +++++++++++++++++------------ 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/branch.c b/src/branch.c index 74e8d843e..5f4e338ba 100644 --- a/src/branch.c +++ b/src/branch.c @@ -124,40 +124,43 @@ on_error: return error; } -typedef struct { - git_branch_foreach_cb branch_cb; - void *callback_payload; - unsigned int branch_type; -} branch_foreach_filter; - -static int branch_foreach_cb(const char *branch_name, void *payload) -{ - branch_foreach_filter *filter = (branch_foreach_filter *)payload; - - if (filter->branch_type & GIT_BRANCH_LOCAL && - git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0) - return filter->branch_cb(branch_name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, filter->callback_payload); - - if (filter->branch_type & GIT_BRANCH_REMOTE && - git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0) - return filter->branch_cb(branch_name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, filter->callback_payload); - - return 0; -} - int git_branch_foreach( git_repository *repo, unsigned int list_flags, - git_branch_foreach_cb branch_cb, + git_branch_foreach_cb callback, void *payload) { - branch_foreach_filter filter; + git_reference_iterator *iter; + const char *name; + int error; - filter.branch_cb = branch_cb; - filter.branch_type = list_flags; - filter.callback_payload = payload; + if (git_reference_iterator_new(&iter, repo) < 0) + return -1; + + while ((error = git_reference_next(&name, iter)) == 0) { + if (list_flags & GIT_BRANCH_LOCAL && + git__prefixcmp(name, GIT_REFS_HEADS_DIR) == 0) { + if (callback(name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, payload)) { + error = GIT_EUSER; + break; + } + } + + if (list_flags & GIT_BRANCH_REMOTE && + git__prefixcmp(name, GIT_REFS_REMOTES_DIR) == 0) { + if (callback(name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, payload)) { + error = GIT_EUSER; + break; + } + } + } + + if (error == GIT_ITEROVER) + error = 0; + + git_reference_iterator_free(iter); + return error; - return git_reference_foreach(repo, &branch_foreach_cb, (void *)&filter); } int git_branch_move( diff --git a/src/remote.c b/src/remote.c index 10bffec44..2f25c0f4a 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1244,14 +1244,6 @@ static int update_branch_remote_config_entry( update_config_entries_cb, &data); } -static int rename_cb(const char *ref, void *data) -{ - if (git__prefixcmp(ref, GIT_REFS_REMOTES_DIR)) - return 0; - - return git_vector_insert((git_vector *)data, git__strdup(ref)); -} - static int rename_one_remote_reference( git_repository *repo, const char *reference_name, @@ -1291,15 +1283,29 @@ static int rename_remote_references( int error = -1; unsigned int i; char *name; + const char *refname; + git_reference_iterator *iter; if (git_vector_init(&refnames, 8, NULL) < 0) + return -1; + + if (git_reference_iterator_new(&iter, repo) < 0) goto cleanup; - if (git_reference_foreach( - repo, - rename_cb, - &refnames) < 0) - goto cleanup; + while ((error = git_reference_next(&refname, iter)) == 0) { + if (git__prefixcmp(refname, GIT_REFS_REMOTES_DIR)) + continue; + + if ((error = git_vector_insert(&refnames, git__strdup(refname))) < 0) + break; + + } + + git_reference_iterator_free(iter); + if (error == GIT_ITEROVER) + error = 0; + else + goto cleanup; git_vector_foreach(&refnames, i, name) { if ((error = rename_one_remote_reference(repo, name, old_name, new_name)) < 0) From c58cac12c24fbb127cf1928bec20decb007a75e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 18:06:14 +0200 Subject: [PATCH 9/9] Introduce a glob-filtering iterator If the backend doesn't provide support for it, the matching is done in refdb on top of a normal iterator. --- include/git2/sys/refdb_backend.h | 14 ++++++++++- src/refdb.c | 42 +++++++++++++++++++++++++++----- src/refdb.h | 1 + src/refs.c | 15 +++++++++--- tests-clar/refdb/testdb.c | 2 +- 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index f5eacd105..8dbf38ca9 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -30,10 +30,11 @@ GIT_BEGIN_DECL * ... * } * - * and assing `iter->parent.backend` to your `git_refdb_backend`. + * and assign `iter->parent.backend` to your `git_refdb_backend`. */ struct git_reference_iterator { git_refdb_backend *backend; + char *glob; }; /** An instance for a custom backend */ @@ -67,6 +68,17 @@ struct git_refdb_backend { git_reference_iterator **iter, struct git_refdb_backend *backend); + /** + * Allocate a glob-filtering iterator object for the backend. + * + * A refdb implementation may provide this function. If it's + * not available, the glob matching will be done by the frontend. + */ + int (*iterator_glob)( + git_reference_iterator **iter, + struct git_refdb_backend *backend, + const char *glob); + /** * Return the current value and advance the iterator. * diff --git a/src/refdb.c b/src/refdb.c index 5e33c2e38..9f9037ce7 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -126,29 +126,59 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name) int git_refdb_iterator(git_reference_iterator **out, git_refdb *db) { - git_reference_iterator *iter; - if (!db->backend || !db->backend->iterator) { giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators"); return -1; } - if (db->backend->iterator(&iter, db->backend) < 0) { - git__free(iter); + if (db->backend->iterator(out, db->backend) < 0) + return -1; + + return 0; +} + +int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const char *glob) +{ + if (!db->backend) { + giterr_set(GITERR_REFERENCE, "There are no backends loaded"); + return -1; + } + + if (db->backend->iterator_glob) + return db->backend->iterator_glob(out, db->backend, glob); + + /* If the backend doesn't support glob-filtering themselves, we have to do it */ + if (db->backend->iterator(out, db->backend) < 0) + return -1; + + (*out)->glob = git__strdup(glob); + if (!(*out)->glob) { + db->backend->iterator_free(*out); return -1; } - *out = iter; return 0; } int git_refdb_next(const char **out, git_reference_iterator *iter) { - return iter->backend->next(out, iter); + int error; + + if (!iter->glob) + return iter->backend->next(out, iter); + + /* If the iterator has a glob, we need to filter */ + while ((error = iter->backend->next(out, iter)) == 0) { + if (!p_fnmatch(iter->glob, *out, 0)) + break; + } + + return error; } void git_refdb_iterator_free(git_reference_iterator *iter) { + git__free(iter->glob); iter->backend->iterator_free(iter); } diff --git a/src/refdb.h b/src/refdb.h index e88dead7a..2edd05d18 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -27,6 +27,7 @@ int git_refdb_lookup( const char *ref_name); int git_refdb_iterator(git_reference_iterator **out, git_refdb *db); +int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const char *glob); int git_refdb_next(const char **out, git_reference_iterator *iter); void git_refdb_iterator_free(git_reference_iterator *iter); diff --git a/src/refs.c b/src/refs.c index a7be117ad..fc6652fec 100644 --- a/src/refs.c +++ b/src/refs.c @@ -593,6 +593,16 @@ int git_reference_iterator_new(git_reference_iterator **out, git_repository *rep return git_refdb_iterator(out, refdb); } +int git_reference_iterator_glob_new(git_reference_iterator **out, git_repository *repo, const char *glob) +{ + git_refdb *refdb; + + if (git_repository_refdb__weakptr(&refdb, repo) < 0) + return -1; + + return git_refdb_iterator_glob(out, refdb, glob); +} + int git_reference_next(const char **out, git_reference_iterator *iter) { return git_refdb_next(out, iter); @@ -928,13 +938,10 @@ int git_reference_foreach_glob( const char *name; int error; - if (git_reference_iterator_new(&iter, repo) < 0) + if (git_reference_iterator_glob_new(&iter, repo, glob) < 0) return -1; while ((error = git_reference_next(&name, iter)) == 0) { - if (p_fnmatch(glob, name, 0)) - continue; - if (callback(name, payload)) { error = GIT_EUSER; goto out; diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c index 4d118562a..961e18d44 100644 --- a/tests-clar/refdb/testdb.c +++ b/tests-clar/refdb/testdb.c @@ -123,7 +123,7 @@ static int refdb_test_backend__iterator(git_reference_iterator **out, git_refdb_ GIT_UNUSED(_backend); - iter = git__malloc(sizeof(refdb_test_iter)); + iter = git__calloc(1, sizeof(refdb_test_iter)); GITERR_CHECK_ALLOC(iter); iter->parent.backend = _backend;