From: "Kristofer Karlsson via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Kristofer Karlsson <krka@spotify.com>,
Kristofer Karlsson <krka@spotify.com>
Subject: [PATCH v5 10/10] commit-reach: remove commit-date ordering fallback
Date: Wed, 01 Jul 2026 16:37:11 +0000 [thread overview]
Message-ID: <d68972b1d735db86395231c3d209f7bd42938761.1782923832.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2149.v5.git.1782923832.gitgitgadget@gmail.com>
From: Kristofer Karlsson <krka@spotify.com>
Remove the fallback that switched paint_down_to_common() from
generation ordering to commit-date ordering when the commit-graph
lacks corrected commit dates (v1 graph with topo levels only).
The fallback was added in 091f4cf3 (commit: don't use generation
numbers if not needed, 2018-08-30) to avoid a performance
regression on the Linux kernel repo where v1 topo levels caused
"git merge-base v4.8 v4.9" to walk 636k commits instead of 167k.
A side branch with a low topo level stayed in the queue behind a
long chain, preventing early STALE propagation.
Side-exhaustion (added in the previous commits) solves this
differently by terminating the walk as soon as one paint side
empties from the queue, preventing the deep walk regardless of
queue ordering. Benchmarks of "git merge-base --all v4.8 v4.9"
on the Linux kernel repo show that side-exhaustion reduces the
step count far below what the date-ordering fallback achieved:
steps time
no graph, baseline: 167,413 3.25 s
v1 graph, baseline: 167,413 0.25 s
v2 graph, baseline: 167,441 0.29 s
v1 graph, this series: 5,725 0.02 s
v2 graph, this series: 3,887 0.01 s
With generation ordering always active, the existing min_generation
check in paint_queue_get() correctly identifies when the walk has
reached the finite generation region. The date ordering fallback
broke this invariant: a commit could have a finite topo level
while the queue was date-ordered, causing the early exit to fire
before all merge bases were found.
Also remove corrected_commit_dates_enabled() from commit-graph.c
which has no remaining callers.
Signed-off-by: Kristofer Karlsson <krka@spotify.com>
---
.../technical/paint-down-to-common.adoc | 40 -------------------
commit-graph.c | 11 -----
commit-graph.h | 6 ---
commit-reach.c | 14 +++----
t/t6600-test-reach.sh | 8 ++--
5 files changed, 10 insertions(+), 69 deletions(-)
diff --git a/Documentation/technical/paint-down-to-common.adoc b/Documentation/technical/paint-down-to-common.adoc
index 8a8a7a930e..7b8e483af2 100644
--- a/Documentation/technical/paint-down-to-common.adoc
+++ b/Documentation/technical/paint-down-to-common.adoc
@@ -40,20 +40,10 @@ and PARENT2, it is a merge-base candidate. A candidate gains the
STALE flag so its ancestors propagate staleness -- any deeper common
ancestor is necessarily redundant.
-NOTE: When the commit-graph uses only topological levels (generation
-number v1) and the caller passes `min_generation = 0`, a legacy
-fallback replaces the generation-ordered comparator with a pure
-commit-date comparator. This breaks the ordering invariants
-described below -- see <<date-ordering-fallback>>.
-
[[generation-regions]]
INFINITY and finite generation regions
--------------------------------------
-The properties in this section assume generation-number ordering (the
-default comparator). They do NOT hold when the date-ordering fallback
-is active -- see <<date-ordering-fallback>>.
-
The commit-graph stores a generation number for each commit. Commits
not in the commit-graph have generation `GENERATION_NUMBER_INFINITY`. The
graph is closed under reachability: if a commit is in the graph, all
@@ -154,36 +144,6 @@ descendant of this candidate (generation ordering guarantees
children are visited first), so it cannot be redundant and the walk
can stop immediately.
-This optimization is NOT safe when the date-ordering fallback is
-active, because commit-date order can visit a deeper ancestor
-before a shallower one -- see <<date-ordering-fallback>>.
-
-[[date-ordering-fallback]]
-Date-ordering fallback
-----------------------
-
-When `min_generation` is zero and the commit-graph does not contain
-corrected commit dates (generation number v1, which stores only
-topological levels), `paint_down_to_common()` replaces the default
-generation-ordered comparator with `compare_commits_by_commit_date`.
-
-This was introduced as a performance heuristic: topological levels
-are coarser than commit dates, so date ordering can reach merge
-bases in fewer steps when timestamps are well-behaved. However,
-commit dates are not required to be monotonic -- a parent can have
-a later date than its child (clock skew, rebases, etc.) -- so the
-queue may visit commits out of topological order.
-
-This disables optimizations that depend on generation ordering:
-
- 1. *Single result*: the first merge-base candidate found may not
- be the shallowest, because a deeper ancestor with a higher
- commit date can be dequeued first.
-
- 2. *Side-exhaustion* (see subsequent commits): one paint side can
- appear to drain from the queue while commits from that side are
- still waiting with lower dates, causing premature termination.
-
Related documentation
---------------------
diff --git a/commit-graph.c b/commit-graph.c
index 801471a098..3d5d41f65a 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -793,17 +793,6 @@ int generation_numbers_enabled(struct repository *r)
return !!first_generation;
}
-int corrected_commit_dates_enabled(struct repository *r)
-{
- struct commit_graph *g;
-
- g = prepare_commit_graph(r);
- if (!g || !g->num_commits)
- return 0;
-
- return g->read_generation_data;
-}
-
struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
{
struct commit_graph *g;
diff --git a/commit-graph.h b/commit-graph.h
index 13ca4ff010..d96147a07c 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -136,12 +136,6 @@ struct commit_graph *parse_commit_graph(struct repository *r,
*/
int generation_numbers_enabled(struct repository *r);
-/*
- * Return 1 if and only if the repository has a commit-graph
- * file and generation data chunk has been written for the file.
- */
-int corrected_commit_dates_enabled(struct repository *r);
-
struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r);
enum commit_graph_write_flags {
diff --git a/commit-reach.c b/commit-reach.c
index 871d67d07a..826c4324f2 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -89,7 +89,6 @@ struct paint_state {
size_t parent1_count;
size_t parent2_count;
size_t mb_candidate_count;
- int gen_ordered;
timestamp_t min_generation;
timestamp_t last_gen;
};
@@ -166,7 +165,6 @@ static struct commit *paint_queue_get(struct paint_state *state)
/* one side is exhausted */
if ((!state->parent1_count || !state->parent2_count) &&
- state->gen_ordered &&
generation < GENERATION_NUMBER_INFINITY)
return NULL;
}
@@ -187,9 +185,13 @@ static int paint_down_to_common(struct repository *r,
enum merge_base_flags mb_flags,
struct commit_list **result)
{
+ /*
+ * Generation ordering is required for the side-exhaustion and
+ * single-result early exits, which rely on topological traversal
+ * order (children visited before parents) in the finite region.
+ */
struct paint_state state = {
- .queue = { compare_commits_by_gen_then_commit_date },
- .gen_ordered = 1,
+ .queue = { compare_commits_by_gen_then_commit_date }
};
struct commit *commit;
int i;
@@ -198,10 +200,6 @@ static int paint_down_to_common(struct repository *r,
state.min_generation = min_generation;
state.last_gen = GENERATION_NUMBER_INFINITY;
- if (!min_generation && !corrected_commit_dates_enabled(r)) {
- state.queue.compare = compare_commits_by_commit_date;
- state.gen_ordered = 0;
- }
one->object.flags |= PARENT1;
if (!n) {
diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh
index fd11febf1a..6a0899c44a 100755
--- a/t/t6600-test-reach.sh
+++ b/t/t6600-test-reach.sh
@@ -382,7 +382,7 @@ 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 4 5 5
+ test_paint_down_steps 5 4 5 4
'
test_expect_success 'setup mixed finite/INFINITY topology' '
@@ -415,7 +415,7 @@ 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 9 57 81
+ test_paint_down_steps 81 9 57 37
'
test_expect_success 'merge-base --all with clock skew and v1 commit-graph (side-exhaustion)' '
@@ -429,7 +429,7 @@ test_expect_success 'merge-base --all with clock skew and v1 commit-graph (side-
>input &&
git rev-parse se-D >expect &&
run_all_modes git merge-base --all se-A se-B &&
- test_paint_down_steps 6 4 6 6
+ test_paint_down_steps 6 4 6 4
'
test_expect_success 'merge-base --all with clock skew returns wrong merge base (side-exhaustion)' '
@@ -445,7 +445,7 @@ test_expect_success 'merge-base --all with clock skew returns wrong merge base (
>input &&
git rev-parse se2-MB1 >expect &&
run_all_modes git merge-base --all se2-A se2-B &&
- test_paint_down_steps 8 6 8 8
+ test_paint_down_steps 8 6 8 6
'
test_expect_success 'reduce_heads' '
--
gitgitgadget
next prev parent reply other threads:[~2026-07-01 16:37 UTC|newest]
Thread overview: 104+ 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 ` [PATCH v3 0/8] " Kristofer Karlsson via GitGitGadget
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
2026-06-28 12:25 ` [PATCH v4 " Kristofer Karlsson via GitGitGadget
2026-06-28 12:25 ` [PATCH v4 1/8] Documentation/technical: add paint-down-to-common doc Kristofer Karlsson via GitGitGadget
2026-06-28 12:25 ` [PATCH v4 2/8] t6600: add test cases for side-exhaustion edge cases Elijah Newren via GitGitGadget
2026-06-28 12:25 ` [PATCH v4 3/8] t6099, t6600: add side-exhaustion regression tests Kristofer Karlsson via GitGitGadget
2026-06-28 12:25 ` [PATCH v4 4/8] commit-reach: add trace2 instrumentation to paint_down_to_common() Kristofer Karlsson via GitGitGadget
2026-06-28 12:25 ` [PATCH v4 5/8] commit-reach: introduce struct paint_state with per-side counters Kristofer Karlsson via GitGitGadget
2026-06-28 12:25 ` [PATCH v4 6/8] commit-reach: remove unused nonstale_queue dedup wrappers Kristofer Karlsson via GitGitGadget
2026-06-29 5:25 ` SZEDER Gábor
2026-06-29 10:09 ` Kristofer Karlsson
2026-06-28 12:25 ` [PATCH v4 7/8] commit-reach: terminate merge-base walk when one paint side is exhausted Kristofer Karlsson via GitGitGadget
2026-06-28 12:25 ` [PATCH v4 8/8] commit-reach: move min_generation check into paint_queue_get() Kristofer Karlsson via GitGitGadget
2026-06-28 15:15 ` Derrick Stolee
2026-06-28 15:16 ` [PATCH v4 0/8] commit-reach: terminate merge-base walk when one side is exhausted Derrick Stolee
2026-06-29 12:11 ` Kristofer Karlsson
2026-06-29 12:40 ` Derrick Stolee
2026-06-29 12:59 ` Kristofer Karlsson
2026-07-01 16:37 ` [PATCH v5 00/10] " Kristofer Karlsson via GitGitGadget
2026-07-01 16:37 ` [PATCH v5 01/10] Documentation/technical: add paint-down-to-common doc Kristofer Karlsson via GitGitGadget
2026-07-01 16:37 ` [PATCH v5 02/10] test-lib-functions: improve diagnostic output for trace2 data assertions Kristofer Karlsson via GitGitGadget
2026-07-01 16:37 ` [PATCH v5 03/10] t6600: add test cases for side-exhaustion edge cases Elijah Newren via GitGitGadget
2026-07-01 16:37 ` [PATCH v5 04/10] t6099, t6600: add side-exhaustion regression tests Kristofer Karlsson via GitGitGadget
2026-07-01 16:37 ` [PATCH v5 05/10] commit-reach: add trace2 instrumentation to paint_down_to_common() Kristofer Karlsson via GitGitGadget
2026-07-01 16:37 ` [PATCH v5 06/10] t6600: add clock-skew topologies and step counts for edge cases Kristofer Karlsson via GitGitGadget
2026-07-01 16:37 ` [PATCH v5 07/10] commit-reach: introduce struct paint_state with per-side counters Kristofer Karlsson via GitGitGadget
2026-07-01 16:37 ` [PATCH v5 08/10] commit-reach: terminate merge-base walk when one paint side is exhausted Kristofer Karlsson via GitGitGadget
2026-07-01 16:37 ` [PATCH v5 09/10] commit-reach: move min_generation check into paint_queue_get() Kristofer Karlsson via GitGitGadget
2026-07-01 16:37 ` Kristofer Karlsson via GitGitGadget [this message]
2026-07-01 20:06 ` [PATCH v5 00/10] commit-reach: terminate merge-base walk when one side is exhausted Junio C Hamano
2026-07-01 21:15 ` Kristofer Karlsson
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=d68972b1d735db86395231c3d209f7bd42938761.1782923832.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=krka@spotify.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.