mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-05 13:15:39 +00:00
refs: introduce an iterator
This allows us to get a list of reference names in a loop instead of callbacks.
This commit is contained in:
parent
b641c00eeb
commit
4def7035ca
@ -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.
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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 */
|
||||
|
24
src/refdb.c
24
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,
|
||||
|
@ -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);
|
||||
|
126
src/refdb_fs.c
126
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;
|
||||
|
20
src/refs.c
20
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));
|
||||
|
76
tests-clar/refs/iterator.c
Normal file
76
tests-clar/refs/iterator.c
Normal file
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user