Subject: Re: [PATCH 1/2] merge-base: fix duplicates and not best ancestors in output

From: Junio C Hamano <[email-masked]>
Date: Mon, 30 Dec 2013 11:01:01 -0800

Василий Макаров <[email-masked]> writes:

> Hi there!
> First of all: I'm new to mailing-lists, sorry if I'm doing it wrong.
>
> I've found a bug in git merge-base, causing it to show not best common
> ancestors and duplicates under some circumstances (example is given in
> attached test case).

Attached???

> Problem cause is algorithm used in get_octopus_merge_bases(), it
> iteratively concatenates merge bases, and don't care if there are
> duplicates or decsendants/ancestors in result.
> What I suggest as a solution is to simply reduce bases list after
> get_octopus_merge_bases().

I do not offhand remember if it was deliberate that we do not dedup
the result from the underlying get_octopus_merge_bases() (the most
likely reason for not deduping is because the caller is expected to
do that if it wants to).

Whether it is an improvement to force deduping here or it is an
regression to do so, I think we should split that helper function
handle_octopus().  It does two totally unrelated things (one is only
to list independent heads without showing merge bases, the other is
to show one or more merge bases across all the heads given).
Perhaps if we split the "independent" codepath introduced by
a1e0ad78 (merge-base --independent to print reduced parent list in a
merge, 2010-08-17) into its own helper function, like this, it would
make it clear what is going on.

Thanks.

 builtin/merge-base.c | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index e88eb93..a00e8f5 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -44,19 +44,36 @@ static struct commit *get_commit_reference(const char *arg)
 	return r;
 }
 
-static int handle_octopus(int count, const char **args, int reduce, int show_all)
+static int handle_independent(int count, const char **args)
 {
 	struct commit_list *revs = NULL;
 	struct commit_list *result;
 	int i;
 
-	if (reduce)
-		show_all = 1;
+	for (i = count - 1; i >= 0; i--)
+		commit_list_insert(get_commit_reference(args[i]), &revs);
+
+	result = reduce_heads(revs);
+	if (!result)
+		return 1;
+
+	while (result) {
+		printf("%s\n", sha1_to_hex(result->item->object.sha1));
+		result = result->next;
+	}
+	return 0;
+}
+
+static int handle_octopus(int count, const char **args, int show_all)
+{
+	struct commit_list *revs = NULL;
+	struct commit_list *result;
+	int i;
 
 	for (i = count - 1; i >= 0; i--)
 		commit_list_insert(get_commit_reference(args[i]), &revs);
 
-	result = reduce ? reduce_heads(revs) : get_octopus_merge_bases(revs);
+	result = get_octopus_merge_bases(revs);
 
 	if (!result)
 		return 1;
@@ -114,8 +131,10 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
 	if (reduce && (show_all || octopus))
 		die("--independent cannot be used with other options");
 
-	if (octopus || reduce)
-		return handle_octopus(argc, argv, reduce, show_all);
+	if (octopus)
+		return handle_octopus(argc, argv, show_all);
+	else if (reduce)
+		return handle_independent(argc, argv);
 
 	rev = xmalloc(argc * sizeof(*rev));
 	while (argc-- > 0)