Implement git_merge_base_octopus
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
diff --git a/include/git2/merge.h b/include/git2/merge.h
index 3563f35..cfec32f 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -191,7 +191,7 @@ GIT_EXTERN(int) git_merge_base(
* @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 0 on success, GIT_ENOTFOUND if not found or error code
+ * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
*/
GIT_EXTERN(int) git_merge_base_many(
git_oid *out,
@@ -200,6 +200,21 @@ GIT_EXTERN(int) git_merge_base_many(
const git_oid input_array[]);
/**
+ * Find a merge base in preparation for an octopus merge
+ *
+ * @param out the OID of a merge base considering all the commits
+ * @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_base_octopus(
+ git_oid *out,
+ git_repository *repo,
+ size_t length,
+ const git_oid input_array[]);
+
+/**
* Creates a `git_merge_head` from the given reference. The resulting
* git_merge_head must be freed with `git_merge_head_free`.
*
diff --git a/src/merge.c b/src/merge.c
index 124e8c3..0b11c0d 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -114,6 +114,31 @@ cleanup:
return error;
}
+int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
+{
+ git_oid result;
+ unsigned int i;
+ int error = -1;
+
+ 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;
+ }
+
+ result = input_array[0];
+ for (i = 1; i < length; i++) {
+ error = git_merge_base(&result, repo, &result, &input_array[i]);
+ if (error < 0)
+ return error;
+ }
+
+ *out = result;
+
+ return 0;
+}
+
int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two)
{
git_revwalk *walk;
diff --git a/tests/revwalk/mergebase.c b/tests/revwalk/mergebase.c
index 2d01647..9766350 100644
--- a/tests/revwalk/mergebase.c
+++ b/tests/revwalk/mergebase.c
@@ -189,7 +189,6 @@ void test_revwalk_mergebase__many_no_common_ancestor_returns_ENOTFOUND(void)
assert_mergebase_many(NULL, 3, "e90810", "41bc8c", "a65fed");
assert_mergebase_many(NULL, 3, "e90810", "a65fed", "41bc8c");
assert_mergebase_many(NULL, 3, "a65fed", "e90810", "41bc8c");
- assert_mergebase_many(NULL, 3, "a65fed", "e90810", "41bc8c");
assert_mergebase_many(NULL, 3, "a65fed", "41bc8c", "e90810");
assert_mergebase_many(NULL, 3, "e90810", "763d71", "a65fed");
@@ -209,6 +208,70 @@ void test_revwalk_mergebase__many_merge_branch(void)
assert_mergebase_many("5b5b025afb0b4c913b4c338a42934a3863bf3644", 5, "5b5b02", "763d71", "a4a7dc", "a65fed", "41bc8c");
}
+static void assert_mergebase_octopus(const char *expected_sha, int count, ...)
+{
+ va_list ap;
+ int i;
+ git_oid *oids;
+ git_oid oid, expected;
+ char *partial_oid;
+ git_object *object;
+
+ oids = git__malloc(count * sizeof(git_oid));
+ cl_assert(oids != NULL);
+
+ memset(oids, 0x0, count * sizeof(git_oid));
+
+ va_start(ap, count);
+
+ for (i = 0; i < count; ++i) {
+ partial_oid = va_arg(ap, char *);
+ cl_git_pass(git_oid_fromstrn(&oid, partial_oid, strlen(partial_oid)));
+
+ cl_git_pass(git_object_lookup_prefix(&object, _repo, &oid, strlen(partial_oid), GIT_OBJ_COMMIT));
+ git_oid_cpy(&oids[i], git_object_id(object));
+ git_object_free(object);
+ }
+
+ va_end(ap);
+
+ if (expected_sha == NULL)
+ cl_assert_equal_i(GIT_ENOTFOUND, git_merge_base_octopus(&oid, _repo, count, oids));
+ else {
+ cl_git_pass(git_merge_base_octopus(&oid, _repo, count, oids));
+ cl_git_pass(git_oid_fromstr(&expected, expected_sha));
+
+ cl_assert(git_oid_cmp(&expected, &oid) == 0);
+ }
+
+ git__free(oids);
+}
+
+void test_revwalk_mergebase__octopus_no_common_ancestor_returns_ENOTFOUND(void)
+{
+ assert_mergebase_octopus(NULL, 3, "41bc8c", "e90810", "a65fed");
+ assert_mergebase_octopus(NULL, 3, "e90810", "41bc8c", "a65fed");
+ assert_mergebase_octopus(NULL, 3, "e90810", "a65fed", "41bc8c");
+ assert_mergebase_octopus(NULL, 3, "a65fed", "e90810", "41bc8c");
+ assert_mergebase_octopus(NULL, 3, "a65fed", "41bc8c", "e90810");
+
+ assert_mergebase_octopus(NULL, 3, "e90810", "763d71", "a65fed");
+
+ assert_mergebase_octopus(NULL, 3, "763d71", "e90810", "a65fed");
+ assert_mergebase_octopus(NULL, 3, "763d71", "a65fed", "e90810");
+
+ assert_mergebase_octopus(NULL, 5, "5b5b02", "763d71", "a4a7dc", "a65fed", "41bc8c");
+}
+
+void test_revwalk_mergebase__octopus_merge_branch(void)
+{
+ assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "a65fed", "763d71", "849607");
+
+ assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "a65fed", "763d71", "849607");
+ assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "a65fed", "849607", "763d71");
+ assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "849607", "a65fed", "763d71");
+}
+
/*
* testrepo.git $ git log --graph --all
* * commit 763d71aadf09a7951596c9746c024e7eece7c7af