commit-reach: introduce struct paint_state with per-side counters
Add a paint_state struct for use by paint_down_to_common() that
wraps a prio_queue with per-side commit counters. 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_update() which adjusts
the appropriate bucket by a signed delta. An exhaustive switch on
the paint+stale bits documents all valid flag combinations in one
place.
Convert paint_down_to_common() to use paint_state. The loop now
drains the queue via paint_queue_get() which returns NULL when all
counters reach zero, replacing the old pointer-based termination
(max_nonstale). This is equivalent behavior -- both conditions
detect that no non-stale entries remain.
paint_queue_get() uses a "pop first" form: it dequeues a commit,
then checks the counters. This means the loop exits one iteration
earlier than the old code in some topologies (the popped stale
commit is never processed), so a few step counts drop by one.
The existing nonstale_queue is left in place for ahead_behind(),
though nonstale_queue_put_dedup() and nonstale_queue_get_dedup()
became unused and are removed.
Signed-off-by: Kristofer Karlsson <krka@spotify.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Kristofer Karlsson committedJul 11, 2026 at 13:27 UTC351dfe5aa75807a4a9ae7880fd39144a24bcb172
3 files changed+82-36
Documentation/technical/paint-down-to-common.adoc
+3-6
index c203f14455..3b3f7ac8af 100644--- a/Documentation/technical/paint-down-to-common.adoc+++ b/Documentation/technical/paint-down-to-common.adoc@@ -94,15 +94,12 @@ re-enqueued is bounded by the number of flag transitions. Termination ------------The walk uses a `nonstale_queue` wrapper around `prio_queue` that-tracks `max_nonstale`: the lowest-priority non-stale commit enqueued-so far. Once that commit is dequeued, every remaining entry is known-to be STALE and the loop terminates. Specifically, the main loop+The walk tracks the number of commits of each type in the queue+(PARENT1-only, PARENT2-only, pending merge-base). The main loop ends when one of the following conditions holds: 1. The queue is empty.- 2. `max_nonstale` has been dequeued, meaning the queue only contains- STALE entries.+ 2. The queue contains only stale entries. 3. Generation cutoff: the dequeued commit's generation is below a caller-supplied `min_generation` threshold. 4. Single result: the caller only needs one merge base, one has