Skip to content

Commit 0e3e832

Browse files
authored
Merge pull request libgit2#4884 from libgit2/ethomson/index_iterator
index: introduce git_index_iterator
2 parents 94fce58 + c358bbc commit 0e3e832

File tree

5 files changed

+184
-3
lines changed

5 files changed

+184
-3
lines changed

include/git2/index.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,46 @@ GIT_EXTERN(int) git_index_entry_is_conflict(const git_index_entry *entry);
492492

493493
/**@}*/
494494

495+
/** @name Index Entry Iteration Functions
496+
*
497+
* These functions provide an iterator for index entries.
498+
*/
499+
/**@{*/
500+
501+
/**
502+
* Create an iterator that will return every entry contained in the
503+
* index at the time of creation. Entries are returned in order,
504+
* sorted by path. This iterator is backed by a snapshot that allows
505+
* callers to modify the index while iterating without affecting the
506+
* iterator.
507+
*
508+
* @param iterator_out The newly created iterator
509+
* @param index The index to iterate
510+
*/
511+
GIT_EXTERN(int) git_index_iterator_new(
512+
git_index_iterator **iterator_out,
513+
git_index *index);
514+
515+
/**
516+
* Return the next index entry in-order from the iterator.
517+
*
518+
* @param out Pointer to store the index entry in
519+
* @param iterator The iterator
520+
* @return 0, GIT_ITEROVER on iteration completion or an error code
521+
*/
522+
GIT_EXTERN(int) git_index_iterator_next(
523+
const git_index_entry **out,
524+
git_index_iterator *iterator);
525+
526+
/**
527+
* Free the index iterator
528+
*
529+
* @param iterator The iterator to free
530+
*/
531+
GIT_EXTERN(void) git_index_iterator_free(git_index_iterator *iterator);
532+
533+
/**@}*/
534+
495535
/** @name Workdir Index Entry Functions
496536
*
497537
* These functions work on index entries specifically in the working

include/git2/types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ typedef struct git_treebuilder git_treebuilder;
137137
/** Memory representation of an index file. */
138138
typedef struct git_index git_index;
139139

140+
/** An iterator for entries in the index. */
141+
typedef struct git_index_iterator git_index_iterator;
142+
140143
/** An iterator for conflicts in the index. */
141144
typedef struct git_index_conflict_iterator git_index_conflict_iterator;
142145

src/index.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1971,6 +1971,51 @@ int git_index_has_conflicts(const git_index *index)
19711971
return 0;
19721972
}
19731973

1974+
int git_index_iterator_new(
1975+
git_index_iterator **iterator_out,
1976+
git_index *index)
1977+
{
1978+
git_index_iterator *it;
1979+
int error;
1980+
1981+
assert(iterator_out && index);
1982+
1983+
it = git__calloc(1, sizeof(git_index_iterator));
1984+
GITERR_CHECK_ALLOC(it);
1985+
1986+
if ((error = git_index_snapshot_new(&it->snap, index)) < 0) {
1987+
git__free(it);
1988+
return error;
1989+
}
1990+
1991+
it->index = index;
1992+
1993+
*iterator_out = it;
1994+
return 0;
1995+
}
1996+
1997+
int git_index_iterator_next(
1998+
const git_index_entry **out,
1999+
git_index_iterator *it)
2000+
{
2001+
assert(out && it);
2002+
2003+
if (it->cur >= git_vector_length(&it->snap))
2004+
return GIT_ITEROVER;
2005+
2006+
*out = (git_index_entry *)git_vector_get(&it->snap, it->cur++);
2007+
return 0;
2008+
}
2009+
2010+
void git_index_iterator_free(git_index_iterator *it)
2011+
{
2012+
if (it == NULL)
2013+
return;
2014+
2015+
git_index_snapshot_release(&it->snap, it->index);
2016+
git__free(it);
2017+
}
2018+
19742019
int git_index_conflict_iterator_new(
19752020
git_index_conflict_iterator **iterator_out,
19762021
git_index *index)

src/index.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ struct git_index {
5555
unsigned int version;
5656
};
5757

58+
struct git_index_iterator {
59+
git_index *index;
60+
git_vector snap;
61+
size_t cur;
62+
};
63+
5864
struct git_index_conflict_iterator {
5965
git_index *index;
6066
size_t cur;

tests/index/tests.c

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ struct test_entry {
1919

2020
static struct test_entry test_entries[] = {
2121
{4, "Makefile", 5064, 0x4C3F7F33},
22-
{62, "tests/Makefile", 2631, 0x4C3F7F33},
23-
{36, "src/index.c", 10014, 0x4C43368D},
2422
{6, "git.git-authors", 2709, 0x4C3F7F33},
25-
{48, "src/revobject.h", 1448, 0x4C3F7FE2}
23+
{36, "src/index.c", 10014, 0x4C43368D},
24+
{48, "src/revobject.h", 1448, 0x4C3F7FE2},
25+
{62, "tests/Makefile", 2631, 0x4C3F7F33}
2626
};
2727

2828
/* Helpers */
@@ -991,3 +991,90 @@ void test_index_tests__can_lock_index(void)
991991
git_index_free(index);
992992
cl_git_sandbox_cleanup();
993993
}
994+
995+
void test_index_tests__can_iterate(void)
996+
{
997+
git_index *index;
998+
git_index_iterator *iterator;
999+
const git_index_entry *entry;
1000+
size_t i, iterator_idx = 0, found = 0;
1001+
int ret;
1002+
1003+
cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
1004+
cl_git_pass(git_index_iterator_new(&iterator, index));
1005+
1006+
cl_assert(git_vector_is_sorted(&iterator->snap));
1007+
1008+
for (i = 0; i < ARRAY_SIZE(test_entries); i++) {
1009+
/* Advance iterator to next test entry index */
1010+
do {
1011+
ret = git_index_iterator_next(&entry, iterator);
1012+
1013+
if (ret == GIT_ITEROVER)
1014+
cl_fail("iterator did not contain all test entries");
1015+
1016+
cl_git_pass(ret);
1017+
} while (iterator_idx++ < test_entries[i].index);
1018+
1019+
cl_assert_equal_s(entry->path, test_entries[i].path);
1020+
cl_assert_equal_i(entry->mtime.seconds, test_entries[i].mtime);
1021+
cl_assert_equal_i(entry->file_size, test_entries[i].file_size);
1022+
found++;
1023+
}
1024+
1025+
while ((ret = git_index_iterator_next(&entry, iterator)) == 0)
1026+
;
1027+
1028+
if (ret != GIT_ITEROVER)
1029+
cl_git_fail(ret);
1030+
1031+
cl_assert_equal_i(found, ARRAY_SIZE(test_entries));
1032+
1033+
git_index_iterator_free(iterator);
1034+
git_index_free(index);
1035+
}
1036+
1037+
void test_index_tests__can_modify_while_iterating(void)
1038+
{
1039+
git_index *index;
1040+
git_index_iterator *iterator;
1041+
const git_index_entry *entry;
1042+
git_index_entry new_entry = {{0}};
1043+
size_t expected = 0, seen = 0;
1044+
int ret;
1045+
1046+
cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
1047+
cl_git_pass(git_index_iterator_new(&iterator, index));
1048+
1049+
expected = git_index_entrycount(index);
1050+
cl_assert(git_vector_is_sorted(&iterator->snap));
1051+
1052+
/*
1053+
* After we've counted the entries, add a new one and change another;
1054+
* ensure that our iterator is backed by a snapshot and thus returns
1055+
* the number of entries from when the iterator was created.
1056+
*/
1057+
cl_git_pass(git_oid_fromstr(&new_entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318"));
1058+
new_entry.path = "newfile";
1059+
new_entry.mode = GIT_FILEMODE_BLOB;
1060+
cl_git_pass(git_index_add(index, &new_entry));
1061+
1062+
cl_git_pass(git_oid_fromstr(&new_entry.id, "4141414141414141414141414141414141414141"));
1063+
new_entry.path = "Makefile";
1064+
new_entry.mode = GIT_FILEMODE_BLOB;
1065+
cl_git_pass(git_index_add(index, &new_entry));
1066+
1067+
while (true) {
1068+
ret = git_index_iterator_next(&entry, iterator);
1069+
1070+
if (ret == GIT_ITEROVER)
1071+
break;
1072+
1073+
seen++;
1074+
}
1075+
1076+
cl_assert_equal_i(expected, seen);
1077+
1078+
git_index_iterator_free(iterator);
1079+
git_index_free(index);
1080+
}

0 commit comments

Comments
 (0)