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>
This commit is contained in:
Kristofer Karlsson
2026-06-24 12:14:09 +00:00
committed by Junio C Hamano
parent 70bff9d102
commit 021c224950
3 changed files with 108 additions and 0 deletions

View File

@@ -786,6 +786,7 @@ integration_tests = [
't6041-bisect-submodule.sh',
't6050-replace.sh',
't6060-merge-index.sh',
't6099-merge-base-side-exhaustion.sh',
't6100-rev-list-in-order.sh',
't6101-rev-parse-parents.sh',
't6102-rev-list-unexpected-objects.sh',

View File

@@ -0,0 +1,82 @@
#!/bin/sh
test_description='merge-base with ancestor among merge-base candidates
Test that merge-base --all correctly handles cases where
multiple merge-base candidates exist and one is an ancestor
of another. The side-exhaustion optimization in
paint_down_to_common may exit before STALE propagation
removes the ancestor, but remove_redundant catches it.
Graph shape (parents are below children):
A ----------- X
|\ /|
| B---------/ |
| | |
e2 \ f2
| | |
e1 d1 f1
\ | /
\ | /
\| /
C
A and X are the two tips.
B and C are both reachable from A and X.
B reaches C through d1.
Only B should appear in merge-base --all output.
'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup ancestor merge-base candidate' '
test_commit C &&
git checkout -b d-chain HEAD &&
test_commit d1 &&
test_commit B &&
git checkout -b e-path C &&
test_commit e1 &&
test_commit e2 &&
git checkout -b f-path C &&
test_commit f1 &&
test_commit f2 &&
git checkout -b branch-A e-path &&
test_merge A B &&
git checkout -b branch-X f-path &&
test_merge X B &&
git commit-graph write --reachable
'
test_expect_success 'merge-base --all excludes ancestor candidate' '
git rev-parse B >expected &&
git merge-base --all A X >actual &&
test_cmp expected actual
'
test_expect_success 'merge-base (single) finds shallowest' '
git rev-parse B >expected &&
git merge-base A X >actual &&
test_cmp expected actual
'
# Without commit-graph: generation numbers are INFINITY,
# side-exhaustion optimization does not fire.
test_expect_success 'merge-base --all without commit-graph' '
rm -f .git/objects/info/commit-graph &&
git rev-parse B >expected &&
git merge-base --all A X >actual &&
test_cmp expected actual
'
test_done

View File

@@ -294,6 +294,31 @@ test_expect_success 'get_merge_bases_many:infinity-both-sides' '
test_all_modes get_merge_bases_many
'
test_expect_success 'setup mixed finite/INFINITY topology' '
# Create a commit outside all saved commit-graph files so it always
# has INFINITY generation, while its parent (ps-X) is in the graph
# with a finite generation. Use the ps-* orphan topology so we do
# not pollute the grid-based rev-list tests.
git checkout ps-X &&
test_env GIT_TEST_COMMIT_GRAPH= test_commit pm-INF
'
test_expect_success 'get_merge_bases_many:mixed-finite-infinity' '
# One tip (pm-INF) is outside the commit-graph with INFINITY
# generation; the other (ps-B) is in the graph with finite
# generation. The walk starts in the INFINITY region and crosses
# into the finite region where side-exhaustion can fire.
cat >input <<-\EOF &&
A:pm-INF
X:ps-B
EOF
{
echo "get_merge_bases_many(A,X):" &&
git rev-parse ps-X
} >expect &&
test_all_modes get_merge_bases_many
'
test_expect_success 'reduce_heads' '
cat >input <<-\EOF &&
X:commit-1-10