* Re: [GSoC PATCH v2 3/4] repo: add path.gitdir with absolute and relative suffix formatting
From: Justin Tobler @ 2026-06-09 14:31 UTC (permalink / raw)
To: K Jayatheerth
Cc: git, a3205153416, gitster, kumarayushjha123, lucasseikioshiro,
phillip.wood, sandals
In-Reply-To: <CA+rGoLdpkuigWXqNSk3bS7-uhtzCizkPx2GGtNaTyy5J1SF7Rg@mail.gmail.com>
On 26/06/09 10:11AM, K Jayatheerth wrote:
> > > + struct strbuf sb = STRBUF_INIT;
> > > + enum path_format fmt = (arg_path_format != -1) ? arg_path_format : def_format;
> >
> > hmmm, so `arg_path_format` specifies what the user-provided format and
> > acts as a sentinel to signal there is no value provided and the fallback
> > format needs to be used. This feels a tad bit awkward to me.
> >
> > I wonder if we should introduce a PATH_FORMAT_DEFAULT to the
> > `path_format` enum that maps to one of the existing enum values in
> > `path.c:format_path()`. Here in `print_path()`, we could then intercept
> > a PATH_FORMAT_DEFAULT value and override it to the specified
> > `def_format`. I'm not sure if this is ultimately that much better
> > though.
>
> You're right that the -1 is awkward
> it forces arg_path_format to be an int rather than the enum type
> itself, which loses type safety.
>
> PATH_FORMAT_DEFAULT is cleaner in that regard, but it pushes the "what
> does default mean?" question into format_path()
> which currently has no notion of a fallback.
> Since the fallback is call-site specific (each path type in rev-parse
> has its own default),
> I'd rather keep that logic in print_path() where the context lives.
>
> A middle ground would be adding PATH_FORMAT_DEFAULT to the enum but
> not handling it in format_path().
>
> ---
> enum path_format_type format = PATH_FORMAT_DEFAULT;
>
> /* ... */
>
> static void print_path(const char *path, const char *prefix,
> enum path_format_type format,
> enum path_format_type def_format)
> {
> struct strbuf sb = STRBUF_INIT;
> enum path_format_type fmt =
> (format == PATH_FORMAT_DEFAULT) ? def_format : format;
>
> format_path(&sb, path, prefix, fmt);
> puts(sb.buf);
> strbuf_release(&sb);
> }
> ---
Intercepting PATH_FORMAT_DEFAULT in print_path() and overriding it to
the appropriate default needed for the specific path printed by
git-rev-parse(1), as shown above, seems reasonable to me.
But I do think that PATH_FORMAT_DEFAULT should have an actual default in
format_path(). Otherwise we would have an enum value that requires
callers to explicitly handle prior to invoking format_path() which would
also be rather awkward. IMO, it probably wouldn't be a big deal to just
say PATH_FORMAT_DEFAULT is treated as PATH_FORMAT_UNMODIFIED when passed
to format_path() and document it. In practice, our rev-parse use-case
would always replace PATH_FORMAT_DEFAULT with the appropriate value
prior to invoking format_path().
-Justin
^ permalink raw reply
* [PATCH v3] transport-helper: fix TSAN race in transfer_debug()
From: Pushkar Singh @ 2026-06-09 13:47 UTC (permalink / raw)
To: pushkarkumarsingh1970; +Cc: git, gitster, peff
In-Reply-To: <20260604132327.277693-3-pushkarkumarsingh1970@gmail.com>
Currently, transfer_debug() lazily initializes a static variable based
on GIT_TRANSLOOP_DEBUG. Since the function may be called from multiple
worker threads, this initialization is racy and is therefore suppressed
in .tsan-suppressions.
Initialize the variable in bidirectional_transfer_loop() before any
worker threads or processes are created. This patch removes the race and
allows dropping the corresponding TSAN suppression.
Signed-off-by: Pushkar Singh <pushkarkumarsingh1970@gmail.com>
---
Changes since v2:
- Treat an uninitialized transfer_debug_enabled as a BUG()
instead of silently treating it as disabled.
- Follow Jeff King's suggestion to distinguish an
uninitialized state from a disabled state.
.tsan-suppressions | 1 -
transport-helper.c | 19 ++++++++-----------
2 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/.tsan-suppressions b/.tsan-suppressions
index 5ba86d6845..d84883bd90 100644
--- a/.tsan-suppressions
+++ b/.tsan-suppressions
@@ -7,7 +7,6 @@
# A static variable is written to racily, but we always write the same value, so
# in practice it (hopefully!) doesn't matter.
race:^want_color$
-race:^transfer_debug$
# A boolean value, which tells whether the replace_map has been initialized or
# not, is read racily with an update. As this variable is written to only once,
diff --git a/transport-helper.c b/transport-helper.c
index 04d55572a9..5b639bff3d 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -1361,24 +1361,18 @@ int transport_helper_init(struct transport *transport, const char *name)
/* This should be enough to hold debugging message. */
#define PBUFFERSIZE 8192
+static int transfer_debug_enabled = -1;
+
/* Print bidirectional transfer loop debug message. */
__attribute__((format (printf, 1, 2)))
static void transfer_debug(const char *fmt, ...)
{
- /*
- * NEEDSWORK: This function is sometimes used from multiple threads, and
- * we end up using debug_enabled racily. That "should not matter" since
- * we always write the same value, but it's still wrong. This function
- * is listed in .tsan-suppressions for the time being.
- */
-
va_list args;
char msgbuf[PBUFFERSIZE];
- static int debug_enabled = -1;
- if (debug_enabled < 0)
- debug_enabled = getenv("GIT_TRANSLOOP_DEBUG") ? 1 : 0;
- if (!debug_enabled)
+ if (transfer_debug_enabled < 0)
+ BUG("somebody forgot to check GIT_TRANSLOOP_DEBUG!");
+ if (!transfer_debug_enabled)
return;
va_start(args, fmt);
@@ -1648,6 +1642,9 @@ int bidirectional_transfer_loop(int input, int output)
{
struct bidirectional_transfer_state state;
+ if (transfer_debug_enabled < 0)
+ transfer_debug_enabled = getenv("GIT_TRANSLOOP_DEBUG") ? 1 : 0;
+
/* Fill the state fields. */
state.ptg.src = input;
state.ptg.dest = 1;
--
2.53.0.582.gca1db8a0f7
^ permalink raw reply related
* Re: [PATCH v1 3/4] environment: move 'trust_executable_bit' into repo_config_values
From: Junio C Hamano @ 2026-06-09 13:45 UTC (permalink / raw)
To: Tian Yuchen
Cc: git, christian.couder, ps, Ayush Chandekar, Olamide Caleb Bello
In-Reply-To: <8083b217-4a56-48ee-b34d-b4596d45e382@malon.dev>
Tian Yuchen <cat@malon.dev> writes:
> On 6/1/26 18:10, Tian Yuchen wrote:
>
>> That’s true: I had actually planned to start migrating has_symlinks as
>> soon as this series was approved. Since you think it would be better to
>> merge them into a single series, I’ll go ahead and do that ;)
>>
>
> I’ve found that migrating has_symlinks seems to be quite a tricky
> business. Some callers in certain files pass very few parameters, and
> the call stack is quite deep, if I am correct. so I feel that adding a
> repo for this purpose might be overkill. Perhaps it would be better to
> focus on trust_executable_bit for now?
>
> Regards, yuchen
Sounds fine. Thanks for digging.
^ permalink raw reply
* Re: [PATCH v2] describe: limit default ref iteration to tags
From: D. Ben Knoble @ 2026-06-09 13:40 UTC (permalink / raw)
To: Jeff King; +Cc: Tamir Duberstein, git, Junio C Hamano, Patrick Steinhardt
In-Reply-To: <20260609110957.GB1509396@coredump.intra.peff.net>
On Tue, Jun 9, 2026 at 7:10 AM Jeff King <peff@peff.net> wrote:
>
> On Mon, Jun 08, 2026 at 07:32:14PM -0700, Tamir Duberstein wrote:
>
> > The benchmark checkout had 120,532 refs, of which 330 were tags. With
> > `$repo` naming the checkout, `$commit` an exactly tagged commit, and
> > `$parent` and `$this` the two binaries, I ran:
> >
> > hyperfine --warmup 3 --runs 15 \
> > --command-name parent \
> > '$parent -C $repo describe --exact-match $commit' \
> > --command-name 'this commit' \
> > '$this -C $repo describe --exact-match $commit'
> >
> > The results were:
> >
> > Benchmark 1: parent
> > Time (mean ± σ): 171.7 ms ± 18.5 ms [User: 23.9 ms, System: 133.6 ms]
> > Range (min … max): 142.3 ms … 198.3 ms 15 runs
> >
> > Benchmark 2: this commit
> > Time (mean ± σ): 9.9 ms ± 1.1 ms [User: 3.3 ms, System: 4.7 ms]
> > Range (min … max): 8.8 ms … 13.1 ms 15 runs
> >
> > Summary
> > this commit ran
> > 17.35 ± 2.63 times faster than parent
> >
> > Both revisions were built with -O3, -mcpu=native, and ThinLTO using
> > Apple clang 21.0.0 on macOS 26.5. The machine was a MacBook Pro
> > (Mac16,6) with a 16-core Apple M4 Max (12 performance and four
> > efficiency cores) and 128 GB RAM.
>
> This patch looks fine to me, but let me pick a nit for a minute, because
> I think there is a broader conversation to be had.
>
> Given the discussion in earlier rounds and sibling topics, I assume the
> commit message here was AI-generated. And it's OK in the sense that it
> is describing what happened and I assume is entirely accurate. But as a
> human reader, it feels so much more verbose than what I'd expect, as it
> is full of semi-irrelevant details. Why set --warmup and --runs? Why
> bother with --command-name, which just means you have to show the
> commands separately anyway? Is the amount of RAM in the machine
> important for this test? Surely it could be if it was absurdly tiny, but
> in general, no, I would not expect it to be.
[You probably know this] It is common in academic papers to report
benchmarks with details about the hardware and how they were run to
contextualize the results and help with reproducibility.
Of course, Git's commits do not form an academic paper… so I have no
real opinion on what to see here. But I've seen a few other mails
where having perf test outputs or similar was suggested (maybe that
was to be reserved for the cover letter? idk).
_If_ we show all the hyperfine details, I think it's reasonable to use
--command-name to make distinguishing the versions easy, unless it's
obvious from the path/to/git in each benchmark (which I think I've
seen from Peff's benchmark reports before?).
Someone with better lore skills can probably dig up a few exemplars of
how to write about performance in a commit message?
--
D. Ben Knoble
^ permalink raw reply
* Re: [PATCH v3 0/3] Documentation: recommend the use of b4
From: Junio C Hamano @ 2026-06-09 13:30 UTC (permalink / raw)
To: Toon Claes
Cc: Patrick Steinhardt, git, Tuomas Ahola, Weijie Yuan, Ramsay Jones,
SZEDER Gábor, Kristoffer Haugsbakk
In-Reply-To: <87a4t32a4g.fsf@emacs.iotcl.com>
Toon Claes <toon@iotcl.com> writes:
> Anyhow, I don't think it's worth it to keep bike shedding about this. In
> all methods we recommend to Cc people, I think that's more important
> then
Good point to stress about whom to involve.
> caring about how messages are threaded (for example, I've noticed
> LKML doesn't thread at all, i.e. `b4.send-same-thread=no` which is the
> default).
As long as it does not hurt automation, I do not care too much, but
that default is somewhat surprising to me. The setting actively
discourages tools from finding previous iterations.
> Bottom line, for me this series is good to go in.
Yeah, sounds good to me.
^ permalink raw reply
* Re: [PATCH RFC v2 2/2] builtin/history: abort reword on same message
From: Phillip Wood @ 2026-06-09 13:25 UTC (permalink / raw)
To: Pablo Sabater, git; +Cc: cat, ps, kaartic.sivaraam, ben.knoble, gitster
In-Reply-To: <20260609-ps-history-reword-v2-2-a0e6028ca9b4@gmail.com>
Hi Pablo
On 09/06/2026 11:42, Pablo Sabater wrote:
> static int commit_tree_ext(struct repository *repo,
> @@ -135,6 +136,13 @@ static int commit_tree_ext(struct repository *repo,
> original_body, action, &commit_message);
> if (ret < 0)
> goto out;
> +
> + if (flags & COMMIT_TREE_ABORT_ON_SAME_MESSAGE &&
> + !strcmp(original_body, commit_message.buf)) {
> + fprintf(stderr, _("Message unchanged, aborting reword.\n"));
> + ret = 1;
> + goto out;
> + }
I wonder if we should check that the committer identity is unchanged as
well in case anyone is using this to fix commits after committing with
the wrong identity.
Aborting when the message and committer identity are unchanged seems
like a good idea.
Thanks
Phillip
> } else {
> strbuf_addstr(&commit_message, original_body);
> }
> @@ -693,7 +701,8 @@ static int cmd_history_reword(int argc,
> struct strbuf reflog_msg = STRBUF_INIT;
> struct commit *original, *rewritten;
> struct rev_info revs = { 0 };
> - enum commit_tree_flags flags = COMMIT_TREE_EDIT_MESSAGE;
> + enum commit_tree_flags flags = COMMIT_TREE_EDIT_MESSAGE |
> + COMMIT_TREE_ABORT_ON_SAME_MESSAGE;
> int ret;
>
> argc = parse_options(argc, argv, prefix, options, usage, 0);
> @@ -721,6 +730,9 @@ static int cmd_history_reword(int argc,
> if (ret < 0) {
> ret = error(_("failed writing reworded commit"));
> goto out;
> + } else if (ret == 1) {
> + ret = 0;
> + goto out;
> }
>
> strbuf_addf(&reflog_msg, "reword: updating %s", argv[0]);
> diff --git a/t/t3451-history-reword.sh b/t/t3451-history-reword.sh
> index de7b357685..6e0e278c42 100755
> --- a/t/t3451-history-reword.sh
> +++ b/t/t3451-history-reword.sh
> @@ -396,4 +396,20 @@ test_expect_success 'retains changes in the worktree and index' '
> )
> '
>
> +test_expect_success 'aborts if the commit message is the same' '
> + test_when_finished "rm -rf repo" &&
> + git init repo &&
> + (
> + cd repo &&
> + test_commit first &&
> + test_commit second &&
> +
> + git rev-parse HEAD >oid-before &&
> + GIT_EDITOR=true git history reword HEAD 2>err &&
> + git rev-parse HEAD >oid-after &&
> + test_cmp oid-before oid-after &&
> + test_grep "Message unchanged" err
> + )
> +'
> +
> test_done
> diff --git a/t/t3453-history-fixup.sh b/t/t3453-history-fixup.sh
> index 868298e248..9f9a3c93de 100755
> --- a/t/t3453-history-fixup.sh
> +++ b/t/t3453-history-fixup.sh
> @@ -443,6 +443,28 @@ test_expect_success '--reedit-message opens editor for the commit message' '
> )
> '
>
> +test_expect_success 'fixup --reedit-message does not abort with the same commit message' '
> + test_when_finished "rm -rf repo" &&
> + git init repo &&
> + (
> + cd repo &&
> + test_commit initial &&
> + echo content > file.txt &&
> + git add file.txt &&
> + git commit -m "add file" &&
> +
> + echo fix >>file.txt &&
> + git add file.txt &&
> + GIT_EDITOR=true git history fixup --reedit-message HEAD &&
> + expect_changes --branches <<-\EOF
> + add file
> + 2 0 file.txt
> + initial
> + 1 0 initial.t
> + EOF
> + )
> +'
> +
> test_expect_success 'retains unstaged working tree changes after fixup' '
> test_when_finished "rm -rf repo" &&
> git init repo &&
>
^ permalink raw reply
* Re: [PATCH v2] describe: limit default ref iteration to tags
From: Junio C Hamano @ 2026-06-09 13:23 UTC (permalink / raw)
To: Jeff King; +Cc: Tamir Duberstein, git, Patrick Steinhardt
In-Reply-To: <20260609110957.GB1509396@coredump.intra.peff.net>
Jeff King <peff@peff.net> writes:
> So while it is perhaps reasonable to document every detail in case
> somebody later wants to verify or reproduce timings, it is a little
> overwhelming when trying to tell a story, the core of which is:
>
> In a repo with ~120k refs, ~300 of which were tags, running:
>
> git describe --exact-match $some_tag
>
> went from ~170ms to ~10ms, since we no longer needed to iterate all of
> those other refs.
>
> That has _way_ less detail, but makes the point succinctly.
>
> I dunno. I am not trying to pick apart your commit in particular, but am
> more interested in the broader use of AI commit messages going forward.
> This kind of verbosity is quite common in the output (from my limited
> experience), and I think creates more work for reviewers. Should we be
> expecting contributors to make things more concise before submitting
> (either manually or through prompting)? Or do people even agree that the
> shorter version is preferable? I could be the only one.
Count me in. You are the one who often gives us a patch with 60
lines that explains a single line change, but I haven't found these
60 lines are _overly verbose_ in the same way as AI generated log
messages.
^ permalink raw reply
* Re: [PATCH v14 2/6] branch: let delete_branches warn instead of error on bulk refusal
From: Phillip Wood @ 2026-06-09 13:21 UTC (permalink / raw)
To: Harald Nordgren via GitGitGadget, git
Cc: Kristoffer Haugsbakk, Johannes Sixt, Harald Nordgren
In-Reply-To: <7ef9502e01055fd5442550cf34d491fd21a9b971.1780999917.git.gitgitgadget@gmail.com>
Hi Harald
On 09/06/2026 11:11, Harald Nordgren via GitGitGadget wrote:
> From: Harald Nordgren <haraldnordgren@gmail.com>
>
> Add a warn-only mode to delete_branches() and check_branch_commit()
> so a bulk caller can report branches that are not fully merged as a
> short warning and carry on, rather than erroring with the longer
> "use 'git branch -D'" advice that the plain "git branch -d" path
> emits. Existing callers are unaffected.
There is no mention here of the conversion to use a flags argument which
should be a separate preparatory commit
> @@ -189,20 +189,33 @@ static int branch_merged(int kind, const char
*name,
> return merged;
> }
>
> +enum delete_branch_flags {
> + DELETE_BRANCH_FORCE = (1 << 0),
> + DELETE_BRANCH_QUIET = (1 << 1),
> + DELETE_BRANCH_WARN_ONLY = (1 << 2),
> +};
> +
> static int check_branch_commit(const char *branchname, const char *refname,
> const struct object_id *oid, struct commit *head_rev,
> - int kinds, int force)
> + int kinds, unsigned int flags)
> {
> + int force = flags & DELETE_BRANCH_FORCE;
This is missing "!!" to keep the value the same (alternatively we could
perhaps convert "force" to a bool, though I haven't looked too closely
at how it is used in the rest of the function). Apart from that this
good, unlike the conversion below it means the rest of the function sees
the same variables in the same state as it did before the conversion. It
would be a good idea to follow this pattern for the new flag.
bool warn = flags & DELETE_BRANCH_WARN_ONLY;
and then just use "warn" later. It is a common pattern in our code to
take a flags argument and split it out into various boolean variables at
the start of the function to avoid a lot of awkward bit masks in the
main body of the function.
> @@ -217,8 +230,8 @@ static void delete_branch_config(const char *branchname)
> strbuf_release(&buf);
> }
>
> -static int delete_branches(int argc, const char **argv, int force, int kinds,
> - int quiet)
> +static int delete_branches(int argc, const char **argv, int kinds,
> + unsigned int flags)
> {
> struct commit *head_rev = NULL;
> struct object_id oid;
> @@ -227,6 +240,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
> int i;
> int ret = 0;
> int remote_branch = 0;
> + int force, quiet;
We should initialize these here using flags so that the rest of the code
sees the same variables and values as it did before the conversion.
> struct strbuf bname = STRBUF_INIT;
> enum interpret_branch_kind allowed_interpret;
> struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
> @@ -241,7 +255,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
> remote_branch = 1;
> allowed_interpret = INTERPRET_BRANCH_REMOTE;
>
> - force = 1;
> + flags |= DELETE_BRANCH_FORCE;
> break;
> case FILTER_REFS_BRANCHES:
> fmt = "refs/heads/%s";
> @@ -252,12 +266,15 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
> }
> branch_name_pos = strcspn(fmt, "%");
>
> + force = flags & DELETE_BRANCH_FORCE;
> + quiet = flags & DELETE_BRANCH_QUIET;
> +
> if (!force)
> head_rev = lookup_commit_reference(the_repository, &head_oid);
>
> for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
> char *target = NULL;
> - int flags = 0;
> + int ref_flags = 0;
This is sensible so we don't shadow the new function argument but this
added complexity is a good reason to split the flags change into its own
commit before adding the warning flag.
I'll take a look at the other patches later this week - there is no need
to send a new version before I've commented on the rest of the series.
Thanks
Phillip
>
> copy_branchname(&bname, argv[i], allowed_interpret);
> free(name);
> @@ -279,7 +296,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
> RESOLVE_REF_READING
> | RESOLVE_REF_NO_RECURSE
> | RESOLVE_REF_ALLOW_BAD_NAME,
> - &oid, &flags);
> + &oid, &ref_flags);
> if (!target) {
> if (remote_branch) {
> error(_("remote-tracking branch '%s' not found"), bname.buf);
> @@ -291,7 +308,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
> | RESOLVE_REF_NO_RECURSE
> | RESOLVE_REF_ALLOW_BAD_NAME,
> &oid,
> - &flags);
> + &ref_flags);
> FREE_AND_NULL(virtual_name);
>
> if (virtual_target)
> @@ -306,16 +323,17 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
> continue;
> }
>
> - if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
> + if (!(ref_flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
> check_branch_commit(bname.buf, name, &oid, head_rev, kinds,
> - force)) {
> - ret = 1;
> + flags)) {
> + if (!(flags & DELETE_BRANCH_WARN_ONLY))
> + ret = 1;
> goto next;
> }
>
> item = string_list_append(&refs_to_delete, name);
> - item->util = xstrdup((flags & REF_ISBROKEN) ? "broken"
> - : (flags & REF_ISSYMREF) ? target
> + item->util = xstrdup((ref_flags & REF_ISBROKEN) ? "broken"
> + : (ref_flags & REF_ISSYMREF) ? target
> : repo_find_unique_abbrev(the_repository, &oid, DEFAULT_ABBREV));
>
> next:
> @@ -872,7 +890,9 @@ int cmd_branch(int argc,
> if (delete) {
> if (!argc)
> die(_("branch name required"));
> - ret = delete_branches(argc, argv, delete > 1, filter.kind, quiet);
> + ret = delete_branches(argc, argv, filter.kind,
> + (delete > 1 ? DELETE_BRANCH_FORCE : 0) |
> + (quiet ? DELETE_BRANCH_QUIET : 0));
> goto out;
> } else if (show_current) {
> print_current_branch_name();
^ permalink raw reply
* Re: [PATCH RFC 1/2] builtin/history: abort reword on unchanged message
From: Junio C Hamano @ 2026-06-09 13:21 UTC (permalink / raw)
To: Pablo Sabater; +Cc: git, Patrick Steinhardt, Kaartic Sivaraam
In-Reply-To: <CAN5EUNRW3gyLKGC7x5BBMTNKtunoQks9AaXJse4PHvCziRF87A@mail.gmail.com>
Pablo Sabater <pabloosabaterr@gmail.com> writes:
> True, after reading it, history being more costly or the in memory are
> not good args.
And no argument, including that history is new, is a good excuse to
make these three things inconsistent, period.
One of the patches in your updated iteration claims
When using `git history reword <commit>` if the new message is the same
as the original, it continues and rewrites the history when nothing
changed.
`git commit --amend` and `git rebase -i` with reword share this behavior
and it is wrong as well, but changing them breaks what people are used
to. Take the opportunity of `git history` being a new command and handle
it correctly from the start.
and I think this is a totally wrong attitude to go about this.
I may have said that it may have been a better default to try hard
to avoid making a change that is a no-op, other than that it changes
committer timestamp, while making the current "always create a new
commit object" behaviour optionally available, for these three
commands, and cited that the behaviour of 'pick' in 'rebase -i' that
avoids unnecessary rewrite as an example of a good practice.
But I do not think the existing behaviour to always rewrite is
*wrong* at all. It may be wrong not to offer the other choice of
pretending no content change means no commit object change, but that
is a different story.
I also do not think *aborting* only when the message happens to be
the same is a valid mode of operation at all.
The most sensible first step, I think, is to add a new command line
option to "git history" (which will gain more history editing
subcommands) that tells the command to leave the original history
as-is when the only change rewriting commits would make would be to
the committer ident or timestamp information. If in a future a new
replace-tree subcommand is added, e.g. if
$ git history replace-tree HEAD~20 HEAD~27^{tree}
were a command to rewrite the history in such a way that 20th direct
ancestor of the current HEAD had a tree object HEAD~27^{tree}, by
derfault the command _should_ rewrite HEAD~10 and everything that
has it as an ancestor. With the "--avoid-unnecsssary-rewrite"
optimization feature on, however, it may silently become a no-op
when HEAD~27^{tree} happened to be the same tree as HEAD~20^{tree}
so the only difference between rewritten and original HEAD~20 would
be when that commit object was created and by whom.
And give the same option to "rebase -i" or "commit --amend". We can
discuss, educate the users, and flip the default at a major version
boundary, if the "avoid unnecessary rewrite" truly turns out to be a
better default (right now it is merely our speculation, and we do
not even know if the current behaviour is a worse default).
^ permalink raw reply
* Re: [PATCH v13 2/6] branch: let delete_branches warn instead of error on bulk refusal
From: Harald Nordgren @ 2026-06-09 13:20 UTC (permalink / raw)
To: Junio C Hamano
Cc: Harald Nordgren via GitGitGadget, git, Kristoffer Haugsbakk,
Johannes Sixt, Phillip Wood
In-Reply-To: <xmqqa4t3ubwj.fsf@gitster.g>
On Tue, Jun 9, 2026 at 2:38 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Harald Nordgren <haraldnordgren@gmail.com> writes:
>
> > The GitHub CI has been broken for some time, maybe I should have told
> > you about this earlier, but it coincided with a period where other
> > open source projects I worked on also had mass CI failures, so I
> > chalked it up to upstream issues (GitHub, Linux, etc). But it seems to
> > have not gone away.
> >
> > All of my GitHub pull requests have broken tests (see e.g. which a
> > quite minimal change: https://github.com/git/git/pull/2313). This
> > makes it harder to detect actual issues. But of course it's not an
> > excuse.
>
> FWIW, the breakage was observed in my local testing, and that is why
> I found it so disturbing. Apparently you didn't see such breakages
> that can be detected so easily during your local testing (otherwise
> you wouldn't have pushed it out to update your GitHub pull request),
> which may mean something in the test are platform dependent?
No, it was broken on my local machine as well. I was sloppy when I
pushed out v13 and didn't run tests locally.
Usually I will push to my GitHub PR incrementally as I work on a new
version. I don’t necessarily keep the GitHub PR clean between
submitting versions. I will diff against my latest version tag to see
my own progress, and if I mess it up I can always hard reset to the
latest version tag to start over from there.
Normally, I would never push out a new version unless the GitHub CI
passes. But it’s been broken for a month.
Harald
^ permalink raw reply
* Re: [PATCH] switch: add --ensure option
From: Junio C Hamano @ 2026-06-09 12:59 UTC (permalink / raw)
To: Lei Zhu via GitGitGadget; +Cc: git, Lei Zhu
In-Reply-To: <pull.2324.git.git.1780997009796.gitgitgadget@gmail.com>
"Lei Zhu via GitGitGadget" <gitgitgadget@gmail.com> writes:
> Users who often switch between topic branches may not know whether the
> local branch already exists. Without this option, they need to check
> for the branch first and then choose between `git switch <branch>` and
> `git switch -c <branch>`. The new option folds that workflow into a
> single command.
Quite honestly, I am not sure if the use case should be supported
with a new option, or we should actively discourage it by rejecting
any patch that takes us in this direction, as the actions the user
would take after seeing the result of "git checkout -b" or "git
switch -c" are quite different among (just off the top of my head):
(1) Ah we already had the branch created exactly to work on this;
instead of forking a new effort, switch to the existing branch
and build on the effort we made previously, as it forks from an
acceptable base, which might have been different from where we
wanted to start at when we said "git switch -c <branch> <base>".
(2) Ah we already had the branch created exactly to work on this.
Unfortunately, it was forked from way too new base before we
realized that this is also an important bugfix that needs to be
mergeable to the maintenance track. Let's create a new branch
that is a copy of the existing one with "-maint" in its name,
rebase it on the maintenance track, and work there.
(3) Ah we already had a branch that happened to have the same name,
but created for totally different reasons. We do want to fork
a new branch but need to give it a different name.
(4) There wasn't a branch with the given name, so we created a new
branch at the right starting point we just picked when we ran
"git checkout -b"/"git switch -c". Let's start working on the
topic.
You cover *only* case (4) perfectly. When your "-c <branch>" picks
an existing branch, the user still needs to figure out which among
situations (1)-(3) (of course, there may be others) they are in, and
act accordingly. "git checkout -b" and "git switch -c" that fails,
reminding that there is an existing branch with the same name, gives
users a stronger form of reminder than switching blindly to the
existing branch, which may (in case (1)) or may not (in cases (2)
and (3)) be where the user wants to be when taking the next action.
Having said that.
* The option name "-e" would make all users expect that this has
something to do with "--editor". Start with a longer name,
perhaps "--create-if-missing" or something, and then see if
others can come up with a better short-hand. Obviously whoever
chooses "-e" is not equipped well to do so (yet), and the
reviewer who pointed out "-e" is not a good idea without being
able to offer a better alternative is not, either ;-).
* Adding a new flag only to "switch" without "checkout" will
unnecessary confuse users. This is because, even though
"switch/restore" started as an experiment to _supersede_
"checkout", they were not successful, not in the sense that
"switch/restore" were harder to use than the original, but in the
sense that the userbase and teaching materials are already used
to the original and removing it is practically infeasible.
^ permalink raw reply
* Re: [PATCH 0/3] config: allow disabling config includes
From: Derrick Stolee @ 2026-06-09 12:59 UTC (permalink / raw)
To: Jeff King, Derrick Stolee via GitGitGadget; +Cc: git, gitster
In-Reply-To: <20260608225149.GB340696@coredump.intra.peff.net>
On 6/8/2026 6:51 PM, Jeff King wrote:
> On Mon, Jun 08, 2026 at 01:57:03PM +0000, Derrick Stolee via GitGitGadget wrote:
>
>> This series introduces a new way to ignore config include directives via two
>> mechanisms:
>>
>> * GIT_CONFIG_INCLUDES=0 in the environment.
>> * git --no-includes ... in the command line.
>>
>> My motivation is from a tricky situation where users want to do the risky
>> thing and include a repo-tracked file for sharing aliases and other
>> recommended config. They are then struggling in a later build step that is
>> running Git commands (under a tool we don't control and can't change) that
>> then cause filesystem accesses outside of the build system's sandbox.
>
> I'm not opposed to global control of includes, but this is just one way
> in which config can escape the sandbox. They can always point to files
> (e.g., core.attributesFile) or even commands that totally leave the
> sandbox (e.g., ext diff or textconv commands). Fundamentally git config
> is equivalent to arbitrary code execution, so pointing an include at a
> repo-tracked file carries the risk of confusion both malicious and
> accidental.
>
> So I dunno. From the described motivation, this feels like a band-aid
> that fixes only one narrow instance of a greater problem.
>
> The notion of enabling/disabling includes per-command is itself a
> flexible building block. So it's possible that it has other uses in
> general. But it's also a fairly broad hammer that covers more than your
> use case. If you're planning to use "git --no-includes" in some script,
> then it breaks the config of anybody who uses includes in their
> user-level ~/.gitconfig file.
>
> So you may need some more directed limiting.
Are you suggesting some kind of internal sandbox to limit Git from
accessing repository paths from config includes and other config-set keys?
That would be a more complete solution, but I'm not sure how we could plug
all of those holes at once. I'll think on it, though.
>> One thing I do worry about is whether or not this would cause a significant
>> break in behavior, or if this is a relatively safe thing to allow.
>
> Yeah. Consider something like:
>
> $ cat ~/.gitconfig
> [user]
> name = My Name
> email = me@personal.example.com
> [includeIf "gitdir:/path/to/work/stuff"]
> path = .gitconfig-work
>
> $ cat ~/.gitconfig-work
> [user]
> email = me@work.example.com
>
> Using "git --no-include" will silently use the wrong user.email value.
> That's OK if the user is asking for it, but if you are planning to
> sprinkle "--no-include" inside scripts, that's likely to cause
> confusion.
This is exactly the kind of case I was worried about. This specific case
only impacts write operations, but some tools do those things. And this
email case is a common one that users do in their global config to isolate
personal and professional identities.
I'm trying to think if there's a place where we'd have some config that is
critical to the repo functioning not in its local config (like the repo
format version or extensions). Perhaps borrowing from your work/personal
example, a user could use a different credential helper for work than they
use for personal repositories.
Perhaps we need to be very careful to warn users of this option or
environment variable that behavior can change from typical use.
Or: are we venturing into territory where we don't even want to create a
new foot-gun? If there were another way to solve the situation that I'm
facing without these risks, then I'd be open to it. Any ideas?
Thanks,
-Stolee
^ permalink raw reply
* Re: [PATCH v13 2/6] branch: let delete_branches warn instead of error on bulk refusal
From: Junio C Hamano @ 2026-06-09 12:38 UTC (permalink / raw)
To: Harald Nordgren
Cc: Harald Nordgren via GitGitGadget, git, Kristoffer Haugsbakk,
Johannes Sixt, Phillip Wood
In-Reply-To: <CAHwyqnWpkF-8czt8+G4GJpMTb1qXG6FtN1HKrT5H+OcfAjQL=Q@mail.gmail.com>
Harald Nordgren <haraldnordgren@gmail.com> writes:
> The GitHub CI has been broken for some time, maybe I should have told
> you about this earlier, but it coincided with a period where other
> open source projects I worked on also had mass CI failures, so I
> chalked it up to upstream issues (GitHub, Linux, etc). But it seems to
> have not gone away.
>
> All of my GitHub pull requests have broken tests (see e.g. which a
> quite minimal change: https://github.com/git/git/pull/2313). This
> makes it harder to detect actual issues. But of course it's not an
> excuse.
FWIW, the breakage was observed in my local testing, and that is why
I found it so dusturbing. Apparently you didn't see such breakages
that can be detected so easily during your local testing (otherwise
you wouldn't have pushed it out to update your GitHub pull request),
which may mean something in the test are platform dependent?
^ permalink raw reply
* Re: [PATCH v4 0/8] Auto-configure advertised remotes via URL allowlist
From: Toon Claes @ 2026-06-09 12:25 UTC (permalink / raw)
To: Christian Couder
Cc: git, Junio C Hamano, Patrick Steinhardt, Taylor Blau,
Karthik Nayak, Elijah Newren, Kristoffer Haugsbakk
In-Reply-To: <CAP8UFD0r96KxU3kW2khJ_MySgtv0ZpU26KR1vNimp_FwigQfXA@mail.gmail.com>
Christian Couder <christian.couder@gmail.com> writes:
>> But I previously mentioned I felt the naming of 'acceptFromServer' and
>> 'acceptFromServerUrl' are a bit confusing. So I'm wondering whether we
>> can consider another proposal:
>>
>> What if 'acceptFromServer' would configure if 'acceptFromServerUrl'
>> should be used? I mean, imagine we put this in the config:
>>
>> [promisor]
>> acceptFromServer = Match
>> acceptFromServerUrl = https://my-org.com/*
>>
>> (we can still argue over naming, but to get the idea)
>>
>> So the value "Match" for 'acceptFromServer' would inform Git to use
>> 'acceptFromServerUrl'. This way precedence isn't a concern no more,
>> because every value for 'acceptFromServer' is mutually exclusive.
>
> In this case I would prefer to remove 'acceptFromServerUrl' entirely
> and to make acceptFromServer accept values like:
>
> match:https://my-org.com/*
>
> By the way "match" might not be the best term. Maybe something like
> "auto-configure" would be better.
I think that's too complicated. Let's not do that.
>> This has one downside though, you can no longer combine
>> acceptFromServer=KnownUrl with a 'acceptFromServerUrl'. So URLs
>> advertised by the server can no longer fall-through to
>> 'acceptFromServer' if they don't match 'acceptFromServerUrl'. You can
>> argue whether that's a good thing or not.
>
> I think it's a good thing to have this fall-through. It allows setting
> up things like this:
>
> In the global config:
>
> [promisor]
> acceptFromServerUrl = https://my-org.com/*
>
> In the config of only a few repo that need it:
>
> [promisor]
> acceptFromServer = knownUrl
>
> This way remotes from my-org.com are accepted in all the repos, while
> other remotes are accepted only if their name and URLs have already
> been configured in the repos that need them.
>
> This allows relatively lenient security for internal repos and more
> strict security for external ones, and I suspect that many users will
> want something like that.
>
> What you suggest doesn't allow that. It could force users to choose
> for each repo between either URL based allowlist or local
> configuration of every remote.
Well yes, that's why I mentioned:
> You can argue whether that's a good thing or not.
If it's intentional and as you mention there's a valid use-case for
this, then I agree with your approach in this series.
> Also I think it's easier to explain that 'acceptFromServerUrl' is a
> different mechanism (that allows auto-configuration, contrary to
> 'acceptFromServer') if these two variables are independent.
True, although naming-wise it doesn't feel like that. But I no longer
gonna keep picking on that, so ignore this comment please. :-)
>> What do you think? If you disagree, I'm fine with the current approach
>> and I think this version looks good.
>
> Thanks for your review and for being fine with the current approach if
> I disagree.
Thanks for explaining, I still agree moving on like this.
--
Cheers,
Toon
^ permalink raw reply
* Re: [PATCH v3 0/3] Documentation: recommend the use of b4
From: Toon Claes @ 2026-06-09 12:04 UTC (permalink / raw)
To: Patrick Steinhardt, git
Cc: Junio C Hamano, Tuomas Ahola, Weijie Yuan, Ramsay Jones,
SZEDER Gábor, Kristoffer Haugsbakk
In-Reply-To: <20260608-pks-b4-v3-0-f5e497d10c56@pks.im>
Patrick Steinhardt <ps@pks.im> writes:
> Hi,
>
> this small patch series wires up b4 in Git and recommends the use
> thereof via "MyFirstContribution", as discussed in [1].
>
> Changes in v3:
> - I wasn't really able to judge consensus one way or the other
> regarding the deep vs shallow nesting of cover letters, so I still
> have the change to shallow nesting of cover letters part of this
> series. If we continue to be split on this one (or if we favor the
> current status quo) I'm happy to drop the first patch and adapt the
> last patch to use deep nesting of cover letters instead.
Personally I don't care too much. I'm used to the shallow threading, so
I'm fine with that.
Now on the other hand, looking at a few examples I see GitGitGadget does
deep nesting. Wouldn't it make sense to be consistent?
Anyhow, I don't think it's worth it to keep bike shedding about this. In
all methods we recommend to Cc people, I think that's more important
then caring about how messages are threaded (for example, I've noticed
LKML doesn't thread at all, i.e. `b4.send-same-thread=no` which is the
default).
Bottom line, for me this series is good to go in.
--
Cheers,
Toon
^ permalink raw reply
* Re: [PATCH v2] describe: limit default ref iteration to tags
From: Jeff King @ 2026-06-09 11:09 UTC (permalink / raw)
To: Tamir Duberstein; +Cc: git, Junio C Hamano, Patrick Steinhardt
In-Reply-To: <20260608-describe-tag-ref-scope-v2-1-256fd36dca32@gmail.com>
On Mon, Jun 08, 2026 at 07:32:14PM -0700, Tamir Duberstein wrote:
> The benchmark checkout had 120,532 refs, of which 330 were tags. With
> `$repo` naming the checkout, `$commit` an exactly tagged commit, and
> `$parent` and `$this` the two binaries, I ran:
>
> hyperfine --warmup 3 --runs 15 \
> --command-name parent \
> '$parent -C $repo describe --exact-match $commit' \
> --command-name 'this commit' \
> '$this -C $repo describe --exact-match $commit'
>
> The results were:
>
> Benchmark 1: parent
> Time (mean ± σ): 171.7 ms ± 18.5 ms [User: 23.9 ms, System: 133.6 ms]
> Range (min … max): 142.3 ms … 198.3 ms 15 runs
>
> Benchmark 2: this commit
> Time (mean ± σ): 9.9 ms ± 1.1 ms [User: 3.3 ms, System: 4.7 ms]
> Range (min … max): 8.8 ms … 13.1 ms 15 runs
>
> Summary
> this commit ran
> 17.35 ± 2.63 times faster than parent
>
> Both revisions were built with -O3, -mcpu=native, and ThinLTO using
> Apple clang 21.0.0 on macOS 26.5. The machine was a MacBook Pro
> (Mac16,6) with a 16-core Apple M4 Max (12 performance and four
> efficiency cores) and 128 GB RAM.
This patch looks fine to me, but let me pick a nit for a minute, because
I think there is a broader conversation to be had.
Given the discussion in earlier rounds and sibling topics, I assume the
commit message here was AI-generated. And it's OK in the sense that it
is describing what happened and I assume is entirely accurate. But as a
human reader, it feels so much more verbose than what I'd expect, as it
is full of semi-irrelevant details. Why set --warmup and --runs? Why
bother with --command-name, which just means you have to show the
commands separately anyway? Is the amount of RAM in the machine
important for this test? Surely it could be if it was absurdly tiny, but
in general, no, I would not expect it to be.
So while it is perhaps reasonable to document every detail in case
somebody later wants to verify or reproduce timings, it is a little
overwhelming when trying to tell a story, the core of which is:
In a repo with ~120k refs, ~300 of which were tags, running:
git describe --exact-match $some_tag
went from ~170ms to ~10ms, since we no longer needed to iterate all of
those other refs.
That has _way_ less detail, but makes the point succinctly.
I dunno. I am not trying to pick apart your commit in particular, but am
more interested in the broader use of AI commit messages going forward.
This kind of verbosity is quite common in the output (from my limited
experience), and I think creates more work for reviewers. Should we be
expecting contributors to make things more concise before submitting
(either manually or through prompting)? Or do people even agree that the
shorter version is preferable? I could be the only one.
I have a few other comments on the patch itself below.
> diff --git a/builtin/describe.c b/builtin/describe.c
> index 1c47d7c0b7..3532c8ff22 100644
> --- a/builtin/describe.c
> +++ b/builtin/describe.c
> @@ -740,6 +740,9 @@ int cmd_describe(int argc,
> return ret;
> }
>
> + if (!all)
> + for_each_ref_opts.prefix = "refs/tags/";
> +
> hashmap_init(&names, commit_name_neq, NULL, 0);
> refs_for_each_ref_ext(get_main_ref_store(the_repository),
> get_name, NULL, &for_each_ref_opts);
The code change looks fine. It creates a bit of a subtle dependency
between what's happening here, and the filtering inside get_name(). But
I think that's OK for the scope of a single command. It _might_ be
possible to simplify the top of get_name(), since we'd no longer see
non-tag refs in the input. But it also may not, since we have to strip
out the prefix anyway. It can certainly come on top as a cleanup later
if we want.
> diff --git a/t/perf/p6100-describe.sh b/t/perf/p6100-describe.sh
It is a little curious that we add a perf test here, but the commit
message does not even show it off. ;)
I ran it myself here and had trouble showing improvement, simply because
it is already quite fast! I guess that's because I'm on Linux, where
warm-cache filesystem operations are pretty fast. Bumping $ref_count by
a factor of 10 made the "before" case 30ms, and after is still sub-1ms.
> +test_expect_success 'set up many unrelated refs' '
> + ref_count=10000 &&
> + git tag -m tip tip HEAD &&
> + for i in $(test_seq $ref_count)
> + do
> + printf "create refs/heads/describe-perf/%05d HEAD\n" $i ||
> + return 1
> + done >instructions &&
> + git update-ref --stdin <instructions
> +'
A few things come to mind on reading this.
I have mixed feelings on sticking synthetic constructions in the t/perf
suite. Part of the original point was that we'd run it against real
repos to see how they perform. But that implies that people running it
have some clue about which tests may be interesting on which repos,
which is hopeful at best. So we've turned to this kind of synthetic
construction at times (and this is certainly not the first). It's
probably a reasonable tactic here.
I suspect the resulting state is not all that realistic, though. If you
have 10,000 refs, you probably didn't make them all at once. And so in
practice the majority of them would be packed. Sticking "git pack-refs
--all" at the end might give more realistic numbers.
Bumping to a larger number of refs shows the effect more clearly, but at
the cost of making the setup take a long time (since we have to take a
lockfile on each ref!). We could sneak around it by generating a
packed-refs file directly, but now the test really would be
backend-specific. Probably better not to go there.
And finally, the loop can be written a bit more succinctly these days
as:
diff --git a/t/perf/p6100-describe.sh b/t/perf/p6100-describe.sh
index ed9f1abe18..b365dc67ee 100755
--- a/t/perf/p6100-describe.sh
+++ b/t/perf/p6100-describe.sh
@@ -30,12 +30,8 @@ test_perf 'describe HEAD with one tag' '
test_expect_success 'set up many unrelated refs' '
ref_count=10000 &&
git tag -m tip tip HEAD &&
- for i in $(test_seq $ref_count)
- do
- printf "create refs/heads/describe-perf/%05d HEAD\n" $i ||
- return 1
- done >instructions &&
- git update-ref --stdin <instructions
+ test_seq -f "create refs/heads/describe-perf/%05d HEAD" $ref_count |
+ git update-ref --stdin
'
test_perf 'describe exact tag with many unrelated refs' '
Probably not worth re-rolling on its own, though.
-Peff
^ permalink raw reply related
* Re: [PATCH v1 0/1] environment: move protect_hfs and protect_ntfs
From: Christian Couder @ 2026-06-09 10:54 UTC (permalink / raw)
To: Tian Yuchen
Cc: git, christian, phillip.wood123, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260606143412.15443-2-cat@malon.dev>
On Sat, Jun 6, 2026 at 4:35 PM Tian Yuchen <cat@malon.dev> wrote:
>
> Hi everyone,
>
> This series continues the ongoing libification effort by moving the
> global **filesystem** variables, protect_hfs and protect_ntfs, into
> struct repo_config_values.
>
> Place them within the **per-repository** configuration structure
> aligns with our goal of removing global states.
>
> RFC Questions:
>
> 1. Should we keep PROTECT_HFS_DEFAULT and PROTECT_NTFS_DEFAULT
> in repo_config_values_init()?
>
> void repo_config_values_init(struct repo_config_values *cfg)
> {
> cfg->attributes_file = NULL;
> cfg->apply_sparse_checkout = 0;
> cfg->protect_hfs = PROTECT_HFS_DEFAULT;
> cfg->protect_ntfs = PROTECT_NTFS_DEFAULT;
> cfg->branch_track = BRANCH_TRACK_REMOTE;
> }
>
> Or is it better if they are used anywhere other than in environment.c?
I think it's better to keep them in "environment.c". The
repo_protect_ntfs() and repo_protect_hfs() function I suggest adding
to "environment.c" in my reply to the patch should help with keeping
the macros in "environment.c".
> If so...
> 2. Is it worth introducing a Macro or Getter for safe access?
>
> ((the_repository->gitdir ? repo_config_values(the_repository)->protect_hfs : 0))
Yes, it seems to me that a getter is enough.
> The current approach looks verbose and lacks readability, and
> hard-coded 0 and 1 are used as fallback values. I wonder if a macro or a
> getter could be introduced, for example...
>
> #define SAFE_PROTECT_HFS(repo) \
> (((repo) && (repo)->gitdir && (repo) == the_repository) ? \
> repo_config_values(repo)->protect_hfs : PROTECT_HFS_DEFAULT)
>
> ...to improve the coding style a bit. Although I am aware that introducing
> new macros is generally frowned upon, I would still like to know which
> parts this might make difficult to maintain.
Unless there are features that we really want which a function can't
provide, a function is better as it provides type safety and is
usually easier to maintain.
> 3. Note that Derrick attempted to use get_int_config_global to wrap
> this kind of Filesystem Level global variables. This approach bypassed
> struct repository, did not actually eliminate global state, and the
> reviewer politely rejected it. Nevertheless, I am still curious as
> to whether this approach might still be inspiring today.
>
> https://lore.kernel.org/git/a42dd9397d07b2dc4a0d7e75bfe1af2e46cad262.1685716420.git.gitgitgadget@gmail.com/
To help your reviewers, it might be interesting if you could already
tell why this was rejected by Glen Choo (who reviewed Derrick Stolee's
work then). It seems to me that Glen said that using plain fields in a
struct should be better as long as the fields are always initialized
during the setup process.
And it seems to me that our patch follows the direction that Glen suggested.
Thanks.
^ permalink raw reply
* Re: [PATCH 04/16] odb/source-packed: start converting to a proper `struct odb_source`
From: Patrick Steinhardt @ 2026-06-09 10:47 UTC (permalink / raw)
To: Karthik Nayak; +Cc: git
In-Reply-To: <aifAVpxanV31KUpC@pks.im>
On Tue, Jun 09, 2026 at 09:27:25AM +0200, Patrick Steinhardt wrote:
> On Mon, Jun 08, 2026 at 08:29:04AM -0700, Karthik Nayak wrote:
> > Patrick Steinhardt <ps@pks.im> writes:
> > > diff --git a/odb/source-packed.c b/odb/source-packed.c
> > > index 12e785be48..f81a990cbd 100644
> > > --- a/odb/source-packed.c
> > > +++ b/odb/source-packed.c
> > > + CALLOC_ARRAY(packed, 1);
> > > + odb_source_init(&packed->base, parent->base.odb, ODB_SOURCE_PACKED,
> > > + parent->base.path, parent->base.local);
> > > + packed->files = parent;
> > > + strmap_init(&packed->packs_by_path);
> > > +
> > > + packed->base.free = odb_source_packed_free;
> > > +
> > > + if (!is_absolute_path(parent->base.path))
> > > + chdir_notify_register(NULL, odb_source_packed_reparent, packed);
> > > +
> >
> > Tangent: seems like no one sets the 'name' field within
> > `chdir_notify_register()`. It is meant for tracing purposes, but if no
> > one is using it, we might as well remove it...? Perhaps #leftoverbits
>
> There are some callers: `chdir_notify_reparent()` calls
> `chdir_notify_register()` with a name, and the reference backends call
> that function with names.
>
> Ultimately though I think that this infrastructure is somewhat misguided
> overall: we use this to update relative paths after chdir(3p), but if we
> stored absolute paths in the first place then we wouldn't have to care
> about the paths changing at all.
>
> I plan to revisit this infra for the object database going forward: we
> expose and use `struct odb_source::path` in various other subsystems,
> including user-facing ones. This is inherently wrong though, as there
> may be sources that don't even have an on-disk path. So there's a need
> to drop that field and make it an internal implementation detail of the
> source's backend. And once we've done that, we can just as well start to
> store absolute paths.
>
> For the reference backends we can already do that refactoring now-ish.
> I'll send a patch series later today.
Well, as ever so often I went down the rabbit hole and discovered way
more than I wanted:
- We never free the `struct repository::refs_private` field.
- We create the refdb multiple times in case we have "onbranch"
conditionals and never free them.
- The chicken-and-egg problem we have with "onbranch" and refdb
creation is awkward.
So I'm now sitting at 11 patches to fix all of those bugs, and I'll also
have to make changes to "setup.c" to fix them. So I'll first have to
wait for ps/setup-centralize-odb-creation to land.
Patrick
^ permalink raw reply
* [PATCH RFC v2 2/2] builtin/history: abort reword on same message
From: Pablo Sabater @ 2026-06-09 10:42 UTC (permalink / raw)
To: git; +Cc: cat, ps, kaartic.sivaraam, pabloosabaterr, ben.knoble, gitster
In-Reply-To: <20260609-ps-history-reword-v2-0-a0e6028ca9b4@gmail.com>
When using `git history reword <commit>` if the new message is the same
as the original, it continues and rewrites the history when nothing
changed.
`git commit --amend` and `git rebase -i` with reword share this behavior
and it is wrong as well, but changing them breaks what people are used
to. Take the opportunity of `git history` being a new command and handle
it correctly from the start.
Create COMMIT_TREE_ABORT_ON_SAME_MESSAGE and make a check for if the
messages are the same and the flag is set so other subcommands like
fixup that do not want this behavior just don't send the abort flag.
Make commit_tree_ext() return 1 when facing the same message so its
callers can choose what to do.
Signed-off-by: Pablo Sabater <pabloosabaterr@gmail.com>
---
builtin/history.c | 14 +++++++++++++-
t/t3451-history-reword.sh | 16 ++++++++++++++++
t/t3453-history-fixup.sh | 22 ++++++++++++++++++++++
3 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/builtin/history.c b/builtin/history.c
index b3e2e5270d..be07690da4 100644
--- a/builtin/history.c
+++ b/builtin/history.c
@@ -96,6 +96,7 @@ static int fill_commit_message(struct repository *repo,
enum commit_tree_flags {
COMMIT_TREE_EDIT_MESSAGE = (1 << 0),
+ COMMIT_TREE_ABORT_ON_SAME_MESSAGE = (1 << 1),
};
static int commit_tree_ext(struct repository *repo,
@@ -135,6 +136,13 @@ static int commit_tree_ext(struct repository *repo,
original_body, action, &commit_message);
if (ret < 0)
goto out;
+
+ if (flags & COMMIT_TREE_ABORT_ON_SAME_MESSAGE &&
+ !strcmp(original_body, commit_message.buf)) {
+ fprintf(stderr, _("Message unchanged, aborting reword.\n"));
+ ret = 1;
+ goto out;
+ }
} else {
strbuf_addstr(&commit_message, original_body);
}
@@ -693,7 +701,8 @@ static int cmd_history_reword(int argc,
struct strbuf reflog_msg = STRBUF_INIT;
struct commit *original, *rewritten;
struct rev_info revs = { 0 };
- enum commit_tree_flags flags = COMMIT_TREE_EDIT_MESSAGE;
+ enum commit_tree_flags flags = COMMIT_TREE_EDIT_MESSAGE |
+ COMMIT_TREE_ABORT_ON_SAME_MESSAGE;
int ret;
argc = parse_options(argc, argv, prefix, options, usage, 0);
@@ -721,6 +730,9 @@ static int cmd_history_reword(int argc,
if (ret < 0) {
ret = error(_("failed writing reworded commit"));
goto out;
+ } else if (ret == 1) {
+ ret = 0;
+ goto out;
}
strbuf_addf(&reflog_msg, "reword: updating %s", argv[0]);
diff --git a/t/t3451-history-reword.sh b/t/t3451-history-reword.sh
index de7b357685..6e0e278c42 100755
--- a/t/t3451-history-reword.sh
+++ b/t/t3451-history-reword.sh
@@ -396,4 +396,20 @@ test_expect_success 'retains changes in the worktree and index' '
)
'
+test_expect_success 'aborts if the commit message is the same' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit first &&
+ test_commit second &&
+
+ git rev-parse HEAD >oid-before &&
+ GIT_EDITOR=true git history reword HEAD 2>err &&
+ git rev-parse HEAD >oid-after &&
+ test_cmp oid-before oid-after &&
+ test_grep "Message unchanged" err
+ )
+'
+
test_done
diff --git a/t/t3453-history-fixup.sh b/t/t3453-history-fixup.sh
index 868298e248..9f9a3c93de 100755
--- a/t/t3453-history-fixup.sh
+++ b/t/t3453-history-fixup.sh
@@ -443,6 +443,28 @@ test_expect_success '--reedit-message opens editor for the commit message' '
)
'
+test_expect_success 'fixup --reedit-message does not abort with the same commit message' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ echo content > file.txt &&
+ git add file.txt &&
+ git commit -m "add file" &&
+
+ echo fix >>file.txt &&
+ git add file.txt &&
+ GIT_EDITOR=true git history fixup --reedit-message HEAD &&
+ expect_changes --branches <<-\EOF
+ add file
+ 2 0 file.txt
+ initial
+ 1 0 initial.t
+ EOF
+ )
+'
+
test_expect_success 'retains unstaged working tree changes after fixup' '
test_when_finished "rm -rf repo" &&
git init repo &&
--
2.54.0
^ permalink raw reply related
* [PATCH RFC v2 1/2] builtin/history: refactor function signature
From: Pablo Sabater @ 2026-06-09 10:42 UTC (permalink / raw)
To: git; +Cc: cat, ps, kaartic.sivaraam, pabloosabaterr, ben.knoble, gitster
In-Reply-To: <20260609-ps-history-reword-v2-0-a0e6028ca9b4@gmail.com>
commit_tree_with_edited_message() calls commit_tree_ext() with the flag
COMMIT_TREE_EDIT_MESSAGE hardcoded and we can't set new flags on callers
like cmd_history_reword() to choose their own flags.
This refactor is needed for a subsequent commit.
Refactor commit_tree_with_edited_message() signature to accept flags
which are passed down to commit_tree_ext() instead of the hardcoded one.
Signed-off-by: Pablo Sabater <pabloosabaterr@gmail.com>
---
builtin/history.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/builtin/history.c b/builtin/history.c
index 0fc06fb204..b3e2e5270d 100644
--- a/builtin/history.c
+++ b/builtin/history.c
@@ -160,7 +160,8 @@ static int commit_tree_ext(struct repository *repo,
static int commit_tree_with_edited_message(struct repository *repo,
const char *action,
struct commit *original,
- struct commit **out)
+ struct commit **out,
+ enum commit_tree_flags flags)
{
struct object_id parent_tree_oid;
const struct object_id *tree_oid;
@@ -181,7 +182,7 @@ static int commit_tree_with_edited_message(struct repository *repo,
}
return commit_tree_ext(repo, action, original, original->parents,
- &parent_tree_oid, tree_oid, out, COMMIT_TREE_EDIT_MESSAGE);
+ &parent_tree_oid, tree_oid, out, flags);
}
enum ref_action {
@@ -692,6 +693,7 @@ static int cmd_history_reword(int argc,
struct strbuf reflog_msg = STRBUF_INIT;
struct commit *original, *rewritten;
struct rev_info revs = { 0 };
+ enum commit_tree_flags flags = COMMIT_TREE_EDIT_MESSAGE;
int ret;
argc = parse_options(argc, argv, prefix, options, usage, 0);
@@ -714,7 +716,8 @@ static int cmd_history_reword(int argc,
if (ret)
goto out;
- ret = commit_tree_with_edited_message(repo, "reworded", original, &rewritten);
+ ret = commit_tree_with_edited_message(repo, "reworded", original,
+ &rewritten, flags);
if (ret < 0) {
ret = error(_("failed writing reworded commit"));
goto out;
--
2.54.0
^ permalink raw reply related
* [PATCH RFC v2 0/2] builtin/history: abort reword on same message
From: Pablo Sabater @ 2026-06-09 10:42 UTC (permalink / raw)
To: git; +Cc: cat, ps, kaartic.sivaraam, pabloosabaterr, ben.knoble, gitster
In-Reply-To: <20260607-ps-history-reword-v1-0-ba43a3cbb81b@gmail.com>
This short series aims to improve the behavior of `git history reword`
to abort when the new commit message is the same as the original,
avoiding unnecessary history rewrites.
`git commit --amend` and `git rebase -i` with reword share this flaw but
changing them faces not just technical challenges but also breaks what
people are used to, so that is not a viable option. Let's take the
opportunity that `git history` is a new command and handle this
correctly from the start.
This is made so any other future subcommand or option that does want
this behavior just has to add the abort flag.
A questions I have is why don't we want this abort behavior on
`git history fixup --reedit-message` it makes more sense on
`git history reword` because if the message is the same then it has
nothing to do while fixup can still have files to update, but
--reedit-message is still a redundant option there.
Signed-off-by: Pablo Sabater <pabloosabaterr@gmail.com>
---
Changes in v2:
- Changed the reason on why is this needed.
- Changed tests with same message to use GIT_EDITOR=true instead of the
script.
- Abort on same message only happens when its own flag is set so no
other subcommand that does not want this behavior and depend on
commit_tree_ext() is affected.
- Dropped the feedback on successful reword for another series.
---
Pablo Sabater (2):
builtin/history: refactor function signature
builtin/history: abort reword on same message
builtin/history.c | 21 ++++++++++++++++++---
t/t3451-history-reword.sh | 16 ++++++++++++++++
t/t3453-history-fixup.sh | 22 ++++++++++++++++++++++
3 files changed, 56 insertions(+), 3 deletions(-)
---
base-commit: 9ac3f193c05c2237e2b14ebaa1149e9fc8a1abe0
change-id: 20260607-ps-history-reword-fcb70eaa4aa9
Best regards,
--
Pablo Sabater <pabloosabaterr@gmail.com>
^ permalink raw reply
* Re: [PATCH v2] ls-files: filter pathspec before lstat
From: Jeff King @ 2026-06-09 10:41 UTC (permalink / raw)
To: Tamir Duberstein
Cc: git, René Scharfe, Patrick Steinhardt, Junio C Hamano
In-Reply-To: <20260608-ls-files-pathspec-lstat-v2-1-fb734b28422e@gmail.com>
On Mon, Jun 08, 2026 at 07:37:15PM -0700, Tamir Duberstein wrote:
> + /*
> + * match_pathspec() is linear in pathspec.nr, so prefilter only
> + * the single-pathspec case. Only entries shown by show_ce()
> + * satisfy --error-unmatch.
> + */
> + if (pathspec.nr == 1 &&
> + !match_pathspec(repo->index, &pathspec, fullname.buf,
> + fullname.len, max_prefix_len, NULL,
> + S_ISDIR(ce->ce_mode) ||
> + S_ISGITLINK(ce->ce_mode)))
> + continue;
This feels...kind of arbitrary, no? Surely it's also faster with
pathspec.nr == 2, and so on up to some nr closer to the size of the
total index. It feels weird to be making an arbitrary cutoff based on
pathspec performance in calling code like this.
It is not wrong, per se, as you are optimizing your case without trying
to hurt any others. But what do we do when somebody profiles it and
comes along trying to bump the number to 2, or 10?
I dunno.
-Peff
^ permalink raw reply
* Re: [PATCH RFC 1/2] builtin/history: abort reword on unchanged message
From: Kristoffer Haugsbakk @ 2026-06-09 10:30 UTC (permalink / raw)
To: Pablo Sabater, Junio C Hamano; +Cc: git, Patrick Steinhardt, Kaartic Sivaraam
In-Reply-To: <CAN5EUNRW3gyLKGC7x5BBMTNKtunoQks9AaXJse4PHvCziRF87A@mail.gmail.com>
On Tue, Jun 9, 2026, at 12:14, Pablo Sabater wrote:
> El lun, 8 jun 2026 a las 14:16, Junio C Hamano (<gitster@pobox.com>) escribió:
>>
> [snip]
>>
>> `git rebase -i` may have an excuse that because it, unlike "git
>> commit --amend", operates on multiple commits by design. A single
>> "--force" option given to the command would not have worked as an
>> escape hatch to allow the user to tell the command "in this reword
>> of this particular commit, I ended up doing nothing, but I still
>> want an updated committer log timestamp". Perhaps giving the
>> "--force" (or --force-rewrite") option at "rebase --continue" time
>> may work, but in any case, unless we plan to transition to these
>> "better" default behaviour at a big version boundary, speculating
>> what a "better" behaviour would have been may be fun but not very
>> productive.
>>
>>
>> [Footnote]
>>
>> *1* Besides, doesn't "--update-refs" in "rebase -i" allow you to
>> adjust the branches?
>>
>> *2* But it is an established behaviour people _rely_ on, so even
>> though it may have been better if these commands behaved
>> differently, it probably is a bit too late to change it now.
>>
>> *3* This includes the case where the original author is especially
>> difficult to work with and would complain any change to their
>> commits, even if the only change you made for them is a
>> typofix. Fixing a small typo/grammo may not be worth your time
>> and unpleasant exchanges with them after touching their commit.
>
>[snip]
>
> About the --force sounds good to me. I could seek to implement it in
> this series if it's ok.
When starting without historical baggage anyway, I have doubts about the
`--force` name in general. This often just begs me to ask what it is
forcing. Why not name the thing that is being forced? Verbosity
shouldn’t be a problem for a “force” option. So `--force-rewrite` if you
are forcing new commits to be created (like already mentioned).
See git-clean(1) which has two levels of `--force`.
> The footnote 3 is indeed a good example haha, but yeah, why rewrite
> the history unnecessarily.
^ permalink raw reply
* Re: [PATCH v1 1/1] environment.c: move 'protect_hfs' and 'protect_ntfs' into 'repo_config_values'
From: Christian Couder @ 2026-06-09 10:20 UTC (permalink / raw)
To: Tian Yuchen
Cc: git, christian, phillip.wood123, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260606143412.15443-1-cat@malon.dev>
"environment.c:" in the subject could be replaced by just
"environment:". It's a bit shorter and it better describes the area of
the code where the main changes are made, as changes are not just made
in "environment.c" but also in "environment.h".
On Sat, Jun 6, 2026 at 4:34 PM Tian Yuchen <cat@malon.dev> wrote:
>
> Move the global 'protect_hfs' and 'protect_ntfs' configurations
> into the repository-specific 'repo_config_values' struct.
> This will help with the elimination of 'the_repository'
>
> For now, associated functions access this configuration by
> explicitly falling back to 'the_repository', which needs to
> be addressed in the future.
>
> Note: In 't/helper/test-path-utils.c', there is a function
> 'protect_ntfs_hfs_benchmark()' where these two global
> variables are used as loop iterators. New local variables
> have been created to replace them.
> diff --git a/compat/mingw.c b/compat/mingw.c
> index aa7525f419..c77696ba8a 100644
> --- a/compat/mingw.c
> +++ b/compat/mingw.c
> @@ -3392,7 +3392,7 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
> const char *p = path;
> int preceding_space_or_period = 0, i = 0, periods = 0;
>
> - if (!protect_ntfs)
> + if (!(the_repository->gitdir ? repo_config_values(the_repository)->protect_ntfs : 1))
> return 1;
I think the code would benefit from functions like:
int repo_protect_ntfs(struct repository *repo)
{
return repo->gitdir ?
repo_config_values(repo)->protect_ntfs :
PROTECT_NTFS_DEFAULT;
}
int repo_protect_hfs(struct repository *repo)
{
return repo->gitdir ?
repo_config_values(repo)->protect_hfs :
PROTECT_HFS_DEFAULT;
}
They could be called by passing `the_repository` for now, but perhaps
later in future commits `istate->repo` or something like that could be
passed instead. Also a code comment could explain that the `gitdir`
check prevents calling `repo_config_values()` before config is loaded.
Thanks.
^ permalink raw reply
* Re: [PATCH RFC 1/2] builtin/history: abort reword on unchanged message
From: Pablo Sabater @ 2026-06-09 10:14 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Patrick Steinhardt, Kaartic Sivaraam
In-Reply-To: <xmqqmrx5z0po.fsf@gitster.g>
El lun, 8 jun 2026 a las 14:16, Junio C Hamano (<gitster@pobox.com>) escribió:
>
[snip]
>
> `git rebase -i` may have an excuse that because it, unlike "git
> commit --amend", operates on multiple commits by design. A single
> "--force" option given to the command would not have worked as an
> escape hatch to allow the user to tell the command "in this reword
> of this particular commit, I ended up doing nothing, but I still
> want an updated committer log timestamp". Perhaps giving the
> "--force" (or --force-rewrite") option at "rebase --continue" time
> may work, but in any case, unless we plan to transition to these
> "better" default behaviour at a big version boundary, speculating
> what a "better" behaviour would have been may be fun but not very
> productive.
>
>
> [Footnote]
>
> *1* Besides, doesn't "--update-refs" in "rebase -i" allow you to
> adjust the branches?
>
> *2* But it is an established behaviour people _rely_ on, so even
> though it may have been better if these commands behaved
> differently, it probably is a bit too late to change it now.
>
> *3* This includes the case where the original author is especially
> difficult to work with and would complain any change to their
> commits, even if the only change you made for them is a
> typofix. Fixing a small typo/grammo may not be worth your time
> and unpleasant exchanges with them after touching their commit.
True, after reading it, history being more costly or the in memory are
not good args.
I do agree that these commands that do reword should check if the
reword ends up being the same message, given that history is a new
command we can have it from the start so users do not really expect
other behavior.
About the --force sounds good to me. I could seek to implement it in
this series if it's ok.
The footnote 3 is indeed a good example haha, but yeah, why rewrite
the history unnecessarily.
Thanks,
Pablo
^ 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