Commit c358bbc5 by Edward Thomson

index: introduce git_index_iterator

Provide a public git_index_iterator API that is backed by an index
snapshot.  This allows consumers to provide a stable iteration even
while manipulating the index during iteration.
parent 11fbead8
......@@ -492,6 +492,46 @@ GIT_EXTERN(int) git_index_entry_is_conflict(const git_index_entry *entry);
/**@}*/
/** @name Index Entry Iteration Functions
*
* These functions provide an iterator for index entries.
*/
/**@{*/
/**
* Create an iterator that will return every entry contained in the
* index at the time of creation. Entries are returned in order,
* sorted by path. This iterator is backed by a snapshot that allows
* callers to modify the index while iterating without affecting the
* iterator.
*
* @param iterator_out The newly created iterator
* @param index The index to iterate
*/
GIT_EXTERN(int) git_index_iterator_new(
git_index_iterator **iterator_out,
git_index *index);
/**
* Return the next index entry in-order from the iterator.
*
* @param out Pointer to store the index entry in
* @param iterator The iterator
* @return 0, GIT_ITEROVER on iteration completion or an error code
*/
GIT_EXTERN(int) git_index_iterator_next(
const git_index_entry **out,
git_index_iterator *iterator);
/**
* Free the index iterator
*
* @param iterator The iterator to free
*/
GIT_EXTERN(void) git_index_iterator_free(git_index_iterator *iterator);
/**@}*/
/** @name Workdir Index Entry Functions
*
* These functions work on index entries specifically in the working
......
......@@ -137,6 +137,9 @@ typedef struct git_treebuilder git_treebuilder;
/** Memory representation of an index file. */
typedef struct git_index git_index;
/** An iterator for entries in the index. */
typedef struct git_index_iterator git_index_iterator;
/** An iterator for conflicts in the index. */
typedef struct git_index_conflict_iterator git_index_conflict_iterator;
......
......@@ -1976,6 +1976,51 @@ int git_index_has_conflicts(const git_index *index)
return 0;
}
int git_index_iterator_new(
git_index_iterator **iterator_out,
git_index *index)
{
git_index_iterator *it;
int error;
assert(iterator_out && index);
it = git__calloc(1, sizeof(git_index_iterator));
GITERR_CHECK_ALLOC(it);
if ((error = git_index_snapshot_new(&it->snap, index)) < 0) {
git__free(it);
return error;
}
it->index = index;
*iterator_out = it;
return 0;
}
int git_index_iterator_next(
const git_index_entry **out,
git_index_iterator *it)
{
assert(out && it);
if (it->cur >= git_vector_length(&it->snap))
return GIT_ITEROVER;
*out = (git_index_entry *)git_vector_get(&it->snap, it->cur++);
return 0;
}
void git_index_iterator_free(git_index_iterator *it)
{
if (it == NULL)
return;
git_index_snapshot_release(&it->snap, it->index);
git__free(it);
}
int git_index_conflict_iterator_new(
git_index_conflict_iterator **iterator_out,
git_index *index)
......
......@@ -55,6 +55,12 @@ struct git_index {
unsigned int version;
};
struct git_index_iterator {
git_index *index;
git_vector snap;
size_t cur;
};
struct git_index_conflict_iterator {
git_index *index;
size_t cur;
......
......@@ -19,10 +19,10 @@ struct test_entry {
static struct test_entry test_entries[] = {
{4, "Makefile", 5064, 0x4C3F7F33},
{62, "tests/Makefile", 2631, 0x4C3F7F33},
{36, "src/index.c", 10014, 0x4C43368D},
{6, "git.git-authors", 2709, 0x4C3F7F33},
{48, "src/revobject.h", 1448, 0x4C3F7FE2}
{36, "src/index.c", 10014, 0x4C43368D},
{48, "src/revobject.h", 1448, 0x4C3F7FE2},
{62, "tests/Makefile", 2631, 0x4C3F7F33}
};
/* Helpers */
......@@ -991,3 +991,90 @@ void test_index_tests__can_lock_index(void)
git_index_free(index);
cl_git_sandbox_cleanup();
}
void test_index_tests__can_iterate(void)
{
git_index *index;
git_index_iterator *iterator;
const git_index_entry *entry;
size_t i, iterator_idx = 0, found = 0;
int ret;
cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
cl_git_pass(git_index_iterator_new(&iterator, index));
cl_assert(git_vector_is_sorted(&iterator->snap));
for (i = 0; i < ARRAY_SIZE(test_entries); i++) {
/* Advance iterator to next test entry index */
do {
ret = git_index_iterator_next(&entry, iterator);
if (ret == GIT_ITEROVER)
cl_fail("iterator did not contain all test entries");
cl_git_pass(ret);
} while (iterator_idx++ < test_entries[i].index);
cl_assert_equal_s(entry->path, test_entries[i].path);
cl_assert_equal_i(entry->mtime.seconds, test_entries[i].mtime);
cl_assert_equal_i(entry->file_size, test_entries[i].file_size);
found++;
}
while ((ret = git_index_iterator_next(&entry, iterator)) == 0)
;
if (ret != GIT_ITEROVER)
cl_git_fail(ret);
cl_assert_equal_i(found, ARRAY_SIZE(test_entries));
git_index_iterator_free(iterator);
git_index_free(index);
}
void test_index_tests__can_modify_while_iterating(void)
{
git_index *index;
git_index_iterator *iterator;
const git_index_entry *entry;
git_index_entry new_entry = {{0}};
size_t expected = 0, seen = 0;
int ret;
cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
cl_git_pass(git_index_iterator_new(&iterator, index));
expected = git_index_entrycount(index);
cl_assert(git_vector_is_sorted(&iterator->snap));
/*
* After we've counted the entries, add a new one and change another;
* ensure that our iterator is backed by a snapshot and thus returns
* the number of entries from when the iterator was created.
*/
cl_git_pass(git_oid_fromstr(&new_entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318"));
new_entry.path = "newfile";
new_entry.mode = GIT_FILEMODE_BLOB;
cl_git_pass(git_index_add(index, &new_entry));
cl_git_pass(git_oid_fromstr(&new_entry.id, "4141414141414141414141414141414141414141"));
new_entry.path = "Makefile";
new_entry.mode = GIT_FILEMODE_BLOB;
cl_git_pass(git_index_add(index, &new_entry));
while (true) {
ret = git_index_iterator_next(&entry, iterator);
if (ret == GIT_ITEROVER)
break;
seen++;
}
cl_assert_equal_i(expected, seen);
git_index_iterator_free(iterator);
git_index_free(index);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment