diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2013-03-02 19:31:03 +0100 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2013-05-11 11:20:37 +0200 |
commit | 4def7035cac133607256fd91352ce54ac4548a7c (patch) | |
tree | 6956733c0ef644f9f2a6bea1e603e3fcb72fd954 /src | |
parent | b641c00eebb3c60e8719c0dfc55dde91ca30a5d2 (diff) | |
download | libgit2-4def7035cac133607256fd91352ce54ac4548a7c.tar.gz |
refs: introduce an iterator
This allows us to get a list of reference names in a loop instead of callbacks.
Diffstat (limited to 'src')
-rw-r--r-- | src/refdb.c | 24 | ||||
-rw-r--r-- | src/refdb.h | 4 | ||||
-rw-r--r-- | src/refdb_fs.c | 126 | ||||
-rw-r--r-- | src/refs.c | 20 |
4 files changed, 174 insertions, 0 deletions
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 <git2/tag.h> #include <git2/object.h> @@ -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)); |