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

Speed up git-merge-base a lot

From
Linus Torvalds <torvalds@osdl.org>
Date
Aug 10, 2005, 23:26 UTC
Message-ID
<Pine.LNX.4.58.0508101615520.3295@g5.osdl.org>

In commit 4f7eb2e5a351e0d1f19fd4eab7e92834cc4528c2 I fixed git-merge-base getting confused by datestamps that caused it to traverse things in a non-obvious order.

However, my fix was a very brute-force one, and it had some really horrible implications for more complex trees with lots of parallell development. It might end up traversing all the way to the root commit.

Now, normally that isn't that horrible: it's used mainly for merging, and the bad cases really tend to happen fairly rarely, so if it takes a few seconds, we're not in too bad shape.

However, gitk will also do the git-merge-base for every merge it shows, because it basically re-does the trivial merge in order to show the "interesting" parts. And there we'd really like the result to be instantaneous.

This patch does that by walking the tree more completely, and using the same heuristic as git-rev-list to decide "ok, the rest is uninteresting".

In one - hopefully fairly extreme - case, it made a git-merge-base go from just under five seconds(!) to a tenth of a second on my machine.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
---
diff --git a/merge-base.c b/merge-base.c
--- a/merge-base.c
+++ b/merge-base.c
@@ -2,6 +2,22 @@
 #include "cache.h"
 #include "commit.h"
 
+#define PARENT1 1
+#define PARENT2 2
+#define UNINTERESTING 4
+
+static int interesting(struct commit_list *list)
+{
+	while (list) {
+		struct commit *commit = list->item;
+		list = list->next;
+		if (commit->object.flags & UNINTERESTING)
+			continue;
+		return 1;
+	}
+	return 0;
+}
+
 static struct commit *common_ancestor(struct commit *rev1, struct commit *rev2)
 {
 	struct commit_list *list = NULL;
@@ -18,19 +34,18 @@ static struct commit *common_ancestor(st
 	insert_by_date(rev1, &list);
 	insert_by_date(rev2, &list);
 
-	while (list) {
+	while (interesting(list)) {
 		struct commit *commit = list->item;
 		struct commit_list *tmp = list, *parents;
-		int flags = commit->object.flags & 3;
+		int flags = commit->object.flags & 7;
 
 		list = list->next;
 		free(tmp);
-		switch (flags) {
-		case 3:
+		if (flags == 3) {
 			insert_by_date(commit, &result);
-			continue;
-		case 0:
-			die("git-merge-base: commit without either parent?");
+
+			/* Mark children of a found merge uninteresting */
+			flags |= UNINTERESTING;
 		}
 		parents = commit->parents;
 		while (parents) {
Message 1 of 1 in “Speed up git-merge-base a lot”
  1. Linus TorvaldsAug 10, 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.