Subject: [PATCH v2 09/10] merge-ort: copy find_first_merges() implementation from merge-recursive.c

From: "Elijah Newren via GitGitGadget" <[email-masked]>
Date: Fri, 01 Jan 2021 02:34:47 +0000

From: Elijah Newren <[email-masked]>

Code is identical for the function body in the two files, the call
signature is just slightly different in merge-ort than merge-recursive
as noted a couple commits ago.

Signed-off-by: Elijah Newren <[email-masked]>
---
 merge-ort.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 56 insertions(+), 1 deletion(-)

diff --git a/merge-ort.c b/merge-ort.c
index bf704bcd34d..203fa987e43 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -25,6 +25,7 @@
 #include "dir.h"
 #include "ll-merge.h"
 #include "object-store.h"
+#include "revision.h"
 #include "strmap.h"
 #include "submodule.h"
 #include "tree.h"
@@ -658,7 +659,61 @@ static int find_first_merges(struct repository *repo,
 			     struct commit *b,
 			     struct object_array *result)
 {
-	die("Not yet implemented.");
+	int i, j;
+	struct object_array merges = OBJECT_ARRAY_INIT;
+	struct commit *commit;
+	int contains_another;
+
+	char merged_revision[GIT_MAX_HEXSZ + 2];
+	const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
+				   "--all", merged_revision, NULL };
+	struct rev_info revs;
+	struct setup_revision_opt rev_opts;
+
+	memset(result, 0, sizeof(struct object_array));
+	memset(&rev_opts, 0, sizeof(rev_opts));
+
+	/* get all revisions that merge commit a */
+	xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
+		  oid_to_hex(&a->object.oid));
+	repo_init_revisions(repo, &revs, NULL);
+	rev_opts.submodule = path;
+	/* FIXME: can't handle linked worktrees in submodules yet */
+	revs.single_worktree = path != NULL;
+	setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
+
+	/* save all revisions from the above list that contain b */
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+	while ((commit = get_revision(&revs)) != NULL) {
+		struct object *o = &(commit->object);
+		if (in_merge_bases(b, commit))
+			add_object_array(o, NULL, &merges);
+	}
+	reset_revision_walk();
+
+	/* Now we've got all merges that contain a and b. Prune all
+	 * merges that contain another found merge and save them in
+	 * result.
+	 */
+	for (i = 0; i < merges.nr; i++) {
+		struct commit *m1 = (struct commit *) merges.objects[i].item;
+
+		contains_another = 0;
+		for (j = 0; j < merges.nr; j++) {
+			struct commit *m2 = (struct commit *) merges.objects[j].item;
+			if (i != j && in_merge_bases(m2, m1)) {
+				contains_another = 1;
+				break;
+			}
+		}
+
+		if (!contains_another)
+			add_object_array(merges.objects[i].item, NULL, result);
+	}
+
+	object_array_clear(&merges);
+	return result->nr;
 }
 
 static int merge_submodule(struct merge_options *opt,
-- 
gitgitgadget