Skip to content

commit-reach: terminate merge-base walk when one paint side is exhausted#2149

Draft
spkrka wants to merge 5 commits into
gitgitgadget:masterfrom
spkrka:side-exhaust-pr
Draft

commit-reach: terminate merge-base walk when one paint side is exhausted#2149
spkrka wants to merge 5 commits into
gitgitgadget:masterfrom
spkrka:side-exhaust-pr

Conversation

@spkrka

@spkrka spkrka commented Jun 13, 2026

Copy link
Copy Markdown

TBD

@spkrka spkrka force-pushed the side-exhaust-pr branch from 653ed8d to c6e85dd Compare June 14, 2026 09:55
spkrka and others added 5 commits June 14, 2026 12:19
Replace the nonstale_queue abstraction in paint_down_to_common() with
a new paint_queue struct that tracks per-side commit counts.  Each
non-stale queued commit occupies exactly one counter bucket based on
its paint flags: PARENT1-only, PARENT2-only, or both sides (a pending
merge-base candidate).

The counters are maintained by paint_count_transition() which handles
all flag changes as bucket transfers: remove from the old bucket, add
to the new one.  Either step is a no-op when the respective state has
no bucket (stale or zero).

paint_queue_put() atomically updates flags, handles ENQUEUED-based
dedup, and adjusts counters.  paint_queue_get() pops a commit and
decrements its counter.

The loop condition changes from pointer-based (while max_nonstale) to
counter-based (while any counter is positive), which is equivalent
for termination but gives callers precise visibility into the queue
composition.

The ahead_behind() function continues to use its own insert_no_dup()
helper with a single max_nonstale pointer, since it only needs one
nonstale dimension and has no per-side tracking.

No behavior change.

Signed-off-by: Kristofer Karlsson <krka@spotify.com>
Add an early termination check to paint_down_to_common() using the
per-side counters introduced in the previous commit.  Once the walk
enters the finite-generation region (topological order guaranteed by
commit-graph), terminate early when one side's exclusive count drops
to zero -- no new merge-base can form without both paint sides
meeting.

The check also waits for pending_merge_bases to reach zero, ensuring
all merge-base candidates have been popped and recorded before
exiting.  This is necessary for FIND_ALL to return all merge bases
in criss-cross merge topologies.

The INFINITY gate ensures correctness: commits without a commit-graph
entry have GENERATION_NUMBER_INFINITY and are ordered by commit date,
which is not topologically reliable.  The optimization only fires
once the walk enters the finite-generation region where ordering
guarantees hold.

On large repositories with commit-graph, this yields 100-1000x
speedups for merge-base queries where one side (e.g. a PR branch) is
much smaller than the other.

Signed-off-by: Kristofer Karlsson <krka@spotify.com>
Add tests for 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.

Test with and without commit-graph to verify the optimization
is a no-op when generation numbers are unavailable.

Signed-off-by: Kristofer Karlsson <krka@spotify.com>
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>
Add a perf test that benchmarks merge-base on a synthetic 500k-commit
repository with a deep side branch.  The test creates a main branch,
a side branch forked from an early commit with old timestamps, a merge
commit bringing the side branch in, and a PR branch just before the
merge.  Computing merge-base(main, pr) exercises the side-exhaustion
optimization.

Includes variants with and without a commit-graph to cover the case
where both tips are outside the commit-graph (INFINITY region).

Signed-off-by: Kristofer Karlsson <krka@spotify.com>
@spkrka spkrka force-pushed the side-exhaust-pr branch from c6e85dd to bf8f525 Compare June 14, 2026 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants