]> git.ipfire.org Git - thirdparty/git.git/commitdiff
merge: use repo_in_merge_bases for octopus up-to-date check
authorKristofer Karlsson <krka@spotify.com>
Tue, 12 May 2026 06:11:26 +0000 (06:11 +0000)
committerJunio C Hamano <gitster@pobox.com>
Tue, 12 May 2026 15:43:39 +0000 (00:43 +0900)
The octopus merge path checks whether each remote head is already
an ancestor of HEAD by computing all merge-bases via
repo_get_merge_bases() and comparing the first result's OID to
the remote head.  This is more expensive than necessary:
repo_get_merge_bases() calls paint_down_to_common() with
min_generation=0, performs the full STALE drain, and may run
remove_redundant(), when all we need is a yes/no reachability
answer.

Replace this with repo_in_merge_bases(), which answers the
is-ancestor question directly.  When generation numbers are
available, repo_in_merge_bases() uses can_all_from_reach() -- a
DFS bounded by generation number that stops as soon as the target
is found or ruled out, without entering paint_down_to_common() at
all.  Without generation numbers, it still benefits from a tighter
min_generation floor.

Signed-off-by: Kristofer Karlsson <krka@spotify.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/merge.c
t/t6408-merge-up-to-date.sh

index 2cbce56f8da9f7027cc95ad98540084b0ee98111..862107cf412c3c6b1ba4942a8bc364f475bc3cd9 100644 (file)
@@ -1735,21 +1735,11 @@ int cmd_merge(int argc,
                struct commit_list *j;
 
                for (j = remoteheads; j; j = j->next) {
-                       struct commit_list *common_one = NULL;
-                       struct commit *common_item;
-
-                       /*
-                        * Here we *have* to calculate the individual
-                        * merge_bases again, otherwise "git merge HEAD^
-                        * HEAD^^" would be missed.
-                        */
-                       if (repo_get_merge_bases(the_repository, head_commit,
-                                                j->item, &common_one) < 0)
+                       int ret = repo_in_merge_bases(the_repository,
+                                                     j->item, head_commit);
+                       if (ret < 0)
                                exit(128);
-
-                       common_item = common_one->item;
-                       commit_list_free(common_one);
-                       if (!oideq(&common_item->object.oid, &j->item->object.oid)) {
+                       if (!ret) {
                                up_to_date = 0;
                                break;
                        }
index 7763c1ba98080d5d1d68e1009fb70f6c80cf479a..be0840efb697f222f9614ab631e206ee40b11aeb 100755 (executable)
@@ -89,4 +89,14 @@ test_expect_success 'merge fast-forward octopus' '
        test "$expect" = "$current"
 '
 
+test_expect_success 'merge octopus already up to date' '
+
+       git reset --hard c2 &&
+       test_tick &&
+       git merge c0 c1 &&
+       expect=$(git rev-parse c2) &&
+       current=$(git rev-parse HEAD) &&
+       test "$expect" = "$current"
+'
+
 test_done