* Re: [PATCH 4/7] hash: make git_hash_discard() idempotent
From: Junio C Hamano @ 2026-07-07 16:22 UTC (permalink / raw)
To: Jeff King; +Cc: git, Patrick Steinhardt, brian m. carlson
In-Reply-To: <20260707050700.GD1288294@coredump.intra.peff.net>
Jeff King <peff@peff.net> writes:
> You must always either finalize or discard a hash context to release any
> resources, but you must call only one such function. This creates extra
> work for some callers, since their cleanup code paths need to know
> whether they got there via their happy path (and the finalization
> happened) or due to an error (in which case they need to discard).
>
> Let's add an "active" flag that turns a redundant discard into a noop.
> That lets you safely do this:
>
> git_hash_init(&ctx, algo);
> ...
> if (some_error)
> goto out;
> ...
> git_hash_final(result, &ctx);
>
> out:
> git_hash_discard(&ctx);
>
> This should avoid future errors, and will also let us simplify a few
> existing callers (in future patches).
Hmph, so is the point of this change to allow _discard() to be
called even after _final() was already called that we do not need an
early return or something before the out: label?
Unlike commit_*() and rollback_*() used in lockfile API, where the
names clearly say which one is for happy and which one is for error
case, the _final() and _discard() pair does not exactly tell me
which is which, but I guess I will get used to it, perhaps.
But the change nevertheless looks mostly good except for one "hmph".
When _init() is called, active gets turned on automatically, and
either _discard() or _final() turns it off. Only _discard() is
protected from getting called multiple times. Is this because
it is already a no-op to call _final() multiple times?
Thanks.
> Signed-off-by: Jeff King <peff@peff.net>
> ---
> hash.c | 6 ++++++
> hash.h | 1 +
> 2 files changed, 7 insertions(+)
>
> diff --git a/hash.c b/hash.c
> index 55d1d41770..b1296f0018 100644
> --- a/hash.c
> +++ b/hash.c
> @@ -285,6 +285,7 @@ void git_hash_free(struct git_hash_ctx *ctx)
> void git_hash_init(struct git_hash_ctx *ctx, const struct git_hash_algo *algop)
> {
> algop->init_fn(ctx);
> + ctx->active = true;
> }
>
> void git_hash_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
> @@ -300,16 +301,21 @@ void git_hash_update(struct git_hash_ctx *ctx, const void *in, size_t len)
> void git_hash_final(unsigned char *hash, struct git_hash_ctx *ctx)
> {
> ctx->algop->final_fn(hash, ctx);
> + ctx->active = false;
> }
>
> void git_hash_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
> {
> ctx->algop->final_oid_fn(oid, ctx);
> + ctx->active = false;
> }
>
> void git_hash_discard(struct git_hash_ctx *ctx)
> {
> + if (!ctx->active)
> + return;
> ctx->algop->discard_fn(ctx);
> + ctx->active = false;
> }
>
> uint32_t hash_algo_by_name(const char *name)
> diff --git a/hash.h b/hash.h
> index 5686914b71..f97f7b9ff4 100644
> --- a/hash.h
> +++ b/hash.h
> @@ -281,6 +281,7 @@ struct git_hash_ctx {
> git_SHA_CTX_unsafe sha1_unsafe;
> git_SHA256_CTX sha256;
> } state;
> + bool active;
> };
>
> typedef void (*git_hash_init_fn)(struct git_hash_ctx *ctx);
^ permalink raw reply
* Re: CVE-2026-55200 libssh2
From: Todd Zullinger @ 2026-07-07 16:24 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Berner Martin, git
In-Reply-To: <26531fd0-4a21-c8ef-84a9-25c871cde303@gmx.de>
Hi,
Johannes Schindelin wrote:
> Back to your question why Git for Windows still only includes v1.11.1 of
> libssh2. The answer is rather trivial: MSYS2 (on which Git for Windows is
> based through a healthy collaboration) includes only that version:
>
> https://packages.msys2.org/base/mingw-w64-libssh2
>
> And the reason for _that_ might be rooted in the fact that both the
> repository as well as the website of libssh2 list that as the very latest
> available version:
>
> - https://github.com/libssh2/libssh2/releases/latest currently redirects
> to https://github.com/libssh2/libssh2/releases/tag/libssh2-1.11.1
>
> - https://libssh2.org/ says:
>
> Download
> libssh2 1.11.1, released on 2024-10-16. *link to Changelog*
>
> Easy explanation, right?
Indeed. :)
An upstream issue requesting a release to aid in the
distribution of these fixes was filed about 2 months ago
(after CVE-2026-7598, before CVE-2026-55200 and some
others):
https://github.com/libssh2/libssh2/issues/1925
That may be worth tracking for anyone curious.
--
Todd
^ permalink raw reply
* Re: [PATCH 6/7] http: use idempotent git_hash_discard()
From: Junio C Hamano @ 2026-07-07 16:25 UTC (permalink / raw)
To: Jeff King; +Cc: git, Patrick Steinhardt, brian m. carlson
In-Reply-To: <20260707050814.GF1288294@coredump.intra.peff.net>
Jeff King <peff@peff.net> writes:
> Now that it is OK to call git_hash_discard() even after finalizing the
> hash, we no longer need the ctx_valid bool added by a2d8ea5a76 (http:
> discard hash in dumb-http http_object_request, 2026-07-02).
>
> Signed-off-by: Jeff King <peff@peff.net>
> ---
> http.c | 5 +----
> http.h | 1 -
> 2 files changed, 1 insertion(+), 5 deletions(-)
OK, because calling _discard() on an already discarded or finished
hash context is a no-op, we do not have to remember if we finialized
or discarded anymore, allowing us to be extra lazy and safe. Nice.
> diff --git a/http.c b/http.c
> index 0341de5031..caccf2108e 100644
> --- a/http.c
> +++ b/http.c
> @@ -2880,7 +2880,6 @@ struct http_object_request *new_http_object_request(const char *base_url,
> git_inflate_init(&freq->stream);
>
> git_hash_init(&freq->c, the_hash_algo);
> - freq->hash_ctx_valid = 1;
>
> freq->url = get_remote_object_url(base_url, hex, 0);
>
> @@ -2989,7 +2988,6 @@ int finish_http_object_request(struct http_object_request *freq)
> }
>
> git_hash_final_oid(&freq->real_oid, &freq->c);
> - freq->hash_ctx_valid = 0;
> if (freq->zret != Z_STREAM_END) {
> unlink_or_warn(freq->tmpfile.buf);
> return -1;
> @@ -3030,8 +3028,7 @@ void release_http_object_request(struct http_object_request **freq_p)
> curl_slist_free_all(freq->headers);
> strbuf_release(&freq->tmpfile);
> git_inflate_end(&freq->stream);
> - if (freq->hash_ctx_valid)
> - git_hash_discard(&freq->c);
> + git_hash_discard(&freq->c);
>
> free(freq);
> *freq_p = NULL;
> diff --git a/http.h b/http.h
> index 6b0639150f..729c51904d 100644
> --- a/http.h
> +++ b/http.h
> @@ -255,7 +255,6 @@ struct http_object_request {
> struct object_id oid;
> struct object_id real_oid;
> struct git_hash_ctx c;
> - int hash_ctx_valid;
> git_zstream stream;
> int zret;
> int rename;
^ permalink raw reply
* Re: [PATCH 7/7] hash: check ctx->active flag in all wrapper functions
From: Junio C Hamano @ 2026-07-07 16:33 UTC (permalink / raw)
To: Jeff King; +Cc: git, Patrick Steinhardt, brian m. carlson
In-Reply-To: <20260707050952.GG1288294@coredump.intra.peff.net>
Jeff King <peff@peff.net> writes:
> It only makes sense to call git_hash_update(), etc, on a hash context
> that has been initialized but not yet finalized or discarded. This is an
> unlikely error to make, but it's easy for us to catch it and complain.
>
> It's especially important because it would quietly "work" for many hash
> backends (like sha1dc, which is just manipulating some bytes) but would
> cause undefined behavior with others (like OpenSSL, which puts the
> context onto the heap). Checking the flag lets us catch problems
> consistently on every build.
>
> Note that we can't do the same for git_init_hash(). Even though it would
> cause a leak to call it twice (without an intervening final/discard),
> the point of the function is that the contents of the struct are
> undefined before the call. But calling it twice is an even less likely
> error to make, so not covering it is OK.
>
> Signed-off-by: Jeff King <peff@peff.net>
> ---
> hash.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
Among the four we see here, I agree that calling _clone and _update
on an already discarded or finalized context should be caught as an
error. As I alluded to earlier, though, I am not sure about
_final. The asymmetry in a design that allows _discard after _final
but not _final after _final disturbs me slightly, but perhaps that
is only because my morning caffeine has not yet kicked in.
>
> diff --git a/hash.c b/hash.c
> index b1296f0018..82f7e24404 100644
> --- a/hash.c
> +++ b/hash.c
> @@ -290,22 +290,32 @@ void git_hash_init(struct git_hash_ctx *ctx, const struct git_hash_algo *algop)
>
> void git_hash_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
> {
> + if (!src->active)
> + BUG("attempt to copy from an inactive hash context");
> + if (!dst->active)
> + BUG("attempt to copy to an inactive hash context");
> src->algop->clone_fn(dst, src);
> }
>
> void git_hash_update(struct git_hash_ctx *ctx, const void *in, size_t len)
> {
> + if (!ctx->active)
> + BUG("attempt to update an inactive hash context");
> ctx->algop->update_fn(ctx, in, len);
> }
>
> void git_hash_final(unsigned char *hash, struct git_hash_ctx *ctx)
> {
> + if (!ctx->active)
> + BUG("attempt to finalize an inactive hash context");
> ctx->algop->final_fn(hash, ctx);
> ctx->active = false;
> }
>
> void git_hash_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
> {
> + if (!ctx->active)
> + BUG("attempt to finalize an inactive hash context");
> ctx->algop->final_oid_fn(oid, ctx);
> ctx->active = false;
> }
^ permalink raw reply
* Re: [PATCH v5 1/2] Makefile: add $(GITLIBS) prerequisite to osxkeychain
From: Shnatu @ 2026-07-07 16:51 UTC (permalink / raw)
To: gitster
Cc: ben.knoble, git, gitgitgadget, koji.nakamaru, kristofferhaugsbakk,
ps, shardul.27591, snatu
In-Reply-To: <xmqqmrw3aoas.fsf@gitster.g>
> Sorry if I am mistaken, but as far as I can see, $(GITLIBS) includes
> common-main.o (and it being .o, not .a, it is always included in the
> result), and git-credential-osxkeychain.c comes with its own main()
> function.
>
> Using a list of things to link that contains common-main.o does not
> sound like a right thing to do; in other words, linking too many is
> just as bad as linking too little.
You are completely right, and I missed that altogether!!
In v6, I have reverted Patch 1 back to depending explicitly on
$(LIB_FILE) $(RUST_LIB) rather than $(GITLIBS) so that common-main.o is
excluded from the link step.
To ensure that linking errors in osxkeychain are caught automatically in
future CI runs, I have also added a third patch to the series:
"contrib: wire up osxkeychain in contrib/Makefile on macOS". This adds a
"test" target to contrib/credential/osxkeychain/Makefile and wires it
into contrib/Makefile under "all", "test", and "clean" whenever running
on macOS (Darwin). Now, when CI runs "make test" with TEST_CONTRIB_TOO=yes
on macOS runners, osxkeychain will always be compiled and linked.
^ permalink raw reply
* Re: [PATCH 1/2] commit-graph: add trace2 instrumentation for generation DFS
From: Junio C Hamano @ 2026-07-07 16:55 UTC (permalink / raw)
To: Kristofer Karlsson via GitGitGadget; +Cc: git, Kristofer Karlsson
In-Reply-To: <b865c2bcff53a32637aac426dd2c6ef4a4c27077.1783418384.git.gitgitgadget@gmail.com>
"Kristofer Karlsson via GitGitGadget" <gitgitgadget@gmail.com>
writes:
> From: Kristofer Karlsson <krka@spotify.com>
>
> Add a step counter and trace2_data_intmax call to
> compute_reachable_generation_numbers() to make the cost of
> the generation number DFS observable. This exposes a
> regression introduced in 199d452758 (commit-graph: fix
> "filling in" topological levels, 2025-04-07) where
Where did "fix filling in" came from? Are you blaming
199d452758 (commit-graph: return the prepared commit graph from
`prepare_commit_graph()`, 2025-09-04)
or something else that happend in April that year?
> incremental commit-graph writes re-walk the entire commit
> ancestry instead of reading topo levels from lower graph
> layers.
> Add a test that demonstrates the problem: with a two-layer
> split commit-graph, writing a new incremental layer for a
> commit whose parent is in the base layer walks all the way
> down to the root (7 steps for 5 base commits) instead of
> reading the existing topo level and stopping immediately
> (1 step).
OK. I expect that [2/2] would update this exact test to demonstrate
that with code updated in [2/2] the extra walk will no longer happen.
> Signed-off-by: Kristofer Karlsson <krka@spotify.com>
> ---
> commit-graph.c | 5 +++++
> t/t5324-split-commit-graph.sh | 28 ++++++++++++++++++++++++++++
> 2 files changed, 33 insertions(+)
>
> diff --git a/commit-graph.c b/commit-graph.c
> index 801471a098..4e39a048c4 100644
> --- a/commit-graph.c
> +++ b/commit-graph.c
> @@ -1653,6 +1653,7 @@ static void compute_reachable_generation_numbers(
> {
> int i;
> struct commit_list *list = NULL;
> + intmax_t steps = 0;
>
> for (i = 0; i < info->commits->nr; i++) {
> struct commit *c = info->commits->items[i];
> @@ -1671,6 +1672,7 @@ static void compute_reachable_generation_numbers(
> int all_parents_computed = 1;
> timestamp_t max_gen = 0;
>
> + steps++;
> for (parent = current->parents; parent; parent = parent->next) {
> repo_parse_commit(info->r, parent->item);
> gen = info->get_generation(parent->item, info->data);
> @@ -1694,6 +1696,9 @@ static void compute_reachable_generation_numbers(
> }
> }
> }
> +
> + trace2_data_intmax("commit-graph", info->r,
> + "generation-dfs-steps", steps);
> }
Pretty-much trivial addition of a trace element.
> diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh
> index 49a057cc2e..f9c57760f4 100755
> --- a/t/t5324-split-commit-graph.sh
> +++ b/t/t5324-split-commit-graph.sh
> @@ -718,6 +718,34 @@ test_expect_success 'write generation data chunk when commit-graph chain is repl
> )
> '
>
> +test_expect_success 'incremental write reads topo levels from all layers' '
> + git init topo-from-lower &&
> + (
> + cd topo-from-lower &&
> +
> + for i in $(test_seq 5)
> + do
> + test_commit base-$i || return 1
> + done &&
> + git commit-graph write --reachable &&
> +
> + test_commit extra &&
> + git commit-graph write --reachable --split=no-merge &&
> +
> + git checkout base-3 &&
> + test_commit new-branch &&
> +
> + GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
> + git commit-graph write --reachable --split=no-merge &&
> +
> + # BUG: topo levels from lower graph layers are not
> + # propagated, so the DFS re-walks from base-3 down to
> + # the root (7 steps) instead of reading topo levels
> + # from the existing graph (1 step).
> + test_trace2_data commit-graph generation-dfs-steps 7 <trace.txt
> + )
> +'
> +
> test_expect_success 'temporary graph layer is discarded upon failure' '
> git init layer-discard &&
> (
^ permalink raw reply
* Re: [PATCH 2/2] commit-graph: propagate topo_levels slab to all chain layers
From: Junio C Hamano @ 2026-07-07 17:00 UTC (permalink / raw)
To: Kristofer Karlsson via GitGitGadget; +Cc: git, Kristofer Karlsson
In-Reply-To: <f9c1482a76493520b948a2e918de7a5481fa1043.1783418384.git.gitgitgadget@gmail.com>
"Kristofer Karlsson via GitGitGadget" <gitgitgadget@gmail.com>
writes:
> From: Kristofer Karlsson <krka@spotify.com>
>
> Fix a regression introduced in 199d452758 (commit-graph: fix
> "filling in" topological levels, 2025-04-07) where the loop
I guess the same comment from [1/2] applies. We might be chasing
ghosts here. Is that elusive commit a total hallucination?
> On a repository with 2.78M commits and a multi-layer split
> commit-graph, this caused a single incremental commit-graph
> write to spend ~3.7 seconds in the generation DFS instead of
> microseconds.
Nice.
> Signed-off-by: Kristofer Karlsson <krka@spotify.com>
> ---
> commit-graph.c | 2 +-
> t/t5324-split-commit-graph.sh | 6 +-----
> 2 files changed, 2 insertions(+), 6 deletions(-)
>
> diff --git a/commit-graph.c b/commit-graph.c
> index 4e39a048c4..c2a711cceb 100644
> --- a/commit-graph.c
> +++ b/commit-graph.c
> @@ -2610,7 +2610,7 @@ int write_commit_graph(struct odb_source *source,
>
> g = prepare_commit_graph(ctx.r);
> for (struct commit_graph *chain = g; chain; chain = chain->base_graph)
> - g->topo_levels = &topo_levels;
> + chain->topo_levels = &topo_levels;
>
> if (flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS)
> ctx.changed_paths = 1;
> diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh
> index f9c57760f4..9e5ab7dbd0 100755
> --- a/t/t5324-split-commit-graph.sh
> +++ b/t/t5324-split-commit-graph.sh
> @@ -738,11 +738,7 @@ test_expect_success 'incremental write reads topo levels from all layers' '
> GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
> git commit-graph write --reachable --split=no-merge &&
>
> - # BUG: topo levels from lower graph layers are not
> - # propagated, so the DFS re-walks from base-3 down to
> - # the root (7 steps) instead of reading topo levels
> - # from the existing graph (1 step).
> - test_trace2_data commit-graph generation-dfs-steps 7 <trace.txt
> + test_trace2_data commit-graph generation-dfs-steps 1 <trace.txt
> )
> '
^ permalink raw reply
* [PATCH v7 1/3] Makefile: add $(RUST_LIB) prerequisite to osxkeychain
From: Shardul Natu via GitGitGadget @ 2026-07-07 17:02 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Shardul Natu, Koji Nakamaru,
Patrick Steinhardt, Shardul Natu, Ben Knoble, Shardul Natu
In-Reply-To: <pull.2288.v7.git.git.1783443745.gitgitgadget@gmail.com>
From: Shardul Natu <snatu@google.com>
When Rust is enabled, the git-credential-osxkeychain helper depends on
Rust symbols compiled into $(RUST_LIB). While commit 522ea8ef7d
("osxkeychain: fix build with Rust") updated the linker command line to
use $(LIBS), it omitted $(RUST_LIB) from the target prerequisite list.
Without this prerequisite, running a parallel build ("make -j") from a
clean working tree can fail because Make does not know to invoke Cargo
to build libgitcore.a before linking git-credential-osxkeychain.
Note that we depend explicitly on $(LIB_FILE) and $(RUST_LIB) rather
than $(GITLIBS). Unlike standard Git builtins and programs like scalar
(which define cmd_main() and rely on common-main.o to supply main()),
git-credential-osxkeychain.c defines its own standalone int main().
If $(GITLIBS) were used, $(filter %.o,$^) in the link recipe would
match both git-credential-osxkeychain.o and common-main.o, causing a
duplicate symbol linking error for _main on macOS.
Additionally, wrap the definitions of $(RUST_LIB) and the "rust" build
target in "ifndef NO_RUST". This ensures that when NO_RUST=1 is
specified, $(RUST_LIB) evaluates to empty, making the Rust dependency a
clean no-op without needing intermediate variables.
Signed-off-by: Shardul Natu <snatu@google.com>
---
Makefile | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 1f3f099f5c..7db38ecce9 100644
--- a/Makefile
+++ b/Makefile
@@ -939,6 +939,7 @@ TEST_SHELL_PATH = $(SHELL_PATH)
LIB_FILE = libgit.a
+ifndef NO_RUST
ifdef DEBUG
RUST_TARGET_DIR = target/debug
else
@@ -950,6 +951,7 @@ RUST_LIB = $(RUST_TARGET_DIR)/gitcore.lib
else
RUST_LIB = $(RUST_TARGET_DIR)/libgitcore.a
endif
+endif
GITLIBS = common-main.o $(LIB_FILE)
EXTLIBS =
@@ -3019,11 +3021,13 @@ scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS)
$(LIB_FILE): $(LIB_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
+ifndef NO_RUST
$(RUST_LIB): Cargo.toml $(RUST_SOURCES) $(LIB_FILE)
$(QUIET_CARGO)cargo build $(CARGO_ARGS)
.PHONY: rust
rust: $(RUST_LIB)
+endif
export DEFAULT_EDITOR DEFAULT_PAGER
@@ -4074,7 +4078,8 @@ $(LIBGIT_HIDDEN_EXPORT): $(LIBGIT_PARTIAL_EXPORT)
contrib/libgit-sys/libgitpub.a: $(LIBGIT_HIDDEN_EXPORT)
$(AR) $(ARFLAGS) $@ $^
-contrib/credential/osxkeychain/git-credential-osxkeychain: contrib/credential/osxkeychain/git-credential-osxkeychain.o $(LIB_FILE) GIT-LDFLAGS
+# When Rust is enabled, git-credential-osxkeychain depends on Rust symbols in $(RUST_LIB)
+contrib/credential/osxkeychain/git-credential-osxkeychain: contrib/credential/osxkeychain/git-credential-osxkeychain.o $(LIB_FILE) $(RUST_LIB) GIT-LDFLAGS
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
$(filter %.o,$^) $(LIBS) -framework Security -framework CoreFoundation
--
gitgitgadget
^ permalink raw reply related
* [PATCH v7 2/3] Makefile: support universal macOS builds via RUST_TARGETS
From: Shardul Natu via GitGitGadget @ 2026-07-07 17:02 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Shardul Natu, Koji Nakamaru,
Patrick Steinhardt, Shardul Natu, Ben Knoble, Shardul Natu
In-Reply-To: <pull.2288.v7.git.git.1783443745.gitgitgadget@gmail.com>
From: Shardul Natu <snatu@google.com>
On macOS, Universal Binaries contain native executable code for
multiple architectures (such as Intel x86_64 and Apple Silicon arm64)
bundled into a single file. This is standard practice for macOS
distribution and CI packaging (such as internal distribution packages
or tooling like Burrito/Homebrew), allowing a single build artifact
to run natively across all Macs without Rosetta emulation or
maintaining separate packages.
When building Git C code for multiple architectures on macOS, the
Apple toolchain (clang) natively supports universal builds via
CFLAGS/LDFLAGS. When "-arch x86_64 -arch arm64" is passed, clang
automatically compiles and links universal binaries for all C object
files and executables out of the box.
Cargo and rustc, however, do not support multiple "-arch" flags or
emitting universal binaries in a single invocation. Instead, Cargo
requires invoking each target triple independently (e.g., passing
"--target x86_64-apple-darwin" and "--target aarch64-apple-darwin").
To bridge this gap when Rust is enabled:
1. Allow specifying space-separated target triples in RUST_TARGETS.
2. Introduce declarative pattern rules (target/%/...) to compile
each target-specific library slice via Cargo.
3. On macOS, if multiple targets are specified, use "lipo" (part of
the mandatory Xcode Command Line Tools) to combine the resulting
static libraries into target/release/libgitcore.a.
Once $(RUST_LIB) is compiled into a universal static archive, the
standard C linker seamlessly links it with the C object files to
produce universal Git executables.
Signed-off-by: Shardul Natu <snatu@google.com>
---
Makefile | 39 +++++++++++++++++++++++++++++++++++----
1 file changed, 35 insertions(+), 4 deletions(-)
diff --git a/Makefile b/Makefile
index 7db38ecce9..9921af992b 100644
--- a/Makefile
+++ b/Makefile
@@ -500,6 +500,14 @@ include shared.mak
#
# Building Rust code requires Cargo.
#
+# Define RUST_TARGETS if you want to cross-compile. If left unspecified, it uses
+# the default Rust target on the system.
+#
+# On macOS, this supports specifying multiple targets, separated by a space.
+# This will produce a Universal static library using `lipo`.
+#
+# Example: RUST_TARGETS="aarch64-apple-darwin x86_64-apple-darwin"
+#
# == SHA-1 and SHA-256 defines ==
#
# === SHA-1 backend ===
@@ -941,16 +949,17 @@ LIB_FILE = libgit.a
ifndef NO_RUST
ifdef DEBUG
-RUST_TARGET_DIR = target/debug
+RUST_BUILD_CONFIG = debug
else
-RUST_TARGET_DIR = target/release
+RUST_BUILD_CONFIG = release
endif
ifeq ($(uname_S),Windows)
-RUST_LIB = $(RUST_TARGET_DIR)/gitcore.lib
+RUST_LIB_NAME = gitcore.lib
else
-RUST_LIB = $(RUST_TARGET_DIR)/libgitcore.a
+RUST_LIB_NAME = libgitcore.a
endif
+RUST_LIB = target/$(RUST_BUILD_CONFIG)/$(RUST_LIB_NAME)
endif
GITLIBS = common-main.o $(LIB_FILE)
@@ -3022,8 +3031,30 @@ $(LIB_FILE): $(LIB_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
ifndef NO_RUST
+ifeq ($(RUST_TARGETS),)
$(RUST_LIB): Cargo.toml $(RUST_SOURCES) $(LIB_FILE)
$(QUIET_CARGO)cargo build $(CARGO_ARGS)
+else
+ifneq ($(words $(RUST_TARGETS)),1)
+ifneq ($(uname_S),Darwin)
+$(error Building universal Rust libraries requires macOS (lipo is not available on $(uname_S)))
+endif
+endif
+
+RUST_MEMBER_LIBS = $(foreach target,$(RUST_TARGETS),target/$(target)/$(RUST_BUILD_CONFIG)/$(RUST_LIB_NAME))
+$(RUST_MEMBER_LIBS): target/%/$(RUST_BUILD_CONFIG)/$(RUST_LIB_NAME): Cargo.toml $(RUST_SOURCES) $(LIB_FILE)
+ $(QUIET_CARGO)cargo build $(CARGO_ARGS) --target $*
+
+$(RUST_LIB): $(RUST_MEMBER_LIBS)
+ $(call mkdir_p_parent_template)
+ $(QUIET_GEN)\
+ if test $(words $(RUST_TARGETS)) -gt 1; \
+ then \
+ lipo -create $^ -output $@; \
+ else \
+ cp $< $@; \
+ fi
+endif
.PHONY: rust
rust: $(RUST_LIB)
--
gitgitgadget
^ permalink raw reply related
* [PATCH v7 3/3] contrib: wire up osxkeychain in contrib/Makefile on macOS
From: Shardul Natu via GitGitGadget @ 2026-07-07 17:02 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Shardul Natu, Koji Nakamaru,
Patrick Steinhardt, Shardul Natu, Ben Knoble, Shardul Natu
In-Reply-To: <pull.2288.v7.git.git.1783443745.gitgitgadget@gmail.com>
From: Shardul Natu <snatu@google.com>
When running "make test" with TEST_CONTRIB_TOO=yes (which is default in
macOS CI workflows), $(MAKE) -C contrib/ test is invoked. However,
contrib/Makefile only invoked tests for diff-highlight and subtree,
meaning git-credential-osxkeychain was never built or verified during
standard CI test runs.
Add a "test" target to contrib/credential/osxkeychain/Makefile that
depends on building git-credential-osxkeychain. Additionally, wire up
credential/osxkeychain in contrib/Makefile under "all", "test", and
"clean" whenever running on macOS (Darwin).
This ensures that running "make test" or "make all" in contrib on macOS
automatically builds and links git-credential-osxkeychain, preventing
future build or symbol linking regressions from slipping through CI.
Signed-off-by: Shardul Natu <snatu@google.com>
---
contrib/Makefile | 10 ++++++++++
contrib/credential/osxkeychain/Makefile | 4 +++-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/contrib/Makefile b/contrib/Makefile
index 787cd07f52..7962a9ff12 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -1,10 +1,20 @@
+-include ../config.mak.autogen
+-include ../config.mak
+
+ifeq ($(uname_S),Darwin)
+OS_CONTRIB += credential/osxkeychain
+endif
+
all::
+ $(foreach dir,$(OS_CONTRIB),$(MAKE) -C $(dir) $@;)
test::
$(MAKE) -C diff-highlight $@
$(MAKE) -C subtree $@
+ $(foreach dir,$(OS_CONTRIB),$(MAKE) -C $(dir) $@;)
clean::
$(MAKE) -C contacts $@
$(MAKE) -C diff-highlight $@
$(MAKE) -C subtree $@
+ $(foreach dir,$(OS_CONTRIB),$(MAKE) -C $(dir) $@;)
diff --git a/contrib/credential/osxkeychain/Makefile b/contrib/credential/osxkeychain/Makefile
index 219b0d7f49..d9fba07e8d 100644
--- a/contrib/credential/osxkeychain/Makefile
+++ b/contrib/credential/osxkeychain/Makefile
@@ -10,4 +10,6 @@ install:
clean:
$(MAKE) -C ../../.. clean-git-credential-osxkeychain
-.PHONY: all git-credential-osxkeychain install clean
+test: git-credential-osxkeychain
+
+.PHONY: all git-credential-osxkeychain install clean test
--
gitgitgadget
^ permalink raw reply related
* [PATCH v7 0/3] Makefile: link osxkeychain helper against Rust
From: Shardul Natu via GitGitGadget @ 2026-07-07 17:02 UTC (permalink / raw)
To: git
Cc: Kristoffer Haugsbakk, Shardul Natu, Koji Nakamaru,
Patrick Steinhardt, Shardul Natu, Ben Knoble
In-Reply-To: <pull.2288.v6.git.git.1783378333.gitgitgadget@gmail.com>
This series improves macOS build reliability, automated CI verification, and
distribution support when Rust is enabled in the Git build system. It
addresses three distinct challenges: a parallel build race condition in
git-credential-osxkeychain, support for macOS Universal Binaries
(multi-architecture distribution), and missing automated CI test wiring for
macOS contrib utilities.
Why This Series is Needed
=========================
1. Parallel Build Race Condition (make -j): While commit 522ea8ef7d
("osxkeychain: fix build with Rust") updated the link command for
git-credential-osxkeychain to pass $(LIBS), it omitted $(RUST_LIB) from
the target prerequisite list. When running a parallel build (make -j)
from a clean working tree, Make can attempt to link
git-credential-osxkeychain before Cargo has finished compiling
libgitcore.a, causing linker failures.
2. macOS Universal Binary (lipo) Support: On macOS, Universal Binaries
bundle native executable code for multiple architectures (Intel x86_64
and Apple Silicon arm64) into a single file. This is standard practice
for macOS distribution and CI packaging (such as Burrito, Homebrew, and
Git's macOS CI runners), allowing a single artifact to run natively
across all Macs without Rosetta translation.
While Apple's C compiler (clang) natively supports universal builds by
passing -arch x86_64 -arch arm64 in CFLAGS and LDFLAGS, Cargo and rustc do
not support multiple -arch flags in a single invocation. Instead, Cargo must
be invoked separately for each target triple (--target x86_64-apple-darwin
and --target aarch64-apple-darwin). This series bridges that gap.
3. Automated CI Verification for Contrib on macOS: When running make test
with TEST_CONTRIB_TOO=yes (default in macOS CI workflows), $(MAKE) -C
contrib/ test is invoked. However, contrib/Makefile only invoked tests
for diff-highlight and subtree, meaning git-credential-osxkeychain was
never compiled or verified during standard CI test runs.
Overview of Patches
===================
* Patch 1: Makefile: add $(RUST_LIB) prerequisite to osxkeychain Adds
$(RUST_LIB) as a prerequisite dependency to the osxkeychain target,
eliminating the parallel build race condition. Additionally, wraps the
definitions of $(RUST_LIB) and the rust build target in ifndef NO_RUST so
that disabling Rust cleanly makes the dependency a no-op.
* Patch 2: Makefile: support universal macOS builds via RUST_TARGETS Allows
users to specify space-separated target triples in RUST_TARGETS.
Introduces declarative pattern rules (target/%/...) to compile each
target slice via Cargo, and uses lipo (part of the mandatory Xcode
Command Line Tools) to combine the resulting static archives into a
universal library at target/release/libgitcore.a. Uses
mkdir_p_parent_template to guarantee directory creation before lipo.
* Patch 3: contrib: wire up osxkeychain in contrib/Makefile on macOS Adds
a test target to contrib/credential/osxkeychain/Makefile that depends
on building git-credential-osxkeychain. Introduces a generic OS_CONTRIB
variable in contrib/Makefile to conditionally wire
credential/osxkeychain into all, test, and clean whenever running on
macOS (Darwin). This guarantees that standard CI test runs on macOS
automatically compile and link the helper, preventing build
regressions.
Changes since v5:
* Reverted Patch 1 to depend explicitly on $(LIB_FILE) $(RUST_LIB) rather
than $(GITLIBS). Unlike Git builtins or scalar (which define cmd_main()),
git-credential-osxkeychain.c defines its own standalone main(), meaning
$(GITLIBS) caused a duplicate symbol error for _main during linking.
* Added Patch 3 ("contrib: wire up osxkeychain in contrib/Makefile on
macOS") using a scalable OS_CONTRIB variable so that running make test
with TEST_CONTRIB_TOO=yes in macOS CI workflows automatically verifies
compilation and linking integrity.
Changes since v4:
* Changed the osxkeychain prerequisite dependency from $(LIB_FILE)
$(RUST_LIB) to $(GITLIBS) to match the canonical prerequisite pattern
used by all other core Git targets linking $(LIBS).
Changes since v3:
* Removed leading @ from $(call mkdir_p_parent_template) so it relies on
the built-in $(QUIET_MKDIR_P_PARENT) behavior, matching existing Makefile
conventions.
* Replaced if [ with if test in Bourne shell recipe snippets to strictly
adhere to the project's CodingGuidelines.
Changes since v2:
* Split the original combined commit into a two-patch series to separate
prerequisite bug fixes from Universal Binary features.
* Added $(call mkdir_p_parent_template) prior to invoking lipo to guarantee
that parent target directories exist.
Shardul Natu (3):
Makefile: add $(RUST_LIB) prerequisite to osxkeychain
Makefile: support universal macOS builds via RUST_TARGETS
contrib: wire up osxkeychain in contrib/Makefile on macOS
Makefile | 46 ++++++++++++++++++++++---
contrib/Makefile | 10 ++++++
contrib/credential/osxkeychain/Makefile | 4 ++-
3 files changed, 54 insertions(+), 6 deletions(-)
base-commit: 00534a21ce949ef80a5b8b9d7fc20b7d381038e9
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2288%2Fkiranani%2Fnext-v7
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2288/kiranani/next-v7
Pull-Request: https://github.com/git/git/pull/2288
Range-diff vs v6:
1: 0d215139406 = 1: 8f2bd4b14a3 Makefile: add $(RUST_LIB) prerequisite to osxkeychain
2: 21dedb91f09 = 2: a999be69392 Makefile: support universal macOS builds via RUST_TARGETS
3: 8455e449f38 = 3: 32af2c51a89 contrib: wire up osxkeychain in contrib/Makefile on macOS
--
gitgitgadget
^ permalink raw reply
* What's cooking in git.git (Jul 2026, #03)
From: Junio C Hamano @ 2026-07-07 17:18 UTC (permalink / raw)
To: git
Here are the topics that have been cooking in my tree. Commits
prefixed with '+' are in 'next' (being in 'next' is a sign that a
topic is stable enough to be used and is a candidate to be in a
future release). Commits prefixed with '-' are only in 'seen', and
aren't considered "accepted" at all. They may be annotated with a URL
to a message that raises issues but they are by no means exhaustive.
A topic without enough support may be discarded after a long period
of no activity (of course, it can be resubmitted when new interest
arises).
The first batch of topics have now graduated to the 'master' branch.
Copies of the source code to Git live in many repositories, and the
following is a list of the ones I push into or their mirrors. Some
repositories have only a subset of branches.
With maint, master, next, seen, todo:
git://git.kernel.org/pub/scm/git/git.git/
git://repo.or.cz/alt-git.git/
https://kernel.googlesource.com/pub/scm/git/git/
https://github.com/git/git/
https://gitlab.com/git-scm/git/
With all the integration branches and topics broken out:
https://github.com/gitster/git/
Even though the preformatted documentation in HTML and man format
are not sources, they are published in these repositories for
convenience (replace "htmldocs" with "manpages" for the manual
pages):
git://git.kernel.org/pub/scm/git/git-htmldocs.git/
https://github.com/gitster/git-htmldocs.git/
Release tarballs are available at:
https://www.kernel.org/pub/software/scm/git/
--------------------------------------------------
[Graduated to 'master']
* cc/promisor-auto-config-url-more (2026-05-27) 8 commits
(merged to 'next' on 2026-06-15 at d1c99e75cc)
+ doc: promisor: improve acceptFromServer entry
+ promisor-remote: auto-configure unknown remotes
+ promisor-remote: trust known remotes matching acceptFromServerUrl
+ promisor-remote: introduce promisor.acceptFromServerUrl
+ promisor-remote: add 'local_name' to 'struct promisor_info'
+ urlmatch: add url_normalize_pattern() helper
+ urlmatch: change 'allow_globs' arg to bool
+ t5710: simplify 'mkdir X' followed by 'git -C X init'
The handling of promisor-remote protocol capability has been updated
to allow the other side to add to the list of promisor remotes via the
'promisor.acceptFromServerURL' configuration variable.
Graduated to 'master'.
cf. <877bo7294j.fsf@emacs.iotcl.com>
cf. <xmqqh5naxwfc.fsf@gitster.g>
source: <20260527140820.1438165-1-christian.couder@gmail.com>
* en/ort-harden-against-corrupt-trees (2026-06-13) 5 commits
(merged to 'next' on 2026-06-18 at e51bee59ca)
+ cache-tree: fix verify_cache() to catch non-adjacent D/F conflicts
+ merge-ort: abort merge when trees have duplicate entries
+ merge-ort: free diff pairs queue in clear_or_reinit_internal_opts()
+ merge-ort: drop unnecessary show_all_errors from collect_merge_info()
+ merge-ort: propagate callback errors from traverse_trees_wrapper()
The 'ort' merge backend has been hardened against corrupt trees by
ensuring it aborts under appropriate error conditions.
Graduated to 'master'.
cf. <xmqq5x3ldu4h.fsf@gitster.g>
source: <pull.2096.v2.git.1781419047.gitgitgadget@gmail.com>
* hn/status-pull-advice-qualified (2026-05-21) 1 commit
(merged to 'next' on 2026-06-15 at 898a4df940)
+ remote: qualify "git pull" advice for non-upstream compareBranches
Advice shown by 'git status' when the local branch is behind or has
diverged from its push branch has been updated to suggest 'git pull
<remote> <branch>'.
Graduated to 'master'.
cf. <xmqq7bo6xuok.fsf@gitster.g>
source: <pull.2301.v4.git.git.1779372367317.gitgitgadget@gmail.com>
* jc/submittingpatches-design-critiques (2026-06-20) 1 commit
(merged to 'next' on 2026-06-22 at 7495b5f9d6)
+ SubmittingPatches: address design critiques
The documentation in SubmittingPatches has been updated to clarify how
patch contributors should respond to design and viability critiques,
and how the resolution of such critiques should be recorded in the
final commit messages.
Graduated to 'master'.
cf. <ajjwYGWZ6hQWr600@pks.im>
source: <xmqqeci0g4mz.fsf@gitster.g>
* jk/repo-info-path-keys (2026-06-23) 3 commits
(merged to 'next' on 2026-06-29 at fbf9652169)
+ repo: add path.gitdir with absolute and relative suffix formatting
+ repo: add path.commondir with absolute and relative suffix formatting
+ path: extract format_path() and use in rev-parse
(this branch is used by ps/setup-split-discovery-and-setup.)
The 'git repo info' command has been taught new keys to output both
absolute and relative paths for 'gitdir' and 'commondir', supported by
a new path-formatting helper extracted from 'git rev-parse'.
Graduated to 'master'.
cf. <xmqqy0g3iz38.fsf@gitster.g>
source: <20260624033748.108281-1-jayatheerthkulkarni2005@gmail.com>
* jk/setup-gitfile-diag-fix (2026-06-16) 1 commit
(merged to 'next' on 2026-06-18 at b63b3d1f25)
+ read_gitfile(): simplify NOT_A_REPO error message
A regression in the error diagnosis code for invalid '.git' files has
been fixed, avoiding a potential 'NULL'-pointer crash when reporting
that a '.git' file does not point to a valid repository.
Graduated to 'master'.
cf. <xmqqjyry4hax.fsf@gitster.g>
source: <20260616123516.GA2301231@coredump.intra.peff.net>
* kh/submittingpatches-trailers (2026-06-18) 5 commits
(merged to 'next' on 2026-06-22 at 2cd4a152c9)
+ SubmittingPatches: note that trailer order matters
+ SubmittingPatches: be consistent with trailer markup
+ SubmittingPatches: document Based-on-patch-by trailer
+ SubmittingPatches: discourage common Linux trailers
+ SubmittingPatches: encourage trailer use for substantial help
The trailer sections in SubmittingPatches have been updated to
encourage use of standard trailers.
Graduated to 'master'.
cf. <xmqq4ij0vo8f.fsf@gitster.g>
source: <V3_CV_SubPatches_trailers.9ec@msgid.xyz>
* mh/fetch-follow-remote-head-config (2026-06-19) 8 commits
(merged to 'next' on 2026-06-22 at 423079e1c8)
+ fetch: fixup a misaligned comment
+ fetch: add configuration variable fetch.followRemoteHEAD
+ fetch: refactor do_fetch handling of followRemoteHEAD
+ fetch: return 0 on known git_fetch_config
+ fetch: rename function report_set_head
+ t5510: cleanup remote in followRemoteHEAD dangling ref test
+ doc: explain fetchRemoteHEADWarn advice
+ fetch: fixup set_head advice for warn-if-not-branch
The 'fetch.followRemoteHEAD' configuration variable has been added to
provide a default for the per-remote 'remote.<name>.followRemoteHEAD'
setting.
Graduated to 'master'.
cf. <xmqqcxxp1j2t.fsf@gitster.g>
source: <20260619094751.2996804-1-m@lfurio.us>
* mv/log-follow-mergy (2026-06-21) 1 commit
(merged to 'next' on 2026-06-22 at f7e984a003)
+ log: improve --follow following renames for non-linear history
'git log --follow' has been updated to better handle non-linear
history, in which the path being tracked gets renamed differently in
multiple history lines.
Graduated to 'master'.
source: <ajjU4w2B0NlZffw1@collabora.com>
* po/hash-object-size-t (2026-06-16) 6 commits
(merged to 'next' on 2026-06-21 at b780a276b9)
+ hash-object: add a >4GB/LLP64 test case using filtered input
+ hash-object: add another >4GB/LLP64 test case
+ hash-object --stdin: verify that it works with >4GB/LLP64
+ hash algorithms: use size_t for section lengths
+ object-file.c: use size_t for header lengths
+ hash-object: demonstrate a >4GB/LLP64 problem
Support for hashing loose or packed objects larger than 4GB on Windows
and other LLP64 platforms has been improved by converting object header
buffers and data-handling functions from 'unsigned long' to 'size_t'.
Graduated to 'master'.
cf. <ajOQthRjhD3hRM9w@pks.im>
source: <pull.2138.v2.git.1781621398.gitgitgadget@gmail.com>
* ps/connected-generic-promisor-checks (2026-06-25) 5 commits
(merged to 'next' on 2026-06-29 at 10eef65b98)
+ connected: search promisor objects generically
+ connected: split out promisor-based connectivity check
+ odb/source-packed: support flags when iterating an object prefix
+ odb/source-packed: extract logic to skip certain packs
+ Merge branch 'ps/odb-source-packed' into ps/connected-generic-promisor-checks
(this branch uses ps/odb-source-packed.)
The connectivity check has been refactored to search for promisor
objects in a generic way using the object database interface,
rather than iterating packfiles directly. This allows connectivity
checks to work properly in repositories that do not use packfiles.
Graduated to 'master'.
cf. <CAP8UFD07AzNtP3rRj4btYfFfakX0kkLXKpO9T=a3Mds3YWEsXw@mail.gmail.com>
source: <20260625-pks-connected-generic-promisor-checks-v3-0-7308f3b9dc44@pks.im>
* ps/doc-recommend-b4 (2026-06-15) 3 commits
(merged to 'next' on 2026-06-17 at dd9a463369)
+ b4: introduce configuration for the Git project
+ MyFirstContribution: recommend the use of b4
+ MyFirstContribution: recommend shallow threading of cover letters
Project-specific configuration for b4 has been introduced, and the
documentation has been updated to recommend using it as a
streamlined method for submitting patches.
Graduated to 'master'.
cf. <87eci7yomp.fsf@emacs.iotcl.com>
source: <20260615-pks-b4-v4-0-22cfca8f19c5@pks.im>
* ps/odb-source-packed (2026-06-16) 18 commits
(merged to 'next' on 2026-06-19 at dcf0c084e4)
+ odb/source-packed: drop pointer to "files" parent source
+ midx: refactor interfaces to work on "packed" source
+ odb/source-packed: stub out remaining functions
+ odb/source-packed: wire up `freshen_object()` callback
+ odb/source-packed: wire up `find_abbrev_len()` callback
+ odb/source-packed: wire up `count_objects()` callback
+ odb/source-packed: wire up `for_each_object()` callback
+ odb/source-packed: wire up `read_object_stream()` callback
+ odb/source-packed: wire up `read_object_info()` callback
+ packfile: use higher-level interface to implement `has_object_pack()`
+ odb/source-packed: wire up `reprepare()` callback
+ odb/source-packed: wire up `close()` callback
+ odb/source-packed: start converting to a proper `struct odb_source`
+ odb/source-packed: store pointer to "files" instead of generic source
+ packfile: move packed source into "odb/" subsystem
+ packfile: split out packfile list logic
+ packfile: rename `struct packfile_store` to `odb_source_packed`
+ Merge branch 'ps/odb-source-loose' into ps/odb-source-packed
(this branch is used by ps/connected-generic-promisor-checks, ps/libgit-in-subdir, ps/odb-drop-whence and ps/odb-generalize-prepare.)
The packed object source has been refactored into a proper 'struct
odb_source'.
Graduated to 'master'.
cf. <ajK2QKdW-TdflfR0@denethor>
source: <20260617-pks-odb-source-packed-v3-0-b5c7583cd795@pks.im>
* ps/refs-onbranch-fixes (2026-06-25) 12 commits
(merged to 'next' on 2026-06-29 at 7b4929e311)
+ refs: protect against chicken-and-egg recursion
+ refs/reftable: lazy-load configuration to fix chicken-and-egg
+ reftable: split up write options
+ refs/files: lazy-load configuration to fix chicken-and-egg
+ refs: move parsing of "core.logAllRefUpdates" back into ref stores
+ repository: free main reference database
+ chdir-notify: drop unused `chdir_notify_reparent()`
+ refs: unregister reference stores from "chdir_notify"
+ setup: don't apply "GIT_REFERENCE_BACKEND" without a repository
+ setup: stop applying repository format twice
+ setup: inline `check_and_apply_repository_format()`
+ Merge branch 'ps/setup-centralize-odb-creation' into ps/refs-onbranch-fixes
(this branch is used by ps/setup-split-discovery-and-setup.)
Reference backend configuration has been updated to load lazily to
avoid recursive calls during repository initialization when 'onbranch'
configuration conditions are evaluated. This has also fixed a memory
leak and allowed the unused 'chdir_notify_reparent()' machinery to be
dropped.
Graduated to 'master'.
cf. <xmqqse6ae45i.fsf@gitster.g>
source: <20260625-b4-pks-refs-avoid-chdir-notify-reparent-v6-0-41fbca3cf5e3@pks.im>
* ps/setup-drop-global-state (2026-06-10) 8 commits
(merged to 'next' on 2026-06-15 at d9a8b88d47)
+ treewide: drop USE_THE_REPOSITORY_VARIABLE
+ environment: stop using `the_repository` in `is_bare_repository()`
+ environment: split up concerns of `is_bare_repository_cfg`
+ builtin/init: stop modifying `is_bare_repository_cfg`
+ setup: remove global `git_work_tree_cfg` variable
+ builtin/init: simplify logic to configure worktree
+ builtin/init: stop modifying global `git_work_tree_cfg` variable
+ Merge branch 'ps/setup-centralize-odb-creation' into ps/setup-drop-global-state
(this branch is used by ps/setup-split-discovery-and-setup.)
The refactoring of 'setup.c' has been continued to drop remaining
global state ('git_work_tree_cfg', 'is_bare_repository_cfg'), updating
'is_bare_repository()' to no longer implicitly rely on
'the_repository'.
Graduated to 'master'.
cf. <airVOrTboNDDGBak@denethor>
cf. <87ldckyygk.fsf@emacs.iotcl.com>
source: <20260611-b4-pks-setup-drop-global-state-v2-0-a6f7269c841d@pks.im>
* pw/status-rebase-todo (2026-06-23) 2 commits
(merged to 'next' on 2026-06-23 at a0fcde09dc)
+ status: improve rebase todo list parsing
+ sequencer: factor out parsing of todo commands
The display of the rebase todo list in 'git status' has been improved
to correctly abbreviate object IDs for more commands and avoid
misinterpreting refs as object IDs.
Graduated to 'master'.
source: <cover.1782230024.git.phillip.wood@dunelm.org.uk>
* rs/cat-file-default-format-optim (2026-06-14) 1 commit
(merged to 'next' on 2026-06-17 at 43ed8b3969)
+ cat-file: speed up default format
The default format path of 'git cat-file --batch' has been optimized
to use 'strbuf_add_oid_hex()' and 'strbuf_add_uint()' instead of
'strbuf_addf()', yielding a noticeable speedup.
Graduated to 'master'.
cf. <20260615165326.GA91269@coredump.intra.peff.net>
source: <5a7ed929-6fe0-496c-83bd-65dee57c2241@web.de>
* sg/t3420-do-not-grep-in-missing-file (2021-10-10) 1 commit
(merged to 'next' on 2026-06-29 at 2bf33c6a40)
+ t3420-rebase-autostash: don't try to grep non-existing files
A test checking interactions between 'git rebase --quit' and autostash
in 't3420-rebase-autostash.sh' has been corrected to use
'test_path_is_missing' instead of '! grep' on a file that shouldn't
exist in the conflicted state.
Graduated to 'master'.
source: <20211010172809.1472914-1-szeder.dev@gmail.com>
* tb/pack-path-walk-bitmap-delta-islands (2026-06-21) 5 commits
(merged to 'next' on 2026-06-22 at 59cf1663e7)
+ pack-objects: support `--delta-islands` with `--path-walk`
+ pack-objects: extract `record_tree_depth()` helper
+ pack-objects: support reachability bitmaps with `--path-walk`
+ t/perf: drop p5311's lookup-table permutation
+ Merge branch 'ds/path-walk-filters' into tb/pack-path-walk-bitmap-delta-islands
The 'git pack-objects' command has been updated to support
reachability bitmaps and delta-islands concurrently with the '--path-
walk' option, allowing faster packaging by falling back to path-walk
when bitmaps cannot fully satisfy the request.
Graduated to 'master'.
cf. <xmqqwlvq1qyy.fsf@gitster.g>
source: <cover.1782082975.git.me@ttaylorr.com>
* td/ref-filter-restore-prefix-iteration (2026-06-12) 1 commit
(merged to 'next' on 2026-06-19 at a19dbb4193)
+ ref-filter: restore prefix-scoped iteration
Commands that list branches and tags (like 'git branch' and 'git tag')
have been optimized to pass the namespace prefix when initializing
their ref iterator, avoiding a loose-ref scaling regression in
repositories with many unrelated loose references.
Graduated to 'master'.
cf. <xmqqik7fsv2m.fsf@gitster.g>
source: <20260612-fix-git-branch-regression-v4-1-f150038c02f4@gmail.com>
* ty/move-protect-hfs-ntfs (2026-06-20) 2 commits
(merged to 'next' on 2026-06-20 at d8ca0d5180)
+ environment: use 'repo->initialized' for repo_protect_hfs() and repo_protect_ntfs()
(merged to 'next' on 2026-06-15 at c2a30ca954)
+ environment: move 'protect_hfs' and 'protect_ntfs' into 'repo_config_values'
The global configuration variables 'protect_hfs' and 'protect_ntfs'
have been migrated into 'struct repo_config_values' to tie them to
per-repository configuration state.
Graduated to 'master'.
cf. <CAP8UFD35Tiy1_fqpjq8P-z=ZhzR3MTiThqfCs977652umRoSEQ@mail.gmail.com>
cf. <xmqqse6uwdnz.fsf@gitster.g>
source: <20260610124353.149874-2-cat@malon.dev>
source: <20260620140957.667820-1-cat@malon.dev>
* wy/doc-clarify-review-replies (2026-06-21) 2 commits
(merged to 'next' on 2026-06-29 at 21ae0599dc)
+ doc: advise batching patch rerolls
+ doc: encourage review replies before rerolling
Documentation on community contribution guidelines has been updated to
encourage replying to review comments before rerolling, and to advise
a default limit of at most one reroll per day to give reviewers across
different time zones enough time to participate.
Graduated to 'master'.
cf. <ajvDuUiDsmyf5LnX@pks.im>
source: <cover.1782028813.git.wy@wyuan.org>
--------------------------------------------------
[New Topics]
* ds/sparse-index-ita-crash (2026-07-06) 1 commit
- sparse-index: avoid crash on intent-to-add entry outside the cone
A crash in the sparse-index collapse code when encountering an
invalidated cache-tree node (due to an intent-to-add path) has been
fixed by avoiding collapsing such subtrees.
Needs review.
source: <pull.2167.git.1783345853272.gitgitgadget@gmail.com>
* ij/subtree-reject-v2-config (2026-07-06) 2 commits
- git-subtree: Bail out if we find output from Rust rewrite (test)
- git-subtree: Bail out if we find output from Rust rewrite
The shell script implementation of 'git subtree' has been updated to
check for the presence of the configuration file of the new Rust
implementation, preventing users from accidentally running the old
script on repositories already managed by the new tool.
Expecting a reroll.
cf. <27211.50096.133710.528147@chiark.greenend.org.uk>
source: <20260706115816.20267-1-ijackson@chiark.greenend.org.uk>
* kk/reftable-tombstone-quadratic-fix (2026-07-06) 2 commits
- reftable: fix quadratic behavior when re-creating deleted refs
- t: add tests for ref tombstone scenarios
The performance of ref updates and reads using the reftable backend in
the presence of many deletion tombstone records has been optimized by
removing the tombstone suppression flag from the merged iterator and
instead skipping tombstones at higher-level call sites where iteration
bounds are known.
Needs review.
source: <pull.2166.git.1783344957.gitgitgadget@gmail.com>
* rs/blame-abbrev-marks (2026-07-06) 1 commit
- blame: reserve mark column only if necessary
The alignment of commit object name abbreviations in 'git blame'
output has been optimized to reserve a column for marks (caret,
question mark, or asterisk) only when such marks are actually shown.
Will merge to 'next'?
cf. <xmqqzf0397u1.fsf@gitster.g>
source: <92991b5e-0667-4315-89d5-1514a5499297@web.de>
* jm/t0213-skip-emulated-ancestry-tests (2026-07-06) 1 commit
- t0213: skip ancestry tests under user-mode emulation
The 'TRACE2_ANCESTRY' prerequisite in the 't0213' test script has been
refined to avoid failures under user-mode emulation. It now verifies
that the ancestry collector reports the expected process names rather
than the emulator binary name.
Needs review.
source: <pull.2168.git.1783359242130.gitgitgadget@gmail.com>
--------------------------------------------------
[Stalled]
* ap/http-redirect-wwwauth-fix (2026-06-02) 1 commit
- http: preserve wwwauth_headers across redirects
When 'cURL' follows a redirect, the 'WWW-Authenticate' headers from
the redirect target were lost because 'credential_from_url()' cleared
the credential state. This has been fixed by preserving the collected
headers across the redirect update.
Waiting for response(s) to review comment(s) for too long, stalled.
cf. <5144a29d-a53f-4446-beff-e1f549345bf9@nvidia.com>
source: <20260602161150.1527493-1-aplattner@nvidia.com>
* jt/config-lock-timeout (2026-05-17) 1 commit
- config: retry acquiring config.lock, configurable via core.configLockTimeout
Configuration file locking has been updated to retry for a short
period, avoiding failures when multiple processes attempt to update
the configuration simultaneously.
Waiting for response(s) to review comment(s) for too long, stalled.
cf. <agrIrGwSMFlKTx9x@pks.im>
source: <20260517132111.1014901-1-joerg@thalheim.io>
--------------------------------------------------
[Cooking]
* bc/parse-options-exit-0-on-help (2026-07-01) 4 commits
- parse-options: exit 0 on -h
- rev-parse: have --parseopt callers exit 0 on --help
- parse-options: add a separate case for help output on error
- t1517: skip svn tests if svn is not installed
Option parsing with 'git rev-parse --parseopt' and most git
subcommands has been updated to exit with 0 (instead of 129) when the
help option ('-h' or '--help') is requested directly by the user,
aligning with standard Unix convention.
Expecting a reroll.
cf. <akZ6H84Tzzgu8L5W@fruit.crustytoothpaste.net>
source: <20260701212442.1430084-1-sandals@crustytoothpaste.net>
* mg/meson-hook-list-buildfix (2026-07-01) 1 commit
- meson: restore hook-list.h to builtin_sources
A racy build failure under Meson has been corrected by ensuring that
the generated header file hook-list.h is built before compiling files
in builtin_sources that depend on it.
Will merge to 'next'.
cf. <akZGJP1kVtjBFN_e@pks.im>
source: <20260701193928.358825-1-floppym@gentoo.org>
* zy/apply-abandoned-header-fix (2026-07-01) 1 commit
- apply: avoid leaking abandoned git-header state
A candidate git diff header parsed by 'git apply' has been isolated in
a temporary structure, preventing any partially parsed state from
polluting the main patch structure and causing assertions to trip if
the header is ultimately rejected.
Needs review.
source: <20260702041759.51572-1-zhihao.yao@njit.edu>
* jk/hash-algo-leak-fixes (2026-07-02) 9 commits
- hash: add platform-specific discard functions
- hash: fix memory leak copying sha256 gcrypt handles
- http: discard hash in dumb-http http_object_request
- check_stream_oid(): discard hash on read error
- patch-id: discard hash when done
- csum-file: provide a function to release checkpoints
- csum-file: always finalize or discard hash
- hash: add discard primitive
- csum-file: drop discard_hashfile()
Various code paths that initialize a cryptographic hash context but
bail out or finish without calling 'git_hash_final()' have been taught
to call 'git_hash_discard()' to release allocated resources, fixing
memory leaks when Git is built with non-default backends like
'OpenSSL' or 'libgcrypt'.
Will merge to 'next'?
cf. <aktIIKuReMxJmDsi@pks.im>
source: <20260702075234.GA1548258@coredump.intra.peff.net>
* ml/t9811-replace-test-f (2026-07-02) 1 commit
- t9811: replace 'test -f' and '! test -f' with 'test_path_*'
The test script 't/t9811-git-p4-label-import.sh' has been
modernized to use 'test_path_is_file' and 'test_path_is_missing'
instead of raw 'test -f' and '! test -f' calls.
Expecting a reroll.
cf. <akdwp_a2EuhVoGVW@pks.im>
cf. <CAO=vGZpMe3dxyzFVwR7BWBxaAZ-z9Kw3CqQ0kAe5ZZGSQszkzw@mail.gmail.com>
source: <20260702140704.65805-1-marcelomlage@usp.br>
* ps/t-fixes-for-git-test-long (2026-07-05) 9 commits
- gitlab-ci: enable "GIT_TEST_LONG"
- gitlab-ci: disable RAM disk on macOS jobs
- t: use `test_bool_env` to parse GIT_TEST_LONG
- t7900: clean up large EXPENSIVE repository
- t7508: skip EXPENSIVE test that is broken without SIZE_T_IS_64BIT
- t5608: reduce maximum disk usage
- t4141: fix inefficient use of dd(1)
- t0021: skip EXPENSIVE test that is broken without SIZE_T_IS_64BIT
- README: add GitLab CI badge to make it more discoverable
Various test scripts have been updated to clean up large temporary
files and repositories, reducing peak disk usage during testing.
Also, expensive tests have been disabled on platforms that lack
sufficient resources (like 32-bit platforms and Windows CI
runners), and the long test suite has been enabled in GitLab CI.
Will merge to 'next'?
cf. <20260707043026.GB677056@coredump.intra.peff.net>
source: <20260706-b4-pks-t-fixes-for-GIT-TEST-LONG-v3-0-4f6c5a37fd1f@pks.im>
* ih/precompose-flex-array (2026-07-04) 1 commit
- precompose_utf8: use a flex array for d_name
The UTF-8 precomposition wrapper on macOS has been updated to use a
flexible array member to represent the name of a directory entry,
preventing fortified libc checks from failing when name is
reallocated to be larger than NAME_MAX bytes.
Will merge to 'next'.
cf. <20260703050800.GA29216@tb-raspi4>
source: <20260704233724.16928-1-ihar.hrachyshka@gmail.com>
* sn/osxkeychain-rust-universal (2026-07-06) 3 commits
- contrib: wire up osxkeychain in contrib/Makefile on macOS
- Makefile: support universal macOS builds via RUST_TARGETS
- Makefile: add $(RUST_LIB) prerequisite to osxkeychain
The build system has been updated to support building universal macOS
binaries when 'Rust' is enabled, by compiling separate static archives
for each target triple listed in 'RUST_TARGETS' and combining them
using the macOS 'lipo' tool. Additionally, the 'git-credential-
osxkeychain' helper has been updated to link against '$(RUST_LIB)'
when 'Rust' is enabled.
Needs review.
source: <pull.2288.v6.git.git.1783378333.gitgitgadget@gmail.com>
* cl/conditional-config-on-worktree-path (2026-07-02) 2 commits
- config: add "worktree" and "worktree/i" includeIf conditions
- config: refactor include_by_gitdir() into include_by_path()
The '[includeIf "condition"]' conditional inclusion facility for
configuration files has been taught to use the location of the
worktree in its condition.
Waiting for response(s) to review comment(s).
cf. <akeW4yFC8uuu2o8a@pks.im>
cf. <CAC1kPDNBecLbmZwjfR5-CsNheF3rcbZ5=SQ+cwjzpFMjFr9KGQ@mail.gmail.com>
source: <20260703-includeif-worktree-v6-0-a13893ad9a7f@black-desk.cn>
* kk/commit-reach-find-all-fix (2026-06-29) 2 commits
- commit-reach: guard !FIND_ALL early exit with generation ordering check
- t6600: add test for merge-base early exit with clock skew
The early-exit optimization in 'paint_down_to_common()' has been gated
on the queue being generation-ordered, fixing a bug where 'git merge-
base' (without '--all') could return incorrect results on repositories
with v1 commit graphs and clock skew.
Comments?
cf. <xmqqa4sdw55v.fsf@gitster.g>
source: <pull.2162.git.1782739162.gitgitgadget@gmail.com>
* bl/t7412-use-test-path-helpers (2026-06-29) 1 commit
- submodule absorbgitdirs tests: use test_* helper functions
't7412' that tests 'git submodule absorbgitdirs' has been modernized
to use 'test_path_is_file', 'test_path_is_dir', and
'test_path_is_missing' helper functions instead of raw 'test -[fde]'
commands.
Waiting for response(s) to review comment(s).
cf. <akTKHfKPsP3-Rn31@pks.im>
source: <20260630020220.1559190-1-bblima@usp.br>
* jk/format-patch-leakfix (2026-06-29) 2 commits
(merged to 'next' on 2026-07-06 at 35aff0d609)
+ format-patch: fix leak of rev_info in prepare_bases()
+ t: move LSan errors from stdout to stderr
A memory leak in the '--base' handling of 'git format-patch' has been
plugged, and the leak-reporting of the test suite when running under a
TAP harness has been improved.
Will merge to 'master'.
cf. <akOZy-BygZS8fqPM@pks.im>
source: <20260630063944.GA3733670@coredump.intra.peff.net>
* ps/setup-split-discovery-and-setup (2026-06-30) 16 commits
- setup: mark `set_git_work_tree()` as file-local
- setup: pass worktree to `init_db()`
- setup: drop redundant configuration of `startup_info->have_repository`
- setup: make repository discovery self-contained
- setup: propagate prefix via repository discovery
- setup: drop static `cwd` variable
- setup: move prefix into repository
- setup: embed repository format in discovery
- setup: introduce explicit repository discovery
- setup: split up concerns of `setup_git_env_internal()`
- setup: unify setup of shallow file
- setup: mark bogus worktree in `apply_repository_format()`
- setup: rename `check_repository_format_gently()`
- Merge branch 'jk/repo-info-path-keys' into ps/setup-split-discovery-and-setup
- Merge branch 'ps/setup-drop-global-state' into ps/setup-split-discovery-and-setup
- Merge branch 'ps/refs-onbranch-fixes' into ps/setup-split-discovery-and-setup
The repository discovery and repository configuration phases, which
were previously intertwined in 'setup.c', have been split. Repository
discovery has been updated to populate a 'struct repo_discovery'
without modifying the repository state, which is then taken by
repository configuration to initialize the repository, paving the way
for clean unification of repository configuration.
Needs review.
(a newer iteration v2 exists as <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>)
source: <20260630-pks-setup-split-discovery-and-setup-v1-0-13864eb5a032@pks.im>
* pw/rebase-drop-notes-with-commit (2026-06-30) 15 commits
- amend! sequencer: simplify pick_one_commit()
- amend! sequencer: remove unnecessary "or" in pick_one_commit()
- fixup! sequencer: never reschedule on failed commit
- fixup! sequencer: be more careful with external merge
- sequencer: do not record dropped commits as rewritten
- sequencer: use an enum to represent result of picking a commit
- sequencer: return early from pick_one_commit() on success
- sequencer: simplify pick_one_commit()
- sequencer: remove unnecessary condition in pick_one_commit()
- sequencer: simplify handing of fixup with conflicts
- sequencer: remove unnecessary "or" in pick_one_commit()
- sequencer: never reschedule on failed commit
- sequencer: be more careful with external merge
- sequencer: move definition of is_final_fixup()
- t3400: restore coverage for note copying with apply backend
The rebase post-rewrite notes-copying logic has been corrected. When a
commit is dropped during rebase (e.g., because its changes are already
upstream), it is no longer recorded as rewritten, preventing its notes
from being copied to an unrelated commit.
Expecting a reroll.
cf. <dce74d17-eefd-40bb-82f3-f6b3179cc2b6@gmail.com>
source: <cover.1782833268.git.phillip.wood@dunelm.org.uk>
* jk/bloom-leak-fixes (2026-06-30) 3 commits
- line-log: drop extra copy of range with bloom filters
- revision: avoid leaking bloom keyvecs with multiple traversals
- bloom: make bloom-filter slab initialization idempotent
Various memory leaks in the Bloom-filter code paths that are exposed
when running tests with the 'GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1'
environment variable have been plugged.
Will merge to 'next'.
cf. <b641aed4-ad52-477b-b1d8-9d8e470be46f@gmail.com>
cf. <xmqqo6gqobrt.fsf@gitster.g>
source: <20260701063538.GA2579765@coredump.intra.peff.net>
* js/ci-dockerized-pid-limit (2026-07-04) 1 commit
- ci(dockerized): raise the PID limit for private repositories
Dockerized CI jobs running in private GitHub repositories have been
adjusted to use explicit process and file limits, preventing resource
exhaustion errors on private runners.
Will merge to 'next'.
cf. <xmqqh5medmzh.fsf@gitster.g>
source: <pull.2164.v2.git.1783155124926.gitgitgadget@gmail.com>
* js/coverity-fixes (2026-07-05) 12 commits
- mingw: make `exit_process()` own the process handle on all paths
- fsmonitor: plug token-data leak on early daemon-startup failures
- reftable/table: release filter on error path
- imap-send: avoid leaking the IMAP upload buffer
- worktree: fix resource leaks when branch creation fails
- submodule: fix cwd leak in `get_superproject_working_tree()`
- dir: free allocations on parse-error paths in `read_one_dir()`
- line-log: avoid redundant copy that leaks in process_ranges
- run-command: avoid `close(-1)` in `start_command()` error paths
- download_https_uri_to_file(): do not leak fd upon failure
- loose: avoid closing invalid fd on error path
- load_one_loose_object_map(): fix resource leak
Various resource leaks, invalid file descriptor closures, and process
handle ownership issues flagged by Coverity have been fixed.
Needs review.
source: <pull.2163.v2.git.1783239870.gitgitgadget@gmail.com>
* tb/repack-geometric-cruft (2026-06-28) 11 commits
- SQUASH??? bare grep !???
- repack: support combining '--geometric' with '--cruft'
- pack-objects: support '--refs-snapshot' with 'follow-reachable'
- pack-objects: introduce '--stdin-packs=follow-reachable'
- pack-objects: extract `stdin_packs_add_all_pack_entries()`
- repack-geometry: drop unused redundant-pack removal
- repack: delete geometric packs via existing_packs
- repack: teach MIDX retention about geometric rollups
- repack: mark geometric progression of packs as retained
- repack: extract `locate_existing_pack()` helper
- repack: unconditionally exclude non-kept packs
'git repack' has been taught to accept '--geometric' and '--cruft'
together. When both are given, the geometric repack rolls up non-cruft
packs as usual, while a separate cruft pack is written to collect
unreachable objects.
Expecting a reroll.
cf. <aj8cOhH6hGVZIFft@nand.local>
source: <cover.1782500507.git.me@ttaylorr.com>
* jk/reftable-leakfix (2026-06-28) 1 commit
(merged to 'next' on 2026-07-06 at 55ce81f2d5)
+ reftable: fix unlikely leak on API error
A memory leak in the 'reftable_writer_new()' initialization function
has been fixed by delaying the allocation of 'struct reftable_writer'
until after input options are validated.
Will merge to 'master'.
cf. <akIPBJLtPqDjQt-A@pks.im>
source: <20260628090314.GA661068@coredump.intra.peff.net>
* ad/gpg-strip-cr-before-lf (2026-06-24) 1 commit
(merged to 'next' on 2026-07-06 at b099249efd)
+ gpg-interface: fix strip_cr_before_lf to only remove CR before LF
The GPG and SSH signature parsing code has been corrected to strip
carriage return characters only when they immediately precede line
feeds, instead of unconditionally stripping all carriage returns.
Will merge to 'master'.
source: <20260624093618.17456-1-antonio.destefani08@gmail.com>
* jt/receive-pack-use-odb-transactions (2026-06-23) 6 commits
- builtin/receive-pack: stage incoming objects via ODB transactions
- odb/transaction: add transaction env interface
- odb/transaction: propagate commit errors
- odb/transaction: propagate begin errors
- object-file: propagate files transaction errors
- object-file: rename files transaction prepare function
'git receive-pack' has been refactored to use ODB transaction
interfaces instead of directly managing 'tmp_objdir' for staging
incoming objects, bringing it closer to being ODB backend agnostic.
Expecting a reroll.
cf. <aju_AmlKVi5UZaiQ@pks.im>
cf. <akK05yZ6843K8Vdd@denethor>
cf. <akLLB_J-pvJ7iR7c@denethor>
source: <20260624041920.2601961-1-jltobler@gmail.com>
* ps/odb-drop-whence (2026-07-02) 7 commits
- odb: document object info fields
- odb: drop `whence` field from object info
- treewide: convert users of `whence` to the new source field
- odb: add `source` field to struct object_info_source
- odb: make backend-specific fields optional
- packfile: thread odb_source_packed through packed_object_info()
- Merge branch 'ps/odb-source-packed' into ps/odb-drop-whence
The 'whence' field in 'struct object_info' has been removed,
refactoring backend-specific object information retrieval into an opt-
in 'struct object_info_source' structure.
Will merge to 'next'.
cf. <xmqqv7b0rmt6.fsf@gitster.g>
source: <20260702-b4-pks-odb-drop-whence-v2-0-b0af7468ad95@pks.im>
* ps/reftable-hardening (2026-07-03) 12 commits
- reftable/table: fix OOB read on truncated table
- reftable/table: fix NULL pointer access when seeking to bogus offsets
- reftable/block: fix OOB read with bogus restart offset
- reftable/block: fix use of uninitialized memory when binsearch fails
- reftable/block: fix OOB read with bogus restart count
- reftable/block: fix OOB read with bogus block size
- reftable/block: fix OOB write with bogus inflated log size
- t/unit-tests: introduce test helper to write reftable blocks
- reftable/record: don't abort when decoding invalid ref value type
- reftable/basics: fix OOB read on binary search of empty range
- oss-fuzz: add fuzzer for parsing reftables
- meson: support building fuzzers with libFuzzer
The reftable code has been hardened against corrupted tables by
fixing out-of-bounds writes, out-of-bounds reads, and abort calls
during parsing.
Needs review.
source: <20260703-pks-reftable-hardening-v3-0-b87c555b9920@pks.im>
* hn/branch-push-slip-advice (2026-06-27) 2 commits
(merged to 'next' on 2026-07-06 at acdff65ac5)
+ push: suggest <remote> <branch> for a slash slip
+ branch: suggest <remote>/<branch> on upstream slip
When 'git push origin/main' or 'git branch origin main' is run, the
command is now recognized as a potential typo, and advice has been
added to offer a typofix.
Will merge to 'master'.
cf. <xmqqfr272lq7.fsf@gitster.g>
source: <pull.2331.v3.git.git.1782583345.gitgitgadget@gmail.com>
* jc/history-message-prep-fix (2026-06-29) 1 commit
(merged to 'next' on 2026-07-06 at 00534a21ce)
+ history: streamline message preparation and plug file stream leak
A write file stream resource leak has been fixed as part of a code
cleanup.
Will merge to 'master'.
cf. <akO1mhi2u2PntLbt@pks.im>
source: <xmqqmrwdxrat.fsf@gitster.g>
* ty/migrate-excludes-file (2026-07-01) 1 commit
- environment: move excludes_file into repo_config_values
The 'excludes_file' and various other global configuration variables
(including 'editor_program', 'pager_program', 'askpass_program', and
'push_default') have been migrated into the per-repository structure.
Waiting for response(s) to review comment(s).
(a newer iteration v7 exists as <20260706142530.3681520-1-cat@malon.dev>)
cf. <xmqqpl10auhw.fsf@gitster.g>
source: <20260701180813.776173-2-cat@malon.dev>
* kk/merge-base-exhaustion (2026-07-01) 10 commits
. commit-reach: remove commit-date ordering fallback
. commit-reach: move min_generation check into paint_queue_get()
. commit-reach: terminate merge-base walk when one paint side is exhausted
. commit-reach: introduce struct paint_state with per-side counters
. t6600: add clock-skew topologies and step counts for edge cases
. commit-reach: add trace2 instrumentation to paint_down_to_common()
. t6099, t6600: add side-exhaustion regression tests
. t6600: add test cases for side-exhaustion edge cases
. test-lib-functions: improve diagnostic output for trace2 data assertions
. Documentation/technical: add paint-down-to-common doc
The merge-base computation has been optimized by stopping the walk
early when one side's exclusive commits in the queue are exhausted,
yielding significant speedups for queries with one-sided histories.
Expecting a reroll.
cf. <CAL71e4PgcZDK-gJziJa_yjEqX9TE+PFMwZn0xbjAUzuUDDDBYA@mail.gmail.com>
source: <pull.2149.v5.git.1782923832.gitgitgadget@gmail.com>
* dk/meson-enable-use-nsec-build (2026-06-20) 1 commit
- meson: wire up USE_NSEC build knob
The 'USE_NSEC' build knob, which enables support for sub-second file
timestamp resolution, has been wired up to the Meson build system.
Expecting a reroll.
cf. <CALnO6CDm74rCBQu6Q0djsvtuw5U14V=PApptcZTgP+pic1f_AA@mail.gmail.com>
cf. <ajjuoS5Qc3K0nCRl@pks.im>
cf. <akIL6oJgUv8J8SB2@pks.im>
source: <c4c5ade901ff95b0f95939ea818870e4f3d59da1.1781971201.git.ben.knoble+github@gmail.com>
* ps/libgit-in-subdir (2026-06-30) 3 commits
- Move libgit.a sources into separate "lib/" directory
- t/helper: prepare "test-example-tap.c" for introduction of "lib/"
- Merge branch 'ps/odb-source-packed' into ps/libgit-in-subdir
The source files for 'libgit.a' have been moved into a new 'lib/'
directory to clean up the top-level directory and clearly separate
library code.
Comments?
cf. <akX1TMoRr87Id8Ss@pks.im>
source: <20260701-pks-libgit-in-subdir-v3-0-5e4860056094@pks.im>
* ps/odb-generalize-prepare (2026-06-22) 3 commits
(merged to 'next' on 2026-07-06 at 6132517517)
+ odb: introduce `odb_prepare()`
+ odb/source: generalize `reprepare()` callback
+ Merge branch 'ps/odb-source-packed' into ps/odb-generalize-prepare
The 'reprepare()' callback for object database sources has been
generalized into a 'prepare()' callback with an optional flush cache
flag, and a new 'odb_prepare()' wrapper has been introduced to allow
pre-opening object database sources.
Will merge to 'master'.
cf. <87ik704f1j.fsf@emacs.iotcl.com>
source: <20260622-b4-pks-odb-generalize-prepare-v1-0-d2a5c5d13144@pks.im>
* ty/migrate-ignorecase (2026-06-19) 2 commits
- config: use repo_ignore_case() to access core.ignorecase
- environment: move ignore_case into repo_config_values
The global configuration variable 'ignore_case' (representing the
'core.ignorecase' configuration) has been migrated into 'struct
repo_config_values' to tie it to a specific repository instance.
Waiting for comments from Johannes.
cf. <xmqqzf0mzc7j.fsf@gitster.g>
source: <20260619155152.642760-1-cat@malon.dev>
* mm/line-log-limited-ops (2026-06-27) 7 commits
- diffcore-pickaxe: scope -G to the -L tracked range
- diff: support --check with -L line ranges
- line-log: support diff stat formats with -L
- diff: extract a line-range diff helper for reuse
- diff: emit -L hunk headers via xdiff's formatter
- diff: simplify the line-range filter by classifying removals immediately
- diff: rename and group the line-range filter for clarity
The 'git log -L<range>:<path>' command has been taught to limit
various 'diff' operations, such as '--stat', '--check', and '-G', to
the specified range:path.
Needs review.
source: <pull.2152.v2.git.1782581342.gitgitgadget@gmail.com>
* hn/history-squash (2026-07-06) 5 commits
- history: re-edit a squash with every message
- sequencer: extract helpers for the squash message markers
- history: add squash subcommand to fold a range
- history: give commit_tree_ext a message template
- history: extract helper for a commit's parent tree
The experimental 'git history' command has been taught a new 'squash'
subcommand to fold a range of commits into a single commit, replaying
any descendants on top.
Waiting for response(s) to review comment(s).
cf. <38493ca6-8fdd-4b6c-9972-5145f3bf0aa4@gmail.com>
source: <pull.2337.v7.git.git.1783327849.gitgitgadget@gmail.com>
* ps/refs-writing-subcommands (2026-07-06) 5 commits
- builtin/refs: add "rename" subcommand
- builtin/refs: add "create" subcommand
- builtin/refs: add "update" subcommand
- builtin/refs: add "delete" subcommand
- builtin/refs: drop `the_repository`
The 'git refs' toolbox has been extended with new 'create', 'delete',
'update', and 'rename' subcommands to create, delete, update, and
rename references, respectively.
Will merge to 'next'.
source: <20260706-pks-refs-writing-subcommands-v4-0-d51f6ce7f830@pks.im>
* wy/doc-myfirstcontribution-trim-quotes (2026-06-11) 1 commit
- MyFirstContribution: mention trimming quoted text in replies
The contributor guide has been updated to advise new contributors to
trim irrelevant quoted text when replying to review comments, matching
the existing advice given to reviewers.
Comments?
cf. <xmqqcxxwljue.fsf@gitster.g>
source: <080402ff0ac8127b654dccea59a1bf643df62a5c.1781186476.git.wy@wyuan.org>
* tb/midx-incremental-custom-base (2026-06-12) 3 commits
- midx-write: include packs above custom incremental base
- midx: pass custom '--base' through incremental writes
- t5334: expose shared `nth_line()` helper
The 'git multi-pack-index write --incremental' command has been
corrected to properly honor the '--base' option. Previously, the
custom base was ignored by the normal write path, and the pack
exclusion logic incorrectly skipped packs from layers above the
selected base, breaking reachability closure for bitmaps.
Comments?
source: <cover.1781294771.git.me@ttaylorr.com>
* mm/test-grep-lint (2026-07-05) 6 commits
- t: add greplint to detect bare grep assertions
- t: convert grep assertions to test_grep
- t: fix Lexer line count for $() inside double-quoted strings
- t: extract chainlint's parser into shared module
- t: fix grep assertions missing file arguments
- t/README: document test_grep helper
The test suite has been updated to use the 'test_grep' helper instead
of bare 'grep' for test assertions, allowing file contents to be
printed on failure for easier debugging. A new 'greplint' linter has
been introduced to detect and prevent new bare 'grep' assertions from
being added to the test suite.
Needs review.
source: <pull.2135.v4.git.1783314119.gitgitgadget@gmail.com>
* kk/prio-queue-get-put-fusion (2026-06-08) 2 commits
(merged to 'next' on 2026-07-06 at aa748c4564)
+ prio-queue: fold lazy_queue into prio_queue for automatic get+put fusion
+ prio-queue: rename .nr to .nr_ and add accessor helpers
The lazy priority queue optimization pattern (deferring actual removal
in 'prio_queue_get()' to allow get+put fusion) has been folded
directly into 'prio_queue' itself, speeding up commit traversal
workflows and simplifying callers.
Will merge to 'master'.
cf. <xmqqh5mjrbgq.fsf@gitster.g>
source: <pull.2140.v4.git.1780945851.gitgitgadget@gmail.com>
* td/ref-filter-memoize-contains (2026-06-12) 3 commits
- commit-reach: die on contains walk errors
- ref-filter: memoize --contains with generations
- commit-reach: reject cycles in contains walk
'git branch --contains' and 'git for-each-ref --contains' have
been optimized to use the memoized commit traversal previously
used only by 'git tag --contains', significantly speeding up
connectivity checks across many candidate refs with shared
history.
Comments?
cf. <xmqqqzlpulkp.fsf@gitster.g>
source: <20260612-ref-filter-memoized-contains-v4-0-5ed39fd001dd@gmail.com>
* tc/replay-linearize (2026-07-02) 3 commits
- replay: offer an option to linearize the commit topology
- replay: resolve the replay base outside pick_regular_commit()
- replay: add helper to put entry into replayed_commits
The 'git replay' command has been taught the '--linearize' option to
drop merge commits and linearize the replayed history, mimicking 'git
rebase --no-rebase-merges'.
Waiting for response(s) to review comment(s).
cf. <xmqqbjcnhjvk.fsf@gitster.g>
source: <20260702-toon-git-replay-drop-merges-v6-0-78a07cdd0382@iotcl.com>
* ps/cat-file-remote-object-info (2026-07-01) 13 commits
- cat-file: make remote-object-info allow-list dynamic
- cat-file: validate remote atoms with an allow-list
- cat-file: add remote-object-info to batch-command
- transport: add client support for object-info
- serve: advertise object-info feature
- fetch-pack: move fetch initialization
- connect: make `write_fetch_command_and_capabilities()` more generic
- fetch-pack: move `write_fetch_command_and_capabilities()` to connect.c
- fetch-pack: drop static `advertise_sid` variable
- t1006: split test utility functions into new 'lib-cat-file.sh'
- cat-file: declare loop counter inside for()
- git-compat-util: add `strtoumax_szt()` with error handling
- transport-helper: fix memory leak of helper on disconnect
The 'remote-object-info' command has been added to 'git cat-file
--batch-command', allowing clients to request object metadata
(currently size) from a remote server via protocol v2 without
downloading the entire object. Format placeholders are dynamically
filtered on the client based on server-advertised capabilities,
returning empty strings for inapplicable or unsupported fields.
Expecting a reroll.
cf. <CAN5EUNTYeDrQMor29eYMhJD0jcdRQq36ZA6BgupV8gG9xs9rFQ@mail.gmail.com>
source: <20260701-ps-eric-work-rebase-v15-0-c88a43b63917@gmail.com>
* sn/rebase-update-refs-symrefs (2026-06-03) 1 commit
- rebase: skip branch symref aliases
'git rebase --update-refs' has been taught to resolve local branch
symrefs to their referents before queuing updates, ensuring aliases of
the current branch are skipped and duplicate updates are avoided to
prevent failures when branch aliases are present.
Waiting for response(s) to review comment(s) for too long, stalled.
cf. <f982c386-e329-4ab0-b695-e540bcb9de3d@gmail.com>
source: <pull.2126.v2.git.1780482436865.gitgitgadget@gmail.com>
* mm/diff-process-hunks (2026-06-14) 6 commits
- blame: consult diff process for no-hunk detection
- diff: bypass diff process with --no-ext-diff and in format-patch
- diff: add long-running diff process via diff.<driver>.process
- sub-process: separate process lifecycle from hashmap management
- userdiff: add diff.<driver>.process config
- xdiff: support external hunks via xpparam_t
A new 'diff.<driver>.process' configuration has been introduced to
allow a long-running external process to act as a hunk provider,
allowing external tools to control which lines Git considers changed
while leaving all output formatting (word diff, color, blame, etc.) to
Git's standard pipeline.
Expecting a reroll.
cf. <CAC2Qwm+P=fZOtpfMPeMiSXf3Afk6OLYpTP8Br78_PRA8WNL1Wg@mail.gmail.com>
source: <pull.2120.v4.git.1781463564.gitgitgadget@gmail.com>
* ty/migrate-trust-executable-bit (2026-06-19) 3 commits
- environment: move trust_executable_bit into repo_config_values
- read-cache: move 'ce_mode_from_stat()' to 'read-cache.c'
- read-cache: remove redundant extern declarations
The 'trust_executable_bit' (coming from 'core.filemode'
configuration) has been migrated into 'repo_config_values' to tie it
to a specific repository instance.
Comments?
cf. <xmqqcxx9ukvw.fsf@gitster.g>
source: <20260619162105.648495-1-cat@malon.dev>
* kk/prio-queue-cascade-sift (2026-06-01) 1 commit
- prio-queue: use cascade-down for faster extract-min
'prio_queue_get()' has been optimized by using a cascade-down approach
(promoting the smaller child at each level and sifting up the last
element from the leaf vacancy), which halves the number of comparisons
per extract-min operation in the common case.
On hold, waiting for kk/prio-queue-get-put-fusion to land first.
cf. <CAL71e4MYNiScZjTwkApjDAjRh2LM0_SP59h5HCTywV-Pua03tw@mail.gmail.com>
source: <pull.2132.v2.git.1780301856444.gitgitgadget@gmail.com>
* ps/history-drop (2026-07-01) 11 commits
- builtin/history: implement "drop" subcommand
- builtin/history: split handling of ref updates into two phases
- replay: expose `replay_result_queue_update()`
- reset: stop assuming that the caller passes in a clean index
- reset: allow the caller to specify the current HEAD object
- reset: introduce ability to skip updating HEAD
- reset: introduce dry-run mode
- reset: modernize flags passed to `reset_working_tree()`
- reset: rename `reset_head()`
- reset: drop `USE_THE_REPOSITORY_VARIABLE`
- read-cache: split out function to drop unmerged entries to stage 0
The experimental 'git history' command has been taught a new 'drop'
subcommand to remove a commit and replay its descendants onto its
parent.
Will merge to 'next'.
cf. <xmqq1pdmprbk.fsf@gitster.g>
cf. <CAP8UFD3OAktVQsLuqBNFH2uhEO31PH8ZF3ZT1ZW8k++XE8YLPw@mail.gmail.com>
source: <20260701-b4-pks-history-drop-v8-0-19b5cdf1facd@pks.im>
* kh/doc-trailers (2026-06-10) 10 commits
- doc: interpret-trailers: document comment line treatment
- doc: interpret-trailers: commit to “trailer block” term
- doc: interpret-trailers: join new-trailers again
- doc: interpret-trailers: add key format example
- doc: interpret-trailers: explain key format
- doc: interpret-trailers: explain the format after the intro
- doc: interpret-trailers: not just for commit messages
- doc: interpret-trailers: use “metadata” in Name as well
- doc: interpret-trailers: replace “lines” with “metadata”
- doc: interpret-trailers: stop fixating on RFC 822
Documentation for 'git interpret-trailers' has been updated to explain
the format of trailer keys (alphanumeric characters and hyphens),
replace outdated terminology, define key terms upfront, and document
how comment lines in the input are treated.
Expecting a reroll.
cf. <729baf6b-53ea-4e8d-95ab-5935667e66c2@app.fastmail.com>
source: <V3_CV_doc_int-tr_key_format.8a3@msgid.xyz>
* za/completion-hide-dotfiles (2026-06-20) 2 commits
- completion: hide dotfiles by default for path completion
- completion: hide dotfiles for selected path completion
Path completion for commands like 'git rm' and 'git mv' has been
updated to hide dotfiles by default unless the user explicitly starts
the path with a dot, matching standard shell-completion behavior.
Waiting for response(s) to review comment(s).
cf. <xmqqik71t3nr.fsf@gitster.g>
source: <pull.2311.v3.git.git.1781978156.gitgitgadget@gmail.com>
* ec/commit-fixup-options (2026-05-26) 2 commits
- commit: allow -c/-C for all kinds of --fixup
- commit: allow -m/-F for all kinds of --fixup
Support for '-m', '-F', '-c', or '-C' options to supply a commit log
message from outside the editor has been added for all 'git commit
--fixup' variations.
Comments?
source: <cover.1779792311.git.erik@cervined.in>
* kh/doc-replay-config (2026-06-05) 4 commits
- doc: replay: move “default” to the right-hand side
- doc: replay: use a nested description list
- doc: replay: improve config description
- doc: link to config for git-replay(1)
Documentation for 'git replay' has been updated to refer to its
configuration variables.
Comments?
source: <V3_CV_doc_replay_config.780@msgid.xyz>
* hn/branch-delete-merged (2026-06-24) 7 commits
- branch: add --dry-run for --delete-merged
- branch: add branch.<name>.deleteMerged opt-out
- branch: add --delete-merged <branch>
- branch: prepare delete_branches for a bulk caller
- branch: let delete_branches skip unmerged branches on bulk refusal
- branch: convert delete_branches() to a flags argument
- branch: add --forked filter for --list mode
The 'git branch' command has been taught the '--delete-merged' option
to remove local branches that have already been merged to the remote-
tracking branches they track.
Needs review.
source: <pull.2285.v18.git.git.1782338106.gitgitgadget@gmail.com>
* hn/checkout-track-fetch (2026-06-24) 2 commits
- checkout: extend --track with a "fetch" mode to refresh start-point
- branch: expose helpers for finding the remote owning a tracking ref
The 'git checkout --track=...' command has been taught to optionally
fetch the branch from the remote the new branch will work with.
Comments?
cf. <xmqq5x37h6fj.fsf@gitster.g>
source: <pull.2281.v15.git.git.1782338098.gitgitgadget@gmail.com>
* ps/shift-root-in-graph (2026-07-04) 3 commits
- graph: indent visual root in graph
- graph: add a 2 commit buffer for lookahead
- lib-log-graph: move check_graph function
'git log --graph' has been modified to visually distinguish parentless
'root' commits (and commits that become roots due to history
simplification) by indenting them, preventing them from appearing
falsely related to unrelated commits rendered immediately above them.
Expecting a reroll.
cf. <CAN5EUNQoLtJ9cGwe8RNJTTdngM=qoak2=5F+yc7TH94TmQn7uw@mail.gmail.com>
source: <20260704-ps-pre-commit-indent-v7-0-a94706cc8376@gmail.com>
^ permalink raw reply
* Re: [PATCH 1/2] commit-graph: add trace2 instrumentation for generation DFS
From: Kristofer Karlsson @ 2026-07-07 17:39 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Kristofer Karlsson via GitGitGadget, git
In-Reply-To: <xmqq1pde7n8h.fsf@gitster.g>
On Tue, 7 Jul 2026 at 18:56, Junio C Hamano <gitster@pobox.com> wrote:
> >
> > Add a step counter and trace2_data_intmax call to
> > compute_reachable_generation_numbers() to make the cost of
> > the generation number DFS observable. This exposes a
> > regression introduced in 199d452758 (commit-graph: fix
> > "filling in" topological levels, 2025-04-07) where
>
> Where did "fix filling in" came from? Are you blaming
>
> 199d452758 (commit-graph: return the prepared commit graph from
> `prepare_commit_graph()`, 2025-09-04)
>
> or something else that happend in April that year?
Hm, I actually don't remember that exact text, it must have been
an oversight during editing back and forth and I missed it in
my local review -- the commit oid is correct though, that is
the one I was referring to. I will clean this up and shrink it down.
I did not mean April though, but September 4th. I was using
the ISO 8601 date format out of habit.
> OK. I expect that [2/2] would update this exact test to demonstrate
> that with code updated in [2/2] the extra walk will no longer happen.
Yes, I first considered doing this as a single commit, but
I figured it would be easier to reason about the fix if the
problem was identified before-hand.
Thanks,
Kristofer
^ permalink raw reply
* Re: [PATCH 2/2] commit-graph: propagate topo_levels slab to all chain layers
From: Kristofer Karlsson @ 2026-07-07 17:42 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Kristofer Karlsson via GitGitGadget, git
In-Reply-To: <xmqqo6gi68go.fsf@gitster.g>
On Tue, 7 Jul 2026 at 19:00, Junio C Hamano <gitster@pobox.com> wrote:
> >
> > Fix a regression introduced in 199d452758 (commit-graph: fix
> > "filling in" topological levels, 2025-04-07) where the loop
>
> I guess the same comment from [1/2] applies. We might be chasing
> ghosts here. Is that elusive commit a total hallucination?
Oops! The commit exists but the date there is indeed wrong.
Will fix (or just remove it, I am starting to regret trying to make
the commit reference too detailed in the first place).
Thanks,
Kristofer
^ permalink raw reply
* [PATCH] Rust: fix description in Release Notes to 2.55
From: Junio C Hamano @ 2026-07-07 17:47 UTC (permalink / raw)
To: git
Finish incomplete sentence to say that we
- build Git 2.55 by default with Rust,
- but you can opt out and build 2.55 without Rust,
- but Rust will become mandatory in Git 3.0 and later.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
* I usually do not bother with updating "historical" documents, but
this one seems to have already caused a confusion, so...
Documentation/RelNotes/2.55.0.adoc | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/RelNotes/2.55.0.adoc b/Documentation/RelNotes/2.55.0.adoc
index f5643534dc..e7e77a8112 100644
--- a/Documentation/RelNotes/2.55.0.adoc
+++ b/Documentation/RelNotes/2.55.0.adoc
@@ -85,8 +85,8 @@ Performance, Internal Implementation, Development Support etc.
* Promisor remote handling has been refactored and fixed in
preparation for auto-configuration of advertised remotes.
- * Rust support is enabled by default (but still allows opting out) in
- some future version of Git.
+ * Rust support is enabled by default (but still allows opting out);
+ in Git version 3.0, Rust will become mandatory.
* Preparation of the xdiff/ codebase to work with Rust.
--
2.55.0-270-g106a830b98
^ permalink raw reply related
* Re: [PATCH] Rust: fix description in Release Notes to 2.55
From: Michael Montalbo @ 2026-07-07 17:54 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Junio C Hamano <gitster@pobox.com> writes:
> diff --git a/Documentation/RelNotes/2.55.0.adoc b/Documentation/RelNotes/2.55.0.adoc
> index f5643534dc..e7e77a8112 100644
> --- a/Documentation/RelNotes/2.55.0.adoc
> +++ b/Documentation/RelNotes/2.55.0.adoc
> @@ -85,8 +85,8 @@ Performance, Internal Implementation, Development Support etc.
> * Promisor remote handling has been refactored and fixed in
> preparation for auto-configuration of advertised remotes.
>
> - * Rust support is enabled by default (but still allows opting out) in
> - some future version of Git.
> + * Rust support is enabled by default (but still allows opting out);
> + in Git version 3.0, Rust will become mandatory.
>
LGTM.
^ permalink raw reply
* Re: [PATCH v2 00/13] setup: split up repository discovery and setup
From: Junio C Hamano @ 2026-07-07 17:58 UTC (permalink / raw)
To: Justin Tobler; +Cc: Patrick Steinhardt, git
In-Reply-To: <ak0U46-J4qmwL2FD@denethor>
Justin Tobler <jltobler@gmail.com> writes:
> On 26/07/07 09:21AM, Patrick Steinhardt wrote:
>> Changes in v2:
>> - Expand commit message to talk about precedence order between
>> the "GIT_SHALLOW_FILE" environment variable and the "--shallow-file"
>> command line switch.
>> - Remove a now-unused parameter in `set_alternate_shallow_file()`.
>> - Fix a typo.
>> - Link to v1: https://patch.msgid.link/20260630-pks-setup-split-discovery-and-setup-v1-0-13864eb5a032@pks.im
>
> The changes in this version look good to me. Thanks.
Thanks, both. These indeed look good.
Will replace.
^ permalink raw reply
* Re: [PATCH GSoC v15 02/13] git-compat-util: add `strtoumax_szt()` with error handling
From: Junio C Hamano @ 2026-07-07 18:09 UTC (permalink / raw)
To: Pablo Sabater
Cc: git, chandrapratap3519, chriscool, eric.peijian, jltobler,
karthik.188, peff, toon
In-Reply-To: <CAN5EUNTYeDrQMor29eYMhJD0jcdRQq36ZA6BgupV8gG9xs9rFQ@mail.gmail.com>
Pablo Sabater <pabloosabaterr@gmail.com> writes:
>> If you are trying to more explicitly insist that s[] has only
>> digits, which may not be a bad idea, as that is what we generally
>> expect, then
>>
>> if (!s[0] || s[strspn(s, "0123456789")])
>> return -1;
>>
>> perhaps.
>
> I like the idea of only digits but, even though in this series I only
> use this function in base 10, I want the function to work in other
> bases, that's why I left the base in the function signature instead of
> hardcoding it. strspn(s, "0123456789") rejects bases >10 ("ff" for
> base 16) while strtoumax does support higher ones.
> I think that it would be better to explicitly reject what we don't
> want similarly to "-":
Let's step back a bit and think.
Where do we plan to use this function? Remember that being a
superset is not always necessarily good for a helper function that
serves as a format checker.
In the output of "git diff master...ps/cat-file-remote-object-info",
there is only one caller, which is fetch_object_info(). It reads
into object_info_data[].sizep. Do we expect to express the object
size in anything but an unsigned decimal integer? Remember that it
is better to be unambiguous when designing a protocol. We do not
want a third-party reimplementation of whatever is talking to
fetch_object_info() to send object size in hex ;-).
It may also be usable to parse the size of the object payload in
object-file.c::parse_loose_header() but notice that it is already
even stricter not to use strto<anything> system function and instead
handcrafts the trivial number parsing. This would avoid system
dependent funnyness, which is a good thing.
> if (!*s || isspace((unsigned char)*s) || *s == '-' || *s == '+')
> return -1;
>
> About that, strtoumax works fine with "+" and ignores starting
> whitespaces, but for consistency (we reject "-" and whitespaces
> between or at the end) rejecting whitespaces and +/- will be better
> and make the caller format it correctly.
>
> I'll do that for the next version.
^ permalink raw reply
* Re: [PATCH v7 2/3] graph: add a 2 commit buffer for lookahead
From: Pablo Sabater @ 2026-07-07 18:12 UTC (permalink / raw)
To: Chandra Pratap
Cc: Kristofer Karlsson, git, ayu.chandekar, christian.couder, gitster,
jltobler, karthik.188, peff, phillip.wood, siddharthasthana31
In-Reply-To: <CAN5EUNQoLtJ9cGwe8RNJTTdngM=qoak2=5F+yc7TH94TmQn7uw@mail.gmail.com>
El mar, 7 jul 2026 a las 8:31, Pablo Sabater
(<pabloosabaterr@gmail.com>) escribió:
>
> El lun, 6 jul 2026 a las 17:33, Chandra Pratap
> (<chandrapratap3519@gmail.com>) escribió:
> >
> > On Mon, 6 Jul 2026 at 19:15, Kristofer Karlsson <krka@spotify.com> wrote:
> > >
> > > The hardcoded size-2 lookahead buffer was my suggestion,
> > > so I am responding inline with my thoughts although Pablo is
> > > the right person for making further changes (if any).
> > >
> > > On Mon, 6 Jul 2026, Chandra Pratap <chandrapratap3519@gmail.com> wrote:
> > > > Do we need to NULL out the retrieved buffer entries? If so, it is
> > > > worthwhile asserting that the entire buffer is NULLed out in the
> > > > !graph->lookahead_nr check above.
> > >
> > > You're right, it's not technically needed, and there are many places
> > > in the repo where stale data remains in buffers, and it would be possible
> > > to do that here too. I don't think it matters much in practice though,
> > > and NULLing them out would perhaps prevent some accidental reuse on bugs
> > > (NULL would crash instead).
>
> It is not really needed to NULL because every time we access it (pop
> or the graph_is_interesting()) we are limited by graph->lookahead_nr,
> however I thought that it is better to have it NULL.
>
> Imagine that somehow the lookahead_nr is 1 when it should be 0, having
> NULL would segfault or if it doesn't at least we are sure that
> graph_is_interesting() won't re-process as interesting a commit left
> as stale on the buffer. Anyway, this is just speculation. I think it's
> better to leave it like this.
>
> > >
> > > As for asserting: rather than checking that empty slots are NULL
> > > (which just verifies our own cleanup), it might be more useful to
> > > assert that a slot is non-NULL when lookahead_nr says it should be
> > > populated, i.e. assert on read rather than on empty. But even that
> > > may be overkill for a 2-element internal buffer.
> >
> > True. But since we're already going through the pains of initializing the
> > buffer and NULLing it upon a pop, I'd much rather go the extra length
> > and verify what we're trying to do, shouldn't be that complicated anyway.
> >
> > Whether that means checking for NULL here, on a push, or on a read
> > is something I don't feel strongly about, either is fine with me.
>
> About asserting, I think that the best is, because we are popping, to
> check the first element only just in case we are in the imaginary
> scenario that lookahead_nr is lying, but because we pop, we don't
> really care about what's on the second entry.
>
> >
> > > > Not the best engineering practice, but I guess it is fine to constrain
> > > > the logic to _only_ a 2-entry buffer since that's what we'll always
> > > > deal with anyway.
> > >
> > > I did consider making it a proper ring buffer, but it felt like
> > > overkill (and I could not find any other existing ring buffer to
> > > piggy-back on in the repo), and the lookahead depth is
> > > structurally tied to the algorithm - we only ever need two more
> > > elements.
> > >
> > > It also helps that this is entirely internal to graph.c. If the
> > > buffer were part of a broader API, a less hardcoded approach
> > > would be more appropriate indeed.
> >
> > Agreed.
> >
> > > > We should use ARRAY_SIZE(graph->lookahead) instead of hardcoding
> > > > the value 2.
> > >
> > > Agreed, that is a nice improvement. What do you think Pablo?
>
> Yes, I'll do that on reroll.
>
> > >
> > > Thanks,
> > > Kristofer
>
> Not related with this feedback but worth saying:
>
> re-reading what's done on revision.c there is this if line:
> > if (!revs->max_count_stage && !revs->reverse_output_stage)
>
> Graph is not compatible with --reverse, so the right-side will always be true.
> About --max-count, I made a few tests and the lookahead behaves the
> same regardless of the number of commits to be shown (even if capped).
Now that I saw the GitHub CI tests, at t4202 there is a graph option
"--max-count-oldest" that makes the !revs->max_count_stage check
necessary.
After that everything seems to work, if anything I'll explain it on
the cover letter soonly.
>
> So this whole if block can be dropped and we can try to populate the
> lookahead buffer always.
>
> Thanks both for the feedback and review,
> Pablo.
Regards,
Pablo
^ permalink raw reply
* Re: [PATCH v2] config: retry acquiring config.lock, configurable via core.configLockTimeout
From: Junio C Hamano @ 2026-07-07 18:16 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Joerg Thalheim, git, Patrick Steinhardt
In-Reply-To: <10bb26f4-38e7-1bb8-d2d9-4d3e2ef52adc@gmx.de>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi,
>
> On Thu, 28 May 2026, Johannes Schindelin wrote:
>
>> On Sun, 17 May 2026, Joerg Thalheim wrote:
>>
>> > I matched the core.filesRefLockTimeout naming rather than reusing
>> > microsoft/git's core.configWriteLockTimeoutMS, but can switch if the
>> > downstream compat matters more.
>>
>> I see that there is quite a bit of precedent for naming a config setting
>> `*Timeout` and implying that it specifies milliseconds, e.g.
>> https://git-scm.com/docs/git-config#Documentation/git-config.txt-corefilesRefLockTimeout
>>
>> In general, I am pretty wary of unit-less numbers [*1*], that's why I
>> chose that "MS" suffix. However, the prior art in Git is clear, and I
>> should not have missed it. Therefore, I have no objections against
>> `core.configLockTimeout` as-is; I'll take care of providing a smooth
>> upgrade path in Microsoft Git.
>
> For the record: I meant this feedback as _supporting_ the patch. Now I see
> it is stalled... I do not really see any reason for this to be blocked
> from promoting to `next` and then `master`, though.
>
> Ciao,
> Johannes
Heh, this paragraph
microsoft/git carries a similar patch (core.configWriteLockTimeoutMS,
default off) for Scalar's tests. Defaulting to non-zero here because
the worktree case fails silently.
in the proposed log message was enough to convince me that you'd be
favor of it.
I think the "Waiting for response(s) to review comment(s)." is for
So I'd rather lean towards dropping the cache and keeping the
repository parameter.
that was expressed in a separate review in <agrIrGwSMFlKTx9x@pks.im>
and haven't been responded to.
Thanks for pinging.
^ permalink raw reply
* Re: [PATCH] sideband: allow ANSI SGR with colon-separated subfields
From: Junio C Hamano @ 2026-07-07 18:19 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: grawity, git, Mantas Mikulėnas
In-Reply-To: <8addf7c0-ae39-f1c0-20ab-52114702aaf6@gmx.de>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi Mantas,
>
> On Wed, 13 May 2026, grawity@nullroute.lt wrote:
>
>> From: Mantas Mikulėnas <grawity@gmail.com>
>>
>> The SGR values used for 256-color formatting are officially defined to
>> be a single field with :-separated subfields (e.g. "\e[1;38:5:XX;40m")
>> despite the more common but kludgy use of separate values (which then
>> become context-dependent and lead to misinterpretation by incompatible
>> terminals).
>>
>> See also: https://github.com/ThomasDickey/xterm-snapshots/blob/6380a3eaed857c182ea6cfa78cd706966b2628d0/charproc.c#L2047-L2118
>
> This change seems well-motivated and well-executed to me. Just in case
> anybody was waiting for my objections, there ain't any coming ;-)
Should I take it as an Ack?
FWIW, this patch literally flew below my radar coverage. Thanks for
noticing.
A need for fix-up like this does makes me doubt out decision to go
with whitelisting very narrow cases that are known to be OK (and
finding that the cases were too narrow and we need to extend),
instead of rejecting known-bad cases, by the way.
Thanks.
> Ciao,
> Johannes
>
>>
>> Signed-off-by: Mantas Mikulėnas <grawity@gmail.com>
>> ---
>> sideband.c | 6 +++++-
>> 1 file changed, 5 insertions(+), 1 deletion(-)
>>
>> diff --git a/sideband.c b/sideband.c
>> index 04282a568e..6cf70ef6f6 100644
>> --- a/sideband.c
>> +++ b/sideband.c
>> @@ -163,6 +163,10 @@ static int handle_ansi_sequence(struct strbuf *dest, const char *src, int n)
>> *
>> * ESC [ [<n> [; <n>]*] m
>> *
>> + * where <n> can be either zero-length, or a decimal number, or a
>> + * series of decimal numbers separated by a colon (for 256-color or
>> + * true-color codes).
>> + *
>> * These are part of the Select Graphic Rendition sequences which
>> * contain more than just color sequences, for more details see
>> * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.
>> @@ -210,7 +214,7 @@ static int handle_ansi_sequence(struct strbuf *dest, const char *src, int n)
>> strbuf_add(dest, src, i + 1);
>> return i;
>> }
>> - if (!isdigit(src[i]) && src[i] != ';')
>> + if (!isdigit(src[i]) && src[i] != ':' && src[i] != ';')
>> break;
>> }
>>
>> --
>> 2.54.0
>>
>>
^ permalink raw reply
* [PATCH v7 0/3] Teach git-replay(1) to linearize merge commits
From: Toon Claes @ 2026-07-07 19:07 UTC (permalink / raw)
To: git; +Cc: Elijah Newren, Toon Claes, Johannes Schindelin
In-Reply-To: <20260702-toon-git-replay-drop-merges-v6-0-78a07cdd0382@iotcl.com>
As an alternative to dscho's patch series to replay merges[1], add
an option to git-replay(1) to linearize merges. This mimics what
git-rebase(1) does with --no-rebase-merges (the default).
The first two patches do some refactoring. The third patch implements
the actual change. This patch was kindly provided by Dscho, which I've
tweaked to be upstreamed.
The --linearize option is only added to git-replay(1) and not to
git-history(1) because in my opinion it doesn't make much sense to do
so, but I'm happy to hear if anyone disagrees.
This series might conflict with Kristoffer's series to make
documentation changes[2], but should be trivial to resolve. And I don't
think there's a conflict with Patrick's series on adding "drop" to
git-history(1)[3].
dscho's series to replay merges[1] needs a bit of rework to fit on top
of this, but I'm happy to help figuring that out. We've been discussing
to either name the option --flatten or --linearize, but I've decided on
"linearize" because the documentation of git-rebase(1) also mentions
"linearize".
[1]: <pull.2106.git.1778107405.gitgitgadget@gmail.com>
[2]: <V3_CV_doc_replay_config.780@msgid.xyz>
[3]: <20260603-b4-pks-history-drop-v2-0-742cb5b5176d@pks.im>
---
Changes in v7:
- Allow --revert and --linearize to be used together.
- Because quite a lot of changes have been made since the original
patch, change author from Johannes to Toon for the last commit.
Johannes already told me he doesn't really care about authorship when
he initially shared the patch with me.
- Link to v6: https://patch.msgid.link/20260702-toon-git-replay-drop-merges-v6-0-78a07cdd0382@iotcl.com
Changes in v6:
- Reworked the second commit that moves picking the base completely
outside pick_regular_commit(), instead of adding more explanation.
- Drastically extended the commit message on commit #3.
- Extended docs on flattening multiple revision ranges and how it's
different from git-rebase(1)'s --no-rebase-merges.
- Added a bunch of tests to cover various scenarios.
- Remove newline from BUG() message.
- Link to v5: https://patch.msgid.link/20260626-toon-git-replay-drop-merges-v5-0-5e120738b9d0@iotcl.com
Changes in v5:
- Dropped the enum->bool patch and instead added a patch that better
explains how pick_regular_commit() picks a base.
- Order of commits is shuffled.
- (BIGGEST CHANGE) When working on a refactor to undo the enum->bool
patch, I extended the code comments to explain how things work. This
made me realize the use of the "replayed_base" was incorrect when
multiple branches are rebased with --onto. This is fixed now and a
test is added for this scenario.
- Link to v4: https://patch.msgid.link/20260622-toon-git-replay-drop-merges-v4-0-ff257f534319@iotcl.com
Changes in v4:
- Use test_grep instead of a bare grep in the range-diff test, to
prepare for mm/test-grep-lint.
- Link to v3: https://patch.msgid.link/20260616-toon-git-replay-drop-merges-v3-0-153e9eb99ce1@iotcl.com
Changes in v3:
- Add --linearize to Documentation SYNOPSIS, and mention it's
incompatible with --revert.
- Small language change in help message for --linearize.
- Rephrase comment to include last_commit isn't modified when
linearizing merges.
- Remove test that was added in earlier versions, but actually is
a duplicate of 'replaying merge commits is not supported yet'.
- Add test to verify --revert and --linearize are incompatible.
- Properly test that replaying down to root with --linearize works.
- Add test for --linearize with --advance.
- Add test that uses git-range-diff(1) to verify the patches created by
--linearize are correct.
- Link to v2: https://patch.msgid.link/20260610-toon-git-replay-drop-merges-v2-0-5714a71c6d83@iotcl.com
Changes in v2:
- Restructured the conditions to detect merge commits and added a line
of comment why the loop continues.
- Rewrote tests to use the history from the setup step and added a few
test cases.
- Re-added Johannes's Signed-off-by trailer. Johannes gave me the
patches with this trailer, and if I understand correctly, I can keep
it. Please let me know if that wrong.
- Link to v1: https://patch.msgid.link/20260608-toon-git-replay-drop-merges-v1-0-e3ee71fce7b4@iotcl.com
---
Toon Claes (3):
replay: add helper to put entry into replayed_commits
replay: resolve the replay base outside pick_regular_commit()
replay: offer an option to linearize the commit topology
Documentation/git-replay.adoc | 19 +++++-
builtin/replay.c | 4 +-
replay.c | 81 ++++++++++++++++--------
replay.h | 5 ++
t/t3650-replay-basics.sh | 140 +++++++++++++++++++++++++++++++++++++++++-
5 files changed, 221 insertions(+), 28 deletions(-)
Range-diff versus v6:
1: 96637c42a9 ! 1: ce24fba6d6 replay: add helper to put entry into replayed_commits
@@ Commit message
replay: add helper to put entry into replayed_commits
The function replay_revisions() in replay.c is rather lengthy. Extract
- the logic to put a commit entry into mapped_commits into a helper
- function put_mapped_commit().
+ the logic to put a commit entry into a `struct mapped_commits` into a
+ helper function put_mapped_commit().
While at it, rename mapped_commit() to get_mapped_commit() to pair with
this new function.
2: ae6c27aee6 ! 2: 6a39274c1c replay: resolve the replay base outside pick_regular_commit()
@@ Commit message
Move the base selection completely into the caller: replay_revisions().
This bundles all the logic of deciding on the base together. Also, this
- reduces the number of parameters of pick_regular_commit(), making it's
+ reduces the number of parameters of pick_regular_commit(), making its
interface cleaner.
This refactoring doesn't bring any behavior changes.
3: 0208101e9b ! 3: 2960b9fdaf replay: offer an option to linearize the commit topology
@@
## Metadata ##
-Author: Johannes Schindelin <Johannes.Schindelin@gmx.de>
+Author: Toon Claes <toon@iotcl.com>
## Commit message ##
replay: offer an option to linearize the commit topology
@@ Commit message
rather than mirror git-rebase(1)'s `--rebase-merges[=<mode>]` interface,
git-replay(1) uses its own `--linearize` option.
- Co-authored-by: Toon Claes <toon@iotcl.com>
- Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+ Based-on-patches-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Toon Claes <toon@iotcl.com>
## Documentation/git-replay.adoc ##
@@ Documentation/git-replay.adoc: incompatible with `--contained` (which is a modif
+history. Each of their refs is updated to point to its position in that
+history. To linearize ranges separately, replay them in separate `git
+replay` invocations.
-++
-+This option is incompatible with `--revert`.
+
<revision-range>::
Range of commits to replay; see "Specifying Ranges" in
@@ builtin/replay.c: int cmd_replay(int argc,
OPT_END()
};
-@@ builtin/replay.c: int cmd_replay(int argc,
- opts.contained, "--contained");
- die_for_incompatible_opt2(!!opts.ref, "--ref",
- !!opts.contained, "--contained");
-+ die_for_incompatible_opt2(!!opts.revert, "--revert",
-+ opts.linearize, "--linearize");
-
- /* Parse ref action mode from command line or config */
- ref_mode = get_ref_action_mode(repo, ref_action);
## replay.c ##
@@ replay.c: int replay_revisions(struct rev_info *revs,
@@ t/t3650-replay-basics.sh: test_expect_success 'setup' '
git switch -c conflict B &&
- test_commit C.conflict C.t conflict
+ test_commit C.conflict C.t conflict &&
-+ git branch -D unrelated
++ git branch -D unrelated &&
++
++ git switch -c divergent-x main &&
++ test_commit X &&
++ git switch -c divergent-y main &&
++ test_commit Y &&
++ git switch divergent-x &&
++ test_merge Z divergent-y --no-ff
'
test_expect_success 'setup bare' '
-@@ t/t3650-replay-basics.sh: test_expect_success '--advance and --contained cannot be used together' '
- test_grep "cannot be used together" actual
- '
-
-+test_expect_success '--revert and --linearize cannot be used together' '
-+ test_must_fail git replay --revert=main --linearize \
-+ topic1..topic2 2>actual &&
-+ test_grep "cannot be used together" actual
-+'
-+
- test_expect_success 'cannot advance target ... ordering would be ill-defined' '
- echo "fatal: ${SQ}--advance${SQ} cannot be used with multiple revision ranges because the ordering would be ill-defined" >expect &&
- test_must_fail git replay --advance=main main topic1 topic2 2>actual &&
@@ t/t3650-replay-basics.sh: test_expect_success '--onto with --ref rejects multiple revision ranges' '
test_grep "cannot be used with multiple revision ranges" err
'
@@ t/t3650-replay-basics.sh: test_expect_success '--onto with --ref rejects multipl
+'
+
+test_expect_success 'replay with --linearize of a divergent merge keeps both sides' '
-+ test_when_finished "git update-ref -d refs/heads/divergent-x" &&
-+ test_when_finished "git update-ref -d refs/heads/divergent-y" &&
-+
-+ # Build a real merge of two commits that diverged from a common base:
-+ #
-+ # X - Z (divergent-x)
-+ # / /
-+ # M - Y (divergent-y)
-+ #
-+ git switch -c divergent-x main &&
-+ test_commit X &&
-+ git switch -c divergent-y main &&
-+ test_commit Y &&
-+ git switch divergent-x &&
-+ test_merge Z divergent-y --no-ff &&
-+
+ git replay --ref-action=print --linearize \
+ --onto main main..divergent-x >result &&
+ test_line_count = 1 result &&
@@ t/t3650-replay-basics.sh: test_expect_success '--onto with --ref rejects multipl
+ test_write_lines O N J I M L B A >expect &&
+ test_cmp expect actual
+'
++
++test_expect_success 'replay --revert with --linearize reverts a range containing a merge' '
++ git replay --ref-action=print --revert=divergent-x --linearize \
++ main..divergent-x >result &&
++ test_line_count = 1 result &&
++ tip=$(cut -f 3 -d " " result) &&
++
++ git log --format=%s $tip >actual &&
++ test_write_lines \
++ "Revert \"X\"" "Revert \"Y\"" Z Y X M L B A >expect &&
++ test_cmp expect actual &&
++
++ test_must_fail git cat-file -e $tip:X.t &&
++ test_must_fail git cat-file -e $tip:Y.t
++'
+
test_done
---
base-commit: ab776a62a78576513ee121424adb19597fbb7613
change-id: 20260604-toon-git-replay-drop-merges-807fa008d395
^ permalink raw reply
* [PATCH v7 1/3] replay: add helper to put entry into replayed_commits
From: Toon Claes @ 2026-07-07 19:07 UTC (permalink / raw)
To: git; +Cc: Elijah Newren, Toon Claes, Johannes Schindelin
In-Reply-To: <20260707-toon-git-replay-drop-merges-v7-0-808ab9b4afa6@iotcl.com>
The function replay_revisions() in replay.c is rather lengthy. Extract
the logic to put a commit entry into a `struct mapped_commits` into a
helper function put_mapped_commit().
While at it, rename mapped_commit() to get_mapped_commit() to pair with
this new function.
Signed-off-by: Toon Claes <toon@iotcl.com>
---
replay.c | 31 ++++++++++++++++++++-----------
1 file changed, 20 insertions(+), 11 deletions(-)
diff --git a/replay.c b/replay.c
index da531d5bc6..b9f8fc47ce 100644
--- a/replay.c
+++ b/replay.c
@@ -250,9 +250,9 @@ static void set_up_replay_mode(struct repository *repo,
strset_clear(&rinfo.positive_refs);
}
-static struct commit *mapped_commit(kh_oid_map_t *replayed_commits,
- struct commit *commit,
- struct commit *fallback)
+static struct commit *get_mapped_commit(kh_oid_map_t *replayed_commits,
+ struct commit *commit,
+ struct commit *fallback)
{
khint_t pos;
if (!commit)
@@ -263,6 +263,21 @@ static struct commit *mapped_commit(kh_oid_map_t *replayed_commits,
return kh_value(replayed_commits, pos);
}
+static void put_mapped_commit(kh_oid_map_t *replayed_commits,
+ struct commit *commit,
+ struct commit *new_commit)
+{
+ khint_t pos;
+ int ret;
+
+ pos = kh_put_oid_map(replayed_commits, commit->object.oid, &ret);
+ if (ret == 0)
+ BUG("Duplicate rewritten commit: %s",
+ oid_to_hex(&commit->object.oid));
+
+ kh_value(replayed_commits, pos) = new_commit;
+}
+
static struct commit *pick_regular_commit(struct repository *repo,
struct commit *pickme,
kh_oid_map_t *replayed_commits,
@@ -283,7 +298,7 @@ static struct commit *pick_regular_commit(struct repository *repo,
base_tree = lookup_tree(repo, repo->hash_algo->empty_tree);
}
- replayed_base = mapped_commit(replayed_commits, base, onto);
+ replayed_base = get_mapped_commit(replayed_commits, base, onto);
replayed_base_tree = repo_get_commit_tree(repo, replayed_base);
pickme_tree = repo_get_commit_tree(repo, pickme);
@@ -423,8 +438,6 @@ int replay_revisions(struct rev_info *revs,
replayed_commits = kh_init_oid_map();
while ((commit = get_revision(revs))) {
const struct name_decoration *decoration;
- khint_t pos;
- int hr;
if (commit->parents && commit->parents->next)
die(_("replaying merge commits is not supported yet!"));
@@ -436,11 +449,7 @@ int replay_revisions(struct rev_info *revs,
break;
/* Record commit -> last_commit mapping */
- pos = kh_put_oid_map(replayed_commits, commit->object.oid, &hr);
- if (hr == 0)
- BUG("Duplicate rewritten commit: %s\n",
- oid_to_hex(&commit->object.oid));
- kh_value(replayed_commits, pos) = last_commit;
+ put_mapped_commit(replayed_commits, commit, last_commit);
/* Update any necessary branches */
if (ref)
--
2.53.0.1323.g189a785ab5
^ permalink raw reply related
* [PATCH v7 2/3] replay: resolve the replay base outside pick_regular_commit()
From: Toon Claes @ 2026-07-07 19:07 UTC (permalink / raw)
To: git; +Cc: Elijah Newren, Toon Claes, Johannes Schindelin
In-Reply-To: <20260707-toon-git-replay-drop-merges-v7-0-808ab9b4afa6@iotcl.com>
Depending on what gets passed into the function pick_regular_commit(),
it decides the new base for the replayed commit. It first tries to find
the replayed results of `pickme`'s parent in the `replayed_commits` map.
If not found, it falls back to `onto`.
When using git-replay(1) with --onto, the fallback is the revision
passed in with this option, but when using --revert, the fallback is
`last_commit`.
It's rather confusing the base is decided partly inside
pick_regular_commit() and partly by its caller.
Move the base selection completely into the caller: replay_revisions().
This bundles all the logic of deciding on the base together. Also, this
reduces the number of parameters of pick_regular_commit(), making its
interface cleaner.
This refactoring doesn't bring any behavior changes.
Signed-off-by: Toon Claes <toon@iotcl.com>
---
replay.c | 34 +++++++++++++++++++++-------------
1 file changed, 21 insertions(+), 13 deletions(-)
diff --git a/replay.c b/replay.c
index b9f8fc47ce..5aee0eafbc 100644
--- a/replay.c
+++ b/replay.c
@@ -280,25 +280,19 @@ static void put_mapped_commit(kh_oid_map_t *replayed_commits,
static struct commit *pick_regular_commit(struct repository *repo,
struct commit *pickme,
- kh_oid_map_t *replayed_commits,
- struct commit *onto,
+ struct commit *replayed_base,
struct merge_options *merge_opt,
struct merge_result *result,
enum replay_mode mode,
enum replay_empty_commit_action empty)
{
- struct commit *base, *replayed_base;
struct tree *pickme_tree, *base_tree, *replayed_base_tree;
- if (pickme->parents) {
- base = pickme->parents->item;
- base_tree = repo_get_commit_tree(repo, base);
- } else {
- base = NULL;
+ if (pickme->parents)
+ base_tree = repo_get_commit_tree(repo, pickme->parents->item);
+ else
base_tree = lookup_tree(repo, repo->hash_algo->empty_tree);
- }
- replayed_base = get_mapped_commit(replayed_commits, base, onto);
replayed_base_tree = repo_get_commit_tree(repo, replayed_base);
pickme_tree = repo_get_commit_tree(repo, pickme);
@@ -439,12 +433,26 @@ int replay_revisions(struct rev_info *revs,
while ((commit = get_revision(revs))) {
const struct name_decoration *decoration;
+ /*
+ * Decide where to replay this commit on.
+ * If the parent commit was replayed already, the replayed result
+ * can be found in `replayed_commits`. Otherwise fall back to `onto`.
+ * When reverting, commits are replayed in reverse order and thus
+ * its parent isn't replayed yet. Therefore revert commits are
+ * always replayed onto `last_commit`.
+ */
+ struct commit *parent = commit->parents ? commit->parents->item : NULL;
+ struct commit *base = get_mapped_commit(replayed_commits, parent, onto);
+
+ if (mode == REPLAY_MODE_REVERT)
+ base = last_commit;
+
if (commit->parents && commit->parents->next)
die(_("replaying merge commits is not supported yet!"));
- last_commit = pick_regular_commit(revs->repo, commit, replayed_commits,
- mode == REPLAY_MODE_REVERT ? last_commit : onto,
- &merge_opt, &result, mode, opts->empty);
+ last_commit = pick_regular_commit(revs->repo, commit, base,
+ &merge_opt, &result,
+ mode, opts->empty);
if (!last_commit)
break;
--
2.53.0.1323.g189a785ab5
^ permalink raw reply related
* [PATCH v7 3/3] replay: offer an option to linearize the commit topology
From: Toon Claes @ 2026-07-07 19:07 UTC (permalink / raw)
To: git; +Cc: Elijah Newren, Toon Claes, Johannes Schindelin
In-Reply-To: <20260707-toon-git-replay-drop-merges-v7-0-808ab9b4afa6@iotcl.com>
One of the stated goals of git-replay(1) is to allow implementing the
git-rebase(1) functionality on the server side.
The default mode of git-rebase(1) is to act as if `--no-rebase-merges`
was given. This mode drops merge commits instead of replaying them, and
linearizes the history into a sequence of regular (single-parent)
commits.
Add option `--linearize` to git-replay(1) to do the same. Each replayed
commit is stacked on top of the previously replayed one. When a merge is
encountered, the commits reachable from all of its sides are replayed
into the single line and the merge itself is dropped.
If a ref was pointing to a merge commit, that ref is updated to the
merge's last replayed ancestor.
git-replay(1) accepts multiple revision ranges, for example:
$ git replay --onto main topic1 topic2
Without `--linearize` this replays 'topic1' and 'topic2' onto 'main'
independently and updates both refs.
With `--linearize` the whole set is flattened into one line: the ranges
are stacked on top of each other rather than replayed side by side, so
both refs end up pointing at different points along that single history.
Replaying all revision ranges into one single linear history is
intentional and it's the only way to ensure predictable results. A user
who wants to linearize ranges independently is advised to use separate
git-replay(1) invocations.
Linearizing is a distinct operation, and flattening merge commits is
just one aspect of that. Recreating merges would be a separate mode, so
rather than mirror git-rebase(1)'s `--rebase-merges[=<mode>]` interface,
git-replay(1) uses its own `--linearize` option.
Based-on-patches-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Toon Claes <toon@iotcl.com>
---
Documentation/git-replay.adoc | 19 +++++-
builtin/replay.c | 4 +-
replay.c | 54 ++++++++++------
replay.h | 5 ++
t/t3650-replay-basics.sh | 140 +++++++++++++++++++++++++++++++++++++++++-
5 files changed, 199 insertions(+), 23 deletions(-)
diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
index a32f72aead..98e20c1c6e 100644
--- a/Documentation/git-replay.adoc
+++ b/Documentation/git-replay.adoc
@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
(EXPERIMENTAL!) 'git replay' ([--contained] --onto=<newbase> | --advance=<branch> | --revert=<branch>)
- [--ref=<ref>] [--ref-action=<mode>] <revision-range>
+ [--ref=<ref>] [--ref-action=<mode>] [--linearize] <revision-range>
DESCRIPTION
-----------
@@ -88,6 +88,23 @@ incompatible with `--contained` (which is a modifier for `--onto` only).
+
The default mode can be configured via the `replay.refAction` configuration variable.
+--linearize::
+ In this mode, each replayed commit is stacked on top of the
+ previously replayed one, so all replayed commits are flattened into
+ a single linear history.
++
+When a merge commit is encountered, the behavior of git-rebase(1)'s
+option `--no-rebase-merges` is imitated. All commits in the range
+reachable from the merge commit are replayed into a linear history, and
+the merge commit itself is dropped. A ref that pointed to a merge commit
+is updated to the merge's last replayed ancestor.
++
+This flattens the `<revision-range>` as a whole. When multiple revision
+ranges are given they are stacked on top of each other into one linear
+history. Each of their refs is updated to point to its position in that
+history. To linearize ranges separately, replay them in separate `git
+replay` invocations.
+
<revision-range>::
Range of commits to replay; see "Specifying Ranges" in
linkgit:git-rev-parse[1]. In `--advance=<branch>` or
diff --git a/builtin/replay.c b/builtin/replay.c
index 39e3a86f6c..5e6ff4191a 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -85,7 +85,7 @@ int cmd_replay(int argc,
const char *const replay_usage[] = {
N_("(EXPERIMENTAL!) git replay "
"([--contained] --onto=<newbase> | --advance=<branch> | --revert=<branch>)\n"
- "[--ref=<ref>] [--ref-action=<mode>] <revision-range>"),
+ "[--ref=<ref>] [--ref-action=<mode>] [--linearize] <revision-range>"),
NULL
};
struct option replay_options[] = {
@@ -111,6 +111,8 @@ int cmd_replay(int argc,
N_("mode"),
N_("control ref update behavior (update|print)"),
PARSE_OPT_NONEG),
+ OPT_BOOL(0, "linearize", &opts.linearize,
+ N_("drop merge commits, replaying only non-merge commits")),
OPT_END()
};
diff --git a/replay.c b/replay.c
index 5aee0eafbc..bd1f3bb898 100644
--- a/replay.c
+++ b/replay.c
@@ -433,26 +433,40 @@ int replay_revisions(struct rev_info *revs,
while ((commit = get_revision(revs))) {
const struct name_decoration *decoration;
- /*
- * Decide where to replay this commit on.
- * If the parent commit was replayed already, the replayed result
- * can be found in `replayed_commits`. Otherwise fall back to `onto`.
- * When reverting, commits are replayed in reverse order and thus
- * its parent isn't replayed yet. Therefore revert commits are
- * always replayed onto `last_commit`.
- */
- struct commit *parent = commit->parents ? commit->parents->item : NULL;
- struct commit *base = get_mapped_commit(replayed_commits, parent, onto);
-
- if (mode == REPLAY_MODE_REVERT)
- base = last_commit;
-
- if (commit->parents && commit->parents->next)
- die(_("replaying merge commits is not supported yet!"));
-
- last_commit = pick_regular_commit(revs->repo, commit, base,
- &merge_opt, &result,
- mode, opts->empty);
+ if (commit->parents && commit->parents->next) {
+ if (!opts->linearize)
+ die(_("replaying merge commits is not supported yet!"));
+ /*
+ * Drop the merge commit: do not pick it, leave
+ * `last_commit` unchanged, and fall through to the
+ * rest of the loop. As a result:
+ * - refs pointing to the merge commit will be updated
+ * to `last_commit`.
+ * - the next replayed commit uses `last_commit` as its
+ * `base`.
+ */
+ } else {
+ /*
+ * Decide where to replay this commit onto.
+ * If the parent commit was replayed already, the replayed result
+ * can be found in `replayed_commits`. Otherwise fall back to `onto`.
+ * When reverting, commits are replayed in reverse order and thus
+ * its parent isn't replayed yet. Therefore revert commits are
+ * always replayed onto `last_commit`.
+ * Also when opts->linearize is true, set the base to
+ * `last_commit` to create a single linear history.
+ */
+ struct commit *parent = commit->parents ? commit->parents->item : NULL;
+ struct commit *base = get_mapped_commit(replayed_commits, parent, onto);
+
+ if (opts->linearize || mode == REPLAY_MODE_REVERT)
+ base = last_commit;
+
+ last_commit = pick_regular_commit(revs->repo, commit, base,
+ &merge_opt, &result,
+ mode, opts->empty);
+ }
+
if (!last_commit)
break;
diff --git a/replay.h b/replay.h
index faf95c7459..64f42b6512 100644
--- a/replay.h
+++ b/replay.h
@@ -62,6 +62,11 @@ struct replay_revisions_options {
* Defaults to REPLAY_EMPTY_COMMIT_DROP.
*/
enum replay_empty_commit_action empty;
+
+ /*
+ * Whether to linearize the commits (i.e. drop merge commits).
+ */
+ int linearize;
};
/* This struct is used as an out-parameter by `replay_revisions()`. */
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
index 3353bc4a4d..4d3d442e8a 100755
--- a/t/t3650-replay-basics.sh
+++ b/t/t3650-replay-basics.sh
@@ -52,8 +52,19 @@ test_expect_success 'setup' '
test_merge P O --no-ff &&
git switch main &&
+ git switch --orphan unrelated &&
+ test_commit unrelated-root &&
+
git switch -c conflict B &&
- test_commit C.conflict C.t conflict
+ test_commit C.conflict C.t conflict &&
+ git branch -D unrelated &&
+
+ git switch -c divergent-x main &&
+ test_commit X &&
+ git switch -c divergent-y main &&
+ test_commit Y &&
+ git switch divergent-x &&
+ test_merge Z divergent-y --no-ff
'
test_expect_success 'setup bare' '
@@ -565,4 +576,131 @@ test_expect_success '--onto with --ref rejects multiple revision ranges' '
test_grep "cannot be used with multiple revision ranges" err
'
+test_expect_success 'replay to rebase merge commit with --linearize' '
+ git replay --ref-action=print --linearize \
+ --onto main I..topic-with-merge >result &&
+
+ test_line_count = 1 result &&
+
+ git log --format=%s $(cut -f 3 -d " " result) >actual &&
+ test_write_lines O N J M L B A >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'replay to rebase merge commit with --linearize down to the root commit' '
+ git replay --ref-action=print --linearize \
+ --onto unrelated-root topic-with-merge >result &&
+
+ test_line_count = 1 result &&
+
+ git log --format=%s $(cut -f 3 -d " " result) >actual &&
+ test_write_lines O N J I B A unrelated-root >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'replay to cherry-pick merge commit with --linearize' '
+ git replay --ref-action=print --linearize \
+ --advance main I..topic-with-merge >result &&
+
+ test_line_count = 1 result &&
+
+ git log --format=%s $(cut -f 3 -d " " result) >actual &&
+ test_write_lines O N J M L B A >expect &&
+ test_cmp expect actual &&
+
+ printf "update refs/heads/main " >expect &&
+ printf "%s " $(cut -f 3 -d " " result) >>expect &&
+ git rev-parse main >>expect &&
+ test_cmp expect result
+'
+
+test_expect_success 'replay --linearize produces the same patches' '
+ git replay --ref-action=print --linearize \
+ --onto main I..topic-with-merge >result &&
+
+ test_line_count = 1 result &&
+ tip=$(cut -f 3 -d " " result) &&
+
+ # range-diff does not care about the dropped merge,
+ # so the original commits (I..topic-with-merge)
+ # and the replayed chain (main..tip) must produce identical patches.
+ git range-diff I..topic-with-merge main..$tip >out &&
+ test_file_not_empty out &&
+ test_grep ! -v "=" out &&
+
+ git log --oneline main..$tip >out &&
+ test_line_count = 3 out
+'
+
+test_expect_success 'replay with --linearize rebase multiple divergent branches into a single line' '
+ git replay --ref-action=print --linearize \
+ --onto main ^B topic2 topic3 topic4 >result &&
+
+ test_line_count = 3 result &&
+ cut -f 3 -d " " result >new-branch-tips &&
+
+ >expect &&
+ for i in 2 3 4
+ do
+ printf "update refs/heads/topic$i " >>expect &&
+ printf "%s " $(grep topic$i result | cut -f 3 -d " ") >>expect &&
+ git rev-parse topic$i >>expect || return 1
+ done &&
+
+ test_cmp expect result &&
+
+ test_write_lines E D C M L B A >expect2 &&
+ test_write_lines H G F E D C M L B A >expect3 &&
+ test_write_lines J I H G F E D C M L B A >expect4 &&
+
+ for i in 2 3 4
+ do
+ git log --format=%s $(grep topic$i result | cut -f 3 -d " ") >actual &&
+ test_cmp expect$i actual || return 1
+ done
+'
+
+test_expect_success 'replay with --linearize of a divergent merge keeps both sides' '
+ git replay --ref-action=print --linearize \
+ --onto main main..divergent-x >result &&
+ test_line_count = 1 result &&
+ tip=$(cut -f 3 -d " " result) &&
+
+ # The merge Z is dropped, but both X and Y are linearized onto main;
+ # neither side is lost.
+ git log --format=%s main..$tip >actual &&
+ test_write_lines Y X >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '--linearize with --contained updates contained refs' '
+ git replay --ref-action=print --linearize --contained \
+ --onto main ^B topic-with-merge >result &&
+
+ test_line_count = 2 result &&
+
+ git log --format=%s $(head -n 1 result | cut -f 3 -d " ") >actual &&
+ test_write_lines J I M L B A >expect &&
+ test_cmp expect actual &&
+
+ git log --format=%s $(tail -n 1 result | cut -f 3 -d " ") >actual &&
+ test_write_lines O N J I M L B A >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'replay --revert with --linearize reverts a range containing a merge' '
+ git replay --ref-action=print --revert=divergent-x --linearize \
+ main..divergent-x >result &&
+ test_line_count = 1 result &&
+ tip=$(cut -f 3 -d " " result) &&
+
+ git log --format=%s $tip >actual &&
+ test_write_lines \
+ "Revert \"X\"" "Revert \"Y\"" Z Y X M L B A >expect &&
+ test_cmp expect actual &&
+
+ test_must_fail git cat-file -e $tip:X.t &&
+ test_must_fail git cat-file -e $tip:Y.t
+'
+
test_done
--
2.53.0.1323.g189a785ab5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox