Git development
 help / color / mirror / Atom feed
From: "Kristofer Karlsson via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Derrick Stolee <stolee@gmail.com>,
	Elijah Newren <newren@gmail.com>,
	Kristofer Karlsson <krka@spotify.com>
Subject: [PATCH v3 0/8] commit-reach: terminate merge-base walk when one side is exhausted
Date: Fri, 26 Jun 2026 13:07:57 +0000	[thread overview]
Message-ID: <pull.2149.v3.git.1782479286.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2149.v2.git.1782303254.gitgitgadget@gmail.com>

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

Optimize paint_down_to_common() for merge-base queries that hit large
one-sided histories.

When the walk from one side reaches a commit with a very low generation
number that the other side never paints, the walk is forced to drain most of
the graph. A common trigger is a repository import that grafts a separate
history with its own root, but any merge that introduces a low-generation
commit never painted by the other side has the same effect.

A new merge-base candidate can only be discovered when exclusive PARENT1 and
PARENT2 paint meet. This series teaches paint_down_to_common() to stop as
soon as one side has no exclusive commits left in the queue; once one side
is exhausted, no further candidates can appear.

  origin/HEAD  o   o  PR HEAD
               |   |
     (import)  o   :
              / \ /
             |   o  merge-base
             |   |
             :   :  (~2.5M commits)
             |   |
  import root   main root


In the RFC thread [1], Derrick Stolee provided a criss-cross counterexample
that sharpened the halt condition, and Elijah Newren independently
discovered the same optimization and shared an implementation in PR #2150
[2]. Patches 2-3 incorporate test cases from Elijah's branch.

This series implements the optimization only after the walk enters the
finite-generation region, where generation ordering guarantees that paint on
visited commits is final.

Patch layout:

1/8 Documentation/technical: add paint-down-to-common doc 2/8 t6600: add
test cases for side-exhaustion edge cases 3/8 t6099, t6600: add
side-exhaustion regression tests 4/8 commit-reach: add trace2
instrumentation to paint_down_to_common() 5/8 commit-reach: introduce struct
paint_state with per-side counters 6/8 commit-reach: remove unused
nonstale_queue dedup wrappers 7/8 commit-reach: terminate merge-base walk
when one paint side is exhausted 8/8 commit-reach: move min_generation check
into paint_queue_get()

Benchmarks

Step counts are deterministic (measured via trace2_data_intmax added in
patch 4). Wall-clock times are best-of-11 runs.

2.6M-commit monorepo with commit-graph:

                                        steps              wall-clock
  merge-base --all  (across import)  2143438 ->      3     3.67s ->    5ms
  merge-base --all  (1000 apart)     2692915 ->   1035     4.41s ->    7ms
  merge-base --all  (5000 apart)     2692915 ->   6401     4.45s ->   13ms
  merge-base --all  (HEAD vs import) 2698872 ->  45960     4.50s ->   79ms
  merge-tree        (across import)  2143438 ->      3     4.42s ->   11ms


git.git (88k commits, commit-graph):

                                        steps              wall-clock
  merge-base --all v2.0.0 v2.55.0-rc1 72264 ->  44589      110ms ->   68ms
  merge-base --all HEAD HEAD~1000      9891 ->   3828       18ms ->   10ms
  merge-base --all HEAD HEAD~10000    72303 ->  41487      101ms ->   50ms


Changes since v2:

 * New patch 8/8: moved the min_generation termination check and the
   last_gen monotonicity assertion into paint_queue_get(), consolidating
   halt conditions. commit_graph_generation() is now called once per
   dequeued commit and shared across all checks.

 * Widened the generation-monotonicity BUG assertion to fire
   unconditionally, not only when min_generation is set. The side-exhaustion
   optimization depends on correct generation ordering, so the assertion
   should always be active. This is a behavior change: the BUG() now fires
   for any generation ordering violation, regardless of the caller.

 * Moved all halt conditions inside paint_queue_get() with the "pop first"
   form: pop, check, then decrement counters. This keeps the optimization
   commit's diff minimal (just inserting the new checks between pop and
   decrement).

 * Shortened the doc comment on paint_queue_get() to describe what it does
   rather than how. Inline comments on each return NULL explain the specific
   halt condition.

 * Replaced the manual commit-graph setup in the step-count test with
   run_all_modes, which now sets GIT_TRACE2_EVENT per mode and produces
   trace-mode-{none,full,half,no-gdat}.txt files.

 * Added a test_paint_down_steps helper for concise 4-mode step assertions
   with diagnostic output on mismatch (prints "expected X, got Y" instead of
   a silent grep failure).

 * Added step-count assertions to the single-walk edge-case tests:
   in_merge_bases_many:self, pending-stale, infinity-both-sides,
   mixed-finite-infinity.

 * Included step counts alongside wall-clock times in the benchmark tables.

Changes since v1:

 * Reordered patches: documentation first (describing the existing
   algorithm), tests before code changes, so they demonstrate passing with
   old logic first.

 * Dropped the ahead_behind decoupling patch. paint_state is now a NEW
   struct alongside nonstale_queue instead of replacing it. ahead_behind()
   is completely untouched.

 * Removed nonstale_queue_put_dedup() and nonstale_queue_get_dedup() (dead
   code after the conversion) in a separate commit.

 * Renamed: struct paint_queue -> paint_state, field pq -> queue,
   paint_count_add/remove -> paint_count_update (single function with signed
   delta parameter).

 * Split the old paint_count_transition (which handled both old and new
   flags in one call) into separate remove/add calls with a signed delta.
   This eliminates the need for the case 0 handler (which tracked "not in
   the queue") and allows an exhaustive switch on (PARENT1 | PARENT2 |
   STALE) that documents all valid flag combinations, with BUG() in default.

 * Added trace2_data_intmax() instrumentation to report the number of
   commits visited per paint walk (separate commit), with deterministic
   step-count assertions in t6600.

 * Expanded switch statements to multi-line format per .clang-format.

 * Used !count style throughout instead of count == 0.

 * Updated technical documentation alongside code changes.

[1]
https://lore.kernel.org/git/CAL71e4Ps-2_0+uuZu43N9pFnXBemoAohPs_eyRJf8taXHJPAXQ@mail.gmail.com/T/#u
[2] https://github.com/gitgitgadget/git/pull/2150

Elijah Newren (1):
  t6600: add test cases for side-exhaustion edge cases

Kristofer Karlsson (7):
  Documentation/technical: add paint-down-to-common doc
  t6099, t6600: add side-exhaustion regression tests
  commit-reach: add trace2 instrumentation to paint_down_to_common()
  commit-reach: introduce struct paint_state with per-side counters
  commit-reach: remove unused nonstale_queue dedup wrappers
  commit-reach: terminate merge-base walk when one paint side is
    exhausted
  commit-reach: move min_generation check into paint_queue_get()

 Documentation/Makefile                        |   1 +
 Documentation/technical/meson.build           |   1 +
 .../technical/paint-down-to-common.adoc       | 137 +++++++++++++
 commit-reach.c                                | 147 ++++++++++----
 t/meson.build                                 |   1 +
 t/t6099-merge-base-side-exhaustion.sh         |  82 ++++++++
 t/t6600-test-reach.sh                         | 181 ++++++++++++++++--
 7 files changed, 501 insertions(+), 49 deletions(-)
 create mode 100644 Documentation/technical/paint-down-to-common.adoc
 create mode 100755 t/t6099-merge-base-side-exhaustion.sh


base-commit: 6c3d7b73556db708feb3b16232fab1efc4353428
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2149%2Fspkrka%2Fside-exhaust-pr-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2149/spkrka/side-exhaust-pr-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/2149

Range-diff vs v2:

 1:  19ed743bd1 = 1:  2593866bce Documentation/technical: add paint-down-to-common doc
 2:  6151b8e0a3 = 2:  9efc084850 t6600: add test cases for side-exhaustion edge cases
 3:  90f09ecb5c = 3:  14b0d86b93 t6099, t6600: add side-exhaustion regression tests
 4:  6ade4df2ed ! 4:  2592264cda commit-reach: add trace2 instrumentation to paint_down_to_common()
     @@ Commit message
      
          Add a step counter and trace2_data_intmax() call so that the number
          of commits visited during the paint walk is observable via
     -    GIT_TRACE2_PERF. This provides a way to measure the impact of
     +    GIT_TRACE2_EVENT. This provides a way to measure the impact of
          future optimizations without relying on wall-clock benchmarks alone.
      
          Signed-off-by: Kristofer Karlsson <krka@spotify.com>
     @@ commit-reach.c: static int paint_down_to_common(struct repository *r,
       }
      
       ## t/t6600-test-reach.sh ##
     -@@ t/t6600-test-reach.sh: test_expect_success 'get_merge_bases_many:mixed-finite-infinity' '
     - 	test_all_modes get_merge_bases_many
     +@@ t/t6600-test-reach.sh: test_expect_success 'setup' '
       '
       
     -+test_expect_success 'merge-base --all commit-walk steps' '
     -+	test_when_finished rm -rf .git/objects/info/commit-graph \
     -+		.git/objects/info/commit-graphs &&
     -+	rm -rf .git/objects/info/commit-graph \
     -+		.git/objects/info/commit-graphs &&
     -+
     -+	GIT_TRACE2_EVENT="$(pwd)/trace-none.txt" \
     -+		git merge-base --all commit-9-9 commit-9-1 >actual &&
     -+	test_trace2_data paint_down_to_common steps 81 <trace-none.txt &&
     + run_all_modes () {
     +-	test_when_finished rm -rf .git/objects/info/commit-graph &&
     +-	"$@" <input >actual &&
     +-	test_cmp expect actual &&
     +-	cp commit-graph-full .git/objects/info/commit-graph &&
     +-	"$@" <input >actual &&
     +-	test_cmp expect actual &&
     +-	cp commit-graph-half .git/objects/info/commit-graph &&
     +-	"$@" <input >actual &&
     +-	test_cmp expect actual &&
     +-	cp commit-graph-no-gdat .git/objects/info/commit-graph &&
     +-	"$@" <input >actual &&
     +-	test_cmp expect actual
     ++	graph=.git/objects/info/commit-graph &&
     ++	test_when_finished rm -rf "$graph" "${graph}s" &&
     ++	rm -f trace-mode-*.txt &&
      +
     -+	cp commit-graph-full .git/objects/info/commit-graph &&
     -+	GIT_TRACE2_EVENT="$(pwd)/trace-full.txt" \
     -+		git merge-base --all commit-9-9 commit-9-1 >actual &&
     -+	test_trace2_data paint_down_to_common steps 80 <trace-full.txt &&
     ++	for mode in none full half no-gdat
     ++	do
     ++		rm -rf "$graph" "${graph}s" &&
     ++		cp "commit-graph-${mode}" "$graph" 2>/dev/null ||
     ++		true &&
     ++		GIT_TRACE2_EVENT="$(pwd)/trace-mode-${mode}.txt" \
     ++			"$@" <input >actual &&
     ++		test_cmp expect actual || return 1
     ++	done
     + }
     + 
     + test_all_modes () {
     + 	run_all_modes test-tool reach "$@"
     + }
     + 
     ++test_paint_down_steps () {
     ++	for mode in none full half no-gdat
     ++	do
     ++		test_trace2_data paint_down_to_common steps "$1" \
     ++			<"trace-mode-${mode}.txt" || return 1
     ++		shift
     ++	done
     ++}
      +
     -+	cp commit-graph-half .git/objects/info/commit-graph &&
     -+	GIT_TRACE2_EVENT="$(pwd)/trace-half.txt" \
     -+		git merge-base --all commit-9-9 commit-9-1 >actual &&
     -+	test_trace2_data paint_down_to_common steps 81 <trace-half.txt
     + test_expect_success 'ref_newer:miss' '
     + 	cat >input <<-\EOF &&
     + 	A:commit-5-7
     +@@ t/t6600-test-reach.sh: test_expect_success 'in_merge_bases_many:self' '
     + 	X:commit-6-8
     + 	EOF
     + 	echo "in_merge_bases_many(A,X):1" >expect &&
     +-	test_all_modes in_merge_bases_many
     ++	test_all_modes in_merge_bases_many &&
     ++	test_paint_down_steps 45 2 25 3
     + '
     + 
     + test_expect_success 'is_descendant_of:hit' '
     +@@ t/t6600-test-reach.sh: test_expect_success 'get_merge_bases_many:pending-stale' '
     + 		echo "get_merge_bases_many(A,X):" &&
     + 		git rev-parse ps-B
     + 	} >expect &&
     +-	test_all_modes get_merge_bases_many
     ++	test_all_modes get_merge_bases_many &&
     ++	test_paint_down_steps 6 6 6 6
     + '
     + 
     + test_expect_success 'get_merge_bases_many:infinity-both-sides' '
     +@@ t/t6600-test-reach.sh: test_expect_success 'get_merge_bases_many:infinity-both-sides' '
     + 		echo "get_merge_bases_many(A,X):" &&
     + 		git rev-parse pi-B
     + 	} >expect &&
     +-	test_all_modes get_merge_bases_many
     ++	test_all_modes get_merge_bases_many &&
     ++	test_paint_down_steps 5 5 5 5
     + '
     + 
     + test_expect_success 'setup mixed finite/INFINITY topology' '
     +@@ t/t6600-test-reach.sh: test_expect_success 'get_merge_bases_many:mixed-finite-infinity' '
     + 		echo "get_merge_bases_many(A,X):" &&
     + 		git rev-parse ps-X
     + 	} >expect &&
     +-	test_all_modes get_merge_bases_many
     ++	test_all_modes get_merge_bases_many &&
     ++	test_paint_down_steps 3 3 3 3
      +'
      +
     ++test_expect_success 'merge-base --all commit-walk steps' '
     ++	>input &&
     ++	git rev-parse commit-9-1 >expect &&
     ++	run_all_modes git merge-base --all commit-9-9 commit-9-1 &&
     ++	test_paint_down_steps 81 80 81 81
     + '
     + 
       test_expect_success 'reduce_heads' '
     - 	cat >input <<-\EOF &&
     - 	X:commit-1-10
 5:  f24edd45f0 ! 5:  e82e0c72b6 commit-reach: introduce struct paint_state with per-side counters
     @@ Commit message
          (max_nonstale). This is equivalent behavior -- both conditions
          detect that no non-stale entries remain.
      
     -    The existing nonstale_queue is left in place for ahead_behind().
     +    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.
      
     -    Step counts (via trace2 from the previous commit) are identical
     -    before and after this refactoring, confirming no behavioral change.
     +    The existing nonstale_queue is left in place for ahead_behind().
      
          Signed-off-by: Kristofer Karlsson <krka@spotify.com>
      
     @@ commit-reach.c: static struct commit *nonstale_queue_get_dedup(struct nonstale_q
      +
      +static struct commit *paint_queue_get(struct paint_state *state)
      +{
     -+	struct commit *commit;
     ++	struct commit *commit = prio_queue_get(&state->queue);
     ++
     ++	if (!commit)
     ++		return NULL;
     ++
     ++	commit->object.flags &= ~ENQUEUED;
      +
      +	if (!state->p1_count && !state->p2_count &&
      +	    !state->pending_merge_bases)
      +		return NULL;
      +
     -+	commit = prio_queue_get(&state->queue);
     -+	if (commit) {
     -+		commit->object.flags &= ~ENQUEUED;
     -+		paint_count_update(state, commit->object.flags, -1);
     -+	}
     ++	paint_count_update(state, commit->object.flags, -1);
      +	return commit;
      +}
      +
     @@ commit-reach.c: static int paint_down_to_common(struct repository *r,
       	trace2_data_intmax("paint_down_to_common", r,
       			   "steps", steps);
       	commit_list_sort_by_date(result);
     +
     + ## t/t6600-test-reach.sh ##
     +@@ t/t6600-test-reach.sh: test_expect_success 'get_merge_bases_many:pending-stale' '
     + 		git rev-parse ps-B
     + 	} >expect &&
     + 	test_all_modes get_merge_bases_many &&
     +-	test_paint_down_steps 6 6 6 6
     ++	test_paint_down_steps 5 5 5 5
     + '
     + 
     + test_expect_success 'get_merge_bases_many:infinity-both-sides' '
     +@@ t/t6600-test-reach.sh: test_expect_success 'get_merge_bases_many:infinity-both-sides' '
     + 		git rev-parse pi-B
     + 	} >expect &&
     + 	test_all_modes get_merge_bases_many &&
     +-	test_paint_down_steps 5 5 5 5
     ++	test_paint_down_steps 5 4 5 5
     + '
     + 
     + test_expect_success 'setup mixed finite/INFINITY topology' '
 6:  8c72f01083 = 6:  e6181bf3c1 commit-reach: remove unused nonstale_queue dedup wrappers
 7:  d84b932e5b ! 7:  f3572a8a89 commit-reach: terminate merge-base walk when one paint side is exhausted
     @@ Commit message
          once the walk enters the finite-generation region where ordering
          guarantees hold.
      
     +    Widen the existing generation-monotonicity BUG assertion to fire
     +    unconditionally, not only when min_generation is set. The
     +    side-exhaustion optimization depends on correct generation ordering,
     +    so the assertion should always be active.
     +
          Step counts measured with trace2 on git.git with commit-graph:
      
            merge-base --all v2.0.0 v2.55.0-rc1:
     @@ Documentation/technical/paint-down-to-common.adoc: existing candidates by provin
      
       ## commit-reach.c ##
      @@ commit-reach.c: static void paint_queue_put(struct paint_state *state,
     + 	}
     + }
       
     ++/*
     ++ * Dequeue the next commit for the paint walk, or return NULL when
     ++ * no more merge bases can be discovered.
     ++ */
       static struct commit *paint_queue_get(struct paint_state *state)
       {
     --	struct commit *commit;
     -+	struct commit *commit = prio_queue_get(&state->queue);
     + 	struct commit *commit = prio_queue_get(&state->queue);
     +@@ commit-reach.c: static struct commit *paint_queue_get(struct paint_state *state)
     + 
     + 	commit->object.flags &= ~ENQUEUED;
       
      -	if (!state->p1_count && !state->p2_count &&
      -	    !state->pending_merge_bases)
     -+	if (!commit)
     - 		return NULL;
     - 
     --	commit = prio_queue_get(&state->queue);
     --	if (commit) {
     --		commit->object.flags &= ~ENQUEUED;
     --		paint_count_update(state, commit->object.flags, -1);
     -+	commit->object.flags &= ~ENQUEUED;
     -+
     +-		return NULL;
      +	if (!state->pending_merge_bases) {
     ++		/* only stale entries remain */
      +		if (!state->p1_count && !state->p2_count)
      +			return NULL;
     -+		/*
     -+		 * Side exhaustion: a new merge-base can only form
     -+		 * when both PARENT1-only and PARENT2-only commits
     -+		 * remain in the queue. In the finite-generation
     -+		 * region the queue is ordered topologically, so
     -+		 * no future step can add paint to visited commits
     -+		 * and an exhausted side cannot reappear.
     -+		 */
     ++
     ++		/* one side is exhausted */
      +		if ((!state->p1_count || !state->p2_count) &&
      +		    commit_graph_generation(commit) < GENERATION_NUMBER_INFINITY)
      +			return NULL;
     - 	}
     -+
     -+	paint_count_update(state, commit->object.flags, -1);
     ++	}
     + 
     + 	paint_count_update(state, commit->object.flags, -1);
       	return commit;
     - }
     +@@ commit-reach.c: static int paint_down_to_common(struct repository *r,
     + 		timestamp_t generation = commit_graph_generation(commit);
     + 		steps++;
       
     +-		if (min_generation && generation > last_gen)
     ++		if (generation > last_gen)
     + 			BUG("bad generation skip %"PRItime" > %"PRItime" at %s",
     + 			    generation, last_gen,
     + 			    oid_to_hex(&commit->object.oid));
      
       ## t/t6600-test-reach.sh ##
     -@@ t/t6600-test-reach.sh: test_expect_success 'merge-base --all commit-walk steps' '
     - 	cp commit-graph-full .git/objects/info/commit-graph &&
     - 	GIT_TRACE2_EVENT="$(pwd)/trace-full.txt" \
     - 		git merge-base --all commit-9-9 commit-9-1 >actual &&
     --	test_trace2_data paint_down_to_common steps 80 <trace-full.txt &&
     -+	test_trace2_data paint_down_to_common steps 9 <trace-full.txt &&
     +@@ t/t6600-test-reach.sh: test_expect_success 'in_merge_bases_many:self' '
     + 	EOF
     + 	echo "in_merge_bases_many(A,X):1" >expect &&
     + 	test_all_modes in_merge_bases_many &&
     +-	test_paint_down_steps 45 2 25 3
     ++	test_paint_down_steps 45 1 25 1
     + '
       
     - 	cp commit-graph-half .git/objects/info/commit-graph &&
     - 	GIT_TRACE2_EVENT="$(pwd)/trace-half.txt" \
     - 		git merge-base --all commit-9-9 commit-9-1 >actual &&
     --	test_trace2_data paint_down_to_common steps 81 <trace-half.txt
     -+	test_trace2_data paint_down_to_common steps 57 <trace-half.txt
     + test_expect_success 'is_descendant_of:hit' '
     +@@ t/t6600-test-reach.sh: test_expect_success 'merge-base --all commit-walk steps' '
     + 	>input &&
     + 	git rev-parse commit-9-1 >expect &&
     + 	run_all_modes git merge-base --all commit-9-9 commit-9-1 &&
     +-	test_paint_down_steps 81 80 81 81
     ++	test_paint_down_steps 81 9 57 10
       '
       
       test_expect_success 'reduce_heads' '
 -:  ---------- > 8:  4b9f192d98 commit-reach: move min_generation check into paint_queue_get()

-- 
gitgitgadget

  parent reply	other threads:[~2026-06-26 13:08 UTC|newest]

Thread overview: 75+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-20 10:36 [PATCH/RFC 0/6] commit-reach: terminate merge-base walk when one side is exhausted Kristofer Karlsson via GitGitGadget
2026-06-20 10:36 ` [PATCH/RFC 1/6] commit-reach: decouple ahead_behind from nonstale_queue Kristofer Karlsson via GitGitGadget
2026-06-22 18:00   ` Derrick Stolee
2026-06-22 18:53     ` Kristofer Karlsson
2026-06-20 10:36 ` [PATCH/RFC 2/6] commit-reach: introduce struct paint_queue with per-side counters Kristofer Karlsson via GitGitGadget
2026-06-22 18:10   ` Derrick Stolee
2026-06-22 19:14     ` Kristofer Karlsson
2026-06-22 20:23       ` Derrick Stolee
2026-06-23 10:13         ` Kristofer Karlsson
2026-06-23 13:50           ` Derrick Stolee
2026-06-23 14:09             ` Kristofer Karlsson
2026-06-23 14:17               ` Derrick Stolee
2026-06-24 11:25                 ` Kristofer Karlsson
2026-06-20 10:36 ` [PATCH/RFC 3/6] commit-reach: terminate merge-base walk when one paint side is exhausted Kristofer Karlsson via GitGitGadget
2026-06-22 18:12   ` Derrick Stolee
2026-06-22 19:19     ` Kristofer Karlsson
2026-06-22 20:26       ` Derrick Stolee
2026-06-22 21:03         ` Kristofer Karlsson
2026-06-23 13:40           ` Derrick Stolee
2026-06-20 10:36 ` [PATCH/RFC 4/6] t6600: add test cases for side-exhaustion edge cases Elijah Newren via GitGitGadget
2026-06-22 18:15   ` Derrick Stolee
2026-06-22 19:25     ` Kristofer Karlsson
2026-06-22 20:28       ` Derrick Stolee
2026-06-20 10:36 ` [PATCH/RFC 5/6] t6099, t6600: add side-exhaustion regression tests Kristofer Karlsson via GitGitGadget
2026-06-22 18:16   ` Derrick Stolee
2026-06-20 10:36 ` [PATCH/RFC 6/6] Documentation/technical: add paint-down-to-common doc Kristofer Karlsson via GitGitGadget
2026-06-22 18:21   ` Derrick Stolee
2026-06-22 19:30     ` Kristofer Karlsson
2026-06-22 18:22 ` [PATCH/RFC 0/6] commit-reach: terminate merge-base walk when one side is exhausted Derrick Stolee
2026-06-24 12:14 ` [PATCH v2 0/7] " Kristofer Karlsson via GitGitGadget
2026-06-24 12:14   ` [PATCH v2 1/7] Documentation/technical: add paint-down-to-common doc Kristofer Karlsson via GitGitGadget
2026-06-24 17:09     ` Junio C Hamano
2026-06-24 12:14   ` [PATCH v2 2/7] t6600: add test cases for side-exhaustion edge cases Elijah Newren via GitGitGadget
2026-06-24 13:43     ` Derrick Stolee
2026-06-24 14:33       ` Kristofer Karlsson
2026-06-24 12:14   ` [PATCH v2 3/7] t6099, t6600: add side-exhaustion regression tests Kristofer Karlsson via GitGitGadget
2026-06-24 12:14   ` [PATCH v2 4/7] commit-reach: add trace2 instrumentation to paint_down_to_common() Kristofer Karlsson via GitGitGadget
2026-06-24 13:41     ` Derrick Stolee
2026-06-24 14:31       ` Kristofer Karlsson
2026-06-24 12:14   ` [PATCH v2 5/7] commit-reach: introduce struct paint_state with per-side counters Kristofer Karlsson via GitGitGadget
2026-06-24 13:54     ` Derrick Stolee
2026-06-24 14:38       ` Kristofer Karlsson
2026-06-24 12:14   ` [PATCH v2 6/7] commit-reach: remove unused nonstale_queue dedup wrappers Kristofer Karlsson via GitGitGadget
2026-06-24 13:55     ` Derrick Stolee
2026-06-24 12:14   ` [PATCH v2 7/7] commit-reach: terminate merge-base walk when one paint side is exhausted Kristofer Karlsson via GitGitGadget
2026-06-24 14:02     ` Derrick Stolee
2026-06-24 14:47       ` Kristofer Karlsson
2026-06-24 15:07         ` Derrick Stolee
2026-06-24 13:34   ` [PATCH v2 0/7] commit-reach: terminate merge-base walk when one " Derrick Stolee
2026-06-24 14:25     ` Kristofer Karlsson
2026-06-24 14:09   ` Derrick Stolee
2026-06-26 13:07   ` Kristofer Karlsson via GitGitGadget [this message]
2026-06-26 13:07     ` [PATCH v3 1/8] Documentation/technical: add paint-down-to-common doc Kristofer Karlsson via GitGitGadget
2026-06-26 13:07     ` [PATCH v3 2/8] t6600: add test cases for side-exhaustion edge cases Elijah Newren via GitGitGadget
2026-06-26 13:08     ` [PATCH v3 3/8] t6099, t6600: add side-exhaustion regression tests Kristofer Karlsson via GitGitGadget
2026-06-26 13:08     ` [PATCH v3 4/8] commit-reach: add trace2 instrumentation to paint_down_to_common() Kristofer Karlsson via GitGitGadget
2026-06-26 14:31       ` Derrick Stolee
2026-06-26 14:35         ` Kristofer Karlsson
2026-06-26 13:08     ` [PATCH v3 5/8] commit-reach: introduce struct paint_state with per-side counters Kristofer Karlsson via GitGitGadget
2026-06-26 21:13       ` René Scharfe
2026-06-26 21:57         ` Kristofer Karlsson
2026-06-26 13:08     ` [PATCH v3 6/8] commit-reach: remove unused nonstale_queue dedup wrappers Kristofer Karlsson via GitGitGadget
2026-06-26 13:08     ` [PATCH v3 7/8] commit-reach: terminate merge-base walk when one paint side is exhausted Kristofer Karlsson via GitGitGadget
2026-06-26 14:29       ` Kristofer Karlsson
2026-06-26 14:32         ` Derrick Stolee
2026-06-26 16:41           ` Kristofer Karlsson
2026-06-26 14:35       ` Derrick Stolee
2026-06-26 14:39         ` Kristofer Karlsson
2026-06-26 13:08     ` [PATCH v3 8/8] commit-reach: move min_generation check into paint_queue_get() Kristofer Karlsson via GitGitGadget
2026-06-26 14:42       ` Derrick Stolee
2026-06-26 14:53         ` Kristofer Karlsson
2026-06-26 14:58           ` Derrick Stolee
2026-06-26 16:36     ` [PATCH v3 0/8] commit-reach: terminate merge-base walk when one side is exhausted Junio C Hamano
2026-06-26 16:43       ` Kristofer Karlsson
2026-06-26 18:43         ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=pull.2149.v3.git.1782479286.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=krka@spotify.com \
    --cc=newren@gmail.com \
    --cc=stolee@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox