From 0e0108f73f83c7cedeaafd480fdcfe3cd6b69d1f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 17 May 2013 15:59:57 -0500 Subject: [PATCH] introduce git_conflict_iterator --- include/git2/index.h | 46 +++++++-- include/git2/types.h | 3 + src/index.c | 174 ++++++++++++++++++++++++++--------- src/index.h | 5 + tests-clar/index/conflicts.c | 51 +++++++++- tests-clar/reset/default.c | 2 +- 6 files changed, 228 insertions(+), 53 deletions(-) diff --git a/include/git2/index.h b/include/git2/index.h index 8a1ccce55..58b0243e0 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -463,9 +463,9 @@ GIT_EXTERN(int) git_index_conflict_add( /** * Get the index entries that represent a conflict of a single file. * - * The values of this entry can be modified (except the paths) - * and the changes will be written back to disk on the next - * write() call. + * The entries are not modifiable and should not be freed. Because the + * `git_index_entry` struct is a publicly defined struct, you should + * be able to make your own permanent copy of the data if necessary. * * @param ancestor_out Pointer to store the ancestor entry * @param our_out Pointer to store the our entry @@ -474,9 +474,9 @@ GIT_EXTERN(int) git_index_conflict_add( * @param path path to search */ GIT_EXTERN(int) git_index_conflict_get( - git_index_entry **ancestor_out, - git_index_entry **our_out, - git_index_entry **their_out, + const git_index_entry **ancestor_out, + const git_index_entry **our_out, + const git_index_entry **their_out, git_index *index, const char *path); @@ -502,6 +502,40 @@ GIT_EXTERN(void) git_index_conflict_cleanup(git_index *index); */ GIT_EXTERN(int) git_index_has_conflicts(const git_index *index); +/** + * Create an iterator for the conflicts in the index. You may not modify the + * index while iterating, the results are undefined. + * + * @return 0 or an error code + */ +GIT_EXTERN(int) git_index_conflict_iterator_new( + git_index_conflict_iterator **iterator_out, + git_index *index); + +/** + * Returns the current conflict (ancestor, ours and theirs entry) and + * advance the iterator internally to the next value. + * + * @param ancestor_out Pointer to store the ancestor side of the conflict + * @param our_out Pointer to store our side of the conflict + * @param their_out Pointer to store their side of the conflict + * @return 0 (no error), GIT_ITEROVER (iteration is done) or an error code + * (negative value) + */ +GIT_EXTERN(int) git_index_conflict_next( + const git_index_entry **ancestor_out, + const git_index_entry **our_out, + const git_index_entry **their_out, + git_index_conflict_iterator *iterator); + +/** + * Frees a `git_index_conflict_iterator`. + * + * @param it pointer to the iterator + */ +GIT_EXTERN(void) git_index_conflict_iterator_free( + git_index_conflict_iterator *iterator); + /**@}*/ /** @} */ diff --git a/include/git2/types.h b/include/git2/types.h index 43751d3b0..a589cdbaf 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -131,6 +131,9 @@ typedef struct git_treebuilder git_treebuilder; /** Memory representation of an index file. */ typedef struct git_index git_index; +/** An interator for conflicts in the index. */ +typedef struct git_index_conflict_iterator git_index_conflict_iterator; + /** Memory representation of a set of config files */ typedef struct git_config git_config; diff --git a/src/index.c b/src/index.c index f7f7133d6..ec45a5c0e 100644 --- a/src/index.c +++ b/src/index.c @@ -739,7 +739,7 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace) static int index_conflict_to_reuc(git_index *index, const char *path) { - git_index_entry *conflict_entries[3]; + const git_index_entry *conflict_entries[3]; int ancestor_mode, our_mode, their_mode; git_oid const *ancestor_oid, *our_oid, *their_oid; int ret; @@ -978,15 +978,63 @@ on_error: return ret; } -int git_index_conflict_get(git_index_entry **ancestor_out, - git_index_entry **our_out, - git_index_entry **their_out, - git_index *index, const char *path) +static int index_conflict__get_byindex( + const git_index_entry **ancestor_out, + const git_index_entry **our_out, + const git_index_entry **their_out, + git_index *index, + size_t n) { - size_t pos, posmax; - int stage; - git_index_entry *conflict_entry; - int error = GIT_ENOTFOUND; + const git_index_entry *conflict_entry; + const char *path = NULL; + size_t count; + int stage, len = 0; + + assert(ancestor_out && our_out && their_out && index); + + *ancestor_out = NULL; + *our_out = NULL; + *their_out = NULL; + + for (count = git_index_entrycount(index); n < count; ++n) { + conflict_entry = git_vector_get(&index->entries, n); + + if (path && index->entries_cmp_path(conflict_entry->path, path) != 0) + break; + + stage = GIT_IDXENTRY_STAGE(conflict_entry); + path = conflict_entry->path; + + switch (stage) { + case 3: + *their_out = conflict_entry; + len++; + break; + case 2: + *our_out = conflict_entry; + len++; + break; + case 1: + *ancestor_out = conflict_entry; + len++; + break; + default: + break; + }; + } + + return len; +} + +int git_index_conflict_get( + const git_index_entry **ancestor_out, + const git_index_entry **our_out, + const git_index_entry **their_out, + git_index *index, + const char *path) +{ + size_t pos; + int len = 0; assert(ancestor_out && our_out && their_out && index && path); @@ -997,33 +1045,13 @@ int git_index_conflict_get(git_index_entry **ancestor_out, if (git_index_find(&pos, index, path) < 0) return GIT_ENOTFOUND; - for (posmax = git_index_entrycount(index); pos < posmax; ++pos) { - conflict_entry = git_vector_get(&index->entries, pos); + if ((len = index_conflict__get_byindex( + ancestor_out, our_out, their_out, index, pos)) < 0) + return len; + else if (len == 0) + return GIT_ENOTFOUND; - if (index->entries_cmp_path(conflict_entry->path, path) != 0) - break; - - stage = GIT_IDXENTRY_STAGE(conflict_entry); - - switch (stage) { - case 3: - *their_out = conflict_entry; - error = 0; - break; - case 2: - *our_out = conflict_entry; - error = 0; - break; - case 1: - *ancestor_out = conflict_entry; - error = 0; - break; - default: - break; - }; - } - - return error; + return 0; } int git_index_conflict_remove(git_index *index, const char *path) @@ -1093,6 +1121,68 @@ int git_index_has_conflicts(const git_index *index) return 0; } +int git_index_conflict_iterator_new( + git_index_conflict_iterator **iterator_out, + git_index *index) +{ + git_index_conflict_iterator *it = NULL; + + assert(iterator_out && index); + + it = git__calloc(1, sizeof(git_index_conflict_iterator)); + GITERR_CHECK_ALLOC(it); + + it->index = index; + + *iterator_out = it; + return 0; +} + +int git_index_conflict_next( + const git_index_entry **ancestor_out, + const git_index_entry **our_out, + const git_index_entry **their_out, + git_index_conflict_iterator *iterator) +{ + const git_index_entry *entry; + int len; + + assert(ancestor_out && our_out && their_out && iterator); + + *ancestor_out = NULL; + *our_out = NULL; + *their_out = NULL; + + while (iterator->cur < iterator->index->entries.length) { + entry = git_index_get_byindex(iterator->index, iterator->cur); + + if (git_index_entry_stage(entry) > 0) { + if ((len = index_conflict__get_byindex( + ancestor_out, + our_out, + their_out, + iterator->index, + iterator->cur)) < 0) + return len; + + iterator->cur += len; + return 0; + } + + iterator->cur++; + } + + return GIT_ITEROVER; +} + +void git_index_conflict_iterator_free(git_index_conflict_iterator *iterator) +{ + if (iterator == NULL) + return; + + git__free(iterator); +} + unsigned int git_index_name_entrycount(git_index *index) { assert(index); @@ -1283,9 +1373,8 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) size_t len; int i; - /* If called multiple times, the vector might already be initialized */ - if (index->reuc._alloc_size == 0 && - git_vector_init(&index->reuc, 16, reuc_cmp) < 0) + /* This gets called multiple times, the vector might already be initialized */ + if (index->reuc._alloc_size == 0 && git_vector_init(&index->reuc, 16, reuc_cmp) < 0) return -1; while (size) { @@ -1295,9 +1384,12 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) if (size <= len) return index_error_invalid("reading reuc entries"); - lost = git__calloc(1, sizeof(git_index_reuc_entry)); + lost = git__malloc(sizeof(git_index_reuc_entry)); GITERR_CHECK_ALLOC(lost); + if (git_vector_insert(&index->reuc, lost) < 0) + return -1; + /* read NUL-terminated pathname for entry */ lost->path = git__strdup(buffer); GITERR_CHECK_ALLOC(lost->path); @@ -1335,10 +1427,6 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) size -= 20; buffer += 20; } - - /* entry was read successfully - insert into reuc vector */ - if (git_vector_insert(&index->reuc, lost) < 0) - return -1; } /* entries are guaranteed to be sorted on-disk */ diff --git a/src/index.h b/src/index.h index 2ad401741..a59107a7b 100644 --- a/src/index.h +++ b/src/index.h @@ -42,6 +42,11 @@ struct git_index { git_vector_cmp reuc_search; }; +struct git_index_conflict_iterator { + git_index *index; + size_t cur; +}; + extern void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st); extern size_t git_index__prefix_position(git_index *index, const char *path); diff --git a/tests-clar/index/conflicts.c b/tests-clar/index/conflicts.c index 7eee496de..6311b3a75 100644 --- a/tests-clar/index/conflicts.c +++ b/tests-clar/index/conflicts.c @@ -65,7 +65,7 @@ void test_index_conflicts__add(void) void test_index_conflicts__add_fixes_incorrect_stage(void) { git_index_entry ancestor_entry, our_entry, their_entry; - git_index_entry *conflict_entry[3]; + const git_index_entry *conflict_entry[3]; cl_assert(git_index_entrycount(repo_index) == 8); @@ -98,7 +98,7 @@ void test_index_conflicts__add_fixes_incorrect_stage(void) void test_index_conflicts__get(void) { - git_index_entry *conflict_entry[3]; + const git_index_entry *conflict_entry[3]; git_oid oid; cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], @@ -130,6 +130,51 @@ void test_index_conflicts__get(void) cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0); } +void test_index_conflicts__iterate(void) +{ + git_index_conflict_iterator *iterator; + const git_index_entry *conflict_entry[3]; + git_oid oid; + + cl_git_pass(git_index_conflict_iterator_new(&iterator, repo_index)); + + cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator)); + + git_oid_fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID); + cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0); + + git_oid_fromstr(&oid, CONFLICTS_ONE_OUR_OID); + cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0); + + git_oid_fromstr(&oid, CONFLICTS_ONE_THEIR_OID); + cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0); + + cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator)); + + git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID); + cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0); + + git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID); + cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0); + + git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID); + cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0); + + cl_assert(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator) == GIT_ITEROVER); + + cl_assert(conflict_entry[0] == NULL); + cl_assert(conflict_entry[2] == NULL); + cl_assert(conflict_entry[2] == NULL); + + git_index_conflict_iterator_free(iterator); +} + void test_index_conflicts__remove(void) { const git_index_entry *entry; @@ -218,7 +263,7 @@ void test_index_conflicts__remove_all_conflicts(void) void test_index_conflicts__partial(void) { git_index_entry ancestor_entry, our_entry, their_entry; - git_index_entry *conflict_entry[3]; + const git_index_entry *conflict_entry[3]; cl_assert(git_index_entrycount(repo_index) == 8); diff --git a/tests-clar/reset/default.c b/tests-clar/reset/default.c index 506d971ff..e29e63550 100644 --- a/tests-clar/reset/default.c +++ b/tests-clar/reset/default.c @@ -133,7 +133,7 @@ void test_reset_default__resetting_filepaths_replaces_their_corresponding_index_ */ void test_reset_default__resetting_filepaths_clears_previous_conflicts(void) { - git_index_entry *conflict_entry[3]; + const git_index_entry *conflict_entry[3]; git_strarray after; char *paths[] = { "conflicts-one.txt" };