From: Linus Torvalds <torvalds@osdl.org>
Date: Sat, 30 Jul 2005 22:10:20 +0000 (-0700)
Subject: Fix merge-base from getting confused.
X-Git-Tag: v0.99.3~19
X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=4f7eb2e5a351e0d1f19fd4eab7e92834cc4528c2;p=git.git

Fix merge-base from getting confused.

On Sat, 30 Jul 2005, Linus Torvalds wrote:
>
> Yup, it's git-merge-base, and it is confused by the same thing that
> confused git-rev-list.

Hmm.. Here's a tentative fix. I'm not really happy with it, and maybe
somebody else can come up with a better one. I think this one ends up
being quite a bit more expensive than the old one (it will look up _all_
common parents that have a child that isn't common, and then select the
newest one of the bunch), but I haven't really thought it through very
much.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
---

diff --git a/merge-base.c b/merge-base.c
index 12ebb95fb..591956666 100644
--- a/merge-base.c
+++ b/merge-base.c
@@ -2,54 +2,50 @@
 #include "cache.h"
 #include "commit.h"
 
-static struct commit *process_list(struct commit_list **list_p, int this_mark,
-				   int other_mark)
-{
-	struct commit *item = (*list_p)->item;
-
-	if (item->object.flags & other_mark) {
-		return item;
-	} else {
-		pop_most_recent_commit(list_p, this_mark);
-	}
-	return NULL;
-}
-
 static struct commit *common_ancestor(struct commit *rev1, struct commit *rev2)
 {
-	struct commit_list *rev1list = NULL;
-	struct commit_list *rev2list = NULL;
+	struct commit_list *list = NULL;
+	struct commit_list *result = NULL;
 
-	commit_list_insert(rev1, &rev1list);
-	rev1->object.flags |= 0x1;
-	commit_list_insert(rev2, &rev2list);
-	rev2->object.flags |= 0x2;
+	if (rev1 == rev2)
+		return rev1;
 
 	parse_commit(rev1);
 	parse_commit(rev2);
 
-	while (rev1list || rev2list) {
-		struct commit *ret;
-		if (!rev1list) {
-			// process 2
-			ret = process_list(&rev2list, 0x2, 0x1);
-		} else if (!rev2list) {
-			// process 1
-			ret = process_list(&rev1list, 0x1, 0x2);
-		} else if (rev1list->item->date < rev2list->item->date) {
-			// process 2
-			ret = process_list(&rev2list, 0x2, 0x1);
-		} else {
-			// process 1
-			ret = process_list(&rev1list, 0x1, 0x2);
+	rev1->object.flags |= 1;
+	rev2->object.flags |= 2;
+	insert_by_date(rev1, &list);
+	insert_by_date(rev2, &list);
+
+	while (list) {
+		struct commit *commit = list->item;
+		struct commit_list *tmp = list, *parents;
+		int flags = commit->object.flags & 3;
+
+		list = list->next;
+		free(tmp);
+		switch (flags) {
+		case 3:
+			insert_by_date(commit, &result);
+			continue;
+		case 0:
+			die("git-merge-base: commit without either parent?");
 		}
-		if (ret) {
-			free_commit_list(rev1list);
-			free_commit_list(rev2list);
-			return ret;
+		parents = commit->parents;
+		while (parents) {
+			struct commit *p = parents->item;
+			parents = parents->next;
+			if ((p->object.flags & flags) == flags)
+				continue;
+			parse_commit(p);
+			p->object.flags |= flags;
+			insert_by_date(p, &list);
 		}
 	}
-	return NULL;
+	if (!result)
+		return NULL;
+	return result->item;
 }
 
 int main(int argc, char **argv)
@@ -64,6 +60,8 @@ int main(int argc, char **argv)
 	}
 	rev1 = lookup_commit_reference(rev1key);
 	rev2 = lookup_commit_reference(rev2key);
+	if (!rev1 || !rev2)
+		return 1;
 	ret = common_ancestor(rev1, rev2);
 	if (!ret)
 		return 1;