From eca07bcd83ae7aa18230779be1bc73d150da8af2 Mon Sep 17 00:00:00 2001 From: Arthur Schreiber Date: Thu, 9 Oct 2014 13:58:23 +0200 Subject: [PATCH 1/2] Add git_merge_bases_many. --- CHANGELOG.md | 3 ++ include/git2/merge.h | 15 +++++++++ src/merge.c | 67 +++++++++++++++++++++++++++++++++++++++ tests/revwalk/mergebase.c | 24 ++++++++++++-- 4 files changed, 107 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b23e07d93..6ea198407 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,3 +58,6 @@ v0.21 + 1 * Introduce git_merge_bases() and the git_oidarray type to expose all merge bases between two commits. + +* Introduce git_merge_bases_many() to expose all merge bases between + multiple commits. diff --git a/include/git2/merge.h b/include/git2/merge.h index bd5ebc1bd..ed1b9a30f 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -351,6 +351,21 @@ GIT_EXTERN(int) git_merge_base_many( size_t length, const git_oid input_array[]); +/** + * Find all merge bases given a list of commits + * + * @param out array in which to store the resulting ids + * @param repo the repository where the commits exist + * @param length The number of commits in the provided `input_array` + * @param input_array oids of the commits + * @return Zero on success; GIT_ENOTFOUND or -1 on failure. + */ +GIT_EXTERN(int) git_merge_bases_many( + git_oidarray *out, + git_repository *repo, + size_t length, + const git_oid input_array[]); + /** * Find a merge base in preparation for an octopus merge * diff --git a/src/merge.c b/src/merge.c index 1e72520a4..c7ca97c12 100644 --- a/src/merge.c +++ b/src/merge.c @@ -116,6 +116,73 @@ cleanup: return error; } +int git_merge_bases_many(git_oidarray *out, git_repository *repo, size_t length, const git_oid input_array[]) +{ + git_revwalk *walk; + git_vector list; + git_commit_list *current, *result = NULL; + int error = -1; + unsigned int i; + git_commit_list_node *commit; + git_array_oid_t array; + + assert(out && repo && input_array); + + if (length < 2) { + giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length); + return -1; + } + + if (git_vector_init(&list, length - 1, NULL) < 0) + return -1; + + if (git_revwalk_new(&walk, repo) < 0) + goto cleanup; + + for (i = 1; i < length; i++) { + commit = git_revwalk__commit_lookup(walk, &input_array[i]); + if (commit == NULL) + goto cleanup; + + git_vector_insert(&list, commit); + } + + commit = git_revwalk__commit_lookup(walk, &input_array[0]); + if (commit == NULL) + goto cleanup; + + if (git_merge__bases_many(&result, walk, commit, &list) < 0) + goto cleanup; + + if (!result) { + giterr_set(GITERR_MERGE, "No merge base found"); + error = GIT_ENOTFOUND; + goto cleanup; + } + + git_array_init(array); + + current = result; + while (current) { + git_oid *id = git_array_alloc(array); + if (id == NULL) + goto cleanup; + + git_oid_cpy(id, ¤t->item->oid); + current = current->next; + } + + git_oidarray__from_array(out, &array); + + error = 0; + +cleanup: + git_commit_list_free(&result); + git_revwalk_free(walk); + git_vector_free(&list); + return error; +} + int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]) { git_oid result; diff --git a/tests/revwalk/mergebase.c b/tests/revwalk/mergebase.c index 677e1a1b6..fa974eb9a 100644 --- a/tests/revwalk/mergebase.c +++ b/tests/revwalk/mergebase.c @@ -127,7 +127,7 @@ void test_revwalk_mergebase__prefer_youngest_merge_base(void) { git_oid result, one, two, expected; - cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f ")); + cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f")); cl_git_pass(git_oid_fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd")); @@ -140,7 +140,7 @@ void test_revwalk_mergebase__multiple_merge_bases(void) git_oid one, two, expected1, expected2; git_oidarray result = {NULL, 0}; - cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f ")); + cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f")); cl_git_pass(git_oid_fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); cl_git_pass(git_oid_fromstr(&expected1, "c47800c7266a2be04c571c04d5a6614691ea99bd")); cl_git_pass(git_oid_fromstr(&expected2, "9fd738e8f7967c078dceed8190330fc8648ee56a")); @@ -153,6 +153,26 @@ void test_revwalk_mergebase__multiple_merge_bases(void) git_oidarray_free(&result); } +void test_revwalk_mergebase__multiple_merge_bases_many_commits(void) +{ + git_oid expected1, expected2; + git_oidarray result = {NULL, 0}; + + git_oid *input = git__malloc(sizeof(git_oid) * 2); + + cl_git_pass(git_oid_fromstr(&input[0], "a4a7dce85cf63874e984719f4fdd239f5145052f")); + cl_git_pass(git_oid_fromstr(&input[1], "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); + cl_git_pass(git_oid_fromstr(&expected1, "c47800c7266a2be04c571c04d5a6614691ea99bd")); + cl_git_pass(git_oid_fromstr(&expected2, "9fd738e8f7967c078dceed8190330fc8648ee56a")); + + cl_git_pass(git_merge_bases_many(&result, _repo, 2, input)); + cl_assert_equal_i(2, result.count); + cl_assert_equal_oid(&expected1, &result.ids[0]); + cl_assert_equal_oid(&expected2, &result.ids[1]); + + git_oidarray_free(&result); +} + void test_revwalk_mergebase__no_off_by_one_missing(void) { git_oid result, one, two; From 917f85a1a4616a93fcbba30c82caff9c1c011b83 Mon Sep 17 00:00:00 2001 From: Arthur Schreiber Date: Thu, 9 Oct 2014 14:16:10 +0200 Subject: [PATCH 2/2] Extract shared functionality. --- src/merge.c | 104 +++++++++++++++++++++++----------------------------- 1 file changed, 45 insertions(+), 59 deletions(-) diff --git a/src/merge.c b/src/merge.c index c7ca97c12..8252f6767 100644 --- a/src/merge.c +++ b/src/merge.c @@ -62,16 +62,14 @@ struct merge_diff_df_data { /* Merge base computation */ -int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]) +int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, size_t length, const git_oid input_array[]) { - git_revwalk *walk; + git_revwalk *walk = NULL; git_vector list; git_commit_list *result = NULL; + git_commit_list_node *commit; int error = -1; unsigned int i; - git_commit_list_node *commit; - - assert(out && repo && input_array); if (length < 2) { giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length); @@ -82,104 +80,92 @@ int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const return -1; if (git_revwalk_new(&walk, repo) < 0) - goto cleanup; + goto on_error; for (i = 1; i < length; i++) { commit = git_revwalk__commit_lookup(walk, &input_array[i]); if (commit == NULL) - goto cleanup; + goto on_error; git_vector_insert(&list, commit); } commit = git_revwalk__commit_lookup(walk, &input_array[0]); if (commit == NULL) - goto cleanup; + goto on_error; if (git_merge__bases_many(&result, walk, commit, &list) < 0) - goto cleanup; + goto on_error; if (!result) { giterr_set(GITERR_MERGE, "No merge base found"); error = GIT_ENOTFOUND; - goto cleanup; + goto on_error; } + *out = result; + *walk_out = walk; + + git_vector_free(&list); + return 0; + +on_error: + git_vector_free(&list); + git_revwalk_free(walk); + return error; +} + +int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]) +{ + git_revwalk *walk; + git_commit_list *result = NULL; + int error = 0; + + assert(out && repo && input_array); + + if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0) + return error; + git_oid_cpy(out, &result->item->oid); - error = 0; - -cleanup: git_commit_list_free(&result); git_revwalk_free(walk); - git_vector_free(&list); - return error; + + return 0; } int git_merge_bases_many(git_oidarray *out, git_repository *repo, size_t length, const git_oid input_array[]) { git_revwalk *walk; - git_vector list; - git_commit_list *current, *result = NULL; - int error = -1; - unsigned int i; - git_commit_list_node *commit; + git_commit_list *list, *result = NULL; + int error = 0; git_array_oid_t array; assert(out && repo && input_array); - if (length < 2) { - giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length); - return -1; - } - - if (git_vector_init(&list, length - 1, NULL) < 0) - return -1; - - if (git_revwalk_new(&walk, repo) < 0) - goto cleanup; - - for (i = 1; i < length; i++) { - commit = git_revwalk__commit_lookup(walk, &input_array[i]); - if (commit == NULL) - goto cleanup; - - git_vector_insert(&list, commit); - } - - commit = git_revwalk__commit_lookup(walk, &input_array[0]); - if (commit == NULL) - goto cleanup; - - if (git_merge__bases_many(&result, walk, commit, &list) < 0) - goto cleanup; - - if (!result) { - giterr_set(GITERR_MERGE, "No merge base found"); - error = GIT_ENOTFOUND; - goto cleanup; - } + if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0) + return error; git_array_init(array); - current = result; - while (current) { + list = result; + while (list) { git_oid *id = git_array_alloc(array); - if (id == NULL) + if (id == NULL) { + error = -1; goto cleanup; + } - git_oid_cpy(id, ¤t->item->oid); - current = current->next; + git_oid_cpy(id, &list->item->oid); + list = list->next; } git_oidarray__from_array(out, &array); - error = 0; - cleanup: git_commit_list_free(&result); git_revwalk_free(walk); - git_vector_free(&list); + return error; }