t6600: add test cases for side-exhaustion edge cases

Add test cases to t6600-test-reach.sh that exercise edge cases in the side-exhaustion optimization for paint_down_to_common(): - in_merge_bases_many:self: commit is both A and one of the X inputs - get_merge_bases_many:duplicate-twos: duplicate entries in X list - get_merge_bases_many:pending-stale: STALE transition on an already-painted commit (ps-* diamond topology) - get_merge_bases_many:infinity-both-sides: both tips outside the commit-graph with non-monotonic dates (pi-* topology) Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Kristofer Karlsson <krka@spotify.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Jul 11, 2026 at 13:27 UTC 6e1730e22b121280ba6b7e1a25e84d53cca41a78
1 file changed +110
t/t6600-test-reach.sh
+110
@@ -85,6 +85,61 @@ test_expect_success 'setup' '
85 git branch -f skew-P2 "$skew_P2" &&
86 git tag skew-M2 "$skew_M2" &&
87
88 + # Build a small side topology to exercise the (PARENT1|PARENT2) ->
89 + # (PARENT1|PARENT2|STALE) transition in paint_down_to_common(); the
90 + # 10x10 grid above does not exercise it because no merge-base candidate
91 + # there is a descendant of another, so STALE never reaches a
92 + # still-pending candidate.
93 + #
94 + # ps-X
95 + # /|\
96 + # / | \
97 + # ps-Z ps-B ps-W
98 + # | / \ |
99 + # | / \ |
100 + # |/ \|
101 + # ps-T1 ps-T2
102 + #
103 + # where ps-T1=merge(ps-Z,ps-B), ps-T2=merge(ps-W,ps-B), so
104 + # merge-base(ps-T1,ps-T2) = ps-B. During the walk, ps-X transitions
105 + # to (PARENT1|PARENT2) via ps-Z and ps-W before ps-B is dequeued;
106 + # then the STALE-walk from ps-B transitions ps-X to
107 + # (PARENT1|PARENT2|STALE).
108 + git checkout --orphan ps-orphan &&
109 + test_commit ps-X &&
110 + git checkout -b ps-B-br ps-X && test_commit ps-B &&
111 + git checkout -b ps-Z-br ps-X && test_commit ps-Z &&
112 + git checkout -b ps-W-br ps-X && test_commit ps-W &&
113 + git checkout -b ps-T1 ps-Z &&
114 + git merge --no-ff -m ps-T1 ps-B &&
115 + git checkout -b ps-T2 ps-W &&
116 + git merge --no-ff -m ps-T2 ps-B &&
117 +
118 + # Build a side topology that lives entirely outside the half
119 + # commit-graph and has non-monotonic commit dates, to exercise the
120 + # INFINITY-gate in paint_down_to_common. With both tips outside
121 + # the graph, generation is INFINITY and the queue falls back to
122 + # commit-date order, which here is non-monotonic.
123 + #
124 + # pi-X (date 500, PARENT1 tip) --> pi-P, pi-D
125 + # pi-D (date 480) --> pi-C
126 + # pi-C (date 200) --> pi-B
127 + # pi-B (date 100, PARENT2 tip) --> pi-P
128 + # pi-P (date 450, root)
129 + #
130 + # merge-base(pi-X, pi-B) = pi-B (it is an ancestor of pi-X and is
131 + # itself one of the queried tips).
132 + git checkout --orphan pi-orphan &&
133 + test_commit --date "@450 +0000" pi-P &&
134 + test_commit --date "@100 +0000" pi-B &&
135 + test_commit --date "@200 +0000" pi-C &&
136 + test_commit --date "@480 +0000" pi-D &&
137 + GIT_AUTHOR_DATE="@500 +0000" GIT_COMMITTER_DATE="@500 +0000" \
138 + git commit-tree -p pi-D -p pi-P -m pi-X pi-D^{tree} >pi-X-oid &&
139 + pi_x="$(cat pi-X-oid)" &&
140 + git branch -f pi-X-br "$pi_x" &&
141 + git tag pi-X "$pi_x" &&
142 +
143 git commit-graph write --reachable &&
144 mv .git/objects/info/commit-graph commit-graph-full &&
145 chmod u+w commit-graph-full &&
@@ -182,6 +237,16 @@ test_expect_success 'in_merge_bases_many:miss-heuristic' '
237 test_all_modes in_merge_bases_many
238 '
239
240 +test_expect_success 'in_merge_bases_many:self' '
241 + cat >input <<-\EOF &&
242 + A:commit-6-8
243 + X:commit-5-9
244 + X:commit-6-8
245 + EOF
246 + echo "in_merge_bases_many(A,X):1" >expect &&
247 + test_all_modes in_merge_bases_many
248 +'
249 +
250 test_expect_success 'is_descendant_of:hit' '
251 cat >input <<-\EOF &&
252 A:commit-5-7
@@ -219,6 +284,51 @@ test_expect_success 'get_merge_bases_many' '
284 test_all_modes get_merge_bases_many
285 '
286
287 +test_expect_success 'get_merge_bases_many:duplicate-twos' '
288 + cat >input <<-\EOF &&
289 + A:commit-5-7
290 + X:commit-4-8
291 + X:commit-4-8
292 + X:commit-6-6
293 + X:commit-6-6
294 + X:commit-8-3
295 + EOF
296 + {
297 + echo "get_merge_bases_many(A,X):" &&
298 + git rev-parse commit-5-6 \
299 + commit-4-7 | sort
300 + } >expect &&
301 + test_all_modes get_merge_bases_many
302 +'
303 +
304 +test_expect_success 'get_merge_bases_many:pending-stale' '
305 + # Exercises the (PARENT1|PARENT2) -> (...|STALE) transition path in
306 + # paint_down_to_common(). See the topology comment in the setup test.
307 + cat >input <<-\EOF &&
308 + A:ps-T1
309 + X:ps-T2
310 + EOF
311 + {
312 + echo "get_merge_bases_many(A,X):" &&
313 + git rev-parse ps-B
314 + } >expect &&
315 + test_all_modes get_merge_bases_many
316 +'
317 +
318 +test_expect_success 'get_merge_bases_many:infinity-both-sides' '
319 + # Exercises the push-time INFINITY-gate in paint_down_to_common(). See
320 + # the pi-* topology comment in the setup test.
321 + cat >input <<-\EOF &&
322 + A:pi-X
323 + X:pi-B
324 + EOF
325 + {
326 + echo "get_merge_bases_many(A,X):" &&
327 + git rev-parse pi-B
328 + } >expect &&
329 + test_all_modes get_merge_bases_many
330 +'
331 +
332 test_expect_success 'reduce_heads' '
333 cat >input <<-\EOF &&
334 X:commit-1-10