t6099, t6600: add side-exhaustion regression tests

Add t6099 to test the case where multiple merge-base candidates exist and one is an ancestor of another. This exercises the side-exhaustion optimization in paint_down_to_common together with the remove_redundant safety net in get_merge_bases_many_0. Add a mixed finite/INFINITY test to t6600 where one tip is outside the commit-graph (INFINITY generation) and the other is inside. This exercises the region transition: the walk starts in the INFINITY region where side-exhaustion is disabled, then crosses into the finite region where it can fire. Signed-off-by: Kristofer Karlsson <krka@spotify.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Kristofer Karlsson committed Jul 11, 2026 at 13:27 UTC 0789dc8f046c017e1dc69bb720a7b425aa8a1292
2 files changed +83
t/meson.build
+1
@@ -788,6 +788,7 @@ integration_tests = [
788 't6041-bisect-submodule.sh',
789 't6050-replace.sh',
790 't6060-merge-index.sh',
791 + 't6099-merge-base-side-exhaustion.sh',
792 't6100-rev-list-in-order.sh',
793 't6101-rev-parse-parents.sh',
794 't6102-rev-list-unexpected-objects.sh',
t/t6099-merge-base-side-exhaustion.sh new
+82
@@ -0,0 +1,82 @@
1 +#!/bin/sh
2 +
3 +test_description='merge-base with ancestor among merge-base candidates
4 +
5 +Test that merge-base --all correctly handles cases where
6 +multiple merge-base candidates exist and one is an ancestor
7 +of another. The side-exhaustion optimization in
8 +paint_down_to_common may exit before STALE propagation
9 +removes the ancestor, but remove_redundant catches it.
10 +
11 +Graph shape (parents are below children):
12 +
13 + A ----------- X
14 + |\ /|
15 + | B---------/ |
16 + | | |
17 + e2 \ f2
18 + | | |
19 + e1 d1 f1
20 + \ | /
21 + \ | /
22 + \| /
23 + C
24 +
25 +A and X are the two tips.
26 +B and C are both reachable from A and X.
27 +B reaches C through d1.
28 +Only B should appear in merge-base --all output.
29 +'
30 +
31 +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
32 +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
33 +
34 +TEST_PASSES_SANITIZE_LEAK=true
35 +. ./test-lib.sh
36 +
37 +test_expect_success 'setup ancestor merge-base candidate' '
38 + test_commit C &&
39 +
40 + git checkout -b d-chain HEAD &&
41 + test_commit d1 &&
42 + test_commit B &&
43 +
44 + git checkout -b e-path C &&
45 + test_commit e1 &&
46 + test_commit e2 &&
47 +
48 + git checkout -b f-path C &&
49 + test_commit f1 &&
50 + test_commit f2 &&
51 +
52 + git checkout -b branch-A e-path &&
53 + test_merge A B &&
54 +
55 + git checkout -b branch-X f-path &&
56 + test_merge X B &&
57 +
58 + git commit-graph write --reachable
59 +'
60 +
61 +test_expect_success 'merge-base --all excludes ancestor candidate' '
62 + git rev-parse B >expected &&
63 + git merge-base --all A X >actual &&
64 + test_cmp expected actual
65 +'
66 +
67 +test_expect_success 'merge-base (single) finds shallowest' '
68 + git rev-parse B >expected &&
69 + git merge-base A X >actual &&
70 + test_cmp expected actual
71 +'
72 +
73 +# Without commit-graph: generation numbers are INFINITY,
74 +# side-exhaustion optimization does not fire.
75 +test_expect_success 'merge-base --all without commit-graph' '
76 + rm -f .git/objects/info/commit-graph &&
77 + git rev-parse B >expected &&
78 + git merge-base --all A X >actual &&
79 + test_cmp expected actual
80 +'
81 +
82 +test_done