Skip to content

Commit 4def703

Browse files
committed
refs: introduce an iterator
This allows us to get a list of reference names in a loop instead of callbacks.
1 parent b641c00 commit 4def703

File tree

8 files changed

+314
-0
lines changed

8 files changed

+314
-0
lines changed

include/git2/refs.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,31 @@ GIT_EXTERN(void) git_reference_free(git_reference *ref);
346346
*/
347347
GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
348348

349+
/**
350+
* Create an iterator for the repo's references
351+
*
352+
* @param out pointer in which to store the iterator
353+
* @param repo the repository
354+
* @return 0 or an error code
355+
*/
356+
GIT_EXTERN(int) git_reference_iterator_new(git_reference_iterator **out, git_repository *repo);
357+
358+
/**
359+
* Get the next reference name
360+
*
361+
* @param out pointer in which to store the string
362+
* @param iter the iterator
363+
* @param 0, GIT_ITEROVER if there are no more; or an error code
364+
*/
365+
GIT_EXTERN(int) git_reference_next(const char **out, git_reference_iterator *iter);
366+
367+
/**
368+
* Free the iterator and its associated resources
369+
*
370+
* @param iter the iterator to free
371+
*/
372+
GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter);
373+
349374
/**
350375
* Perform a callback on each reference in the repository whose name
351376
* matches the given pattern.

include/git2/sys/refdb_backend.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,22 @@
2020
*/
2121
GIT_BEGIN_DECL
2222

23+
24+
/**
25+
* Every backend's iterator must have a pointer to itself as the first
26+
* element, so the API can talk to it. You'd define your iterator as
27+
*
28+
* struct my_iterator {
29+
* git_reference_iterator parent;
30+
* ...
31+
* }
32+
*
33+
* and assing `iter->parent.backend` to your `git_refdb_backend`.
34+
*/
35+
struct git_reference_iterator {
36+
git_refdb_backend *backend;
37+
};
38+
2339
/** An instance for a custom backend */
2440
struct git_refdb_backend {
2541
unsigned int version;
@@ -66,6 +82,25 @@ struct git_refdb_backend {
6682
void *payload);
6783

6884
/**
85+
* Allocate an iterator object for the backend
86+
*/
87+
int (*iterator)(
88+
git_reference_iterator **iter,
89+
struct git_refdb_backend *backend);
90+
91+
/**
92+
* Return the current value and advance the iterator.
93+
*/
94+
int (*next)(
95+
const char **name,
96+
git_reference_iterator *iter);
97+
98+
/**
99+
* Free the iterator
100+
*/
101+
void (*iterator_free)(
102+
git_reference_iterator *iter);
103+
/*
69104
* Writes the given reference to the refdb. A refdb implementation
70105
* must provide this function.
71106
*/

include/git2/types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ typedef struct git_signature {
165165
/** In-memory representation of a reference. */
166166
typedef struct git_reference git_reference;
167167

168+
/** Iterator for references */
169+
typedef struct git_reference_iterator git_reference_iterator;
170+
171+
168172
/** Basic type of any Git reference. */
169173
typedef enum {
170174
GIT_REF_INVALID = 0, /** Invalid reference */

src/refdb.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,30 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
124124
return error;
125125
}
126126

127+
int git_refdb_iterator(git_reference_iterator **out, git_refdb *db)
128+
{
129+
git_reference_iterator *iter;
130+
131+
/* FIXME: don't segfault when there is no backends */
132+
if (db->backend->iterator(&iter, db->backend) < 0) {
133+
git__free(iter);
134+
return -1;
135+
}
136+
137+
*out = iter;
138+
return 0;
139+
}
140+
141+
int git_refdb_next(const char **out, git_reference_iterator *iter)
142+
{
143+
return iter->backend->next(out, iter);
144+
}
145+
146+
void git_refdb_iterator_free(git_reference_iterator *iter)
147+
{
148+
iter->backend->iterator_free(iter);
149+
}
150+
127151
int git_refdb_foreach(
128152
git_refdb *db,
129153
unsigned int list_flags,

src/refdb.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ int git_refdb_foreach_glob(
3939
git_reference_foreach_cb callback,
4040
void *payload);
4141

42+
int git_refdb_iterator(git_reference_iterator **out, git_refdb *db);
43+
int git_refdb_next(const char **out, git_reference_iterator *iter);
44+
void git_refdb_iterator_free(git_reference_iterator *iter);
45+
4246
int git_refdb_write(git_refdb *refdb, const git_reference *ref);
4347

4448
int git_refdb_delete(git_refdb *refdb, const git_reference *ref);

src/refdb_fs.c

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "reflog.h"
1414
#include "refdb.h"
1515
#include "refdb_fs.h"
16+
#include "iterator.h"
1617

1718
#include <git2/tag.h>
1819
#include <git2/object.h>
@@ -652,6 +653,128 @@ static int refdb_fs_backend__foreach(
652653
return data.callback_error ? GIT_EUSER : result;
653654
}
654655

656+
typedef struct {
657+
git_reference_iterator parent;
658+
unsigned int loose;
659+
/* packed */
660+
git_strmap *h;
661+
khiter_t k;
662+
/* loose */
663+
git_iterator *fsiter;
664+
git_buf buf;
665+
} refdb_fs_iter;
666+
667+
static int refdb_fs_backend__iterator(git_reference_iterator **out, git_refdb_backend *_backend)
668+
{
669+
refdb_fs_iter *iter;
670+
refdb_fs_backend *backend;
671+
672+
assert(_backend);
673+
backend = (refdb_fs_backend *)_backend;
674+
675+
if (packed_load(backend) < 0)
676+
return -1;
677+
678+
iter = git__calloc(1, sizeof(refdb_fs_iter));
679+
GITERR_CHECK_ALLOC(iter);
680+
681+
iter->parent.backend = _backend;
682+
iter->h = backend->refcache.packfile;
683+
iter->k = kh_begin(backend->refcache.packfile);
684+
685+
*out = (git_reference_iterator *)iter;
686+
687+
return 0;
688+
}
689+
690+
static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
691+
{
692+
refdb_fs_iter *iter = (refdb_fs_iter *) _iter;
693+
694+
git_buf_free(&iter->buf);
695+
git_iterator_free(iter->fsiter);
696+
git__free(iter);
697+
}
698+
699+
static int iter_packed(const char **out, refdb_fs_iter *iter)
700+
{
701+
/* Move forward to the next entry */
702+
while (!kh_exist(iter->h, iter->k)) {
703+
iter->k++;
704+
if (iter->k == kh_end(iter->h))
705+
return GIT_ITEROVER;
706+
}
707+
708+
*out = kh_key(iter->h, iter->k);
709+
iter->k++;
710+
711+
return 0;
712+
}
713+
714+
static int iter_loose(const char **out, refdb_fs_iter *iter)
715+
{
716+
const git_index_entry *entry;
717+
int retry;
718+
git_strmap *packfile_refs;
719+
refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend;
720+
721+
packfile_refs = backend->refcache.packfile;
722+
723+
do {
724+
khiter_t pos;
725+
if (git_iterator_current(&entry, iter->fsiter) < 0)
726+
return -1;
727+
728+
git_buf_clear(&iter->buf);
729+
if (!entry)
730+
return GIT_ITEROVER;
731+
732+
if (git_buf_printf(&iter->buf, "refs/%s", entry->path) < 0)
733+
return -1;
734+
735+
git_iterator_advance(NULL, iter->fsiter);
736+
737+
/* Skip this one if we already listed it in packed */
738+
pos = git_strmap_lookup_index(packfile_refs, git_buf_cstr(&iter->buf));
739+
retry = 0;
740+
if (git_strmap_valid_index(packfile_refs, pos) ||
741+
!git_reference_is_valid_name(git_buf_cstr(&iter->buf)))
742+
retry = 1;
743+
744+
*out = git_buf_cstr(&iter->buf);
745+
} while (retry);
746+
747+
return 0;
748+
}
749+
750+
static int iter_loose_setup(refdb_fs_iter *iter)
751+
{
752+
refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend;
753+
754+
git_buf_clear(&iter->buf);
755+
if (git_buf_printf(&iter->buf, "%s/refs", backend->path) < 0)
756+
return -1;
757+
758+
return git_iterator_for_filesystem(&iter->fsiter, git_buf_cstr(&iter->buf), 0, NULL, NULL);
759+
}
760+
761+
static int refdb_fs_backend__next(const char **out, git_reference_iterator *_iter)
762+
{
763+
refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
764+
765+
/* First round of checks to make sure where we are */
766+
if (!iter->loose && iter->k == kh_end(iter->h)) {
767+
if (iter_loose_setup(iter) < 0)
768+
return -1;
769+
iter->loose = 1;
770+
}
771+
772+
if (!iter->loose)
773+
return iter_packed(out, iter);
774+
else
775+
return iter_loose(out, iter);
776+
}
777+
655778
static int loose_write(refdb_fs_backend *backend, const git_reference *ref)
656779
{
657780
git_filebuf file = GIT_FILEBUF_INIT;
@@ -1082,6 +1205,9 @@ int git_refdb_backend_fs(
10821205
backend->parent.exists = &refdb_fs_backend__exists;
10831206
backend->parent.lookup = &refdb_fs_backend__lookup;
10841207
backend->parent.foreach = &refdb_fs_backend__foreach;
1208+
backend->parent.iterator = &refdb_fs_backend__iterator;
1209+
backend->parent.next = &refdb_fs_backend__next;
1210+
backend->parent.iterator_free = &refdb_fs_backend__iterator_free;
10851211
backend->parent.write = &refdb_fs_backend__write;
10861212
backend->parent.delete = &refdb_fs_backend__delete;
10871213
backend->parent.compress = &refdb_fs_backend__compress;

src/refs.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,26 @@ int git_reference_foreach(
568568
return git_refdb_foreach(refdb, list_flags, callback, payload);
569569
}
570570

571+
int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo)
572+
{
573+
git_refdb *refdb;
574+
575+
if (git_repository_refdb__weakptr(&refdb, repo) < 0)
576+
return -1;
577+
578+
return git_refdb_iterator(out, refdb);
579+
}
580+
581+
int git_reference_next(const char **out, git_reference_iterator *iter)
582+
{
583+
return git_refdb_next(out, iter);
584+
}
585+
586+
void git_reference_iterator_free(git_reference_iterator *iter)
587+
{
588+
git_refdb_iterator_free(iter);
589+
}
590+
571591
static int cb__reflist_add(const char *ref, void *data)
572592
{
573593
return git_vector_insert((git_vector *)data, git__strdup(ref));

tests-clar/refs/iterator.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include "clar_libgit2.h"
2+
#include "refs.h"
3+
#include "vector.h"
4+
5+
static git_repository *repo;
6+
7+
void test_refs_iterator__initialize(void)
8+
{
9+
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
10+
}
11+
12+
void test_refs_iterator__cleanup(void)
13+
{
14+
git_repository_free(repo);
15+
}
16+
17+
static const char *refnames[] = {
18+
"refs/heads/br2",
19+
"refs/heads/cannot-fetch",
20+
"refs/heads/chomped",
21+
"refs/heads/haacked",
22+
"refs/heads/master",
23+
"refs/heads/not-good",
24+
"refs/heads/packed",
25+
"refs/heads/packed-test",
26+
"refs/heads/subtrees",
27+
"refs/heads/test",
28+
"refs/heads/track-local",
29+
"refs/heads/trailing",
30+
"refs/notes/fanout",
31+
"refs/remotes/test/master",
32+
"refs/tags/annotated_tag_to_blob",
33+
"refs/tags/e90810b",
34+
"refs/tags/hard_tag",
35+
"refs/tags/point_to_blob",
36+
"refs/tags/taggerless",
37+
"refs/tags/test",
38+
"refs/tags/wrapped_tag",
39+
};
40+
41+
void test_refs_iterator__list(void)
42+
{
43+
git_reference_iterator *iter;
44+
git_vector output;
45+
char *refname;
46+
int error;
47+
size_t i;
48+
49+
cl_git_pass(git_vector_init(&output, 32, git__strcmp_cb));
50+
cl_git_pass(git_reference_iterator_new(&iter, repo));
51+
52+
do {
53+
const char *name;
54+
error = git_reference_next(&name, iter);
55+
cl_assert(error == 0 || error == GIT_ITEROVER);
56+
if (error != GIT_ITEROVER) {
57+
char *dup = git__strdup(name);
58+
cl_assert(dup != NULL);
59+
cl_git_pass(git_vector_insert(&output, dup));
60+
}
61+
} while (!error);
62+
63+
cl_assert_equal_i(output.length, ARRAY_SIZE(refnames));
64+
65+
git_vector_sort(&output);
66+
git_vector_foreach(&output, i, refname) {
67+
cl_assert_equal_s(refname, refnames[i]);
68+
}
69+
70+
git_reference_iterator_free(iter);
71+
72+
git_vector_foreach(&output, i, refname) {
73+
git__free(refname);
74+
}
75+
git_vector_free(&output);
76+
}

0 commit comments

Comments
 (0)