git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Fix interesting git-rev-list corner case

From
Linus Torvalds <torvalds@osdl.org>
Date
Jul 30, 2005, 22:10 UTC
Message-ID
<Pine.LNX.4.58.0507301507390.29650@g5.osdl.org>
In-Reply-To
<Pine.LNX.4.58.0507301428300.29650@g5.osdl.org>
On Sat, 30 Jul 2005, Linus Torvalds wrote:
Show 5 quoted lines
> 
> Yup, it's git-merge-base, and it is confused by the same thing that 
> confused git-rev-list.
> 
> Thanks, I'll fix it.

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.

I bet there is a smarter way to do this, but this _should_ fix the problem Peter sees. Peter?

		Linus
---
diff --git a/merge-base.c b/merge-base.c
--- 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;
Previous: Linus TorvaldsNext: Peter Osterlund
Message 12 of 15 in “Fix interesting git-rev-list corner case”
  1. Linus TorvaldsJul 29, 2005
  2. Ryan AndersonJul 30, 2005
  3. A Large Angry SCMJul 30, 2005
  4. Linus TorvaldsJul 30, 2005
  5. Linus TorvaldsJul 30, 2005
  6. Peter OsterlundJul 30, 2005
  7. Linus TorvaldsJul 30, 2005
  8. Peter OsterlundJul 30, 2005
  9. Linus TorvaldsJul 30, 2005
  10. Peter OsterlundJul 30, 2005
  11. Linus TorvaldsJul 30, 2005
  12. Linus TorvaldsJul 30, 2005
  13. Peter OsterlundJul 30, 2005
  14. Linus TorvaldsJul 31, 2005
  15. Junio C HamanoJul 31, 2005

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.