diff options
-rw-r--r-- | include/git2/index.h | 40 | ||||
-rw-r--r-- | include/git2/types.h | 3 | ||||
-rw-r--r-- | src/index.c | 45 | ||||
-rw-r--r-- | src/index.h | 6 | ||||
-rw-r--r-- | tests/index/tests.c | 93 |
5 files changed, 184 insertions, 3 deletions
diff --git a/include/git2/index.h b/include/git2/index.h index 35af2e5bf..e43d6f857 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -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 diff --git a/include/git2/types.h b/include/git2/types.h index 607a62a5a..e77e6288d 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -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; diff --git a/src/index.c b/src/index.c index de91dd8de..ed29f0951 100644 --- a/src/index.c +++ b/src/index.c @@ -1971,6 +1971,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) diff --git a/src/index.h b/src/index.h index aa54215dd..982afed3a 100644 --- a/src/index.h +++ b/src/index.h @@ -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; diff --git a/tests/index/tests.c b/tests/index/tests.c index a208e2c8f..de57bd898 100644 --- a/tests/index/tests.c +++ b/tests/index/tests.c @@ -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); +} |