* Draft of Git Rev News edition 135
From: Christian Couder @ 2026-05-31 11:01 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jakub Narebski, Markus Jansen, Kaartic Sivaraam,
Štěpán Němec, Taylor Blau,
Johannes Schindelin, Derrick Stolee, Elijah Newren, Jeff King,
Matthias A.
Hi everyone,
A draft of a new Git Rev News edition is available here:
https://github.com/git/git.github.io/blob/master/rev_news/drafts/edition-135.md
Everyone is welcome to contribute in any section either by editing the
above page on GitHub and sending a pull request, or by commenting on
this GitHub issue:
https://github.com/git/git.github.io/issues/844
You can also reply to this email.
In general all kinds of contributions, for example proofreading,
suggestions for articles or links, help on the issues in GitHub,
volunteering for being interviewed and so on, are very much
appreciated.
I tried to Cc everyone who appears in this edition, but maybe I missed
some people, sorry about that.
Jakub, Markus, Kaartic and I plan to publish this edition on Tuesday
June 2nd, 2026.
Thanks,
Christian.
^ permalink raw reply
* Re: [PATCH v3 0/6] [RFC] diff: add diff.<driver>.process for external hunk providers
From: Junio C Hamano @ 2026-05-31 10:44 UTC (permalink / raw)
To: Michael Montalbo via GitGitGadget; +Cc: git, Michael Montalbo
In-Reply-To: <pull.2120.v3.git.1780087700.gitgitgadget@gmail.com>
"Michael Montalbo via GitGitGadget" <gitgitgadget@gmail.com> writes:
> Language-aware diff tools (e.g., Difftastic) and format-specific analyzers
> can produce better line matching than Git's builtin diff algorithm, but
> diff.<driver>.command replaces Git's output entirely, losing downstream
> features like word diff, function context, color, and blame.
This seems to break CI on Windows; take a look at
https://github.com/git/git/actions/runs/26709491830/job/78717295153
for an example.
Thanks.
^ permalink raw reply
* Re: doc: document '@' prefix for raw timestamps
From: Luna Schwalbe @ 2026-05-31 8:29 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Kristoffer Haugsbakk, git
In-Reply-To: <xmqqpl2de41m.fsf@gitster.g>
>> This was introduced in 116eb3ab (parse_date(): allow ancient
>> git-timestamp, 2012-02-02) and 2c733fb2 (parse_date(): '@' prefix
>> forces git-timestamp, 2012-02-02) to allow specifying "ancient"
>> timestamps (like 0 +0000) without conflicting with YYYYMMDD date
>> formats. I do not think neither commit added documentation for this
>> '@' prefix, and Documentation/date-formats would be an excellent
>> place to do so.
>>
>> Care to whip up a patch?
> It might look something like this.
Thanks! And sure, I'll try to submit something later; this will be my
first time using the send-mail workflow, I hope I don't mess anything up.
^ permalink raw reply
* Re: [PATCH] doc: add missing --message long option to merge docs
From: Brandon @ 2026-05-31 6:37 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Brandon Dong via GitGitGadget, git
In-Reply-To: <xmqqo6hyiz9g.fsf@gitster.g>
Junio C Hamano <gitster@pobox.com> writes:
> Hmph. This is still not consistent with "git merge -h" output has,
> which seems to accept --[no-]message as well.
>
> It is not exactly your fault, but there are a few options other than
> this one that support optional [no-] and they are not documented as
> such, even though they appear in "git merge -h". "git merge -m foo
> --no-message other" behaves as if "GIT_EDITOR=: git merge other" was
> run, it seems.
Looking at the code, I believe this might be intentional or maybe a
stylistic choice to document this way?
The overwhelming majority of long name flags have a [no-] variant as
it comes for free when defining a new option and otherwise requires
an explicit opt out (via PARSE_OPT_NONEG).
The -h output auto-generates the inclusion of [no-] but for the
handwritten docs, most examples I see where it's included are for
cases where the [no-] variant has some behavior nuance that needs to
be explained or it's for a bool-like flag. Most string-valued options
do not include mention of the [no-] variant and they share the
default behavior where passing the [no-] variant unsets the option.
For -m/--message in particular, none of the
git-commit/git-notes/git-svn/git-tag docs mention the --no variant
either and I think merge should be consistent with them.
^ permalink raw reply
* Re: [PATCH v2 2/2] status: improve rebase todo list parsing
From: Junio C Hamano @ 2026-05-31 0:46 UTC (permalink / raw)
To: Phillip Wood; +Cc: git, Elijah Newren, Patrick Steinhardt
In-Reply-To: <b80bc1e0a298e2773a2fdab3e73651d59b8d39b7.1777648598.git.phillip.wood@dunelm.org.uk>
Phillip Wood <phillip.wood123@gmail.com> writes:
> +static void abbrev_oid_in_line(struct repository *r,
> + struct strbuf *line, char **pp)
> +{
> ...
> + have_oid = !repo_get_oid(r, p, &oid);
> + *end_of_object_name = saved;
> + if (!have_oid)
> + goto out; /* object name was a label */
Can there be a label "deadbeef123" that is unrelated to an object whose
object name happens to abbreviate to "deadbeef123"?
> + case TODO_MERGE:
> + skip_dash_c(&p);
> + while (true) {
> + p += strspn(p, " \t");
> + if (!p[0] || (p[0] == '#' && (!p[1] || isspace(p[1]))))
> + break;
> + abbrev_oid_in_line(r, line, &p);
> + }
> + break;
What does this loop do? A "merge" command may look like "merge
[[-C|-c] <commit>] <label>", and we give each whitespace-separated
token to abbrev_oid_in_line()? Would "<label>" that is ambiguous
cause an issue? You may want to limit the scope of what the loop
does a bit, e.g., massage only the token after -C/-c, or something?
> + case TODO_FIXUP:
> + skip_dash_c(&p);
> + /* fallthrough */
> + case TODO_DROP:
> + case TODO_EDIT:
> + case TODO_PICK:
> + case TODO_RESET:
Doesn't RESET also take a <label>? And if it happens to be the same
as an abbreviated object name, e.g., "deadbeef123", of an unrelated
object, would wt-status say "reset deadbeef1", causing a mismatch?
If this is indeed an issue, would moving this to the "no-op" section
below, next to TODO_LABEL, solve it?
> + case TODO_REVERT:
> + case TODO_REWORD:
> + case TODO_SQUASH:
> + abbrev_oid_in_line(r, line, &p);
> + break;
> +
> + /*
> + * Avoid "default" and instead list all the other commands so
> + * that -Wswitch (which is included in -Wall) warns if a new
> + * command is added without handling it in this function.
> + */
> + case TODO_BREAK:
> + case TODO_EXEC:
> + case TODO_LABEL:
> + case TODO_NOOP:
> + case TODO_UPDATE_REF:
> + break;
> }
> - string_list_clear(&split, 0);
> +
> + return true;
> }
^ permalink raw reply
* Re: [PATCH] describe: fix --exclude, --match with --contains and --all
From: Junio C Hamano @ 2026-05-30 23:47 UTC (permalink / raw)
To: Jacob Keller; +Cc: git, Jacob Keller, Tuomas Ahola
In-Reply-To: <20260528232950.187002-2-jacob.e.keller@intel.com>
Jacob Keller <jacob.e.keller@intel.com> writes:
> From: Jacob Keller <jacob.keller@gmail.com>
>
> git describe --contains acts as a wrapper around git name-rev. When
> operating with --contains and --all, the --match and --exclude patterns
> are not properly forwarded to name-rev as --exclude and --refs options.
>
> This results in the command silently discarding match and exclude
> requests from the user when operating in --all mode.
>
> We could check and die() if the user provides --contains, --all, and
> --match/--exclude. However, its also straight forward to just pass the
> filters down to git name-rev.
>
> Notice that the documentation for --match and --exclude mention the
> --all mode. It explains that they operate on refs with the prefix
> refs/tags, and additionally refs/heads and refs/remotes when using
> --all.
>
> Fix the describe logic to pass the patterns down with the appropriate
> prefixes when --all is provided. This fixes the support to match the
> documented behavior.
>
> Add tests to check that this works as expected.
>
> Reported-by: Tuomas Ahola <taahol@utu.fi>
> Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
> ---
>
> I was looking into reviving the patch that just added a simple die() and
> realized that its actually pretty straight forward to just fix the support
> instead. I'm open to either route, if we think this support isn't
> necessary... I'm not sure if there are any gotchas or other issues with how
> I implemented this.
It is curious that this fails in some but not all CI jobs, and even
more curious that these failures look the same.
e.g., https://github.com/git/git/actions/runs/26671595367/job/78615760984#step:4:1984
+++ diff -u expect actual
--- expect 2026-05-30 02:21:23
+++ actual 2026-05-30 02:21:23
@@ -1 +1 @@
-branch_A
+remotes/origin/remote_branch_A
error: last command exited with $?=1
not ok 70 - describe --contains --all --exclude
#
# echo "branch_A" >expect &&
# tagged_commit=$(git rev-parse "refs/tags/A^0") &&
# git describe --contains --all --exclude="A" --exclude="c" --exclude="test*" $tagged_commit >actual &&
# test_cmp expect actual
Rings any bell?
^ permalink raw reply
* Re: [PATCH v1 3/4] environment: move 'trust_executable_bit' into repo_config_values
From: Junio C Hamano @ 2026-05-30 23:17 UTC (permalink / raw)
To: Tian Yuchen
Cc: git, christian.couder, ps, Ayush Chandekar, Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-4-cat@malon.dev>
Tian Yuchen <cat@malon.dev> writes:
> diff --git a/apply.c b/apply.c
> index 249248d4f2..73ca9907f8 100644
> --- a/apply.c
> +++ b/apply.c
> @@ -3890,10 +3890,12 @@ static int check_preimage(struct apply_state *state,
> }
>
> if (!state->cached && !previous) {
> + struct repo_config_values *cfg = repo_config_values(the_repository);
> +
> if (*ce && !(*ce)->ce_mode)
> BUG("ce_mode == 0 for path '%s'", old_name);
>
> - if (trust_executable_bit || !S_ISREG(st->st_mode))
> + if (cfg->trust_executable_bit || !S_ISREG(st->st_mode))
> st_mode = ce_mode_from_stat(*ce, st->st_mode);
> else if (*ce)
> st_mode = (*ce)->ce_mode;
> diff --git a/read-cache.c b/read-cache.c
> index 54150fe756..18af533649 100644
> --- a/read-cache.c
> +++ b/read-cache.c
> @@ -204,10 +204,12 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
>
> unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
> {
> + struct repo_config_values *cfg = repo_config_values(the_repository);
> +
> if (!has_symlinks && S_ISREG(mode) &&
> ce && S_ISLNK(ce->ce_mode))
> return ce->ce_mode;
> - if (!trust_executable_bit && S_ISREG(mode)) {
> + if (!cfg->trust_executable_bit && S_ISREG(mode)) {
> if (ce && S_ISREG(ce->ce_mode))
> return ce->ce_mode;
> return create_ce_mode(0666);
How hot are the code paths that call into this helper function? In
the original under some condition, it was possible to return without
even consulting the trust_executable_bit variable, but in the
updated code, the helper unconditionally makes a call to the
repo_config_values() helper function even before it knows it needs
to know the value of trust_executable_bit.
> @@ -217,11 +219,13 @@ unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
>
> static unsigned int st_mode_from_ce(const struct cache_entry *ce)
> {
> + struct repo_config_values *cfg = repo_config_values(the_repository);
> +
> switch (ce->ce_mode & S_IFMT) {
> case S_IFLNK:
> return has_symlinks ? S_IFLNK : (S_IFREG | 0644);
> case S_IFREG:
> - return (ce->ce_mode & (trust_executable_bit ? 0755 : 0644)) | S_IFREG;
> + return (ce->ce_mode & (cfg->trust_executable_bit ? 0755 : 0644)) | S_IFREG;
> case S_IFGITLINK:
> return S_IFDIR | 0755;
> case S_IFDIR:
Ditto.
> @@ -321,6 +325,7 @@ static int ce_modified_check_fs(struct index_state *istate,
> static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
> {
> unsigned int changed = 0;
> + struct repo_config_values *cfg = repo_config_values(the_repository);
>
> if (ce->ce_flags & CE_REMOVE)
> return MODE_CHANGED | DATA_CHANGED | TYPE_CHANGED;
> @@ -331,7 +336,7 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
> /* We consider only the owner x bit to be relevant for
> * "mode changes"
> */
> - if (trust_executable_bit &&
> + if (cfg->trust_executable_bit &&
> (0100 & (ce->ce_mode ^ st->st_mode)))
> changed |= MODE_CHANGED;
> break;
Ditto.
> @@ -732,6 +737,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
> (intent_only ? ADD_CACHE_NEW_ONLY : 0));
> unsigned hash_flags = pretend ? 0 : INDEX_WRITE_OBJECT;
>
> + struct repo_config_values *cfg = repo_config_values(the_repository);
> +
Lose the excess blank line before the new declaration.
> if (flags & ADD_CACHE_RENORMALIZE)
> hash_flags |= INDEX_RENORMALIZE;
>
> @@ -752,7 +759,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
> ce->ce_flags |= CE_INTENT_TO_ADD;
>
>
> - if (trust_executable_bit && has_symlinks) {
> + if (cfg->trust_executable_bit && has_symlinks) {
> ce->ce_mode = create_ce_mode(st_mode);
> } else {
> /* If there is an existing entry, pick the mode bits and type
Almost all of these places that care about trust_executable_bit also
cares about has_symlinks. I wonder if they should be converted to
repo-local settings in the same series.
^ permalink raw reply
* Re: [PATCH 4/4] doc: replay: move “default” to the right-hand-side
From: Junio C Hamano @ 2026-05-30 22:37 UTC (permalink / raw)
To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <default_RHS.70d@msgid.xyz>
kristofferhaugsbakk@fastmail.com writes:
> -`update` (default);; Update refs directly using an atomic transaction.
> +`update`;; (default) Update refs directly using an atomic transaction.
This looks sensible. Nice.
> All refs are updated or none are (all-or-nothing behavior).
> `print`;; Output update-ref commands for pipeline use. This is the
> traditional behavior where output can be piped to `git update-ref --stdin`.
^ permalink raw reply
* Re: [PATCH 3/4] doc: replay: use a nested definition list
From: Junio C Hamano @ 2026-05-30 22:37 UTC (permalink / raw)
To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <--ref-action_definition_list.70c@msgid.xyz>
kristofferhaugsbakk@fastmail.com writes:
> From: Kristoffer Haugsbakk <code@khaugsbakk.name>
>
> This bullet list for `--ref-action` introduces a term with a colon.
> This is exactly what a definition list is, structurally. Let’s be
> sylistically consistent and use the definition list markup construct.
Makes sense.
> --
> - * `update` (default): Update refs directly using an atomic transaction.
> - All refs are updated or none are (all-or-nothing behavior).
> - * `print`: Output update-ref commands for pipeline use. This is the
> - traditional behavior where output can be piped to `git update-ref --stdin`.
> +`update` (default);; Update refs directly using an atomic transaction.
> + All refs are updated or none are (all-or-nothing behavior).
> +`print`;; Output update-ref commands for pipeline use. This is the
> + traditional behavior where output can be piped to `git update-ref --stdin`.
> --
> +
The transition from a bulleted list to a nested definition list
(`;;`) for the `--ref-action` modes indeed makes the document
structure much cleaner.
> The default mode can be configured via the `replay.refAction` configuration variable.
^ permalink raw reply
* Re: [PATCH 2/4] doc: replay: simplify replay.refAction description
From: Junio C Hamano @ 2026-05-30 22:37 UTC (permalink / raw)
To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <simplify_replay.refAction.70b@msgid.xyz>
kristofferhaugsbakk@fastmail.com writes:
> replay.refAction::
> - Specifies the default mode for handling reference updates in
> - `git replay`. The value can be:
> -+
> ---
> - * `update`: Update refs directly using an atomic transaction (default behavior).
> - * `print`: Output update-ref commands for pipeline use.
> ---
> -+
> -This setting can be overridden with the `--ref-action` command-line option.
> -When not configured, `git replay` defaults to `update` mode.
> + Specifies the default mode for handling reference updates. Either `update` or `print`.
> +ifdef::git-replay[]
> +See `--ref-action`.
> +endif::git-replay[]
> +ifndef::git-replay[]
> +See `--ref-action` for linkgit:git-replay[1] for details.
> +endif::git-replay[]
This makes it a bit roundabout for "git config --help" readers who
wanted to figure out what value to set to the configuration
variable, because the valid choices are no longer listed here.
Finding `--ref-action=<mode>` and its description in the other page
is straight-forward, so it may not be too bad, though.
> diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
> index f9ca2db2833..4de85088d6c 100644
> --- a/Documentation/git-replay.adoc
> +++ b/Documentation/git-replay.adoc
> @@ -211,6 +211,7 @@ to use bare commit IDs instead of branch names.
>
> CONFIGURATION
> -------------
> +:git-replay: 1
> include::config/replay.adoc[]
The use of conditional attributes (`ifdef::git-replay[]`) is a neat
and standard way to tailor the description depending on whether it
is read as part of `git-config(1)` or `git-replay(1)`. It correctly
points the reader to `--ref-action` in the latter case, and provides
a full `linkgit` reference in the former. Clean and correct.
^ permalink raw reply
* Re: [PATCH 2/4] doc: replay: simplify replay.refAction description
From: Junio C Hamano @ 2026-05-30 22:29 UTC (permalink / raw)
To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <simplify_replay.refAction.70b@msgid.xyz>
kristofferhaugsbakk@fastmail.com writes:
> replay.refAction::
> - Specifies the default mode for handling reference updates in
> - `git replay`. The value can be:
> -+
> ---
> - * `update`: Update refs directly using an atomic transaction (default behavior).
> - * `print`: Output update-ref commands for pipeline use.
> ---
> -+
> -This setting can be overridden with the `--ref-action` command-line option.
> -When not configured, `git replay` defaults to `update` mode.
> + Specifies the default mode for handling reference updates. Either `update` or `print`.
> +ifdef::git-replay[]
> +See `--ref-action`.
> +endif::git-replay[]
> +ifndef::git-replay[]
> +See `--ref-action` for linkgit:git-replay[1] for details.
> +endif::git-replay[]
This makes it a bit roundabout for "git config --help" readers who
wanted to figure out what value to set to the configuration
variable, because the valid choices are no longer listed here.
Finding `--ref-action=<mode>` and its description in the other page
is straight-forward, so it may not be too bad, though.
> diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
> index f9ca2db2833..4de85088d6c 100644
> --- a/Documentation/git-replay.adoc
> +++ b/Documentation/git-replay.adoc
> @@ -211,6 +211,7 @@ to use bare commit IDs instead of branch names.
>
> CONFIGURATION
> -------------
> +:git-replay: 1
> include::config/replay.adoc[]
The use of conditional attributes (`ifdef::git-replay[]`) is a neat
and standard way to tailor the description depending on whether it
is read as part of `git-config(1)` or `git-replay(1)`. It correctly
points the reader to `--ref-action` in the latter case, and provides
a full `linkgit` reference in the former. Clean and correct.
^ permalink raw reply
* Re: [PATCH 1/4] doc: link to config for git-replay(1)
From: Junio C Hamano @ 2026-05-30 22:18 UTC (permalink / raw)
To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <doc_replay_link_config.70a@msgid.xyz>
kristofferhaugsbakk@fastmail.com writes:
> From: Kristoffer Haugsbakk <code@khaugsbakk.name>
>
> This config doc was added in 336ac90c (replay: add replay.refAction
> config option, 2025-11-06) but never included anywhere. Include it in
> git-replay(1) and git-config(1).
>
> Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
> ---
> Documentation/config.adoc | 2 ++
> Documentation/git-replay.adoc | 4 ++++
> 2 files changed, 6 insertions(+)
It is always nice to see documentation gaps filled.
The `replay.refAction` configuration variable was indeed left
dangling without a proper link from the main command documentation,
which is embarrassing. I wonder if we can add simple "doc-lint"
rule or two to prevent similar mistakes from happening again?
> diff --git a/Documentation/config.adoc b/Documentation/config.adoc
> index 62eebe7c545..51fabecb9b0 100644
> --- a/Documentation/config.adoc
> +++ b/Documentation/config.adoc
> @@ -511,6 +511,8 @@ include::config/remotes.adoc[]
>
> include::config/repack.adoc[]
>
> +include::config/replay.adoc[]
> +
> include::config/rerere.adoc[]
>
> include::config/revert.adoc[]
Placing `include::config/replay.adoc[]` in `config.adoc`
alphabetically between `repack` and `rerere` is correct, as the list
is alphabetical.
> diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
> index a32f72aead3..f9ca2db2833 100644
> --- a/Documentation/git-replay.adoc
> +++ b/Documentation/git-replay.adoc
> @@ -209,6 +209,10 @@ This replays the range `aabbcc..ddeeff` onto commit `112233` and updates
> `refs/heads/mybranch` to point at the result. This can be useful when you want
> to use bare commit IDs instead of branch names.
>
> +CONFIGURATION
> +-------------
> +include::config/replay.adoc[]
> +
Adding the `CONFIGURATION` section near the end of `git-replay.adoc`
is also the standard way we expose configuration variables to the
command's manual page.
Looking good.
> GIT
> ---
> Part of the linkgit:git[1] suite
^ permalink raw reply
* Re: [PATCH 0/4] doc: replay: fix config link
From: Junio C Hamano @ 2026-05-30 22:18 UTC (permalink / raw)
To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <CV_doc_replay_config.709@msgid.xyz>
kristofferhaugsbakk@fastmail.com writes:
> From: Kristoffer Haugsbakk <code@khaugsbakk.name>
>
> [1/4] doc: link to config for git-replay(1)
> [2/4] doc: replay: simplify replay.refAction description
> [3/4] doc: replay: use a nested definition list
> [4/4] doc: replay: move “default” to the right-hand-side
It is always nice to see documentation gaps filled.
^ permalink raw reply
* Re: [PATCH v1 4/4] read-cache: pass 'istate' to stat/mode helper functions
From: Christian Couder @ 2026-05-30 18:14 UTC (permalink / raw)
To: Tian Yuchen; +Cc: git, ps, Ayush Chandekar, Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-5-cat@malon.dev>
On Sat, May 30, 2026 at 6:05 PM Tian Yuchen <cat@malon.dev> wrote:
>
> In the previous commit, the gloabl 'trust_executable_bit' was
s/gloabl/global/
> migrated into 'repo_config_values', but low-level helpers in
> read-cache.c still relied on 'the_repository' to access it.
>
> Refactor the signatures of ce_mode_from_stat(), st_mode_from_ce(),
> fake_lstat(), and check_removed() to accept a 'struct
> index_state *istate'. This allows these functions to retrieve the
> repository context via 'istate->repo'.
The cover letter contains:
"In other words, this series of patches is laying the groundwork for
the eventual elimination of 'the_repository'."
but I think it would be also interesting to have something similar
here, as this is especially relevant to this commit.
For example maybe add something like "which will help with removing
'the_repository' in the future" to the last sentence?
^ permalink raw reply
* Re: [PATCH v1 3/4] environment: move 'trust_executable_bit' into repo_config_values
From: Christian Couder @ 2026-05-30 18:02 UTC (permalink / raw)
To: Tian Yuchen; +Cc: git, ps, Ayush Chandekar, Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-4-cat@malon.dev>
On Sat, May 30, 2026 at 6:05 PM Tian Yuchen <cat@malon.dev> wrote:
> @@ -720,5 +719,6 @@ void repo_config_values_init(struct repo_config_values *cfg)
> {
> cfg->attributes_file = NULL;
> cfg->apply_sparse_checkout = 0;
> + cfg->trust_executable_bit = 1;
Here `trust_executable_bit` is processed after `apply_sparse_checkout` ...
> cfg->branch_track = BRANCH_TRACK_REMOTE;
> }
> diff --git a/environment.h b/environment.h
> index 123a71cdc8..72c400923d 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -90,6 +90,7 @@ struct repository;
> struct repo_config_values {
> /* section "core" config values */
> char *attributes_file;
> + int trust_executable_bit;
> int apply_sparse_checkout;
... but here it is before `apply_sparse_checkout`.
I think it would make more sense to put `trust_executable_bit` after
`apply_sparse_checkout` here.
> /* section "branch" config values */
^ permalink raw reply
* [PATCH v1 1/4] read-cache: remove redundant extern declarations
From: Tian Yuchen @ 2026-05-30 16:05 UTC (permalink / raw)
To: git; +Cc: christian.couder, ps, Tian Yuchen, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-1-cat@malon.dev>
The 'read-cache.c' file already includes 'environment.h', which provides
the extern declarations for variables like 'trust_executable_bit' and
'has_symlinks'.
Remove the redundant extern declarations inside 'st_mode_from_ce()' to
clean up the code.
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>
---
read-cache.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/read-cache.c b/read-cache.c
index 38a04b8de3..c44e4d128f 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -204,8 +204,6 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
static unsigned int st_mode_from_ce(const struct cache_entry *ce)
{
- extern int trust_executable_bit, has_symlinks;
-
switch (ce->ce_mode & S_IFMT) {
case S_IFLNK:
return has_symlinks ? S_IFLNK : (S_IFREG | 0644);
--
2.43.0
^ permalink raw reply related
* [PATCH v1 0/4] environment.c: migrate 'trust_executable_bit' into 'repo_config_values'
From: Tian Yuchen @ 2026-05-30 16:05 UTC (permalink / raw)
To: git; +Cc: christian.couder, ps, Tian Yuchen, Ayush Chandekar,
Olamide Caleb Bello
The 'core.filemode' configuration, which is stored as a global
variable 'trust_executable_bit', is a core filesystem capability flag.
Move it into 'repo_config_values' to tie it to the specific
repository instance it was read from. Eager parsing is maintained
because this flag is heavily consulted in hot paths.
To avoid falling back to 'the_repository', refactor the signatures
of helper functions:
- ce_mode_from_stat()
- st_mode_from_ce()
- fake_lstat()
- check_removed()
These functions now accept a 'struct index_state *istate', ensuring
the correct context is seamlessly passed down to the lowest levels.
Note: 'repo_config_values()' still does not support any 'struct
repository' other than 'the_repository'. In other words, this series
of patches is laying the groundwork for the eventual elimination of
'the_repository'.
Previous related work:
- [PATCH 2/6] config: add trust_executable_bit to global config
- [PATCH] Refactor 'trust_executable_bit' to repository-scoped setting
(This series of patches was unsuccessful because the target location selected
was 'struct repo_settings', which our analysis indicated was not the
optimal choice. For further details, please see: [1])
[1] https://lore.kernel.org/git/837b5360b40f992351f489a0ae05fedf49884c6e.1685716420.git.gitgitgadget@gmail.com/
[2] https://lore.kernel.org/git/20260301190017.53539-1-dronarajgyawali@gmail.com/
[3] https://lore.kernel.org/git/xmqq1pht6nyx.fsf@gitster.g/
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>
Tian Yuchen (4):
read-cache: remove redundant extern declarations
read-cache: move 'ce_mode_from_stat()' to 'read-cache.c'
environment: move 'trust_executable_bit' into repo_config_values
read-cache: pass 'istate' to stat/mode helper functions
apply.c | 6 ++++--
builtin/update-index.c | 2 +-
diff-lib.c | 20 +++++++++---------
environment.c | 4 ++--
environment.h | 2 +-
read-cache-ll.h | 2 +-
read-cache.c | 47 ++++++++++++++++++++++++++++++++----------
read-cache.h | 17 +++------------
8 files changed, 58 insertions(+), 42 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH v1 4/4] read-cache: pass 'istate' to stat/mode helper functions
From: Tian Yuchen @ 2026-05-30 16:05 UTC (permalink / raw)
To: git; +Cc: christian.couder, ps, Tian Yuchen, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-1-cat@malon.dev>
In the previous commit, the gloabl 'trust_executable_bit' was
migrated into 'repo_config_values', but low-level helpers in
read-cache.c still relied on 'the_repository' to access it.
Refactor the signatures of ce_mode_from_stat(), st_mode_from_ce(),
fake_lstat(), and check_removed() to accept a 'struct
index_state *istate'. This allows these functions to retrieve the
repository context via 'istate->repo'.
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>
---
apply.c | 4 ++--
builtin/update-index.c | 2 +-
diff-lib.c | 20 ++++++++++----------
read-cache-ll.h | 2 +-
read-cache.c | 31 +++++++++++++++++++------------
read-cache.h | 3 ++-
6 files changed, 35 insertions(+), 27 deletions(-)
diff --git a/apply.c b/apply.c
index 73ca9907f8..a81bb29a6f 100644
--- a/apply.c
+++ b/apply.c
@@ -3890,13 +3890,13 @@ static int check_preimage(struct apply_state *state,
}
if (!state->cached && !previous) {
- struct repo_config_values *cfg = repo_config_values(the_repository);
+ struct repo_config_values *cfg = repo_config_values(state->repo);
if (*ce && !(*ce)->ce_mode)
BUG("ce_mode == 0 for path '%s'", old_name);
if (cfg->trust_executable_bit || !S_ISREG(st->st_mode))
- st_mode = ce_mode_from_stat(*ce, st->st_mode);
+ st_mode = ce_mode_from_stat(state->repo->index, *ce, st->st_mode);
else if (*ce)
st_mode = (*ce)->ce_mode;
else
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 8a5907767b..3f6967bd84 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -293,7 +293,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
ce->ce_flags = create_ce_flags(0);
ce->ce_namelen = len;
fill_stat_cache_info(the_repository->index, ce, st);
- ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
+ ce->ce_mode = ce_mode_from_stat(the_repository->index, old, st->st_mode);
if (index_path(the_repository->index, &ce->oid, path, st,
info_only ? 0 : INDEX_WRITE_OBJECT)) {
diff --git a/diff-lib.c b/diff-lib.c
index ae91027a02..95fd3ba4b9 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -39,14 +39,14 @@
* exists for ce that is a submodule -- it is a submodule that is not
* checked out). Return negative for an error.
*/
-static int check_removed(const struct cache_entry *ce, struct stat *st)
+static int check_removed(struct index_state *istate, const struct cache_entry *ce, struct stat *st)
{
int stat_err;
if (!(ce->ce_flags & CE_FSMONITOR_VALID))
stat_err = lstat(ce->name, st);
else
- stat_err = fake_lstat(ce, st);
+ stat_err = fake_lstat(istate, ce, st);
if (stat_err < 0) {
if (!is_missing_file_error(errno))
return -1;
@@ -158,9 +158,9 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
int num_compare_stages = 0;
struct stat st;
- changed = check_removed(ce, &st);
+ changed = check_removed(revs->repo->index, ce, &st);
if (!changed)
- wt_mode = ce_mode_from_stat(ce, st.st_mode);
+ wt_mode = ce_mode_from_stat(revs->repo->index, ce, st.st_mode);
else {
if (changed < 0) {
perror(ce->name);
@@ -193,7 +193,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
num_compare_stages++;
oidcpy(&dpath->parent[stage - 2].oid,
&nce->oid);
- dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode);
+ dpath->parent[stage-2].mode = ce_mode_from_stat(revs->repo->index, nce, mode);
dpath->parent[stage-2].status =
DIFF_STATUS_MODIFIED;
}
@@ -249,7 +249,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
} else {
struct stat st;
- changed = check_removed(ce, &st);
+ changed = check_removed(revs->repo->index, ce, &st);
if (changed) {
if (changed < 0) {
perror(ce->name);
@@ -262,7 +262,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
continue;
} else if (revs->diffopt.ita_invisible_in_index &&
ce_intent_to_add(ce)) {
- newmode = ce_mode_from_stat(ce, st.st_mode);
+ newmode = ce_mode_from_stat(revs->repo->index, ce, st.st_mode);
diff_addremove(&revs->diffopt, '+', newmode,
null_oid(the_hash_algo), 0, ce->name, 0);
continue;
@@ -270,7 +270,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
ce_option, &dirty_submodule);
- newmode = ce_mode_from_stat(ce, st.st_mode);
+ newmode = ce_mode_from_stat(revs->repo->index, ce, st.st_mode);
}
if (!changed && !dirty_submodule) {
@@ -324,7 +324,7 @@ static int get_stat_data(const struct cache_entry *ce,
if (!cached && !ce_uptodate(ce)) {
int changed;
struct stat st;
- changed = check_removed(ce, &st);
+ changed = check_removed(diffopt->repo->index, ce, &st);
if (changed < 0)
return -1;
else if (changed) {
@@ -338,7 +338,7 @@ static int get_stat_data(const struct cache_entry *ce,
changed = match_stat_with_submodule(diffopt, ce, &st,
0, dirty_submodule);
if (changed) {
- mode = ce_mode_from_stat(ce, st.st_mode);
+ mode = ce_mode_from_stat(diffopt->repo->index, ce, st.st_mode);
oid = null_oid(the_hash_algo);
}
}
diff --git a/read-cache-ll.h b/read-cache-ll.h
index 2c8b4b21b1..9fb9bedfbf 100644
--- a/read-cache-ll.h
+++ b/read-cache-ll.h
@@ -442,7 +442,7 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
* for lstat() for a tracked path that is known to be up-to-date via
* some out-of-line means (like fsmonitor).
*/
-int fake_lstat(const struct cache_entry *ce, struct stat *st);
+int fake_lstat(struct index_state *istate, const struct cache_entry *ce, struct stat *st);
#define REFRESH_REALLY (1 << 0) /* ignore_valid */
#define REFRESH_UNMERGED (1 << 1) /* allow unmerged */
diff --git a/read-cache.c b/read-cache.c
index 18af533649..28e7f24382 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -202,9 +202,12 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
}
}
-unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
+unsigned int ce_mode_from_stat(struct index_state *istate,
+ const struct cache_entry *ce,
+ unsigned int mode)
{
- struct repo_config_values *cfg = repo_config_values(the_repository);
+ struct repository *repo = (istate && istate->repo) ? istate->repo : the_repository;
+ struct repo_config_values *cfg = repo_config_values(repo);
if (!has_symlinks && S_ISREG(mode) &&
ce && S_ISLNK(ce->ce_mode))
@@ -217,9 +220,10 @@ unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
return create_ce_mode(mode);
}
-static unsigned int st_mode_from_ce(const struct cache_entry *ce)
+static unsigned int st_mode_from_ce(struct index_state *istate, const struct cache_entry *ce)
{
- struct repo_config_values *cfg = repo_config_values(the_repository);
+ struct repository *repo = (istate && istate->repo) ? istate->repo : the_repository;
+ struct repo_config_values *cfg = repo_config_values(repo);
switch (ce->ce_mode & S_IFMT) {
case S_IFLNK:
@@ -235,10 +239,10 @@ static unsigned int st_mode_from_ce(const struct cache_entry *ce)
}
}
-int fake_lstat(const struct cache_entry *ce, struct stat *st)
+int fake_lstat(struct index_state *istate, const struct cache_entry *ce, struct stat *st)
{
fake_lstat_data(&ce->ce_stat_data, st);
- st->st_mode = st_mode_from_ce(ce);
+ st->st_mode = st_mode_from_ce(istate, ce);
/* always succeed as lstat() replacement */
return 0;
@@ -322,10 +326,12 @@ static int ce_modified_check_fs(struct index_state *istate,
return 0;
}
-static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
+static int ce_match_stat_basic(struct index_state *istate,
+ const struct cache_entry *ce, struct stat *st)
{
unsigned int changed = 0;
- struct repo_config_values *cfg = repo_config_values(the_repository);
+ struct repository *repo = (istate && istate->repo) ? istate->repo : the_repository;
+ struct repo_config_values *cfg = repo_config_values(repo);
if (ce->ce_flags & CE_REMOVE)
return MODE_CHANGED | DATA_CHANGED | TYPE_CHANGED;
@@ -430,7 +436,7 @@ int ie_match_stat(struct index_state *istate,
if (ce_intent_to_add(ce))
return DATA_CHANGED | TYPE_CHANGED | MODE_CHANGED;
- changed = ce_match_stat_basic(ce, st);
+ changed = ce_match_stat_basic(istate, ce, st);
/*
* Within 1 second of this sequence:
@@ -737,7 +743,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
(intent_only ? ADD_CACHE_NEW_ONLY : 0));
unsigned hash_flags = pretend ? 0 : INDEX_WRITE_OBJECT;
- struct repo_config_values *cfg = repo_config_values(the_repository);
+ struct repository *repo = (istate && istate->repo) ? istate->repo : the_repository;
+ struct repo_config_values *cfg = repo_config_values(repo);
if (flags & ADD_CACHE_RENORMALIZE)
hash_flags |= INDEX_RENORMALIZE;
@@ -769,7 +776,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
int pos = index_name_pos_also_unmerged(istate, path, namelen);
ent = (0 <= pos) ? istate->cache[pos] : NULL;
- ce->ce_mode = ce_mode_from_stat(ent, st_mode);
+ ce->ce_mode = ce_mode_from_stat(istate, ent, st_mode);
}
/* When core.ignorecase=true, determine if a directory of the same name but differing
@@ -2592,7 +2599,7 @@ static void ce_smudge_racily_clean_entry(struct index_state *istate,
if (lstat(ce->name, &st) < 0)
return;
- if (ce_match_stat_basic(ce, &st))
+ if (ce_match_stat_basic(istate, ce, &st))
return;
if (ce_modified_check_fs(istate, ce, &st)) {
/* This is "racily clean"; smudge it. Note that this
diff --git a/read-cache.h b/read-cache.h
index 3c4af2faeb..61299ed95b 100644
--- a/read-cache.h
+++ b/read-cache.h
@@ -5,7 +5,8 @@
#include "object.h"
#include "pathspec.h"
-unsigned int ce_mode_from_stat(const struct cache_entry *ce,
+unsigned int ce_mode_from_stat(struct index_state *istate,
+ const struct cache_entry *ce,
unsigned int mode);
static inline int ce_to_dtype(const struct cache_entry *ce)
--
2.43.0
^ permalink raw reply related
* [PATCH v1 3/4] environment: move 'trust_executable_bit' into repo_config_values
From: Tian Yuchen @ 2026-05-30 16:05 UTC (permalink / raw)
To: git; +Cc: christian.couder, ps, Tian Yuchen, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-1-cat@malon.dev>
Move the global 'trust_executable_bit' configuration into the
repository-specific 'repo_config_values' struct.
For now, associated functions in read-cache.c access this configuration
by explicitly falling back to 'the_repository'.
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>
---
apply.c | 4 +++-
environment.c | 4 ++--
environment.h | 2 +-
read-cache.c | 15 +++++++++++----
4 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/apply.c b/apply.c
index 249248d4f2..73ca9907f8 100644
--- a/apply.c
+++ b/apply.c
@@ -3890,10 +3890,12 @@ static int check_preimage(struct apply_state *state,
}
if (!state->cached && !previous) {
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+
if (*ce && !(*ce)->ce_mode)
BUG("ce_mode == 0 for path '%s'", old_name);
- if (trust_executable_bit || !S_ISREG(st->st_mode))
+ if (cfg->trust_executable_bit || !S_ISREG(st->st_mode))
st_mode = ce_mode_from_stat(*ce, st->st_mode);
else if (*ce)
st_mode = (*ce)->ce_mode;
diff --git a/environment.c b/environment.c
index fc3ed8bb1c..94f74f39e6 100644
--- a/environment.c
+++ b/environment.c
@@ -41,7 +41,6 @@
static int pack_compression_seen;
static int zlib_compression_seen;
-int trust_executable_bit = 1;
int trust_ctime = 1;
int check_stat = 1;
int has_symlinks = 1;
@@ -305,7 +304,7 @@ int git_default_core_config(const char *var, const char *value,
/* This needs a better name */
if (!strcmp(var, "core.filemode")) {
- trust_executable_bit = git_config_bool(var, value);
+ cfg->trust_executable_bit = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "core.trustctime")) {
@@ -720,5 +719,6 @@ void repo_config_values_init(struct repo_config_values *cfg)
{
cfg->attributes_file = NULL;
cfg->apply_sparse_checkout = 0;
+ cfg->trust_executable_bit = 1;
cfg->branch_track = BRANCH_TRACK_REMOTE;
}
diff --git a/environment.h b/environment.h
index 123a71cdc8..72c400923d 100644
--- a/environment.h
+++ b/environment.h
@@ -90,6 +90,7 @@ struct repository;
struct repo_config_values {
/* section "core" config values */
char *attributes_file;
+ int trust_executable_bit;
int apply_sparse_checkout;
/* section "branch" config values */
@@ -160,7 +161,6 @@ int is_bare_repository(void);
extern char *git_work_tree_cfg;
/* Environment bits from configuration mechanism */
-extern int trust_executable_bit;
extern int trust_ctime;
extern int check_stat;
extern int has_symlinks;
diff --git a/read-cache.c b/read-cache.c
index 54150fe756..18af533649 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -204,10 +204,12 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
{
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+
if (!has_symlinks && S_ISREG(mode) &&
ce && S_ISLNK(ce->ce_mode))
return ce->ce_mode;
- if (!trust_executable_bit && S_ISREG(mode)) {
+ if (!cfg->trust_executable_bit && S_ISREG(mode)) {
if (ce && S_ISREG(ce->ce_mode))
return ce->ce_mode;
return create_ce_mode(0666);
@@ -217,11 +219,13 @@ unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
static unsigned int st_mode_from_ce(const struct cache_entry *ce)
{
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+
switch (ce->ce_mode & S_IFMT) {
case S_IFLNK:
return has_symlinks ? S_IFLNK : (S_IFREG | 0644);
case S_IFREG:
- return (ce->ce_mode & (trust_executable_bit ? 0755 : 0644)) | S_IFREG;
+ return (ce->ce_mode & (cfg->trust_executable_bit ? 0755 : 0644)) | S_IFREG;
case S_IFGITLINK:
return S_IFDIR | 0755;
case S_IFDIR:
@@ -321,6 +325,7 @@ static int ce_modified_check_fs(struct index_state *istate,
static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
{
unsigned int changed = 0;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
if (ce->ce_flags & CE_REMOVE)
return MODE_CHANGED | DATA_CHANGED | TYPE_CHANGED;
@@ -331,7 +336,7 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
/* We consider only the owner x bit to be relevant for
* "mode changes"
*/
- if (trust_executable_bit &&
+ if (cfg->trust_executable_bit &&
(0100 & (ce->ce_mode ^ st->st_mode)))
changed |= MODE_CHANGED;
break;
@@ -732,6 +737,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
(intent_only ? ADD_CACHE_NEW_ONLY : 0));
unsigned hash_flags = pretend ? 0 : INDEX_WRITE_OBJECT;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+
if (flags & ADD_CACHE_RENORMALIZE)
hash_flags |= INDEX_RENORMALIZE;
@@ -752,7 +759,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
ce->ce_flags |= CE_INTENT_TO_ADD;
- if (trust_executable_bit && has_symlinks) {
+ if (cfg->trust_executable_bit && has_symlinks) {
ce->ce_mode = create_ce_mode(st_mode);
} else {
/* If there is an existing entry, pick the mode bits and type
--
2.43.0
^ permalink raw reply related
* [PATCH v1 2/4] read-cache: move 'ce_mode_from_stat()' to 'read-cache.c'
From: Tian Yuchen @ 2026-05-30 16:05 UTC (permalink / raw)
To: git; +Cc: christian.couder, ps, Tian Yuchen, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-1-cat@malon.dev>
The ce_mode_from_stat() function is declared as a static inline function
in 'read-cache.h'. As we want to migrate configuration variables, this
helper function will need access to corresponding repository-specific
configuration logic. Move the implementation to 'read-cache.c' to
cleanly encapsulate its dependencies.
Note that the 'extern int trust_executable_bit, has_symlinks;' line is
discarded because it's not necessary when the function lives in
"read-cache.c".
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>
---
read-cache.c | 13 +++++++++++++
read-cache.h | 16 ++--------------
2 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/read-cache.c b/read-cache.c
index c44e4d128f..54150fe756 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -202,6 +202,19 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
}
}
+unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
+{
+ if (!has_symlinks && S_ISREG(mode) &&
+ ce && S_ISLNK(ce->ce_mode))
+ return ce->ce_mode;
+ if (!trust_executable_bit && S_ISREG(mode)) {
+ if (ce && S_ISREG(ce->ce_mode))
+ return ce->ce_mode;
+ return create_ce_mode(0666);
+ }
+ return create_ce_mode(mode);
+}
+
static unsigned int st_mode_from_ce(const struct cache_entry *ce)
{
switch (ce->ce_mode & S_IFMT) {
diff --git a/read-cache.h b/read-cache.h
index 043da1f1aa..3c4af2faeb 100644
--- a/read-cache.h
+++ b/read-cache.h
@@ -5,20 +5,8 @@
#include "object.h"
#include "pathspec.h"
-static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce,
- unsigned int mode)
-{
- extern int trust_executable_bit, has_symlinks;
- if (!has_symlinks && S_ISREG(mode) &&
- ce && S_ISLNK(ce->ce_mode))
- return ce->ce_mode;
- if (!trust_executable_bit && S_ISREG(mode)) {
- if (ce && S_ISREG(ce->ce_mode))
- return ce->ce_mode;
- return create_ce_mode(0666);
- }
- return create_ce_mode(mode);
-}
+unsigned int ce_mode_from_stat(const struct cache_entry *ce,
+ unsigned int mode);
static inline int ce_to_dtype(const struct cache_entry *ce)
{
--
2.43.0
^ permalink raw reply related
* Unique article idea – your thoughts?
From: Bret Engle @ 2026-05-30 15:07 UTC (permalink / raw)
To: git
Hello,
For many seniors, gardening and lawn care are not just tasks on the
honey-do list. They are enjoyable hobbies that also have amazing
physical and mental health benefits. Still, as we age, it becomes more
difficult to perform the activities that keep a yard beautiful and
functional.
We’d like to write an article for your readers about how to maintain
their outdoor living spaces as they age.
The article is totally free. In it, we can talk about a variety of
topics, including low-maintenance, budget-friendly landscaping
options, senior-friendly gardening features like raised beds, and/or
tips for making your outdoor living spaces more accessible.
To give you a feel for our writing style, here are a couple of samples
from our blog:
Great Home Improvements You Can Do Yourself
https://diyguys.net/blog/great-home-improvements-you-can-do-yourself/
Ultimate Guide to DIY Shelving
https://diyguys.net/blog/ultimate-guide-to-diy-shelving/
If you’re open to featuring our proposed content on your site, please
let us know.
Thank you for your consideration!
Ray Flynn and Bret Engle
DIYguys.net
―Totally fine if this idea doesn’t click - we can reshape it or pitch
something that aligns more naturally with your content style and
goals. Our goal is to deliver value both to readers and through online
visibility. However, if you’d like to opt out, just tell us!
^ permalink raw reply
* git-history drops signatures
From: Alix Brunet @ 2026-05-30 10:44 UTC (permalink / raw)
To: git
Hey team ;
I noticed `git history` drops signatures,
Even though `git rebase` can keep / re-sign commits (`-S`)
Will this ever be implemented?
^ permalink raw reply
* doc: document '@' prefix for raw timestamps
From: Junio C Hamano @ 2026-05-30 7:43 UTC (permalink / raw)
To: Luna Schwalbe; +Cc: Kristoffer Haugsbakk, git
In-Reply-To: <xmqq7bolg762.fsf@gitster.g>
Junio C Hamano <gitster@pobox.com> writes:
> This was introduced in 116eb3ab (parse_date(): allow ancient
> git-timestamp, 2012-02-02) and 2c733fb2 (parse_date(): '@' prefix
> forces git-timestamp, 2012-02-02) to allow specifying "ancient"
> timestamps (like 0 +0000) without conflicting with YYYYMMDD date
> formats. I do not think neither commit added documentation for this
> '@' prefix, and Documentation/date-formats would be an excellent
> place to do so.
>
> Care to whip up a patch?
It might look something like this.
----- >8 -----
The Git internal date format `<unix-timestamp> <time-zone-offset>`
fails to parse when the timestamp is less than 100,000,000 (fewer
than 9 digits). This happens because the parser attempts to guess
the format, and 8-digit numbers are interpreted as YYYYMMDD dates.
To force the parser to interpret the value as a raw timestamp, it
must be prefixed with `@` (e.g., `@0 +0000`). This behavior was
introduced in 2c733fb24c (parse_date(): '@' prefix forces
git-timestamp, 2012-02-02) but was never documented.
Document the `@` prefix in `Documentation/date-formats.adoc` to
make this behavior explicit. Also add test cases to
`t/t0006-date.sh` to verify and demonstrate the difference
between prefixed and unprefixed small timestamps (e.g.,
`@20000101` vs `20000101`).
---
diff --git a/Documentation/date-formats.adoc b/Documentation/date-formats.adoc
index e24517c496..93fd36449c 100644
--- a/Documentation/date-formats.adoc
+++ b/Documentation/date-formats.adoc
@@ -9,6 +9,13 @@ Git internal format::
`<unix-timestamp>` is the number of seconds since the UNIX epoch.
`<time-zone-offset>` is a positive or negative offset from UTC.
For example CET (which is 1 hour ahead of UTC) is `+0100`.
++
+It is safer to prepend the `<unix-timestamp>` with `@`
+(e.g., `@0 +0000`), which forces Git to interpret it as a raw
+timestamp even if it looks like another format (like `YYYYMMDD`).
+This is required for timestamps less than 100,000,000 (which have
+fewer than 9 digits) to avoid confusion with other date formats.
+
RFC 2822::
The standard date format as described by RFC 2822, for example
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 53ced36df4..e11c659716 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -138,6 +138,14 @@ check_parse '1969-12-31 23:59:59 Z' bad
check_parse '1969-12-31 23:59:59 +11' bad
check_parse '1969-12-31 23:59:59 -11' bad
+# pathologically small or easily confused raw timestamps
+check_parse '@99999999 +0000' '1973-03-03 09:46:39 +0000'
+check_parse '@0 +0000' '1970-01-01 00:00:00 +0000'
+check_parse '99999999 +0000' bad
+check_parse '20000101 +0000' '2000-01-01 00:00:00 +0000'
+check_parse '@20000101 +0000' '1970-08-20 11:35:01 +0000'
+
+
REQUIRE_64BIT_TIME=HAVE_64BIT_TIME
check_parse '2099-12-31 23:59:59' '2099-12-31 23:59:59 +0000'
check_parse '2099-12-31 23:59:59 +00' '2099-12-31 23:59:59 +0000'
^ permalink raw reply related
* Re: [PATCH] log: improve --follow following renames in merge commits
From: Miklos Vajna @ 2026-05-30 6:28 UTC (permalink / raw)
To: Jeff King; +Cc: Elijah Newren, git
In-Reply-To: <ahFDgq4TAcs29zCA@collabora.com>
Hi Jeff,
On Sat, May 23, 2026 at 08:04:50AM +0200, Miklos Vajna <vmiklos@collabora.com> wrote:
> > There might be a more useful rule like: if the path is untouched versus
> > the merge result in all parents but one (i.e., TREESAME), then choose
> > the parent where it was changed, including any --follow processing.
>
> I like this idea: it keeps working with the subtree use-case I have in
> mind and goes back to not change behavior when the file has history on
> multiple parents.
>
> > So I dunno. Probably some experimenting could yield more analysis there,
>
> I think requiring TREESAME for all but one parents is too strict, since
> a subtree merge will look like an addition vs the first parent and will
> look like a rename on the first parent. It seems to me that handling
> addition as TREESAME can be correct: if the file was just added, that
> suggests it has no prior history.
>
> So a slightly relaxed rule could be: if the path is untouched or just
> added versus the merge result in all parents but one, then choose the
> parent where it was changed, including any --follow processing.
Could you please comment on this, if this tweaked rule and its
implementation in the patch looks OK to you? Let me know if I should
just wait some more.
I would hope this addresses your concern where naively following an
other parent just makes one use-case better and can be worse in other
cases.
This also explains why the normal history simplification is not enough
here: the "added vs parent" is a change that is not interesting in this
case, but is more than TREESAME.
Finally, because I forgot to react to that earlier: I'm not against the
idea to attempt to improve --follow work better when visiting a tree of
commits in general, but sounds like a larger rework, so it would be nice
to have a fix for the subtree use-case first.
Thanks,
Miklos
^ permalink raw reply
* Re: [BUG] internal date format does not accept small unix timestamps
From: Junio C Hamano @ 2026-05-29 22:52 UTC (permalink / raw)
To: Luna Schwalbe; +Cc: Kristoffer Haugsbakk, git
In-Reply-To: <08a04d91-af90-44dd-b28f-f3d5b9e77413@luna.gl>
Luna Schwalbe <dev@luna.gl> writes:
> > Apparently you need `@` in front for small Unix Epoch values. `@0 +0000`
>
> That is wonderful, thank you so much, I somehow did not find this small
> detail anywhere.
>
> Maybe it could be added to Documentation/date-formats.adoc?
>
> Luna
Good suggestion.
This was introduced in 116eb3ab (parse_date(): allow ancient
git-timestamp, 2012-02-02) and 2c733fb2 (parse_date(): '@' prefix
forces git-timestamp, 2012-02-02) to allow specifying "ancient"
timestamps (like 0 +0000) without conflicting with YYYYMMDD date
formats. I do not think neither commit added documentation for this
'@' prefix, and Documentation/date-formats would be an excellent
place to do so.
Care to whip up a patch?
Thanks.
^ 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