* [PATCH v2 2/2] environment: move excludes_file into repo_config_values
From: Tian Yuchen @ 2026-06-26 7:50 UTC (permalink / raw)
To: git
Cc: cirnovskyv, Tian Yuchen, Christian Couder, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260626075037.532164-1-cat@malon.dev>
Continue the libification effort by moving the 'excludes_file' global
variable into 'struct repo_config_values'.
Since 'excludes_file' is a dynamically allocated string (char *), it
requires proper memory management. Introduce repo_config_values_clear()
to safely free the heap memory when repository instance is destroyed.
Note:
- 'if (repo != the_repository)' fallback logic is temporarily added
in both the getter and the clear function. This prevents calling
repo_config_values() on uninitialized submodules, which triggers BUG().
- 'attribute_file' is another string variable that was migrated
earlier. Its FREE_AND_NULL() call is also added to
repo_config_values_clear().
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>
Mentored-by: Olamide Caleb Bello <belkid98@gmail.com>
Signed-off-by: Tian Yuchen <cat@malon.dev>
---
environment.c | 31 +++++++++++++++++++++++++------
environment.h | 14 ++++++++++----
repository.c | 1 +
3 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/environment.c b/environment.c
index 8efcaeafa6..b8dfa3e213 100644
--- a/environment.c
+++ b/environment.c
@@ -57,7 +57,6 @@ enum fsync_method fsync_method = FSYNC_METHOD_DEFAULT;
enum fsync_component fsync_components = FSYNC_COMPONENTS_DEFAULT;
char *editor_program;
char *askpass_program;
-char *excludes_file;
enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
enum eol core_eol = EOL_UNSET;
int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
@@ -136,9 +135,13 @@ int is_bare_repository(void)
const char *repo_excludes_file(struct repository *repo)
{
- if (!excludes_file)
- excludes_file = xdg_config_home("ignore");
- return excludes_file;
+ if (!repo || !repo->initialized || repo != the_repository)
+ return NULL;
+
+ if (!repo_config_values(repo)->excludes_file)
+ repo_config_values(repo)->excludes_file = xdg_config_home("ignore");
+
+ return repo_config_values(repo)->excludes_file;
}
int have_git_dir(void)
@@ -468,8 +471,8 @@ int git_default_core_config(const char *var, const char *value,
}
if (!strcmp(var, "core.excludesfile")) {
- FREE_AND_NULL(excludes_file);
- return git_config_pathname(&excludes_file, var, value);
+ FREE_AND_NULL(cfg->excludes_file);
+ return git_config_pathname(&cfg->excludes_file, var, value);
}
if (!strcmp(var, "core.whitespace")) {
@@ -722,6 +725,7 @@ int git_default_config(const char *var, const char *value,
void repo_config_values_init(struct repo_config_values *cfg)
{
cfg->attributes_file = NULL;
+ cfg->excludes_file = NULL;
cfg->apply_sparse_checkout = 0;
cfg->branch_track = BRANCH_TRACK_REMOTE;
cfg->trust_ctime = 1;
@@ -733,3 +737,18 @@ void repo_config_values_init(struct repo_config_values *cfg)
cfg->sparse_expect_files_outside_of_patterns = 0;
cfg->warn_on_object_refname_ambiguity = 1;
}
+
+void repo_config_values_clear(struct repository *repo)
+{
+ struct repo_config_values *cfg;
+
+ if (repo != the_repository)
+ return;
+
+ cfg = repo_config_values(repo);
+ if (!cfg)
+ return;
+
+ FREE_AND_NULL(cfg->attributes_file);
+ FREE_AND_NULL(cfg->excludes_file);
+}
diff --git a/environment.h b/environment.h
index 52d531e4ea..2e8352de7f 100644
--- a/environment.h
+++ b/environment.h
@@ -90,6 +90,7 @@ struct repository;
struct repo_config_values {
/* section "core" config values */
char *attributes_file;
+ char *excludes_file;
int apply_sparse_checkout;
int trust_ctime;
int check_stat;
@@ -133,13 +134,19 @@ int git_default_config(const char *, const char *,
int git_default_core_config(const char *var, const char *value,
const struct config_context *ctx, void *cb);
-/*
- * TODO: This still relies on the global state.
- */
const char *repo_excludes_file(struct repository *repo);
void repo_config_values_init(struct repo_config_values *cfg);
+/*
+ * Frees memory allocated for dynamically loaded configuration values
+ * inside `repo_config_values`.
+ *
+ * As dynamically allocated variables are migrated into this struct,
+ * their FREE_AND_NULL() calls should be appended here.
+ */
+void repo_config_values_clear(struct repository *repo);
+
/*
* TODO: All the below state either explicitly or implicitly relies on
* `the_repository`. We should eventually get rid of these and make the
@@ -213,7 +220,6 @@ extern char *git_log_output_encoding;
extern char *editor_program;
extern char *askpass_program;
-extern char *excludes_file;
/*
* The character that begins a commented line in user-editable file
diff --git a/repository.c b/repository.c
index 187dd471c4..b31f1b7852 100644
--- a/repository.c
+++ b/repository.c
@@ -388,6 +388,7 @@ void repo_clear(struct repository *repo)
FREE_AND_NULL(repo->parsed_objects);
repo_settings_clear(repo);
+ repo_config_values_clear(repo);
if (repo->config) {
git_configset_clear(repo->config);
--
2.43.0
^ permalink raw reply related
* [PATCH v2 1/2] dir: encapsulate excludes_file lazy-load
From: Tian Yuchen @ 2026-06-26 7:50 UTC (permalink / raw)
To: git
Cc: cirnovskyv, Tian Yuchen, Christian Couder, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260626075037.532164-1-cat@malon.dev>
The global variable 'excludes_file' is used to track the path to the
global ignore file, 'core.excludesfile'. If this variable is NULL,
setup_standard_excludes() in dir.c forcefully evaluates and assigns
the XDG default path to it.
Introduce repo_excludes_file() as a getter to encapsulate this
lazy-loading logic. This prepares the variable to be safely moved
into 'struct repo_config_values' in the subsequent commit.
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>
Mentored-by: Olamide Caleb Bello <belkid98@gmail.com>
Signed-off-by: Tian Yuchen <cat@malon.dev>
---
dir.c | 4 ++--
environment.c | 7 +++++++
environment.h | 5 +++++
3 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/dir.c b/dir.c
index 7a73690fbc..4f87a52b3c 100644
--- a/dir.c
+++ b/dir.c
@@ -3481,11 +3481,11 @@ static GIT_PATH_FUNC(git_path_info_exclude, "info/exclude")
void setup_standard_excludes(struct dir_struct *dir)
{
+ const char *excludes_file = repo_excludes_file(the_repository);
+
dir->exclude_per_dir = ".gitignore";
/* core.excludesfile defaulting to $XDG_CONFIG_HOME/git/ignore */
- if (!excludes_file)
- excludes_file = xdg_config_home("ignore");
if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
add_patterns_from_file_1(dir, excludes_file,
dir->untracked ? &dir->internal.ss_excludes_file : NULL);
diff --git a/environment.c b/environment.c
index ba2c60103f..8efcaeafa6 100644
--- a/environment.c
+++ b/environment.c
@@ -134,6 +134,13 @@ int is_bare_repository(void)
return is_bare_repository_cfg && !repo_get_work_tree(the_repository);
}
+const char *repo_excludes_file(struct repository *repo)
+{
+ if (!excludes_file)
+ excludes_file = xdg_config_home("ignore");
+ return excludes_file;
+}
+
int have_git_dir(void)
{
return startup_info->have_repository
diff --git a/environment.h b/environment.h
index 6f18286955..52d531e4ea 100644
--- a/environment.h
+++ b/environment.h
@@ -133,6 +133,11 @@ int git_default_config(const char *, const char *,
int git_default_core_config(const char *var, const char *value,
const struct config_context *ctx, void *cb);
+/*
+ * TODO: This still relies on the global state.
+ */
+const char *repo_excludes_file(struct repository *repo);
+
void repo_config_values_init(struct repo_config_values *cfg);
/*
--
2.43.0
^ permalink raw reply related
* [PATCH v2 0/2] environment: move excludes_file into repo_config_values
From: Tian Yuchen @ 2026-06-26 7:50 UTC (permalink / raw)
To: git
Cc: cirnovskyv, Tian Yuchen, Christian Couder, Ayush Chandekar,
Olamide Caleb Bello
This series continues the libification effort by migrating the global
string variable 'excludes_file' into 'struct repo_config_values'. Since
this is a dynamically allocated variable, the migration requires proper
heap memory management.
The series is structured in two commits:
- Abstract the XDG fallback lazy-loading logic out of dir.c into a proper
getter.
- Move the variables into the struct and introduce 'repo_config_values_clear()'.
Note on Submodules: A temporary shield 'if (repo != the_repository)' is
included in both the getter and the clear function. This prevents
uninitialized submodules from triggering the BUG() assertion.
(Inspiration: [1])
Changes since V1:
- fix a typo in the second commit.
- initialize excludes_file to NULL in repo_config_values_init().
- call FREE_AND_NULL() to free attributes_file as well in
repo_config_values_clear().
Thanks!
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>
Mentored-by: Olamide Caleb Bello <belkid98@gmail.com>
Signed-off-by: Tian Yuchen <cat@malon.dev>
[1] https://lore.kernel.org/git/c95a7730-7b14-4be0-a4e4-861b2f5430ea@gmail.com/
Tian Yuchen (2):
dir: encapsulate excludes_file lazy-load
environment: move excludes_file into repo_config_values
dir.c | 4 ++--
environment.c | 32 +++++++++++++++++++++++++++++---
environment.h | 13 ++++++++++++-
repository.c | 1 +
4 files changed, 44 insertions(+), 6 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH 05/11] reftable/block: fix OOB write with bogus inflated log size
From: Christian Couder @ 2026-06-26 7:48 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, oxsignal
In-Reply-To: <20260624-pks-reftable-hardening-v1-5-66e4ce87c6b9@pks.im>
On Wed, Jun 24, 2026 at 10:24 AM Patrick Steinhardt <ps@pks.im> wrote:
> diff --git a/t/unit-tests/u-reftable-block.c b/t/unit-tests/u-reftable-block.c
> index f4bded7d26..40274af5c0 100644
> --- a/t/unit-tests/u-reftable-block.c
> +++ b/t/unit-tests/u-reftable-block.c
> @@ -456,3 +456,47 @@ void test_reftable_block__iterator(void)
> block_writer_release(&writer);
> reftable_buf_release(&data);
> }
> +
> +void test_reftable_block__corrupt_log_block_size(void)
> +{
> + struct reftable_block_source source = { 0 };
> + struct block_writer writer = {
> + .last_key = REFTABLE_BUF_INIT,
> + };
> + struct reftable_record rec = {
> + .type = REFTABLE_BLOCK_TYPE_LOG,
> + .u.log = {
> + .refname = (char *) "refs/heads/main",
> + .update_index = 1,
> + .value_type = REFTABLE_LOG_UPDATE,
> + },
> + };
> + struct reftable_block block = { 0 };
> + struct reftable_buf data;
> +
> + data.len = 1024;
> + REFTABLE_CALLOC_ARRAY(data.buf, data.len);
> + cl_assert(data.buf != NULL);
> +
> + cl_must_pass(block_writer_init(&writer, REFTABLE_BLOCK_TYPE_LOG,
> + (uint8_t *) data.buf, data.len,
> + 0, hash_size(REFTABLE_HASH_SHA1)));
> + cl_must_pass(block_writer_add(&writer, &rec));
> + cl_assert(block_writer_finish(&writer) > 0);
It looks like some of the block writing code above could be simplified
using an helper function like:
int cl_reftable_write_block(struct reftable_buf *buf, uint8_t block_type,
size_t block_size, uint32_t header_off,
struct reftable_record *recs, size_t nrecs)
{
struct block_writer writer = {
.last_key = REFTABLE_BUF_INIT,
};
int block_end;
REFTABLE_CALLOC_ARRAY(buf->buf, block_size);
cl_assert(buf->buf != NULL);
buf->len = block_size;
cl_must_pass(block_writer_init(&writer, block_type, (uint8_t *) buf->buf,
block_size, header_off,
hash_size(REFTABLE_HASH_SHA1)));
for (size_t i = 0; i < nrecs; i++)
cl_must_pass(block_writer_add(&writer, &recs[i]));
block_end = block_writer_finish(&writer);
cl_assert(block_end > 0);
block_writer_release(&writer);
return block_end;
}
This function could be introduced by a preparatory commit in
t/unit-tests/lib-reftable.{c,h}. It would be kind of similar to the
existing cl_reftable_write_to_buf() helper in those files.
It looks like it could already simplify existing tests like:
- test_reftable_block__log_read_write
- test_reftable_block__obj_read_write
- test_reftable_block__ref_read_write
- test_reftable_block__iterator
and it could simplify the new tests introduced by other patches in this series:
- 06/11 reftable/block: fix OOB read with bogus block size
- 07/11 reftable/block: fix OOB read with bogus restart count
- 09/11 reftable/block: fix OOB read with bogus restart offset
> + /*
> + * Log blocks store their inflated size as a big-endian 24-bit integer
> + * right after the one-byte block type. Rewrite it to claim a size that
> + * is smaller than the block header.
> + */
> + reftable_put_be24((uint8_t *) data.buf + 1, 1);
> +
> + block_source_from_buf(&source, &data);
> + cl_assert_equal_i(reftable_block_init(&block, &source, 0, 0, data.len,
> + REFTABLE_HASH_SIZE_SHA1, REFTABLE_BLOCK_TYPE_LOG),
> + REFTABLE_FORMAT_ERROR);
> +
> + reftable_block_release(&block);
> + block_writer_release(&writer);
> + reftable_buf_release(&data);
> +}
Otherwise the series looks great to me.
Thanks.
^ permalink raw reply
* Re: [PATCH v3 4/4] connected: search promisor objects generically
From: Christian Couder @ 2026-06-26 6:55 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Patrick Steinhardt, git, Christian Couder
In-Reply-To: <xmqq4iiqfk0l.fsf@gitster.g>
On Thu, Jun 25, 2026 at 10:22 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Patrick Steinhardt <ps@pks.im> writes:
>
> > When performing connectivity checks we have to figure out whether any of
> > the new objects are promisor objects, as we cannot assume full
> > connectivity if so.
> >
> > This check is performed by iterating through all packfiles in the
> > repository and searching each of them for the given object. Of course,
> > this mechanism is quite specific to implementation details of the object
> > database, as we assume that it uses packfiles in the first place.
> >
> > Refactor the logic so that we instead use `odb_for_each_object_ext()`
> > with an object prefix filter and the `ODB_FOR_EACH_OBJECT_PROMISOR_ONLY`
> > flag. This will yield all objects that have the exact object name and
> > that are part of a promisor pack in a generic way.
> >
> > Add a test to verify that we indeed use the optimization.
>
> OK. The new test is a good way to catch the issue we noticed in the
> previous round, I guess. Looking good.
Yeah, it looks ready to me too.
Thanks.
^ permalink raw reply
* [PATCH v5 3/3] replay: offer an option to linearize the commit topology
From: Toon Claes @ 2026-06-26 5:48 UTC (permalink / raw)
To: git; +Cc: Elijah Newren, Johannes Schindelin, Toon Claes,
Johannes Schindelin
In-Reply-To: <20260626-toon-git-replay-drop-merges-v5-0-5e120738b9d0@iotcl.com>
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
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 commit history into a sequence of the
regular (single-parent) commits.
Add option `--linearize` to git-replay(1) to do the same.
Co-authored-by: Toon Claes <toon@iotcl.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Toon Claes <toon@iotcl.com>
---
Documentation/git-replay.adoc | 8 ++++-
builtin/replay.c | 6 +++-
replay.c | 50 ++++++++++++++++----------
replay.h | 5 +++
t/t3650-replay-basics.sh | 84 ++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 132 insertions(+), 21 deletions(-)
diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
index a32f72aead..ef56ee0f1b 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,12 @@ 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, `git replay` imitates `git rebase --no-rebase-merges`,
+ i.e. it cherry-picks only non-merge commits, each one on top of the
+ previous one.
+ This option is incompatible with `--revert`.
+
<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..62962c73c7 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()
};
@@ -132,6 +134,8 @@ 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);
diff --git a/replay.c b/replay.c
index 86fba47fb9..d803e0312f 100644
--- a/replay.c
+++ b/replay.c
@@ -439,24 +439,38 @@ int replay_revisions(struct rev_info *revs,
while ((commit = get_revision(revs))) {
const struct name_decoration *decoration;
- /*
- * pick_regular_commit() looks up the parent of `commit` in
- * `replayed_commits` to determine the ancestor to replay onto.
- * The `default_base` parameter is used when no ancestor is found,
- * which happens for the first commit in the revision range.
- * When reverting, commits are replayed in reverse order, so the
- * lookup never succeeds, and we need to pass `last_commit`.
- */
- struct commit *base = 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,
- replayed_commits,
- &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:
+ * - the merge commit is mapped to `last_commit` in
+ * `replayed_commits`, this will become the parent for
+ * the child commits.
+ * - refs previously pointing to the merge commit are
+ * rewritten to point to the previous non-merge commit.
+ */
+ } else {
+ /*
+ * pick_regular_commit() looks up the parent of `commit` in
+ * `replayed_commits` to determine the ancestor to replay onto.
+ * The `default_base` parameter is used when no ancestor is found,
+ * which happens for the first commit in the revision range.
+ * When reverting, commits are replayed in reverse order, so the
+ * lookup never succeeds, and we need to pass `last_commit`.
+ */
+ struct commit *base = onto;
+ if (mode == REPLAY_MODE_REVERT)
+ base = last_commit;
+
+ last_commit = pick_regular_commit(revs->repo, commit, base,
+ replayed_commits,
+ &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..34c038eab9 100755
--- a/t/t3650-replay-basics.sh
+++ b/t/t3650-replay-basics.sh
@@ -52,8 +52,12 @@ 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
'
test_expect_success 'setup bare' '
@@ -97,6 +101,12 @@ 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 &&
@@ -565,4 +575,76 @@ 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 to rebase multiple divergent branches' '
+ git replay --ref-action=print --linearize \
+ --onto main ^B topic2 topic-with-merge >result &&
+
+ test_line_count = 2 result &&
+ cut -f 3 -d " " result >new-branch-tips &&
+
+ git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+ test_write_lines E D C M L B A >expect &&
+ test_cmp expect actual &&
+
+ git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+ test_write_lines O N J I M L B A >expect &&
+ test_cmp expect actual
+'
+
test_done
--
2.53.0.1323.g189a785ab5
^ permalink raw reply related
* [PATCH v5 2/3] replay: better explain how pick_regular_commit() picks a base
From: Toon Claes @ 2026-06-26 5:48 UTC (permalink / raw)
To: git; +Cc: Elijah Newren, Toon Claes
In-Reply-To: <20260626-toon-git-replay-drop-merges-v5-0-5e120738b9d0@iotcl.com>
The function pick_regular_commit() will replay the `pickme` commit. To
determine the ancestor where to replay this commit on, it takes the
parent of the commit and looks up its replayed result in
`replayed_commits`. If no ancestor is found, the `onto` parameter is
used as fallback.
The name `onto` is rather confusing, so rename it to `default_base`. And
while at it, shuffle the function parameters so `struct commit`
parameters are immediate siblings.
When in mode REPLAY_MODE_REVERT, the fallback `default_base` will always
be used. This happens because commits are replayed in reverse order, so
looking up the `pickme`'s parent in `replayed_commits` will always
return empty. And to make these commits stack on top of each other, we
need to pass in `last_commit`.
Signed-off-by: Toon Claes <toon@iotcl.com>
---
replay.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/replay.c b/replay.c
index 7bde1c7e93..86fba47fb9 100644
--- a/replay.c
+++ b/replay.c
@@ -280,8 +280,8 @@ static void put_mapped_commit(kh_oid_map_t *replayed_commits,
static struct commit *pick_regular_commit(struct repository *repo,
struct commit *pickme,
+ struct commit *default_base,
kh_oid_map_t *replayed_commits,
- struct commit *onto,
struct merge_options *merge_opt,
struct merge_result *result,
enum replay_mode mode,
@@ -298,7 +298,7 @@ static struct commit *pick_regular_commit(struct repository *repo,
base_tree = lookup_tree(repo, repo->hash_algo->empty_tree);
}
- replayed_base = get_mapped_commit(replayed_commits, base, onto);
+ replayed_base = get_mapped_commit(replayed_commits, base, default_base);
replayed_base_tree = repo_get_commit_tree(repo, replayed_base);
pickme_tree = repo_get_commit_tree(repo, pickme);
@@ -439,11 +439,23 @@ int replay_revisions(struct rev_info *revs,
while ((commit = get_revision(revs))) {
const struct name_decoration *decoration;
+ /*
+ * pick_regular_commit() looks up the parent of `commit` in
+ * `replayed_commits` to determine the ancestor to replay onto.
+ * The `default_base` parameter is used when no ancestor is found,
+ * which happens for the first commit in the revision range.
+ * When reverting, commits are replayed in reverse order, so the
+ * lookup never succeeds, and we need to pass `last_commit`.
+ */
+ struct commit *base = 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,
+ last_commit = pick_regular_commit(revs->repo, commit, base,
+ replayed_commits,
&merge_opt, &result, mode, opts->empty);
if (!last_commit)
break;
--
2.53.0.1323.g189a785ab5
^ permalink raw reply related
* [PATCH v5 1/3] replay: add helper to put entry into mapped_commits
From: Toon Claes @ 2026-06-26 5:48 UTC (permalink / raw)
To: git; +Cc: Elijah Newren, Toon Claes
In-Reply-To: <20260626-toon-git-replay-drop-merges-v5-0-5e120738b9d0@iotcl.com>
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().
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..7bde1c7e93 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\n",
+ 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 v5 0/3] Teach git-replay(1) to linearize merge commits
From: Toon Claes @ 2026-06-26 5:48 UTC (permalink / raw)
To: git; +Cc: Elijah Newren, Toon Claes, Johannes Schindelin,
Johannes Schindelin
In-Reply-To: <20260622-toon-git-replay-drop-merges-v4-0-ff257f534319@iotcl.com>
As an alternative to dscho's patch series to replay merges[1], add
option to git-replay(1) to linearize merges. This mimics what
git-rebase(1) does too 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]: <V2_CV_doc_replay_config.767@msgid.xyz>
[3]: <20260603-b4-pks-history-drop-v2-0-742cb5b5176d@pks.im>
---
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
---
Johannes Schindelin (1):
replay: offer an option to linearize the commit topology
Toon Claes (2):
replay: add helper to put entry into mapped_commits
replay: better explain how pick_regular_commit() picks a base
Documentation/git-replay.adoc | 8 ++++-
builtin/replay.c | 6 +++-
replay.c | 69 ++++++++++++++++++++++++++---------
replay.h | 5 +++
t/t3650-replay-basics.sh | 84 ++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 152 insertions(+), 20 deletions(-)
Range-diff versus v4:
1: a08bc22330 < -: ---------- replay: refactor enum replay_mode into a bool
2: 3117fddcc5 = 1: bbd5a710bd replay: add helper to put entry into mapped_commits
-: ---------- > 2: e08c7b46c0 replay: better explain how pick_regular_commit() picks a base
3: acbb1df6a9 ! 3: 043cf63c1c replay: offer an option to linearize the commit topology
@@ builtin/replay.c: int cmd_replay(int argc,
ref_mode = get_ref_action_mode(repo, ref_action);
## replay.c ##
-@@ replay.c: static struct commit *pick_regular_commit(struct repository *repo,
- struct commit *onto,
- struct merge_options *merge_opt,
- struct merge_result *result,
-+ struct commit *replayed_base,
- bool reverse,
- enum replay_empty_commit_action empty)
- {
-- struct commit *base, *replayed_base;
-+ struct commit *base;
- struct tree *pickme_tree, *base_tree, *replayed_base_tree;
-
-+ if (replayed_base && reverse)
-+ BUG("Linearizing commits is not supported when replaying in reverse");
-+
- if (pickme->parents) {
- base = pickme->parents->item;
- base_tree = repo_get_commit_tree(repo, base);
-@@ replay.c: static struct commit *pick_regular_commit(struct repository *repo,
- base_tree = lookup_tree(repo, repo->hash_algo->empty_tree);
- }
-
-- replayed_base = get_mapped_commit(replayed_commits, base, onto);
-+ if (!replayed_base)
-+ 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);
-
@@ replay.c: int replay_revisions(struct rev_info *revs,
while ((commit = get_revision(revs))) {
const struct name_decoration *decoration;
+- /*
+- * pick_regular_commit() looks up the parent of `commit` in
+- * `replayed_commits` to determine the ancestor to replay onto.
+- * The `default_base` parameter is used when no ancestor is found,
+- * which happens for the first commit in the revision range.
+- * When reverting, commits are replayed in reverse order, so the
+- * lookup never succeeds, and we need to pass `last_commit`.
+- */
+- struct commit *base = 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,
+- replayed_commits,
+- &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 and leave
-+ * last_commit unchanged, so its children (and any ref
-+ * pointing at it) are reparented onto the previous
-+ * non-merge commit, which the ref-update loop below uses.
++ * Drop the merge commit: do not pick it, leave
++ * `last_commit` unchanged, and fall through to the
++ * rest of the loop. As a result:
++ * - the merge commit is mapped to `last_commit` in
++ * `replayed_commits`, this will become the parent for
++ * the child commits.
++ * - refs previously pointing to the merge commit are
++ * rewritten to point to the previous non-merge commit.
+ */
+ } else {
-+ struct commit *to_pick = reverse ? last_commit : onto;
-+ last_commit =
-+ pick_regular_commit(revs->repo, commit,
-+ replayed_commits, to_pick,
-+ &merge_opt, &result,
-+ opts->linearize ? last_commit : NULL,
-+ reverse, opts->empty);
++ /*
++ * pick_regular_commit() looks up the parent of `commit` in
++ * `replayed_commits` to determine the ancestor to replay onto.
++ * The `default_base` parameter is used when no ancestor is found,
++ * which happens for the first commit in the revision range.
++ * When reverting, commits are replayed in reverse order, so the
++ * lookup never succeeds, and we need to pass `last_commit`.
++ */
++ struct commit *base = onto;
++ if (mode == REPLAY_MODE_REVERT)
++ base = last_commit;
++
++ last_commit = pick_regular_commit(revs->repo, commit, base,
++ replayed_commits,
++ &merge_opt, &result,
++ mode, opts->empty);
+ }
-
-- last_commit = pick_regular_commit(revs->repo, commit, replayed_commits,
-- reverse ? last_commit : onto,
-- &merge_opt, &result, reverse, opts->empty);
++
if (!last_commit)
break;
@@ t/t3650-replay-basics.sh: test_expect_success '--onto with --ref rejects multipl
+ git log --oneline main..$tip >out &&
+ test_line_count = 3 out
+'
++
++test_expect_success 'replay with --linearize to rebase multiple divergent branches' '
++ git replay --ref-action=print --linearize \
++ --onto main ^B topic2 topic-with-merge >result &&
++
++ test_line_count = 2 result &&
++ cut -f 3 -d " " result >new-branch-tips &&
++
++ git log --format=%s $(head -n 1 new-branch-tips) >actual &&
++ test_write_lines E D C M L B A >expect &&
++ test_cmp expect actual &&
++
++ git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
++ test_write_lines O N J I M L B A >expect &&
++ test_cmp expect actual
++'
+
test_done
---
base-commit: ab776a62a78576513ee121424adb19597fbb7613
change-id: 20260604-toon-git-replay-drop-merges-807fa008d395
^ permalink raw reply
* Re: [PATCH v4 3/3] replay: offer an option to linearize the commit topology
From: Toon Claes @ 2026-06-26 5:36 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Elijah Newren, Johannes Schindelin
In-Reply-To: <ajk-a4a3KSJ2u7Ju@pks.im>
Patrick Steinhardt <ps@pks.im> writes:
> git-rebase(1) essentially knows about three different modes:
>
> - "--no-rebase-merges", which is the default and maps to your
> "--linearize".
>
> - "--rebase-merges", which by default doesn't rebase cousins by using
> "--ancestry-path" internally.
>
> - "--rebase-merges=rebase-cousins", which doesn't pass the above
> option.
>
> So it's not a simple boolean there, which makes me wonder whether we
> should mirror the same interface so that all of git-rebase(1)'s modes
> can be represented, as well.
That's a valid question, although I don't know a good answer to that.
Basically you're asking for what the command line options will look
like? Allow me to think out loud.
In this series I'm adding --linearize to git-replay(1). As mentioned, I
don't think it makes sense to add it to git-history(1) as well. Without
this option, the process aborts when it encounters a merge.
Dscho sent a patch series to properly replay (2-way) merges. I think
this should become the default for both git-replay(1) and
git-history(1).
But then, do we want to have an option that brings back the current
behavior of aborting at merges? Maybe with --no-merges?
Then there's the option of rebasing cousins left. That's something that
isn't covered by Dscho's series yet. Maybe --replay-cousins?
To reiterate what the final design could look like:
* <nothing>: replay merges preserving topology.
* "--linearize": flattens merges (only git-replay(1)).
* "--no-merges": dies when the process tries to replay a merge.
* "--replay-cousins": does what --rebase-merges=rebase-cousins does.
Now, all these options are (I think) mutually exclusive, so we could
consider an option "--replay-merges=<mode>", but personally I find
"--<option>=<value>" arguments harder to use than specifying separate
options.
I think I'm avoiding your question, because the design of the command
line parameters doesn't need tot 1-on-1 correlate to the internal
datastructure. And I agree the mode isn't a boolean, but does that mean
we want to use an enum internally? Well, I don't know. And I also don't
think that matters right now. Code is easy to change, I think the
command line options should be designed with the future in mind, which I
believe we do with "--linearize".
Sorry for this long-winded rambling, but bottom line I think it's fine
to add --linearize and in the future add more options and see how the
code should evolve to support those.
>> diff --git a/replay.c b/replay.c
>> index 7921d7dba3..5539daff00 100644
>> --- a/replay.c
>> +++ b/replay.c
>> @@ -277,12 +277,16 @@ static struct commit *pick_regular_commit(struct repository *repo,
>> struct commit *onto,
>> struct merge_options *merge_opt,
>> struct merge_result *result,
>> + struct commit *replayed_base,
>> bool reverse,
>> enum replay_empty_commit_action empty)
>> {
>> - struct commit *base, *replayed_base;
>> + struct commit *base;
>> struct tree *pickme_tree, *base_tree, *replayed_base_tree;
>>
>> + if (replayed_base && reverse)
>> + BUG("Linearizing commits is not supported when replaying in reverse");
>
> Nit: Error messages should typically start with a lower-case letter.
Thanks.
>> @@ -430,12 +435,25 @@ int replay_revisions(struct rev_info *revs,
>> while ((commit = get_revision(revs))) {
>> const struct name_decoration *decoration;
>>
>> - if (commit->parents && commit->parents->next)
>> - die(_("replaying merge commits is not supported yet!"));
>> + 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 and leave
>> + * last_commit unchanged, so its children (and any ref
>> + * pointing at it) are reparented onto the previous
>> + * non-merge commit, which the ref-update loop below uses.
>> + */
>
> One could add a hint here that tells the user to pass the option. But I
> guess that might be somewhat weird, as we cannot assume that we're
> called by git-replay(1) here.
Yeah, true...
--
Cheers,
Toon
^ permalink raw reply
* Re: [RFH] Why do osx CI jobs so unreliable?
From: Jeff King @ 2026-06-26 5:16 UTC (permalink / raw)
To: Michael Montalbo; +Cc: Patrick Steinhardt, git, Junio C Hamano
In-Reply-To: <CAC2QwmJA2TH6BmO0O61qRYvV2pqURUk0dTXpkJtb9e-TZNZDZQ@mail.gmail.com>
On Thu, Jun 25, 2026 at 08:27:35PM -0700, Michael Montalbo wrote:
> I think that is the trigger for issues we've been seeing. I spent
> some time investigating the Apache side over the last week and maybe
> found a mod_http2 bug, which I filed upstream with a potential fix:
>
> bug: https://bz.apache.org/bugzilla/show_bug.cgi?id=70131
> fix: https://github.com/mmontalbo/httpd/pull/2
Thanks both of you for digging into this. I'm not familiar enough with
Apache's code to pass confident judgement, but your findings certainly
convinced me that this is just an apache bug.
> Given there could be a potential reliability issue with an upstream
> dependency like Apache, I was considering what mitigation strategies
> might help:
> [...]
Depending on how widespread the Apache bug is, another option might just
be: do nothing and wait for it to get fixed.
Trying to make the wedged state fail fast and loudly is mostly just
punting on the problem. We'd still see spurious failures. We've so far
resisted the urge to do any automatic flaky-test retries, preferring
instead to just try to root out the flakes. I'm a little hesitant to
start now, because I think our strategy has mostly been good so far, and
I've seen some horrible counter-examples where flakes and retries become
a routine drag on development (and I'm afraid that accommodating flakes
might make them more common).
> - Make slow tests faster by optimizing the test itself and/or
> the test runner configuration (e.g., job number matching
> cores) so wedges become less likely.
It sounds like the bad state is triggered when Apache hits a timeout,
and we hit that timeout because the system is slow or busy. We could try
to make things less slow, but would it work equally well to increase
that timeout?
-Peff
^ permalink raw reply
* Re: [PATCH v6 00/11] refs: fix "onbranch" conditions
From: Jeff King @ 2026-06-26 5:06 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Karthik Nayak, Justin Tobler
In-Reply-To: <20260625-b4-pks-refs-avoid-chdir-notify-reparent-v6-0-41fbca3cf5e3@pks.im>
On Thu, Jun 25, 2026 at 11:19:58AM +0200, Patrick Steinhardt wrote:
> - Fix the "onbranch" recursion properly: instead of papering over the
> issue, this series now refactors reference store initialization to
> not read any configuration at all anymore. Instead, the config is
> now parsed lazily. This fixes the recursion, but also makes us
> respect configuration guarded by "onbranch" conditions properly.
Sorry, I was offline and missed reviewing some of the intermediate
stages.
The approach you take in v6 looks good to me, and I'm glad the result
does not look too painful to maintain. If we ever add a config option
that is absolutely required for initializing the reading side of the
backends, we'll be back to our chicken-and-egg problem. But I won't be
surprised if that never happens, and if it does, we can decide on the
user-visible interaction with onbranch includes then (and whatever the
result is, document it).
Thanks for taking the time to re-work all of this.
-Peff
^ permalink raw reply
* Re: [RFH] Why do osx CI jobs so unreliable?
From: Michael Montalbo @ 2026-06-26 3:27 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Jeff King, git, Junio C Hamano
In-Reply-To: <ajkOoRhqaAcy6gBg@pks.im>
Patrick Steinhardt <ps@pks.im> writes:
> I think the issue is rather simple: we're hitting timeouts in Apache.
> [...]
> This is because our keepalive mechanisms aren't helping [...]
> Whether that's the same issue like we see in macOS sometimes is a
> different question.
I think that is the trigger for issues we've been seeing. I spent
some time investigating the Apache side over the last week and maybe
found a mod_http2 bug, which I filed upstream with a potential fix:
bug: https://bz.apache.org/bugzilla/show_bug.cgi?id=70131
fix: https://github.com/mmontalbo/httpd/pull/2
To Patrick's earlier question of whether this is a Git, curl, or Apache
bug: as best I can tell it's Apache. I could reproduce it with no Git
involved at all (just Apache and a small CGI that goes quiet past the
Timeout), and across several curl versions (8.6.0, which is what the
GitHub runners use, up to 8.20.0), so I don't think bumping curl would
help. It also seems to wear two faces from the same trigger: over
HTTP/1.1 Apache closes the connection and curl bails with the
"transfer closed" error (which looks like what you hit with Timeout=1,
and the recent failures on both macOS and Linux), and over HTTP/2 it
does not reliably reset the stream, so the client just waits, which is
the six-hour macOS hang. I share the pessimism from earlier in the
thread, though: I think the real fix is upstream in Apache, and
anything we do on our side mostly just bounds the symptom in the
meantime.
Given there could be a potential reliability issue with an upstream
dependency like Apache, I was considering what mitigation strategies
might help:
- Enforce some kind of lower bound speed limit and a client-side
timeout so runs that wedge fail fast (and loudly) instead of
hanging.
- Potentially provide some affordance for retrying flaky tests
that might fail due to upstream dependencies. Git already has
some HTTP retry support (http.maxRetries and friends, added
recently), but as far as I can tell it only triggers on HTTP 429
rate limiting, so it would not catch a stall like this on its
own. A test-level retry is not something I like that much, since
it might encourage papering over flakiness that should be
resolved, but it was a consideration vs requiring a fresh CI run
to resolve the flake.
- Make slow tests faster by optimizing the test itself and/or
the test runner configuration (e.g., job number matching
cores) so wedges become less likely.
For the first one, I think Git already provides some affordances. There
is a stall-based timeout that just ships disabled: as I understand it
http.lowSpeedLimit sets a bytes/sec floor and http.lowSpeedTime how long
a transfer can sit below it before curl gives up, so it would catch a
wedged connection without punishing one that is just slow. Enabling it
for the http tests might look something like:
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
@@ GIT_TRACE=$GIT_TRACE; export GIT_TRACE
+# Abort a transfer that makes essentially no progress for a while,
+# so a wedged connection fails in seconds instead of hanging to the
+# job cap. Tiny limit, generous window, so it only trips on a true
+# stall; override either var, or set the limit to 0, to disable.
+GIT_HTTP_LOW_SPEED_LIMIT=${GIT_HTTP_LOW_SPEED_LIMIT-1}
+GIT_HTTP_LOW_SPEED_TIME=${GIT_HTTP_LOW_SPEED_TIME-60}
+export GIT_HTTP_LOW_SPEED_LIMIT GIT_HTTP_LOW_SPEED_TIME
I went conservative on the values on purpose: a floor of 1 byte/sec
should only really fire on a true zero-progress stall, not on something
that is just crawling on a slow runner, and the 60s window is generous
for the same reason. When I tried it locally against a stall-proxy it
did turn an otherwise indefinite hang into a bounded abort (a tighter
limit/window brings that down to single-digit seconds). It probably does
not need to be suite-wide either; it could be scoped per-command with
git -c, which the http tests already lean on for this kind of thing
(t5551 passes http.postbuffer and http.extraheader that way), if a
narrower blast radius feels safer.
I only dug into the first option in any depth, since I wanted to
sanity-check the direction before writing patches. Does turning on a
stall timeout for the http tests seem reasonable? Are there other
strategies that we should implement?
^ permalink raw reply
* What's cooking in git.git (Jun 2026, #10)
From: Junio C Hamano @ 2026-06-26 0:30 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 and 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 they can be resubmitted when new interests
arise).
Git 2.55-rc2 has been tagged. The tree is in deep feature-freeze,
and remaining topics in 'next' will stay in "Will cook in 'next'"
instead of "Will merge to 'master'" state, until Git 2.55 final.
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/
--------------------------------------------------
[New Topics]
* ad/gpg-strip-cr-before-lf (2026-06-24) 1 commit
- 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.
Needs review.
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.
Waiting for response(s) to review comment(s).
cf. <aju_AmlKVi5UZaiQ@pks.im>
source: <20260624041920.2601961-1-jltobler@gmail.com>
* ps/odb-drop-whence (2026-06-24) 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
(this branch uses ps/odb-source-packed.)
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.
Needs review.
source: <20260624-b4-pks-odb-drop-whence-v1-0-8d1877b790ac@pks.im>
* ps/reftable-hardening (2026-06-24) 11 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
- 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: <20260624-pks-reftable-hardening-v1-0-66e4ce87c6b9@pks.im>
* hn/branch-push-slip-advice (2026-06-24) 3 commits
- SQUASH??? use test_grep
- push: suggest <remote> <branch> for a slash slip
- branch: suggest <remote>/<branch> on upstream slip
"git push origin/main" and "git branch origin main" could both be
an obvious typo, in which case offer the obvious typofix.
Waiting for response(s) to review comment(s).
cf. <xmqqfr2ae2wp.fsf@gitster.g>
source: <pull.2331.v2.git.git.1782338114.gitgitgadget@gmail.com>
* jc/history-message-prep-fix (2026-06-25) 1 commit
- history: streamline message preparation and plug file stream leak
Code clean-up with leakfix for a write file stream.
Needs review.
source: <xmqqh5mqfkpv.fsf@gitster.g>
* ty/migrate-excludes-file (2026-06-25) 2 commits
- environment: move excludes_file into repo_config_values
- dir: encapsulate excludes_file lazy-load
Move excludes_file global variable into per-repository structure.
Waiting for response(s) to review comment(s).
cf. <xmqqwlvme4lz.fsf@gitster.g>
source: <20260625161845.7543-1-cat@malon.dev>
--------------------------------------------------
[Stalled]
* jt/config-lock-timeout (2026-05-17) 1 commit
- config: retry acquiring config.lock, configurable via core.configLockTimeout
Configuration file locking now retries 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>
* js/parseopt-subcommand-autocorrection (2026-04-27) 11 commits
- SQUASH???
- doc: document autocorrect API
- parseopt: add tests for subcommand autocorrection
- parseopt: enable subcommand autocorrection for git-remote and git-notes
- parseopt: autocorrect mistyped subcommands
- autocorrect: provide config resolution API
- autocorrect: rename AUTOCORRECT_SHOW to AUTOCORRECT_HINT
- autocorrect: use mode and delay instead of magic numbers
- help: move tty check for autocorrection to autocorrect.c
- help: make autocorrect handling reusable
- parseopt: extract subcommand handling from parse_options_step()
The parse-options library learned to auto-correct misspelled
subcommand names.
Waiting for response(s) to review comment(s) for too long, stalled.
cf. <xmqq33yzd9yf.fsf@gitster.g>
cf. <SY0P300MB0801E50FCB7EB2F45CD15208CE042@SY0P300MB0801.AUSP300.PROD.OUTLOOK.COM>
source: <SY0P300MB0801677A2A1E0FD38D06A841CE2A2@SY0P300MB0801.AUSP300.PROD.OUTLOOK.COM>
* cl/conditional-config-on-worktree-path (2026-05-24) 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 learned to use the location of worktree
in its condition.
Waiting for response(s) to review comment(s) for too long, stalled.
cf. <xmqq8q97et9b.fsf@gitster.g>
source: <20260525-includeif-worktree-v5-0-1efe525d025a@black-desk.cn>
--------------------------------------------------
[Cooking]
* kk/merge-base-exhaustion (2026-06-24) 7 commits
- commit-reach: terminate merge-base walk when one paint side is exhausted
- commit-reach: remove unused nonstale_queue dedup wrappers
- commit-reach: introduce struct paint_state with per-side counters
- 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
- 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. <CAL71e4MnA36ZchLaUsMSoLcb9LO77aac274jES8+oV=yxuigOA@mail.gmail.com>
source: <pull.2149.v2.git.1782303254.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.
Waiting for response(s) to review comment(s).
cf. <ajjuoS5Qc3K0nCRl@pks.im>
source: <c4c5ade901ff95b0f95939ea818870e4f3d59da1.1781971201.git.ben.knoble+github@gmail.com>
* ps/connected-generic-promisor-checks (2026-06-25) 5 commits
- 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.
Will merge to 'next'?
cf. <xmqq4iiqfk0l.fsf@gitster.g>
source: <20260625-pks-connected-generic-promisor-checks-v3-0-7308f3b9dc44@pks.im>
* ps/libgit-in-subdir (2026-06-22) 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
(this branch uses ps/odb-source-packed.)
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.
Needs review.
source: <20260622-pks-libgit-in-subdir-v2-0-cb946c51ee7b@pks.im>
* ps/odb-generalize-prepare (2026-06-22) 3 commits
- odb: introduce `odb_prepare()`
- odb/source: generalize `reprepare()` callback
- Merge branch 'ps/odb-source-packed' into ps/odb-generalize-prepare
(this branch uses ps/odb-source-packed.)
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.
Needs review.
source: <20260622-b4-pks-odb-generalize-prepare-v1-0-d2a5c5d13144@pks.im>
* 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.
Will cook in 'next'.
cf. <ajjwYGWZ6hQWr600@pks.im>
source: <xmqqeci0g4mz.fsf@gitster.g>
* wy/doc-clarify-review-replies (2026-06-21) 2 commits
- 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.
Will merge to 'next'?
cf. <ajvDuUiDsmyf5LnX@pks.im>
source: <cover.1782028813.git.wy@wyuan.org>
* 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-18) 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
"git log -L<range>:<path>" learned to limit various "diff" operations
like --stat, --check, -G, to the specified range:path.
Expecting a reroll.
cf. <CAC2QwmKEHb+LL4ZkQwq+Rw8eyDXzdBp_nxa_d+Ecx0K1icNqQA@mail.gmail.com>
source: <pull.2152.git.1781806593.gitgitgadget@gmail.com>
* hn/history-squash (2026-06-24) 4 commits
- history: re-edit a squash with every message
- 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.
Needs review.
source: <pull.2337.v5.git.git.1782338102.gitgitgadget@gmail.com>
* ps/t4216-tap-fix (2026-06-19) 1 commit
(merged to 'next' on 2026-06-25 at 42296fc12e)
+ t4216: fix no-op test that breaks TAP output
TAP output breakage fix.
Will merge to 'master'.
cf. <xmqq8q82fk8r.fsf@gitster.g>
source: <20260619-pks-t4216-drop-unused-prereq-v1-1-2ce0d7bea088@pks.im>
* 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.
Will cook in 'next'.
cf. <xmqqcxxp1j2t.fsf@gitster.g>
source: <20260619094751.2996804-1-m@lfurio.us>
* ps/refs-writing-subcommands (2026-06-17) 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.
Needs review.
source: <20260617-pks-refs-writing-subcommands-v2-0-07f3d18336f9@pks.im>
* 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'.
Will cook in 'next'.
cf. <ajOQthRjhD3hRM9w@pks.im>
source: <pull.2138.v2.git.1781621398.gitgitgadget@gmail.com>
* 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.
Will cook in 'next'.
cf. <xmqq4ij0vo8f.fsf@gitster.g>
source: <V3_CV_SubPatches_trailers.9ec@msgid.xyz>
* 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 handle non-linear history, in
which the path being tracked gets renamed differently in multiple
history lines, better.
Will cook in 'next'.
source: <ajjU4w2B0NlZffw1@collabora.com>
* 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.
Needs review.
source: <cover.1781294771.git.me@ttaylorr.com>
* mm/test-grep-lint (2026-06-12) 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
Needs review.
source: <pull.2135.v2.git.1781323575.gitgitgadget@gmail.com>
* 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
Will cook in 'next'.
cf. <20260615165326.GA91269@coredump.intra.peff.net>
source: <5a7ed929-6fe0-496c-83bd-65dee57c2241@web.de>
* kk/prio-queue-get-put-fusion (2026-06-08) 2 commits
- 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.
On hold, waiting for kk/prio-queue-get-put-fusion to land first.
cf. <CAL71e4MYNiScZjTwkApjDAjRh2LM0_SP59h5HCTywV-Pua03tw@mail.gmail.com>
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.
Needs review.
source: <20260612-ref-filter-memoized-contains-v4-0-5ed39fd001dd@gmail.com>
* tc/replay-linearize (2026-06-22) 3 commits
- replay: offer an option to linearize the commit topology
- replay: add helper to put entry into mapped_commits
- replay: refactor enum replay_mode into a bool
git replay learns --linearize option to drop merge commits and
linearize the replayed history, mimicking git rebase
--no-rebase-merges.
Expecting a reroll.
cf. <87tsqrycke.fsf@emacs.iotcl.com>
source: <20260622-toon-git-replay-drop-merges-v4-0-ff257f534319@iotcl.com>
* 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
Continuation of "setup.c" refactoring to drop remaining global state
(`git_work_tree_cfg`, `is_bare_repository_cfg`). The most notable
outcome is that `is_bare_repository()` has been updated to no longer
implicitly rely on `the_repository`.
Will cook in 'next'.
cf. <airVOrTboNDDGBak@denethor>
cf. <87ldckyygk.fsf@emacs.iotcl.com>
source: <20260611-b4-pks-setup-drop-global-state-v2-0-a6f7269c841d@pks.im>
* ps/refs-avoid-chdir-notify-reparent (2026-06-25) 12 commits
- 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-avoid-chdir-notify-reparent
The reference backends have been converted to always use absolute
paths internally. This allows dropping the calls to
`chdir_notify_reparent()` and fixes a memory leak in how the
reference database is constructed with an "onbranch" condition.
Will merge to 'next'?
cf. <aj1DuUzusBUqmF_C@denethor>
source: <20260625-b4-pks-refs-avoid-chdir-notify-reparent-v6-0-41fbca3cf5e3@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.
Will cook in 'next'.
cf. <ajK2QKdW-TdflfR0@denethor>
source: <20260617-pks-odb-source-packed-v3-0-b5c7583cd795@pks.im>
* 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.
Will cook in 'next'.
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.
Will cook in 'next'.
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>
* ps/cat-file-remote-object-info (2026-06-25) 13 commits
- cat-file: make remote-object-info allow-list dynamic
- cat-file: validate remote atoms with 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: refactor packet writing
- fetch-pack: move function to connect.c
- fetch-pack: prepare function to be moved
- t1006: split test utility functions into new "lib-cat-file.sh"
- cat-file: declare loop counter inside for()
- git-compat-util: add strtoul_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.
The client dynamically filters format placeholders based on
server-advertised capabilities and safely returns empty strings for
inapplicable or unsupported fields.
Waiting for response(s) to review comment(s).
cf. <xmqqjyrme393.fsf@gitster.g>
source: <20260625-ps-eric-work-rebase-v14-0-09f7ffe21a53@gmail.com>
* 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.
Expecting a reroll.
cf. <5144a29d-a53f-4446-beff-e1f549345bf9@nvidia.com>
source: <20260602161150.1527493-1-aplattner@nvidia.com>
* 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.
Will cook in 'next'.
cf. <87eci7yomp.fsf@emacs.iotcl.com>
source: <20260615-pks-b4-v4-0-22cfca8f19c5@pks.im>
* 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. This correctly
skips aliases of the current branch and avoids duplicate updates for
underlying real branches, fixing failures when branch aliases (like a
default branch rename) are present.
Waiting for response(s) to review comment(s).
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 to
allows 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>
* 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 pack-objects command now supports using 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.
Will cook in 'next'.
cf. <xmqqwlvq1qyy.fsf@gitster.g>
source: <cover.1782082975.git.me@ttaylorr.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.
Needs review.
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>
* jk/repo-info-path-keys (2026-06-23) 3 commits
- 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
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".
Will merge to 'next'?
cf. <xmqqy0g3iz38.fsf@gitster.g>
source: <20260624033748.108281-1-jayatheerthkulkarni2005@gmail.com>
* ps/history-drop (2026-06-15) 10 commits
- builtin/history: implement "drop" subcommand
- builtin/history: split handling of ref updates into two phases
- 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.
Waiting for response(s) to review comment(s).
cf. <CAP8UFD1evTZqj1ymW9g5g2RmMkYMaE0rPa0Hzt+irH94M6LD6A@mail.gmail.com>
source: <20260615-b4-pks-history-drop-v6-0-2e329e536d78@pks.im>
* 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.
Will cook in 'next'.
cf. <xmqqjyry4hax.fsf@gitster.g>
source: <20260616123516.GA2301231@coredump.intra.peff.net>
* 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 updates.
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
The path completion for commands like `git rm` and `git mv`, is being
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. <xmqq1pe0g08t.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
The -m/-F/-c/-C options to supply commit log message from outside the
editor are now supported for all "git commit --fixup" variations.
Needs review.
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)
Doc update for "git replay" to actually refer to its configuration
variables.
Needs review.
source: <V3_CV_doc_replay_config.780@msgid.xyz>
* 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>".
Will cook in 'next'.
cf. <xmqq7bo6xuok.fsf@gitster.g>
source: <pull.2301.v4.git.git.1779372367317.gitgitgadget@gmail.com>
* 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
"git branch" command learned "--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>
* 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
loosened to allow the other side to add to the list of promisor
remotes via the promisor.acceptFromServerURL configuration
variable.
Will cook in 'next'.
cf. <877bo7294j.fsf@emacs.iotcl.com>
cf. <xmqqh5naxwfc.fsf@gitster.g>
source: <20260527140820.1438165-1-christian.couder@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
"git checkout --track=..." learned to optionally fetch the branch
from the remote the new branch will work with.
Waiting for response(s) to review comment(s).
cf. <12998c3a-ff69-4a98-9ed6-18aa0224e75e@gmail.com>
source: <pull.2281.v15.git.git.1782338098.gitgitgadget@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()
"ort" merge backend handles merging corrupt trees better by
aborting when it should.
Will cook in 'next'.
cf. <xmqq5x3ldu4h.fsf@gitster.g>
source: <pull.2096.v2.git.1781419047.gitgitgadget@gmail.com>
* 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.
Will cook in 'next'.
source: <cover.1782230024.git.phillip.wood@dunelm.org.uk>
* ps/shift-root-in-graph (2026-06-20) 3 commits
- graph: indent visual root in graph
- revision: add peek functions 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.
Waiting for response(s) to review comment(s).
The peek-ahead approach may need to be scratched.
cf. <CAN5EUNSj-2hkEBF7N_M6RLsuujDNFNUF3w53zR7SN1_5i2BRyg@mail.gmail.com>
cf. <CAL71e4OQ_kGb+UwHgikHG236-8BVtc7P9OdpV4i4UzYRCoPczw@mail.gmail.com>
source: <20260620-ps-pre-commit-indent-v6-0-cdc6d8fd5fbc@gmail.com>
^ permalink raw reply
* Re: [PATCH v2 1/2] branch: suggest <remote>/<branch> on upstream slip
From: Junio C Hamano @ 2026-06-25 21:16 UTC (permalink / raw)
To: Harald Nordgren; +Cc: Harald Nordgren via GitGitGadget, git
In-Reply-To: <CAHwyqnXZ_eGUPOhq1hXs==uYuYbRBWw120fXRQa=apWKekxVAQ@mail.gmail.com>
Harald Nordgren <haraldnordgren@gmail.com> writes:
>> Do we still need the _if_enabled() thing here? Isn't the caller
>> gated with the same condition in this version?
>>
>> > + strbuf_release(&remote_ref);
>> > + exit(code);
>> > +}
>> > +
>> > int cmd_branch(int argc,
>> > const char **argv,
>> > const char *prefix,
>> > @@ -957,6 +980,9 @@ int cmd_branch(int argc,
>> > if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname)) {
>> > if (!argc || branch_checked_out(branch->refname))
>> > die(_("no commit on branch '%s' yet"), branch->name);
>> > + if (argc == 1 &&
>> > + advice_enabled(ADVICE_SET_UPSTREAM_FAILURE))
>> > + die_if_upstream_looks_like_remote(new_upstream, argv[0]);
>> > die(_("branch '%s' does not exist"), branch->name);
>> > }
>
> I think we do, so it will give the advice and tell the user that it
> can be disabled in the standard format.
I was hoping that unconditional advise() should be sufficient, but
the caller there needs to say if_enabled, even though it _knows_
that it is enabled, only to give the turn-off instructions.
I wonder if future readers would be confused just like I was,
without a comment on the callsite of _if_enabled() added by this
patch?
Thanks.
^ permalink raw reply
* Re: [PATCH GSoC v14 02/13] git-compat-util: add strtoul_szt() with error handling
From: Junio C Hamano @ 2026-06-25 21:09 UTC (permalink / raw)
To: Pablo Sabater
Cc: git, chandrapratap3519, chriscool, eric.peijian, jltobler,
karthik.188, peff, toon
In-Reply-To: <20260625-ps-eric-work-rebase-v14-2-09f7ffe21a53@gmail.com>
Pablo Sabater <pabloosabaterr@gmail.com> writes:
> From: Eric Ju <eric.peijian@gmail.com>
>
> We already have strtoul_ui() and similar functions that provide proper
> error handling using strtoul from the standard library. However,
> there isn't currently a variant that returns an unsigned long.
But this one no longer returns an unsigned long anymore ;-)
> This variant is needed in a subsequent commit to enable returning an
> size_t with proper error handling.
I think it would allow a lot of code paths that want to deal with
size_t not to worry about "is ulong large enough?" to have a
function like this, but for that to happen, the implementation of
the function must carefully think through if these steps do sensible
things on platforms with too small ulong (which often is OK when we
are coming from decimal string to ulong and then to size_t) and too
large ulong (which is not OK, when coming from decimal string to
ulong which might be fine, but will bust the size of the final
type), etc.
Also, would it make sense to add yet another "static inline" like
this? After the dust settles, we may want to rethink these strtoX
wrappers we have, benchmark, and possibly make them into a proper
library function, not "static inline" that may bloat the runtime.
> diff --git a/git-compat-util.h b/git-compat-util.h
> index 8809776407..7f417f1acf 100644
> --- a/git-compat-util.h
> +++ b/git-compat-util.h
> @@ -975,6 +975,26 @@ static inline int strtoul_ui(char const *s, int base, unsigned int *result)
> return 0;
> }
>
> +/*
> + * Convert a string to a size_t using the standard library's strtoul, with
> + * additional error handling to ensure robustness.
> + */
> +static inline int strtoul_szt(char const *s, int base, size_t *result)
> +{
> + unsigned long ul;
> + char *p;
> +
> + errno = 0;
> + /* negative values would be accepted by strtoul */
> + if (strchr(s, '-'))
> + return -1;
> + ul = strtoul(s, &p, base);
> + if (errno || *p || p == s)
> + return -1;
> + *result = ul;
> + return 0;
> +}
> +
> static inline int strtol_i(char const *s, int base, int *result)
> {
> long ul;
^ permalink raw reply
* Re: [PATCH v6 10/10] builtin/history: implement "drop" subcommand
From: Junio C Hamano @ 2026-06-25 20:50 UTC (permalink / raw)
To: Christian Couder
Cc: Patrick Steinhardt, git, Pablo Sabater, Kristoffer Haugsbakk,
Phillip Wood
In-Reply-To: <CAP8UFD3jsepRaiHDen_CzWcse-atvBfCdzAQovk+1csaQeDxmQ@mail.gmail.com>
Christian Couder <christian.couder@gmail.com> writes:
> On Mon, Jun 15, 2026 at 3:55 PM Patrick Steinhardt <ps@pks.im> wrote:
>
>> + /*
>> + * If HEAD will move as a result of the rewrite then we'll have to
>> + * merge in the changes into the worktree and index. This merge can of
>> + * course conflict, which will cause the whole operation to abort.
>> + *
>> + * If we had already updated the refs at that point then we'd have an
>> + * inconsistent repository state. So we first perform a dry-run merge
>> + * here before updating refs.
>> + */
>> + if (!is_bare_repository()) {
>
> When your ps/setup-drop-global-state series is merged, this will look like:
>
> if (!is_bare_repository(repo)) {
>
> which is nicer.
>
> So except for perhaps the replay_result_queue_update() duplication,
> the series looks great to me.
Thanks. Let me mark it as "Waiting for response(s) to review
comment(s)." then.
^ permalink raw reply
* Re: [PATCH v6 00/11] refs: fix "onbranch" conditions
From: Junio C Hamano @ 2026-06-25 20:50 UTC (permalink / raw)
To: Justin Tobler; +Cc: Patrick Steinhardt, git, Karthik Nayak, Jeff King
In-Reply-To: <aj1DuUzusBUqmF_C@denethor>
Justin Tobler <jltobler@gmail.com> writes:
> On 26/06/25 11:19AM, Patrick Steinhardt wrote:
>> Changes in v6:
>> - Drop redundant condition when setting the default for
>> "core.logallrefupdates".
>> - Leave breakcrumb for why we lazy-load write options for the "files"
>> backend.
>> - Fix commit message typo.
>
> Thanks. This version of the series looks good to me.
>
> -Justin
Thanks, both. Let's call it ready for 'next' then.
^ permalink raw reply
* Re: [PATCH v1 2/2] environment: move excludes_file into repo_config_values
From: Junio C Hamano @ 2026-06-25 20:40 UTC (permalink / raw)
To: Tian Yuchen
Cc: git, cirnovskyv, Christian Couder, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260625161845.7543-3-cat@malon.dev>
Tian Yuchen <cat@malon.dev> writes:
> Continue the libification effor by moving the 'excludes_file' global
"effort"?
> variable into 'struct repo_config_values'.
>
> Since 'excludes_file' is a dynamically allocated string (char *), it
> requires proper memory management. Introduce repo_config_values_clear()
> to safely free the heap memory when repository instance is destroyed.
>
> Note: 'if (repo != the_repository)' fallback logic is temporarily added
> in both the getter and the clear function. This prevents calling
> repo_config_values() on uninitialized submodules, which triggers BUG().
>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>
> Mentored-by: Olamide Caleb Bello <belkid98@gmail.com>
> Signed-off-by: Tian Yuchen <cat@malon.dev>
> ---
> environment.c | 28 ++++++++++++++++++++++------
> environment.h | 15 +++++++++++----
> repository.c | 1 +
> 3 files changed, 34 insertions(+), 10 deletions(-)
> ...
> @@ -733,3 +736,16 @@ void repo_config_values_init(struct repo_config_values *cfg)
> cfg->sparse_expect_files_outside_of_patterns = 0;
> cfg->warn_on_object_refname_ambiguity = 1;
Shouldn't cfg->excludes_file be explicitly initialized to NULL here
for completeness? There are other explicit but redundant 0 assignment
to the members of this struct in the same function.
> }
> +
> +void repo_config_values_clear(struct repository *repo)
> +{
> + struct repo_config_values *cfg;
> +
> + if (repo != the_repository)
> + return;
> +
> + cfg = repo_config_values(repo);
> + if (!cfg)
> + return;
> + FREE_AND_NULL(cfg->excludes_file);
> +}
> diff --git a/environment.h b/environment.h
> index 52d531e4ea..2839913551 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -98,6 +98,7 @@ struct repo_config_values {
> int precomposed_unicode;
> int core_sparse_checkout_cone;
> int warn_on_object_refname_ambiguity;
> + char *excludes_file;
>
> /* section "sparse" config values */
> int sparse_expect_files_outside_of_patterns;
> @@ -133,13 +134,20 @@ int git_default_config(const char *, const char *,
> int git_default_core_config(const char *var, const char *value,
> const struct config_context *ctx, void *cb);
>
> -/*
> - * TODO: This still relies on the global state.
> - */
> const char *repo_excludes_file(struct repository *repo);
Good.
> +/*
> + * Frees memory allocated for dynamically loaded configuration values
> + * inside `repo_config_values`.
> + *
> + * Note: `excludes_file` is currently the only heap-allocated field in
> + * this struct. As other dynamically allocated variables are migrated,
> + * their FREE_AND_NULL() calls should be appended here.
Isn't attributes_file also heap-allocated member in this struct as well?
^ permalink raw reply
* Re: [PATCH v3 4/4] connected: search promisor objects generically
From: Junio C Hamano @ 2026-06-25 20:22 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Christian Couder
In-Reply-To: <20260625-pks-connected-generic-promisor-checks-v3-4-7308f3b9dc44@pks.im>
Patrick Steinhardt <ps@pks.im> writes:
> When performing connectivity checks we have to figure out whether any of
> the new objects are promisor objects, as we cannot assume full
> connectivity if so.
>
> This check is performed by iterating through all packfiles in the
> repository and searching each of them for the given object. Of course,
> this mechanism is quite specific to implementation details of the object
> database, as we assume that it uses packfiles in the first place.
>
> Refactor the logic so that we instead use `odb_for_each_object_ext()`
> with an object prefix filter and the `ODB_FOR_EACH_OBJECT_PROMISOR_ONLY`
> flag. This will yield all objects that have the exact object name and
> that are part of a promisor pack in a generic way.
>
> Add a test to verify that we indeed use the optimization.
OK. The new test is a good way to catch the issue we noticed in the
previous round, I guess. Looking good.
Thanks.
^ permalink raw reply
* Re: [PATCH] t4216: fix no-op test that breaks TAP output
From: Junio C Hamano @ 2026-06-25 20:17 UTC (permalink / raw)
To: Todd Zullinger; +Cc: Patrick Steinhardt, Taylor Blau, git, Jeff King
In-Reply-To: <20260625185112.jjH0K9LI@teonanacatl.net>
Todd Zullinger <tmz@pobox.com> writes:
> Patrick Steinhardt wrote:
>> On Fri, Jun 19, 2026 at 09:29:44AM -0700, Junio C Hamano wrote:
>>> Taylor Blau <me@ttaylorr.com> writes:
>>>
>>>> Given this and the above, I would probably err on the side of
>>>> designating this as 'test_lazy_prereq' or otherwise silencing the output
>>>> of 'test_cmp' so that this does not taint the TAP output.
>>>
>>> We can argue the merit and demerit with a good log message. The
>>> central issue at hand is how precious 52a9 in the script lost by
>>> this patch is (in other words, are we checking more than "is our
>>> char signed or unsigned?").
>>
>> Ultimately, I don't mind much which way we go. But if we want to retain
>> this, would you mind sending a rewritten v2, Taylor? I feel like you're
>> in a better position to argue why we should retain it.
>
> Is this something which can be merged before 2.55.0 final?
> It's certainly not a grave issue, but it is a new test
> failure for anyone who diligently runs the test suite on
> many (most?) non-x86 architectures. It seems a shame to
> punish those folks. :)
>
> FWIW, Tested-by: Todd Zullinger <tmz@pobox.com>
>
> I tested the earlier test_lazy_prereq version as well.
Good that you pinged, as I forgot that we haven't seen Taylor for
some time. Let's merge down Patrick's one you have already tested
while leaving it as an option to resurrect the "52a9 is still
precious" tweak from Taylor perhaps after the release.
Thanks.
^ permalink raw reply
* Re: Re* [PATCH] history: close COMMIT_EDITMSG before launching the editor
From: Junio C Hamano @ 2026-06-25 20:12 UTC (permalink / raw)
To: Johannes Schindelin via GitGitGadget
Cc: git, Patrick Steinhardt, Johannes Schindelin
In-Reply-To: <xmqqh5mqfkpv.fsf@gitster.g>
Junio C Hamano <gitster@pobox.com> writes:
Of course, this should be ...
> + fwrite(out.buf, 1, out.len, s.fp);
...
fwrite(out->buf, 1, out->len, s.fp);
^ permalink raw reply
* Re* [PATCH] history: close COMMIT_EDITMSG before launching the editor
From: Junio C Hamano @ 2026-06-25 20:06 UTC (permalink / raw)
To: Johannes Schindelin via GitGitGadget
Cc: git, Patrick Steinhardt, Johannes Schindelin
In-Reply-To: <pull.2158.git.1782412427801.gitgitgadget@gmail.com>
"Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
writes:
> Close the handle once the status has been written, before handing the
> file off to the editor.
>
> Assisted-by: Opus 4.8
Do we even need this?
> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
> history: close COMMIT_EDITMSG before launching the editor
>
> I noticed this problem while trying to whip MinGit-BusyBox into a better
> shape during the -rc phase. Technically, this is not a fix for a
> regression during the v2.55.0 period, but I figured it'd be better to
> send it now anyway than to forget about sending it after v2.55.0 is
> released.
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2158%2Fdscho%2Ffix-fd-leak-in-history-reword-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2158/dscho/fix-fd-leak-in-history-reword-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/2158
>
> builtin/history.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/builtin/history.c b/builtin/history.c
> index 9526938085..4a5d9192f3 100644
> --- a/builtin/history.c
> +++ b/builtin/history.c
> @@ -74,6 +74,14 @@ static int fill_commit_message(struct repository *repo,
> wt_status_collect_free_buffers(&s);
> string_list_clear_func(&s.change, change_data_free);
>
> + /*
> + * Close the handle before launching the editor: on Windows an open
> + * handle would prevent the editor from replacing the file (e.g.
> + * BusyBox' `ash` cannot overwrite a file that another process keeps
> + * open), and leaving it open leaks the descriptor everywhere else.
> + */
> + fclose(s.fp);
> +
> strbuf_reset(out);
> if (launch_editor(path, out, NULL)) {
> fprintf(stderr, _("Aborting commit as launching the editor failed.\n"));
The function is extremely sloppy beyond words X-<. Thanks for
taking the first step to clean it up.
* It calls git_path_commit_editmsg() to obtain a constant pathname
into "const char *path", but then the part that leads to this
file stream leak does not even use that "path" constant. It
makes two independent calls to git_path_commit_editmsg()!
* The function first calls write_file_buf() to the file, which is a
convenience function when you have something you need to write
upfront and just want to write it and be done with it.
* And then the unclosed file stream you just fixed.
What is surprising is that all of this was created in a single
commit. I suspected that this part that does fopen() to leak the
file stream was a later addition than the initial write_file_buf(),
which should have been critiqued with "once you want to do your
custom writing that is more than "I have this block of memory, write
it into file", you should rewrite write_file_buf() call and roll it
into your own custom writing", but that is not the case.
So taking the opportunity to clean things up, how about doing it
this way intead?
----- >8 ---------- >8 ---------- >8 -----
Subject: [PATCH] history: streamline message preparation and plug file stream leak
An early part of fill_commit_mmessage() function uses write_file_buf()
to write out what was prepared in a strbuf, which is primarily meant
for use by callers that have their own message prepared fully and
called as the last thing to flush it to the destination file.
However, the function then opens a file stream in append mode to
further write into it. It may have been understandable if this was
a later addition, but it seems it came from a single commit,
d205234c (builtin/history: implement "reword" subcommand,
2026-01-13), which is somewhat puzzling, but anyway...
Just open the file stream upfront for writing, write the message
the function has in the strbuf, and then keep writing whatever it
wants to write to the same open file stream.
And do not forget to close the stream. We are about to pass the
resulting file to an external editor, and on some systems, notably
Windows, you are not supposed to keep a file open while expecting
another program to access it.
Diagnosed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/history.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/builtin/history.c b/builtin/history.c
index 8dcb9a6046..a882ad82e5 100644
--- a/builtin/history.c
+++ b/builtin/history.c
@@ -41,11 +41,6 @@ static int fill_commit_message(struct repository *repo,
" empty message aborts the commit.\n");
struct wt_status s;
- strbuf_addstr(out, default_message);
- strbuf_addch(out, '\n');
- strbuf_commented_addf(out, comment_line_str, hint, action, comment_line_str);
- write_file_buf(path, out->buf, out->len);
-
wt_status_prepare(repo, &s);
FREE_AND_NULL(s.branch);
s.ahead_behind_flags = AHEAD_BEHIND_QUICK;
@@ -57,14 +52,20 @@ static int fill_commit_message(struct repository *repo,
s.whence = FROM_COMMIT;
s.committable = 1;
- s.fp = fopen(git_path_commit_editmsg(), "a");
+ s.fp = fopen(path, "w");
if (!s.fp)
- return error_errno(_("could not open '%s'"), git_path_commit_editmsg());
+ return error_errno(_("could not open '%s'"), path);
+
+ strbuf_addstr(out, default_message);
+ strbuf_addch(out, '\n');
+ strbuf_commented_addf(out, comment_line_str, hint, action, comment_line_str);
+ fwrite(out.buf, 1, out.len, s.fp);
wt_status_collect_changes_trees(&s, old_tree, new_tree);
wt_status_print(&s);
wt_status_collect_free_buffers(&s);
string_list_clear_func(&s.change, change_data_free);
+ fclose(s.fp);
strbuf_reset(out);
if (launch_editor(path, out, NULL)) {
--
2.55.0-rc2-165-g3249676ba5
^ permalink raw reply related
* [PATCH] gpg-interface: still print ssh signatures when allowed signers file is not set
From: Grayson Tinker @ 2026-06-25 19:43 UTC (permalink / raw)
To: git
Cc: Grayson Tinker, Junio C Hamano, Patrick Steinhardt, Elijah Newren,
Fabian Stelzer, Jeff King, René Scharfe
"show-signature" errors when the allowed signers file is not configured,
which means that the user can't see the key that the ref was signed with
without creating and configuring the file. Change the logic so that the file
is only used when configured, and so the signature status is always displayed.
Example of previous output:
```
error: gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification
commit b437db5ddc38ebda223bbae2087eee90a7b1c6e2 (HEAD -> master)
No signature
Author: Grayson Tinker <graysontinker@gmail.com>
```
Example of new output:
```
commit b437db5ddc38ebda223bbae2087eee90a7b1c6e2 (HEAD -> master)
hint: Configure gpg.ssh.allowedSignersFile for automatic principal matching
Good "git" signature with ED25519-SK key SHA256:yTU4KFs/g6MY7biDSlVStB63Gi1rCKg7dOFDXbe0yuw
Author: Grayson Tinker <graysontinker@gmail.com>
```
Signed-off-by: Grayson Tinker <graysontinker@gmail.com>
---
gpg-interface.c | 42 +++++++++++++++++++++++-------------------
1 file changed, 23 insertions(+), 19 deletions(-)
diff --git a/gpg-interface.c b/gpg-interface.c
index dafd5371fa..ef3e1a0aa0 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -1,6 +1,7 @@
#define USE_THE_REPOSITORY_VARIABLE
#include "git-compat-util.h"
+#include "advice.h"
#include "commit.h"
#include "config.h"
#include "date.h"
@@ -480,11 +481,6 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
.local = 1,
};
- if (!ssh_allowed_signers) {
- error(_("gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification"));
- return -1;
- }
-
buffer_file = mks_tempfile_t(".git_vtag_tmpXXXXXX");
if (!buffer_file)
return error_errno(_("could not create temporary file"));
@@ -500,22 +496,26 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
strbuf_addf(&verify_time, "-Overify-time=%s",
show_date(sigc->payload_timestamp, 0, verify_date_mode));
- /* Find the principal from the signers */
- strvec_pushl(&ssh_keygen.args, fmt->program,
- "-Y", "find-principals",
- "-f", ssh_allowed_signers,
- "-s", buffer_file->filename.buf,
- verify_time.buf,
- NULL);
- ret = pipe_command(&ssh_keygen, NULL, 0, &ssh_principals_out, 0,
- &ssh_principals_err, 0);
- if (ret && strstr(ssh_principals_err.buf, "usage:")) {
- error(_("ssh-keygen -Y find-principals/verify is needed for ssh signature verification (available in openssh version 8.2p1+)"));
- goto out;
+ if (ssh_allowed_signers) {
+ /* Find the principal from the signers */
+ strvec_pushl(&ssh_keygen.args, fmt->program,
+ "-Y", "find-principals",
+ "-f", ssh_allowed_signers,
+ "-s", buffer_file->filename.buf,
+ verify_time.buf,
+ NULL);
+ ret = pipe_command(&ssh_keygen, NULL, 0, &ssh_principals_out, 0,
+ &ssh_principals_err, 0);
+ if (ret && strstr(ssh_principals_err.buf, "usage:")) {
+ error(_("ssh-keygen -Y find-principals/verify is needed for ssh signature verification (available in openssh version 8.2p1+)"));
+ goto out;
+ }
}
- if (ret || !ssh_principals_out.len) {
+
+ if (!ssh_allowed_signers || ret || !ssh_principals_out.len) {
/*
- * We did not find a matching principal in the allowedSigners
+ * We did not find a matching principal in the allowedSigners,
+ * or no allowedSigners file was configured
* Check without validation
*/
child_process_init(&ssh_keygen);
@@ -528,6 +528,10 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
pipe_command(&ssh_keygen, sigc->payload, sigc->payload_len,
&ssh_keygen_out, 0, &ssh_keygen_err, 0);
+ if (!ssh_allowed_signers) {
+ advise(_("Configure gpg.ssh.allowedSignersFile for automatic principal matching\n"));
+ }
+
/*
* Fail on unknown keys
* we still call check-novalidate to display the signature info
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] t4216: fix no-op test that breaks TAP output
From: Todd Zullinger @ 2026-06-25 18:51 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Junio C Hamano, Taylor Blau, git, Jeff King
In-Reply-To: <ajjBmi39IFJW5p5V@pks.im>
Patrick Steinhardt wrote:
> On Fri, Jun 19, 2026 at 09:29:44AM -0700, Junio C Hamano wrote:
>> Taylor Blau <me@ttaylorr.com> writes:
>>
>>> Given this and the above, I would probably err on the side of
>>> designating this as 'test_lazy_prereq' or otherwise silencing the output
>>> of 'test_cmp' so that this does not taint the TAP output.
>>
>> We can argue the merit and demerit with a good log message. The
>> central issue at hand is how precious 52a9 in the script lost by
>> this patch is (in other words, are we checking more than "is our
>> char signed or unsigned?").
>
> Ultimately, I don't mind much which way we go. But if we want to retain
> this, would you mind sending a rewritten v2, Taylor? I feel like you're
> in a better position to argue why we should retain it.
Is this something which can be merged before 2.55.0 final?
It's certainly not a grave issue, but it is a new test
failure for anyone who diligently runs the test suite on
many (most?) non-x86 architectures. It seems a shame to
punish those folks. :)
FWIW, Tested-by: Todd Zullinger <tmz@pobox.com>
I tested the earlier test_lazy_prereq version as well.
--
Todd
^ permalink raw reply
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