* Re: [PATCH v4 0/1] environment: move protect_hfs and protect_ntfs into repo_config_values
From: Junio C Hamano @ 2026-06-19 17:14 UTC (permalink / raw)
To: Tian Yuchen; +Cc: git, Christian Couder, Ayush Chandekar, Olamide Caleb Bello
In-Reply-To: <20260619163823.652091-1-cat@malon.dev>
Tian Yuchen <cat@malon.dev> writes:
> This series continues the ongoing libification effort by moving the
> global filesystem variables, 'protect_hfs' and 'protect_ntfs', into
> 'struct repo_config_values'.
> ...
> Change since V3:
>
> - In repo_protect_hfs() and repo_protect_ntfs(), change repo->gitdir to
> using (repo && repo->initialized).
While I think that it is a good change for consistency with other
two topics, the hfs/ntfs topic is already in 'next', so it needs to
be handled differently. Namely, a topic in 'next' should not be
replaced, but be improved by additional patches on top.
In this particular case case, I think it would be good to have "to
match how we refrain from calling repo_config_values() on an
uninitialized instance of a repository object in other two topics
that deal with X bit and Y bit, check the repo->initialized bit
instead of the repo->gitdir member" or something like that in the
log message to explain why we are making the change, perhaps.
The patch text may look like this.
environment.c | 4 ++--
environment.h | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git c/environment.c w/environment.c
index 683fe1b4d3..f34f6fc750 100644
--- c/environment.c
+++ w/environment.c
@@ -142,14 +142,14 @@ int is_bare_repository(void)
int repo_protect_ntfs(struct repository *repo)
{
- return repo->gitdir ?
+ return (repo && repo->initialized) ?
repo_config_values(repo)->protect_ntfs :
PROTECT_NTFS_DEFAULT;
}
int repo_protect_hfs(struct repository *repo)
{
- return repo->gitdir ?
+ return (repo && repo->initialized) ?
repo_config_values(repo)->protect_hfs :
PROTECT_HFS_DEFAULT;
}
diff --git c/environment.h w/environment.h
index fdd9775900..b1ae4a70de 100644
--- c/environment.h
+++ w/environment.h
@@ -127,8 +127,8 @@ int git_default_core_config(const char *var, const char *value,
/*
* Getters for the `protect_hfs` and `protect_ntfs` fields of `struct repo_config_values`.
- * They check `repo->gitdir` to prevent calling repo_config_values()
- * before the configuration is loaded or in bare environments.
+ * They check `repo->initialized` to prevent calling `repo_config_values()`
+ * before the repository setup is fully complete or in non-git environments.
*/
int repo_protect_hfs(struct repository *repo);
int repo_protect_ntfs(struct repository *repo);
^ permalink raw reply related
* [PATCH v2] help: include arguments in autocorrect=prompt message
From: Jishnu C K @ 2026-06-19 17:04 UTC (permalink / raw)
To: git; +Cc: gitster, Justin Tobler
In-Reply-To: <ajQuqTB580gqNP8D@denethor>
v2: Reworked as an incremental improvement to the existing
autocorrect=prompt code path rather than a parallel reimplementation,
per feedback from Junio and Justin.
---
From a4e8fb6fd6dd6a501e565c7500cbf927d7cb0b42 Mon Sep 17 00:00:00 2001
From: calicomills <jishnuck26@gmail.com>
Date: Fri, 19 Jun 2026 13:01:40 +0530
Subject: [PATCH v2 v2] help: include arguments in autocorrect=prompt message
When 'help.autocorrect=prompt' is configured and the user mistypes
a git command, the prompt currently shows only the corrected command
name:
Run 'checkout' instead [y/N]?
This leaves the user unsure whether their original arguments will be
preserved. Update the prompt to include the full corrected invocation:
Run 'git checkout neo' instead [y/N]?
The help_unknown_cmd() signature is updated to accept the args vector
so the prompt can show the original arguments alongside the corrected
command name. Callers that do not have access to the args (e.g.
builtin/help.c) pass NULL, which is handled gracefully.
Signed-off-by: calicomills <jishnuck26@gmail.com>
---
help.c | 49 +++++++++++++----------------------
t/t9003-help-autocorrect.sh | 51 +++++--------------------------------
2 files changed, 23 insertions(+), 77 deletions(-)
diff --git a/help.c b/help.c
index 30f32a7206..9ea4c076e1 100644
--- a/help.c
+++ b/help.c
@@ -739,7 +739,16 @@ char *help_unknown_cmd(const char *cmd, const struct strvec *args)
else if (cfg.autocorrect == AUTOCORRECT_PROMPT) {
char *answer;
struct strbuf msg = STRBUF_INIT;
- strbuf_addf(&msg, _("Run '%s' instead [y/N]? "), assumed);
+ struct strbuf full_cmd = STRBUF_INIT;
+ strbuf_addstr(&full_cmd, assumed);
+ if (args) {
+ for (size_t j = 1; j < args->nr; j++) {
+ strbuf_addch(&full_cmd, ' ');
+ strbuf_addstr(&full_cmd, args->v[j]);
+ }
+ }
+ strbuf_addf(&msg, _("Run 'git %s' instead [y/N]? "), full_cmd.buf);
+ strbuf_release(&full_cmd);
answer = git_prompt(msg.buf, PROMPT_ECHO);
strbuf_release(&msg);
if (!(starts_with(answer, "y") ||
@@ -762,37 +771,13 @@ char *help_unknown_cmd(const char *cmd, const struct strvec *args)
fprintf_ln(stderr, _("git: '%s' is not a git command. See 'git --help'."), cmd);
if (SIMILAR_ENOUGH(best_similarity)) {
- if (n == 1 && isatty(0) && isatty(2)) {
- char *answer;
- struct strbuf msg = STRBUF_INIT;
- struct strbuf full_cmd = STRBUF_INIT;
- strbuf_addstr(&full_cmd, main_cmds.names[0]->name);
- if (args) {
- for (size_t j = 1; j < args->nr; j++) {
- strbuf_addch(&full_cmd, ' ');
- strbuf_addstr(&full_cmd, args->v[j]);
- }
- }
- strbuf_addf(&msg, _("\nDid you mean 'git %s'? [y/N] "),
- full_cmd.buf);
- strbuf_release(&full_cmd);
- answer = git_prompt(msg.buf, PROMPT_ECHO);
- strbuf_release(&msg);
- if (starts_with(answer, "y") || starts_with(answer, "Y")) {
- char *assumed = xstrdup(main_cmds.names[0]->name);
- cmdnames_release(&cfg.aliases);
- cmdnames_release(&main_cmds);
- cmdnames_release(&other_cmds);
- return assumed;
- }
- } else {
- fprintf_ln(stderr,
- Q_("\nThe most similar command is",
- "\nThe most similar commands are",
- n));
- for (i = 0; i < n; i++)
- fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
- }
+ fprintf_ln(stderr,
+ Q_("\nThe most similar command is",
+ "\nThe most similar commands are",
+ n));
+
+ for (i = 0; i < n; i++)
+ fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
}
exit(1);
diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh
index 6fe2da1595..75821d63e1 100755
--- a/t/t9003-help-autocorrect.sh
+++ b/t/t9003-help-autocorrect.sh
@@ -70,57 +70,18 @@ test_expect_success 'autocorrect works in work tree created from bare repo' '
git -C worktree -c help.autocorrect=immediate status
'
-# Default behaviour (no help.autocorrect set): when there is exactly one
-# similar command but the session is non-interactive, fall back to printing
-# the suggestion list and exiting rather than showing a prompt.
-test_expect_success 'default: single match non-interactive shows suggestion and fails' '
- test_might_fail git config --unset help.autocorrect &&
-
- test_must_fail git lfg 2>actual &&
- grep "most similar command" actual &&
- grep "lgf" actual
-'
-
-test_expect_success 'default: multiple matches non-interactive shows list and fails' '
- test_might_fail git config --unset help.autocorrect &&
-
- test_must_fail git com 2>actual &&
- grep "most similar commands" actual &&
- grep "commit" actual
-'
-
-# Interactive prompt tests require a real TTY. On macOS the TTY prereq is
-# skipped due to IO::Pty reliability issues; these tests run on Linux CI.
-test_expect_success TTY 'default: single match interactive, answer y runs command' '
- git config --unset help.autocorrect &&
-
- write_script git-typotest <<-\EOF &&
- echo typotest-ran
- EOF
- PATH="$PATH:." export PATH &&
-
- # Feed "y" to /dev/tty via a wrapper that answers the prompt
- write_script answer-prompt <<-\EOF &&
- # Write the answer to the controlling terminal
- printf "y\n" >/dev/tty
- exec "$@"
- EOF
-
- test_terminal ./answer-prompt git typotest 2>err >out &&
- grep "typotest-ran" out &&
- grep "Did you mean" err
-'
-
-test_expect_success TTY 'default: single match interactive, answer n exits cleanly' '
- git config --unset help.autocorrect &&
+# autocorrect=prompt should include the original arguments in the prompt.
+# Requires a TTY; skipped on macOS due to IO::Pty reliability issues.
+test_expect_success TTY 'autocorrect=prompt includes arguments in prompt' '
+ git config help.autocorrect prompt &&
write_script answer-prompt-no <<-\EOF &&
printf "n\n" >/dev/tty
exec "$@"
EOF
- test_must_fail test_terminal ./answer-prompt-no git typotest 2>err &&
- grep "Did you mean" err
+ test_must_fail test_terminal ./answer-prompt-no git lfg --oneline 2>actual &&
+ grep "lgf --oneline" actual
'
test_done
--
2.50.1
^ permalink raw reply related
* Re: Strange behavior of "git log" with file argument
From: Junio C Hamano @ 2026-06-19 17:02 UTC (permalink / raw)
To: Vincent Lefevre; +Cc: git
In-Reply-To: <20260619154448.GA769454@qaa.vinc17.org>
Vincent Lefevre <vincent@vinc17.net> writes:
> "git log git-gui/git-gui--askyesno.sh" outputs nothing. To get logs, I
> can add the -m option. In particular, this shows 3 non-merge commits.
This is a known joy of subtree-merge hack.
You could probably do
$ git log -- git-gui/git-gui--askyesno.sh git-gui--askyesno.sh
The thing is, in git-gui project, git-gui--askyesno.sh script is at
the root level of its working tree, and we are subtree-merging it in
a subdirectory. Once the history traversal realizes that a change
to the script came from git-gui history, it would need to be told
that it needs to pay attention to git-gui--askyesno.sh at the root
tree as well.
^ permalink raw reply
* Re: [PATCH v3 15/17] odb/source-packed: stub out remaining functions
From: Junio C Hamano @ 2026-06-19 16:40 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Karthik Nayak, Justin Tobler
In-Reply-To: <ajTRK4nBxyv3YIgs@pks.im>
Patrick Steinhardt <ps@pks.im> writes:
>> diff --git a/odb/source-packed.c b/odb/source-packed.c
>> index 42c28fba0e..decc81aa52 100644
>> --- a/odb/source-packed.c
>> +++ b/odb/source-packed.c
>> @@ -503,7 +503,7 @@ static int odb_source_packed_freshen_object(struct odb_source *source,
>>
>> static int odb_source_packed_write_object(struct odb_source *source UNUSED,
>> const void *buf UNUSED,
>> - unsigned long len UNUSED,
>> + size_t len UNUSED,
>> enum object_type type UNUSED,
>> struct object_id *oid UNUSED,
>> struct object_id *compat_oid UNUSED,
>
> Thanks for the heads up, the change looks obviously correct to me. I'm
> also happy to send a rebased version -- just give me a nudge and I'll do
> that.
Nah, I expect the other topic would be ready and among the first
batch to graduate post 2.55 final, so it is not too much of a hassle
to carry the merge-fix around for me. I do not even expect I need
"refs rename" for this, as the merge order will unlikely to be
flipped ;-)
THanks.
^ permalink raw reply
* [PATCH v4 1/1] environment: move 'protect_hfs' and 'protect_ntfs' into 'repo_config_values'
From: Tian Yuchen @ 2026-06-19 16:38 UTC (permalink / raw)
To: git; +Cc: Tian Yuchen, Christian Couder, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260619163823.652091-1-cat@malon.dev>
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'
To ensure code readability, the getter functions
'repo_protect_hfs()' and 'repo_protect_ntfs()'
have been introduced.
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.
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>
---
compat/mingw.c | 2 +-
environment.c | 22 ++++++++++++++++++----
environment.h | 12 ++++++++++--
read-cache.c | 7 ++++---
t/helper/test-path-utils.c | 24 +++++++++++++++---------
5 files changed, 48 insertions(+), 19 deletions(-)
diff --git a/compat/mingw.c b/compat/mingw.c
index aa7525f419..af87df77fd 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 (!repo_protect_ntfs(the_repository))
return 1;
skip_dos_drive_prefix((char **)&path);
diff --git a/environment.c b/environment.c
index fc3ed8bb1c..f34f6fc750 100644
--- a/environment.c
+++ b/environment.c
@@ -82,12 +82,10 @@ unsigned long pack_size_limit_cfg;
#ifndef PROTECT_HFS_DEFAULT
#define PROTECT_HFS_DEFAULT 0
#endif
-int protect_hfs = PROTECT_HFS_DEFAULT;
#ifndef PROTECT_NTFS_DEFAULT
#define PROTECT_NTFS_DEFAULT 1
#endif
-int protect_ntfs = PROTECT_NTFS_DEFAULT;
/*
* The character that begins a commented line in user-editable file
@@ -142,6 +140,20 @@ int is_bare_repository(void)
return is_bare_repository_cfg && !repo_get_work_tree(the_repository);
}
+int repo_protect_ntfs(struct repository *repo)
+{
+ return (repo && repo->initialized) ?
+ repo_config_values(repo)->protect_ntfs :
+ PROTECT_NTFS_DEFAULT;
+}
+
+int repo_protect_hfs(struct repository *repo)
+{
+ return (repo && repo->initialized) ?
+ repo_config_values(repo)->protect_hfs :
+ PROTECT_HFS_DEFAULT;
+}
+
int have_git_dir(void)
{
return startup_info->have_repository
@@ -541,12 +553,12 @@ int git_default_core_config(const char *var, const char *value,
}
if (!strcmp(var, "core.protecthfs")) {
- protect_hfs = git_config_bool(var, value);
+ cfg->protect_hfs = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "core.protectntfs")) {
- protect_ntfs = git_config_bool(var, value);
+ cfg->protect_ntfs = git_config_bool(var, value);
return 0;
}
@@ -720,5 +732,7 @@ 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;
}
diff --git a/environment.h b/environment.h
index 9eb97b3869..b1ae4a70de 100644
--- a/environment.h
+++ b/environment.h
@@ -91,6 +91,8 @@ struct repo_config_values {
/* section "core" config values */
char *attributes_file;
int apply_sparse_checkout;
+ int protect_hfs;
+ int protect_ntfs;
/* section "branch" config values */
enum branch_track branch_track;
@@ -123,6 +125,14 @@ int git_default_config(const char *, const char *,
int git_default_core_config(const char *var, const char *value,
const struct config_context *ctx, void *cb);
+/*
+ * Getters for the `protect_hfs` and `protect_ntfs` fields of `struct repo_config_values`.
+ * They check `repo->initialized` to prevent calling `repo_config_values()`
+ * before the repository setup is fully complete or in non-git environments.
+ */
+int repo_protect_hfs(struct repository *repo);
+int repo_protect_ntfs(struct repository *repo);
+
void repo_config_values_init(struct repo_config_values *cfg);
/*
@@ -173,8 +183,6 @@ extern int pack_compression_level;
extern unsigned long pack_size_limit_cfg;
extern int precomposed_unicode;
-extern int protect_hfs;
-extern int protect_ntfs;
extern int core_sparse_checkout_cone;
extern int sparse_expect_files_outside_of_patterns;
diff --git a/read-cache.c b/read-cache.c
index 21829102ae..2c6a60c756 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1002,7 +1002,7 @@ static enum verify_path_result verify_path_internal(const char *path,
return PATH_OK;
if (is_dir_sep(c)) {
inside:
- if (protect_hfs) {
+ if (repo_protect_hfs(the_repository)) {
if (is_hfs_dotgit(path))
return PATH_INVALID;
@@ -1011,7 +1011,7 @@ static enum verify_path_result verify_path_internal(const char *path,
return PATH_INVALID;
}
}
- if (protect_ntfs) {
+ if (repo_protect_ntfs(the_repository)) {
#if defined GIT_WINDOWS_NATIVE || defined __CYGWIN__
if (c == '\\')
return PATH_INVALID;
@@ -1035,7 +1035,8 @@ static enum verify_path_result verify_path_internal(const char *path,
if (c == '\0')
return S_ISDIR(mode) ? PATH_DIR_WITH_SEP :
PATH_INVALID;
- } else if (c == '\\' && protect_ntfs) {
+ } else if (c == '\\' &&
+ repo_protect_ntfs(the_repository)) {
if (is_ntfs_dotgit(path))
return PATH_INVALID;
if (S_ISLNK(mode)) {
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index 15eb44485c..f77b3f9d70 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -250,6 +250,7 @@ static int protect_ntfs_hfs_benchmark(int argc, const char **argv)
double m[3][2], v[3][2];
uint64_t cumul;
double cumul2;
+ int ntfs, hfs;
if (argc > 1 && !strcmp(argv[1], "--with-symlink-mode")) {
file_mode = 0120000;
@@ -276,8 +277,13 @@ static int protect_ntfs_hfs_benchmark(int argc, const char **argv)
names[i][--len] = (char)(' ' + (my_random() % ('\x7f' - ' ')));
}
- for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++)
- for (protect_hfs = 0; protect_hfs < 2; protect_hfs++) {
+ if (!the_repository->gitdir)
+ the_repository->gitdir = xstrdup(".git");
+
+ for (ntfs = 0; ntfs < 2; ntfs++)
+ for (hfs = 0; hfs < 2; hfs++) {
+ repo_config_values(the_repository)->protect_ntfs = ntfs;
+ repo_config_values(the_repository)->protect_hfs = hfs;
cumul = 0;
cumul2 = 0;
for (i = 0; i < repetitions; i++) {
@@ -285,18 +291,18 @@ static int protect_ntfs_hfs_benchmark(int argc, const char **argv)
for (j = 0; j < nr; j++)
verify_path(names[j], file_mode);
end = getnanotime();
- printf("protect_ntfs = %d, protect_hfs = %d: %lfms\n", protect_ntfs, protect_hfs, (end-begin) / (double)1e6);
+ printf("protect_ntfs = %d, protect_hfs = %d: %lfms\n", ntfs, hfs, (end-begin) / (double)1e6);
cumul += end - begin;
cumul2 += (end - begin) * (end - begin);
}
- m[protect_ntfs][protect_hfs] = cumul / (double)repetitions;
- v[protect_ntfs][protect_hfs] = my_sqrt(cumul2 / (double)repetitions - m[protect_ntfs][protect_hfs] * m[protect_ntfs][protect_hfs]);
- printf("mean: %lfms, stddev: %lfms\n", m[protect_ntfs][protect_hfs] / (double)1e6, v[protect_ntfs][protect_hfs] / (double)1e6);
+ m[ntfs][hfs] = cumul / (double)repetitions;
+ v[ntfs][hfs] = my_sqrt(cumul2 / (double)repetitions - m[ntfs][hfs] * m[ntfs][hfs]);
+ printf("mean: %lfms, stddev: %lfms\n", m[ntfs][hfs] / (double)1e6, v[ntfs][hfs] / (double)1e6);
}
- for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++)
- for (protect_hfs = 0; protect_hfs < 2; protect_hfs++)
- printf("ntfs=%d/hfs=%d: %lf%% slower\n", protect_ntfs, protect_hfs, (m[protect_ntfs][protect_hfs] - m[0][0]) * 100 / m[0][0]);
+ for (ntfs = 0; ntfs < 2; ntfs++)
+ for (hfs = 0; hfs < 2; hfs++)
+ printf("ntfs=%d/hfs=%d: %lf%% slower\n", ntfs, hfs, (m[ntfs][hfs] - m[0][0]) * 100 / m[0][0]);
return 0;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v4 0/1] environment: move protect_hfs and protect_ntfs into repo_config_values
From: Tian Yuchen @ 2026-06-19 16:38 UTC (permalink / raw)
To: git; +Cc: Tian Yuchen, Christian Couder, Ayush Chandekar,
Olamide Caleb Bello
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.
For reviewers familiar with previous libification efforts, Derrick Stolee
attempted to wrap this kind of filesystem-level variable using a
lazy-loaded global accessor get_int_config_global() [1].
However, as Glen Choo pointed out in his review of that series [2],
it is strongly preferred to use plain fields in a repository-scoped
struct over global lazy-loaders, provided those fields are properly
initialized during the setup process.
By moving these variables into repo_config_values and parsing
them eagerly, we successfully tie the filesystem security flags
to the specific repository instance without altering the timing
of configuration warnings or introducing new global states.
Thanks!
Change since V3:
- In repo_protect_hfs() and repo_protect_ntfs(), change repo->gitdir to
using (repo && repo->initialized).
[1] https://lore.kernel.org/git/a42dd9397d07b2dc4a0d7e75bfe1af2e46cad262.1685716420.git.gitgitgadget@gmail.com/
[2] https://lore.kernel.org/git/kl6lbkhpzujf.fsf@chooglen-macbookpro.roam.corp.google.com/
[3] https://lore.kernel.org/git/20260612160527.167203-1-cat@malon.dev/
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 (1):
environment: move 'protect_hfs' and 'protect_ntfs' into
'repo_config_values'
compat/mingw.c | 2 +-
environment.c | 22 ++++++++++++++++++----
environment.h | 12 ++++++++++--
read-cache.c | 7 ++++---
t/helper/test-path-utils.c | 24 +++++++++++++++---------
5 files changed, 48 insertions(+), 19 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH] help: prompt user to run corrected command on typo
From: Junio C Hamano @ 2026-06-19 16:37 UTC (permalink / raw)
To: Jishnu C K; +Cc: Justin Tobler, git
In-Reply-To: <6a34dc40.2c570c9e.381c97.203f@mx.google.com>
Jishnu C K <jishnuck26@gmail.com> writes:
> You're right that `help.autocorrect=prompt` exists and is similar.
> Our change differs in two ways:
>
> 1. No configuration needed. The existing prompt mode requires the user
I do not particularly see it as an advantage.
> 2. The prompt includes the original arguments. `help.autocorrect=prompt`
> shows only:
>
> Run 'checkout' instead [y/N]?
>
> Our prompt shows the full corrected invocation:
>
> Did you mean 'git checkout neo'? [y/N]
>
> This lets the user confirm exactly what will run, including their
> original arguments, before pressing 'y'.
This may be an improvement, but is there a reason why such a change
must be done as a parallel and unrelated (re)implementation and not
as an incrementa improvement to the code that implements the
existing feature?
^ permalink raw reply
* Bug: Checkout in sparse mode can overwrite uncommited files
From: charmocc @ 2026-06-19 16:31 UTC (permalink / raw)
To: git@vger.kernel.org
Hey. Today I discovered inconsistent behavior in sparse mode that may lead to data loss during initial checkout. Please have a look.
What did you do before the bug happened? (Steps to reproduce your issue)
$ git init foo
$ cd foo
$ echo 1 > file
$ git add file
$ git commit -m 'test'
$ cd ..
$ git clone --no-checkout foo bar
$ cd bar
$ echo 2 > file
$ git checkout
error: The following untracked working tree files would be overwritten by checkout:
file
Please move or remove them before you switch branches.
Aborting
$ cat file
2
$ git sparse-checkout set
$ git checkout
warning: The following paths were already present and thus not updated despite sparse patterns:
file
After fixing the above paths, you may want to run `git sparse-checkout reapply`.
Your branch is up to date with 'origin/master'.
$ cat file
1
What did you expect to happen? (Expected behavior)
Checkout should fail due to uncommited changes
What happened instead? (Actual behavior)
After enabling sparse mode checkout overwrites file in working directory
What's different between what you expected and what actually happened?
In normal mode checkout is aborted correctly but in sparse it overwrites data and gives misleading message
Anything else you want to add:
[System Info]
git version:
git version 2.53.0
cpu: x86_64
no commit associated with this build
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
rust: disabled
gettext: enabled
libcurl: 8.18.0
zlib: 1.3.1
SHA-1: SHA1_DC
SHA-256: SHA256_BLK
default-ref-format: files
default-hash: sha1
uname: Linux 7.0.0-22-generic #22-Ubuntu SMP PREEMPT_DYNAMIC Mon May 25 15:54:34 UTC 2026 x86_64
compiler info: gnuc: 15.2
libc info: glibc: 2.43
$SHELL (typically, interactive shell): /bin/bash
[Enabled Hooks]
^ permalink raw reply
* Re: [PATCH] t4216: fix no-op test that breaks TAP output
From: Junio C Hamano @ 2026-06-19 16:29 UTC (permalink / raw)
To: Taylor Blau; +Cc: Patrick Steinhardt, git, Todd Zullinger, Jeff King
In-Reply-To: <ajVMZpjTKiXc7TRe@nand.local>
Taylor Blau <me@ttaylorr.com> writes:
> Given this and the above, I would probably err on the side of
> designating this as 'test_lazy_prereq' or otherwise silencing the output
> of 'test_cmp' so that this does not taint the TAP output.
We can argue the merit and demerit with a good log message. The
central issue at hand is how precious 52a9 in the script lost by
this patch is (in other words, are we checking more than "is our
char signed or unsigned?").
By the way, I do not quite get the _BY_DEFAULT in the name
SIGNED_CHAR_BY_DEFAULT. The builder may have configured to use
signed char on a platform that can handle both and their char is by
default unsigned, and under such a condition, we would set this
prerequisite, even though the default on such a platform is
unsigned, no?
^ permalink raw reply
* Re: Pinned references?
From: Junio C Hamano @ 2026-06-19 16:25 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Erik Östlund, git
In-Reply-To: <ajTx9vLIWK5wvTHM@pks.im>
Patrick Steinhardt <ps@pks.im> writes:
> You can already kind of do this:
>
> $ git rev-parse v2.54.0
> 0b13e48a3a30cdfa94e8ef842e24d6045ab3d015
>
> $ git rev-parse v2.54.0-0-g0b13e48a3
> 0b13e48a3a30cdfa94e8ef842e24d6045ab3d015
>
> $ git rev-parse v2.54.0-0-g95e20213f
> 95e20213faefeb95df29277c58ac1980ab68f701
>
> This is described under gitrevisions(7), `<describeOutput>`. The only
> gotcha is that this format will not verify that the tag and the object
> ID actually match. But other than that it gives you the ability to have
> both the human-readable name and the machine-readable commit ID in
> there.
>
> As said, we don't verify that those two revisions actually match. So in
> the case where they don't the result is certainly going to be lots of
> confusion. It certainly is one of the more surprising syntaxes that we
> have in Git.
It is very unlikely we would change this, but it is a fun thought
experiment to imagine what would have happened if we insisted (i.e.,
verified and then died if it does not hold true) on the presence of
v2.54.0 tag and when the "hop" count is "-0-", we also insisted that
the hexadecimal part exactly matched the contents of v2.54.0 tag, or
when the "hop" count is not zero, we insisted that the hexadecimal
part names a commit that is descendant of the commit v2.54.0 names.
^ permalink raw reply
* Re: [PATCH] help: prompt user to run corrected command on typo
From: Justin Tobler @ 2026-06-19 16:24 UTC (permalink / raw)
To: Jishnu C K; +Cc: git, gitster
In-Reply-To: <6a34dc40.2c570c9e.381c97.203f@mx.google.com>
On 26/06/18 11:05PM, Jishnu C K wrote:
> If the consensus is that the default should remain non-interactive,
> we are happy to rework this as an improvement to the existing
> `autocorrect=prompt` mode (showing args in the prompt) with
> documentation updates to make the option more discoverable.
I'm not sure what the current consensus is here, but as it would be a
change to the existing behavior it would need its merits discussed
accordingly. Personally, I think requiring users to opt-in to
autocorrect isn't a significant barrier itself. It does require that
users know that the option is available in the first place though. So if
discoverablility of this feature is lacking and can be improved in
documentation, that certainly seems like a reasonable change.
Regarding changing the prompt to display command arguments, I'm
relatively indifferent towards it. I'm not sure it adds a ton of value,
but maybe other folks will have a different opinion.
-Justin
^ permalink raw reply
* [PATCH v4 3/3] environment: move trust_executable_bit into repo_config_values
From: Tian Yuchen @ 2026-06-19 16:21 UTC (permalink / raw)
To: git; +Cc: ps, Tian Yuchen, Christian Couder, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260619162105.648495-1-cat@malon.dev>
Move the global 'trust_executable_bit' configuration
into the repository-specific 'repo_config_values'
struct. To ensure code readability, the getter function
'repo_trust_executable_bit()' has been introduced.
For now, associated functions 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 | 2 +-
environment.c | 11 +++++++++--
environment.h | 9 ++++++++-
read-cache.c | 8 ++++----
4 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/apply.c b/apply.c
index 249248d4f2..fbb907d3c0 100644
--- a/apply.c
+++ b/apply.c
@@ -3893,7 +3893,7 @@ static int check_preimage(struct apply_state *state,
if (*ce && !(*ce)->ce_mode)
BUG("ce_mode == 0 for path '%s'", old_name);
- if (trust_executable_bit || !S_ISREG(st->st_mode))
+ if (repo_trust_executable_bit(the_repository) || !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..348053146e 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;
@@ -142,6 +141,13 @@ int is_bare_repository(void)
return is_bare_repository_cfg && !repo_get_work_tree(the_repository);
}
+int repo_trust_executable_bit(struct repository *repo)
+{
+ return (repo && repo->initialized) ?
+ repo_config_values(repo)->trust_executable_bit :
+ 1;
+}
+
int have_git_dir(void)
{
return startup_info->have_repository
@@ -305,7 +311,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 +726,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..6607ac9408 100644
--- a/environment.h
+++ b/environment.h
@@ -91,6 +91,7 @@ struct repo_config_values {
/* section "core" config values */
char *attributes_file;
int apply_sparse_checkout;
+ int trust_executable_bit;
/* section "branch" config values */
enum branch_track branch_track;
@@ -123,6 +124,13 @@ int git_default_config(const char *, const char *,
int git_default_core_config(const char *var, const char *value,
const struct config_context *ctx, void *cb);
+/*
+ * Getter for the `trust_executable_bit` field of `struct repo_config_values`.
+ * It checks `repo->initialized` to prevent calling repo_config_values()`
+ * before the repository setup is fully complete or in non-git environments.
+ */
+int repo_trust_executable_bit(struct repository *repo);
+
void repo_config_values_init(struct repo_config_values *cfg);
/*
@@ -160,7 +168,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..757249a449 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -207,7 +207,7 @@ 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 (!repo_trust_executable_bit(the_repository) && S_ISREG(mode)) {
if (ce && S_ISREG(ce->ce_mode))
return ce->ce_mode;
return create_ce_mode(0666);
@@ -221,7 +221,7 @@ static unsigned int st_mode_from_ce(const struct cache_entry *ce)
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 & (repo_trust_executable_bit(the_repository) ? 0755 : 0644)) | S_IFREG;
case S_IFGITLINK:
return S_IFDIR | 0755;
case S_IFDIR:
@@ -331,7 +331,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 (repo_trust_executable_bit(the_repository) &&
(0100 & (ce->ce_mode ^ st->st_mode)))
changed |= MODE_CHANGED;
break;
@@ -752,7 +752,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 (repo_trust_executable_bit(the_repository) && 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 v4 2/3] read-cache: move 'ce_mode_from_stat()' to 'read-cache.c'
From: Tian Yuchen @ 2026-06-19 16:21 UTC (permalink / raw)
To: git; +Cc: ps, Tian Yuchen, Christian Couder, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260619162105.648495-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".
At present, this change has no visible impact, but it is crucial
for our future plans to pass in the repo context. Comment
has been added whilst we are at it.
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 | 23 +++++++++--------------
2 files changed, 22 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..9088a0724a 100644
--- a/read-cache.h
+++ b/read-cache.h
@@ -5,20 +5,15 @@
#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);
-}
+/*
+ * Determine the appropriate index mode for a file based on its stat()
+ * information and the existing cache entry (if any).
+ *
+ * This function handles degradation for filesystems that lack
+ * symlink support or reliable executable bits.
+ */
+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
* [PATCH v4 1/3] read-cache: remove redundant extern declarations
From: Tian Yuchen @ 2026-06-19 16:21 UTC (permalink / raw)
To: git; +Cc: ps, Tian Yuchen, Christian Couder, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260619162105.648495-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 v4 0/3] environment: migrate 'trust_executable_bit' into 'repo_config_values'
From: Tian Yuchen @ 2026-06-19 16:21 UTC (permalink / raw)
To: git; +Cc: ps, Tian Yuchen, Christian Couder, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260612160527.167203-1-cat@malon.dev>
The 'core.filemode' (stored as 'trust_executable_bit') configuration
act as a core filesystem capability flag.
This series moves it into 'struct 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.
Note: 'repo_config_values()' still does not support any struct
repository other than the_repository due to how deeply these flags
are accessed. 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 [1]
[PATCH] Refactor 'trust_executable_bit' to repository-scoped setting [2]
(This previous attempt 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: [3])
Change since V3:
- In repo_trust_executable_bit(), change repo->gitdir to using
(repo && repo->initialized).
Thanks!
[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 (3):
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
apply.c | 2 +-
environment.c | 11 +++++++++--
environment.h | 9 ++++++++-
read-cache.c | 21 ++++++++++++++++-----
read-cache.h | 23 +++++++++--------------
5 files changed, 43 insertions(+), 23 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH v3] SubmittingPatches: address design critiques
From: Junio C Hamano @ 2026-06-19 16:17 UTC (permalink / raw)
To: git; +Cc: Michael Montalbo, Kristoffer Haugsbakk
In-Reply-To: <xmqqpl1oteoi.fsf@gitster.g>
Contributors sometimes fail to answer fundamental design or
viability comments from reviewers and submit subsequent rounds
without addressing them. When design decisions are resolved on the
mailing list, the final justification should be recorded in the
commit messages.
Instruct authors to be particularly mindful of critiques regarding
high-level design or viability, to defend their choices on the list,
and to accompany new iterations with clearer explanations in the cover
letter, responses, and revised commit messages. Also instruct them to
explicitly document the resolution of these concerns in the commit
message body to keep the historical record complete.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
* Updated a bit after reading comments by Kichael and Kristoffer
Documentation/SubmittingPatches | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index f042bb5aaf..a9789e5303 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -51,6 +51,21 @@ area.
respond to them with "Reply-All" on the mailing list, while taking
them into account while preparing an updated set of patches.
+
+Be particularly mindful of critiques regarding the high-level design
+or viability of your proposal (e.g., questioning if the feature is
+worth implementing, or if the chosen approach is appropriate). Defend
+your design decisions on the list first, work with reviewers and other
+members to improve the design before revising the implementation, to
+avoid wasting effort on an implementation before its design is solid.
++
+Make sure that any new version explains and justifies those design
+decisions more clearly, in the cover letter and in the revised commit
+messages. Aim to make the reviewers say "it is now clear why we may
+want to do this with the updated version".
++
+Topics with unresolved fundamental design critiques will not be
+considered ready for merging.
++
It is often beneficial to allow some time for reviewers to provide
feedback before sending a new version, rather than sending an updated
series immediately after receiving a review. This helps collect broader
@@ -323,6 +338,10 @@ The body should provide a meaningful commit message, which:
. alternate solutions considered but discarded, if any.
+. records the resolution of design or viability concerns raised by the
+ community during the review, if any, ensuring the historical record
+ explains why the chosen approach was accepted over alternatives.
+
[[present-tense]]
The problem statement that describes the status quo is written in the
present tense. Write "The code does X when it is given input Y",
--
2.55.0-rc1-129-gff98d784de
^ permalink raw reply related
* Re: [PATCH v3 0/4] history: add squash subcommand to fold a range
From: Junio C Hamano @ 2026-06-19 16:11 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Harald Nordgren via GitGitGadget, git, Harald Nordgren
In-Reply-To: <ajU4JYYUTz5r-Xgc@pks.im>
Patrick Steinhardt <ps@pks.im> writes:
> There are two more modes:
>
> - If a reference points at an intermediate commit then it stays there.
>
> - We detect this case and reject the update. Optionally, we may ask
> the user what they intend to do with those other refs.
>
> It really is kind of ambiguous what is supposed to happen, and I can
> think of different scenarios where each of the possibilities would be
> the best choice. So ultimately, I think the last option is the best one,
> as it also gives us a way to iterate.
>
> If so, a user would already be able to achieve that other refs keep
> pointing at X by saying `git history squash --update-refs=head`. The
> other modes can then be added at a later point in time as the need
> arises.
Yeah, sounds like we should detect and fail this case, with advice()
to use --update-refs.
^ permalink raw reply
* Re: [PATCH v2 0/2] environment: move ignore_case into repo_config_values
From: Tian Yuchen @ 2026-06-19 16:01 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, ps, phillip.wood123, johannes.schindelin, stolee
In-Reply-To: <xmqqldcct2hx.fsf@gitster.g>
On 6/18/26 21:14, Junio C Hamano wrote:
> Tian Yuchen <cat@malon.dev> writes:
>
>> Related materials:
>>
>> [1] In this patch to migrate protect_hfs and protect_ntfs, the approach
>> of introducing getters has been endorsed.
>>
>> [2] Derrick Stolee's previous attempt. The reasons for the failure are
>> also mentioned in [1].
>
> [1] here refers to the starting message of the whole hfs/ntfs thing.
> Do you mean that people must read the entire thread to find out what
> the reasons for the failure was? For that matter, it is not clear,
> unless readers read the whole thread, where the approach of using
> getters was "endorsed", either.
>
What I meant to say was that the reasons were already mentioned in the
_cover-letter_ for the patch which the link refers to. It’s certainly my
fault for not expressing myself clearly. Nevertheless, I’ve briefly
explained the content of the link directly in the cover-letter for V3.
>> [1] https://lore.kernel.org/git/20260606143412.15443-1-cat@malon.dev/
>> [2] https://lore.kernel.org/git/2b4198c09cb6c04c60608d19072d419503dfe5df.1685716421.git.gitgitgadget@gmail.com/
>
>> Changes since V1:
>>
>> - s/repo_get_ignore_case()/repo_ignore_case()
>>
>> - Use repo->initialized instead of repo->gitdir
>
> I do not think I have any objections to these changes from the
> previous iteration. There may be some other things in the new
> iteration but I'll have to go in and read the patches to find them
> out (if they exist).
>
> Thanks.
Additionally, I’ll change 'repo->gitdir' to using 'repo->initialized'
for the previous three bits as well.
Regards, yuchen
^ permalink raw reply
* Re: [PATCH v14 4/6] branch: add --prune-merged <branch>
From: Junio C Hamano @ 2026-06-19 16:01 UTC (permalink / raw)
To: Phillip Wood
Cc: Harald Nordgren, Harald Nordgren via GitGitGadget, git,
Kristoffer Haugsbakk, Johannes Sixt
In-Reply-To: <xmqqh5mymt8i.fsf@gitster.g>
Junio C Hamano <gitster@pobox.com> writes:
> Phillip Wood <phillip.wood123@gmail.com> writes:
>
>> I was thinking that if I have feature1 with upstream origin/master and
>> feature2 with upstream feautre1, then once feature1 is merged I'd still
>> like "git log @{u}.." and "git rebase" without an explicit upstream to
>> work when feature2 is checked out. If "git branch --prune-merged
>> origin/master" deletes feautre1 then those commands stop working. Maybe
>> it would be sensible to update feature2's upstream once feature1 is
>> merged (which I think is what you're saying above) but do we really want
>> to force the user to do that by deleting feature1?
>
> Ahh, reference with @{upstream}. Yeah, that _does_ make sense.
Oops, hit [Send] too soon.
This reminds me of the discussion on a separate thread on "history
squash" and "history drop", that squashes a segment of history
o---A---B---C
into a single commit (i.e., "We want to remove A and B and keep C" or
"We want to squash A, B, and C into a single commit")
o---X
It is natural to consider "X" in the new history a counterpart of
"C" in the original history, but what should happen to refs that
used to point at A and B? Sometimes it may make sense to drop the
refs that point into commits that disappear in the final history,
sometimes it may make sense to move them to point at the surviving
commit, sometimes it may make sense to leave them as-is (i.e., refs
still point at A and B in the old history, preventing them from
getting gc'ed).
And the safest behaviour would be to ask the user, i.e., notice that
we would get into this iffy situation and abort, asking the user
what to do.
Now that safest behaviour in the other topic translates to
- Notice that a branch that is deleted (because it itself is
merged) is still depended upon by being @{upstream} of somebody
else, and when it happens, fail the operation (i.e., do not
delete the branch).
What are the viable choices we can offer to the user in such a
situation? I think there are a few viable choices.
- Make the dependent branch no longer depend on anything. Asking
feature2@{upstream} would fail.
- Move the @{upstream} of feature2 to the branch that "merged"
feature1 and caused its removal. Asking feature2@{upstream}
would answer origin/master, which feature1 was removed after
getting merged.
There may be others. And there may be relationship similar to
feature1 vs feature2 that is not @{upstream} but something else that
makes a branch still "depend on" the other branch getting deleted.
Do we also need the same safety around "git branch -d feature1" by
the way? The "-d" option with safety checks the same "is feature1
already merged (to its upstream)?" condition, so it can protect the
feature2 branch the same way, by saying either "oops, you cannot
delete feature1 because you still have other branches like feature2
that depend on it", or "ok, featur2 used to depend on feature1, but
because we are deleting feature1 based on it being in origin/master,
we will make feature2 depend on origin/master from now on".
Thanks.
^ permalink raw reply
* [PATCH v3 2/2] config: use repo_ignore_case() to access core.ignorecase
From: Tian Yuchen @ 2026-06-19 15:51 UTC (permalink / raw)
To: git
Cc: ps, phillip.wood123, johannes.schindelin, stolee, Tian Yuchen,
Christian Couder, Ayush Chandekar, Olamide Caleb Bello
In-Reply-To: <20260619155152.642760-1-cat@malon.dev>
Replace the accesses to the global 'ignore_case' variable with
calls to 'repo_ignore_case(the_repository)'. This step eliminates
the 'ignore_case' global state.
Note on compat/win32/path-utils.c:
To eliminate the global state, several helper functions
(e.g. 'win32_fspathncmp()') now read from
'repo_ignore_case(the_repository)'. While this introduces
dependency on 'repository.h' into the 'compat/', it avoids massive
refactoring of the signatures across the codebase.
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 | 2 +-
builtin/fetch.c | 2 +-
builtin/mv.c | 2 +-
compat/win32/path-utils.c | 3 ++-
dir.c | 18 +++++++++---------
environment.c | 3 +--
environment.h | 1 -
fsmonitor.c | 2 +-
name-hash.c | 6 +++---
read-cache.c | 6 +++---
refs/files-backend.c | 4 ++--
submodule.c | 2 +-
t/helper/test-lazy-init-name-hash.c | 2 +-
unpack-trees.c | 2 +-
14 files changed, 27 insertions(+), 28 deletions(-)
diff --git a/apply.c b/apply.c
index 249248d4f2..620c88d2a0 100644
--- a/apply.c
+++ b/apply.c
@@ -4008,7 +4008,7 @@ static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *na
struct cache_entry *ce;
ce = index_file_exists(state->repo->index, name->buf,
- name->len, ignore_case);
+ name->len, repo_ignore_case(the_repository));
if (ce && S_ISLNK(ce->ce_mode))
return 1;
} else {
diff --git a/builtin/fetch.c b/builtin/fetch.c
index e4e8a72ed9..073e716bc4 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1819,7 +1819,7 @@ static void ref_transaction_rejection_handler(const char *refname,
{
struct ref_rejection_data *data = cb_data;
- if (err == REF_TRANSACTION_ERROR_CASE_CONFLICT && ignore_case &&
+ if (err == REF_TRANSACTION_ERROR_CASE_CONFLICT && repo_ignore_case(the_repository) &&
!data->case_sensitive_msg_shown) {
error(_("You're on a case-insensitive filesystem, and the remote you are\n"
"trying to fetch from has references that only differ in casing. It\n"
diff --git a/builtin/mv.c b/builtin/mv.c
index 948b330639..d60582262c 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -419,7 +419,7 @@ int cmd_mv(int argc,
goto act_on_entry;
}
if (lstat(dst, &st) == 0 &&
- (!ignore_case || strcasecmp(src, dst))) {
+ (!repo_ignore_case(the_repository) || strcasecmp(src, dst))) {
bad = _("destination exists");
if (force) {
/*
diff --git a/compat/win32/path-utils.c b/compat/win32/path-utils.c
index 966ef779b9..f779f367cf 100644
--- a/compat/win32/path-utils.c
+++ b/compat/win32/path-utils.c
@@ -2,6 +2,7 @@
#include "../../git-compat-util.h"
#include "../../environment.h"
+#include "../../repository.h"
int win32_has_dos_drive_prefix(const char *path)
{
@@ -75,7 +76,7 @@ int win32_fspathncmp(const char *a, const char *b, size_t count)
} else if (is_dir_sep(*b))
return +1;
- diff = ignore_case ?
+ diff = repo_ignore_case(the_repository) ?
(unsigned char)tolower(*a) - (int)(unsigned char)tolower(*b) :
(unsigned char)*a - (int)(unsigned char)*b;
if (diff)
diff --git a/dir.c b/dir.c
index 33c81c256e..540dd372c1 100644
--- a/dir.c
+++ b/dir.c
@@ -126,7 +126,7 @@ int count_slashes(const char *s)
int git_fspathcmp(const char *a, const char *b)
{
- return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
+ return repo_ignore_case(the_repository) ? strcasecmp(a, b) : strcmp(a, b);
}
int fspatheq(const char *a, const char *b)
@@ -136,7 +136,7 @@ int fspatheq(const char *a, const char *b)
int git_fspathncmp(const char *a, const char *b, size_t count)
{
- return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
+ return repo_ignore_case(the_repository) ? strncasecmp(a, b, count) : strncmp(a, b, count);
}
int paths_collide(const char *a, const char *b)
@@ -153,7 +153,7 @@ int paths_collide(const char *a, const char *b)
unsigned int fspathhash(const char *str)
{
- return ignore_case ? strihash(str) : strhash(str);
+ return repo_ignore_case(the_repository) ? strihash(str) : strhash(str);
}
int git_fnmatch(const struct pathspec_item *item,
@@ -202,7 +202,7 @@ static int fnmatch_icase_mem(const char *pattern, int patternlen,
use_str = str_buf.buf;
}
- if (ignore_case)
+ if (repo_ignore_case(the_repository))
flags |= WM_CASEFOLD;
match_status = wildmatch(use_pat, use_str, flags);
@@ -1851,7 +1851,7 @@ static struct dir_entry *dir_add_name(struct dir_struct *dir,
struct index_state *istate,
const char *pathname, int len)
{
- if (index_file_exists(istate, pathname, len, ignore_case))
+ if (index_file_exists(istate, pathname, len, repo_ignore_case(the_repository)))
return NULL;
ALLOC_GROW(dir->entries, dir->nr+1, dir->internal.alloc);
@@ -1888,7 +1888,7 @@ static enum exist_status directory_exists_in_index_icase(struct index_state *ist
if (index_dir_exists(istate, dirname, len))
return index_directory;
- ce = index_file_exists(istate, dirname, len, ignore_case);
+ ce = index_file_exists(istate, dirname, len, repo_ignore_case(the_repository));
if (ce && S_ISGITLINK(ce->ce_mode))
return index_gitdir;
@@ -1907,7 +1907,7 @@ static enum exist_status directory_exists_in_index(struct index_state *istate,
{
int pos;
- if (ignore_case)
+ if (repo_ignore_case(the_repository))
return directory_exists_in_index_icase(istate, dirname, len);
pos = index_name_pos(istate, dirname, len);
@@ -2447,7 +2447,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
/* Always exclude indexed files */
has_path_in_index = !!index_file_exists(istate, path->buf, path->len,
- ignore_case);
+ repo_ignore_case(the_repository));
if (dtype != DT_DIR && has_path_in_index)
return path_none;
@@ -3201,7 +3201,7 @@ static int cmp_icase(char a, char b)
{
if (a == b)
return 0;
- if (ignore_case)
+ if (repo_ignore_case(the_repository))
return toupper(a) - toupper(b);
return a - b;
}
diff --git a/environment.c b/environment.c
index bfa3cb3045..c288c3613d 100644
--- a/environment.c
+++ b/environment.c
@@ -46,7 +46,6 @@ int trust_ctime = 1;
int check_stat = 1;
int has_symlinks = 1;
int minimum_abbrev = 4, default_abbrev = -1;
-int ignore_case;
int assume_unchanged;
int is_bare_repository_cfg = -1; /* unspecified */
int warn_on_object_refname_ambiguity = 1;
@@ -342,7 +341,7 @@ int git_default_core_config(const char *var, const char *value,
}
if (!strcmp(var, "core.ignorecase")) {
- ignore_case = git_config_bool(var, value);
+ cfg->ignore_case = git_config_bool(var, value);
return 0;
}
diff --git a/environment.h b/environment.h
index 39a8bf0b49..c15121db65 100644
--- a/environment.h
+++ b/environment.h
@@ -171,7 +171,6 @@ extern int trust_ctime;
extern int check_stat;
extern int has_symlinks;
extern int minimum_abbrev, default_abbrev;
-extern int ignore_case;
extern int assume_unchanged;
extern int warn_on_object_refname_ambiguity;
extern char *apply_default_whitespace;
diff --git a/fsmonitor.c b/fsmonitor.c
index d07dc18967..107767527e 100644
--- a/fsmonitor.c
+++ b/fsmonitor.c
@@ -453,7 +453,7 @@ static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
* case-insensitive file system, try again using the name-hash
* and dir-name-hash.
*/
- if (!nr_in_cone && ignore_case) {
+ if (!nr_in_cone && repo_ignore_case(the_repository)) {
nr_in_cone = handle_using_name_hash_icase(istate, name);
if (!nr_in_cone)
nr_in_cone = handle_using_dir_name_hash_icase(
diff --git a/name-hash.c b/name-hash.c
index b91e276267..83757db874 100644
--- a/name-hash.c
+++ b/name-hash.c
@@ -126,7 +126,7 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
hashmap_add(&istate->name_hash, &ce->ent);
}
- if (ignore_case)
+ if (repo_ignore_case(the_repository))
add_dir_entry(istate, ce);
}
@@ -207,7 +207,7 @@ static int lookup_lazy_params(struct index_state *istate)
* code to build the "istate->name_hash". We don't
* need the complexity here.
*/
- if (!ignore_case)
+ if (!repo_ignore_case(the_repository))
return 0;
nr_cpus = online_cpus();
@@ -651,7 +651,7 @@ void remove_name_hash(struct index_state *istate, struct cache_entry *ce)
ce->ce_flags &= ~CE_HASHED;
hashmap_remove(&istate->name_hash, &ce->ent, ce);
- if (ignore_case)
+ if (repo_ignore_case(the_repository))
remove_dir_entry(istate, ce);
}
diff --git a/read-cache.c b/read-cache.c
index 21829102ae..fcdf0e5ef1 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -760,12 +760,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
* case of the file being added to the repository matches (is folded into) the existing
* entry's directory case.
*/
- if (ignore_case) {
+ if (repo_ignore_case(the_repository)) {
adjust_dirname_case(istate, ce->name);
}
if (!(flags & ADD_CACHE_RENORMALIZE)) {
alias = index_file_exists(istate, ce->name,
- ce_namelen(ce), ignore_case);
+ ce_namelen(ce), repo_ignore_case(the_repository));
if (alias &&
!ce_stage(alias) &&
!ie_match_stat(istate, alias, st, ce_option)) {
@@ -786,7 +786,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
} else
set_object_name_for_intent_to_add_entry(ce);
- if (ignore_case && alias && different_name(ce, alias))
+ if (repo_ignore_case(the_repository) && alias && different_name(ce, alias))
ce = create_alias_ce(istate, ce, alias);
ce->ce_flags |= CE_ADDED;
diff --git a/refs/files-backend.c b/refs/files-backend.c
index a4c7858787..c1da06b1d5 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -806,7 +806,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,
} else {
unable_to_lock_message(ref_file.buf, myerr, err);
if (myerr == EEXIST) {
- if (ignore_case &&
+ if (repo_ignore_case(the_repository) &&
transaction_has_case_conflicting_update(transaction, update)) {
/*
* In case-insensitive filesystems, ensure that conflicts within a
@@ -920,7 +920,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,
* conflicts between 'foo' and 'Foo/bar'. So let's lowercase
* the refname.
*/
- if (ignore_case) {
+ if (repo_ignore_case(the_repository)) {
struct strbuf lower = STRBUF_INIT;
strbuf_addstr(&lower, refname);
diff --git a/submodule.c b/submodule.c
index a939ff5072..6e7f8b9f7c 100644
--- a/submodule.c
+++ b/submodule.c
@@ -2389,7 +2389,7 @@ static int validate_submodule_encoded_git_dir(char *git_dir, const char *submodu
/* Prevent conflicts on case-folding filesystems */
repo_config_get_bool(the_repository, "core.ignorecase", &config_ignorecase);
- if (ignore_case || config_ignorecase) {
+ if (repo_ignore_case(the_repository) || config_ignorecase) {
bool suffixes_match = !strcmp(last_submodule_name, submodule_name);
return check_casefolding_conflict(git_dir, submodule_name,
suffixes_match);
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c
index e542985c94..43cead6d7d 100644
--- a/t/helper/test-lazy-init-name-hash.c
+++ b/t/helper/test-lazy-init-name-hash.c
@@ -218,7 +218,7 @@ int cmd__lazy_init_name_hash(int argc, const char **argv)
/*
* istate->dir_hash is only created when ignore_case is set.
*/
- ignore_case = 1;
+ repo_config_values(the_repository)->ignore_case = 1;
if (dump) {
if (perf || analyze > 0)
diff --git a/unpack-trees.c b/unpack-trees.c
index 998a1e6dc7..d13b004f71 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -2428,7 +2428,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
*
* Ignore that lstat() if it matches.
*/
- if (ignore_case && icase_exists(o, name, len, st))
+ if (repo_ignore_case(the_repository) && icase_exists(o, name, len, st))
return 0;
if (o->internal.dir &&
--
2.43.0
^ permalink raw reply related
* [PATCH v3 1/2] environment: move ignore_case into repo_config_values
From: Tian Yuchen @ 2026-06-19 15:51 UTC (permalink / raw)
To: git
Cc: ps, phillip.wood123, johannes.schindelin, stolee, Tian Yuchen,
Christian Couder, Ayush Chandekar, Olamide Caleb Bello
In-Reply-To: <20260619155152.642760-1-cat@malon.dev>
The 'core.ignorecase' configuration which is stored as the
global variable 'ignore_case' acts as a core filesystem
capability flag.
Move this global variable into 'struct repo_config_values' to tie it
to the specific repository instance it was read from. This reduces
global state and aligns with the ongoing libification effort.
To ensure code readability, the getter function
'repo_ignore_case()' is introduced.
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>
Mentored-by: Olamide Caleb Bello <belkid98@gmail.com>
Signed-off-by: Tian Yuchen <cat@malon.dev>
---
environment.c | 8 ++++++++
environment.h | 8 ++++++++
2 files changed, 16 insertions(+)
diff --git a/environment.c b/environment.c
index fc3ed8bb1c..bfa3cb3045 100644
--- a/environment.c
+++ b/environment.c
@@ -142,6 +142,13 @@ int is_bare_repository(void)
return is_bare_repository_cfg && !repo_get_work_tree(the_repository);
}
+int repo_ignore_case(struct repository *repo)
+{
+ return (repo && repo->initialized) ?
+ repo_config_values(repo)->ignore_case :
+ 0;
+}
+
int have_git_dir(void)
{
return startup_info->have_repository
@@ -720,5 +727,6 @@ void repo_config_values_init(struct repo_config_values *cfg)
{
cfg->attributes_file = NULL;
cfg->apply_sparse_checkout = 0;
+ cfg->ignore_case = 0;
cfg->branch_track = BRANCH_TRACK_REMOTE;
}
diff --git a/environment.h b/environment.h
index 9eb97b3869..39a8bf0b49 100644
--- a/environment.h
+++ b/environment.h
@@ -91,6 +91,7 @@ struct repo_config_values {
/* section "core" config values */
char *attributes_file;
int apply_sparse_checkout;
+ int ignore_case;
/* section "branch" config values */
enum branch_track branch_track;
@@ -123,6 +124,13 @@ int git_default_config(const char *, const char *,
int git_default_core_config(const char *var, const char *value,
const struct config_context *ctx, void *cb);
+/*
+ * Getter for the `ignore_case` field of `struct repo_config_values`.
+ * It checks `repo->initialized` to prevent calling repo_config_values()`
+ * before the repository setup is fully complete or in non-git environments.
+ */
+int repo_ignore_case(struct repository *repo);
+
void repo_config_values_init(struct repo_config_values *cfg);
/*
--
2.43.0
^ permalink raw reply related
* [PATCH v3 0/2] environment: move ignore_case into repo_config_values
From: Tian Yuchen @ 2026-06-19 15:51 UTC (permalink / raw)
To: git; +Cc: ps, phillip.wood123, johannes.schindelin, stolee, Tian Yuchen
In-Reply-To: <20260618114207.605211-1-cat@malon.dev>
This series continues the ongoing libification effort by moving
this global variable into 'struct repo_config_values', tying it
to the specific repository instance it was read from. This allows
us to encapsulate the configuration without altering its
eager-parsing behavior.
The getter function 'repo_ignore_case()' is introduced so
that we can safely retrieve the configuration value whilst
maintaining the correct fallback logic.
RFC Questions:
dir.c --- Performance overhead?
compat/win32/path-utils.c --- Is it appropriate to include the
repository.h header file?
Related materials:
[1] The practice of introducing getters for filesystem flags
to ensure safe access was previously introduced in this patch
to migrate 'protect_hfs' and 'protect_ntfs'.
When migrating 'ignore_case', the same approach is strictly followed.
[2] Derrick Stolee's previous attempt. This patch series attempted
to wrap this kind of filesystem-level variable using a lazy-loaded
global accessor get_int_config_global().
However, as Glen Choo pointed out in his review of that
series, it is strongly preferred to use plain fields in a
repository-scoped struct over global lazy-loaders, provided
those fields are properly initialized during the setup process.
Changes since V2:
- Revise the cover letter to clarify what the links lead to.
Thanks!
Mentored-by: Christian Couder christian.couder@gmail.com
Mentored-by: Ayush Chandekar ayu.chandekar@gmail.com
Mentored-by: Olamide Caleb Bello belkid98@gmail.com
Signed-off-by: Tian Yuchen cat@malon.dev
[1] https://lore.kernel.org/git/20260606143412.15443-1-cat@malon.dev/
[2] https://lore.kernel.org/git/2b4198c09cb6c04c60608d19072d419503dfe5df.1685716421.git.gitgitgadget@gmail.com/
Tian Yuchen (2):
environment: move ignore_case into repo_config_values
config: use repo_ignore_case() to access core.ignorecase
apply.c | 2 +-
builtin/fetch.c | 2 +-
builtin/mv.c | 2 +-
compat/win32/path-utils.c | 3 ++-
dir.c | 18 +++++++++---------
environment.c | 11 +++++++++--
environment.h | 9 ++++++++-
fsmonitor.c | 2 +-
name-hash.c | 6 +++---
read-cache.c | 6 +++---
refs/files-backend.c | 4 ++--
submodule.c | 2 +-
t/helper/test-lazy-init-name-hash.c | 2 +-
unpack-trees.c | 2 +-
14 files changed, 43 insertions(+), 28 deletions(-)
--
2.43.0
^ permalink raw reply
* Strange behavior of "git log" with file argument
From: Vincent Lefevre @ 2026-06-19 15:44 UTC (permalink / raw)
To: git
With 2.53.0 under Debian/unstable:
* https://github.com/git/git.git repository
at 95e20213faefeb95df29277c58ac1980ab68f701
"git log git-gui/git-gui--askyesno.sh" outputs nothing. To get logs, I
can add the -m option. In particular, this shows 3 non-merge commits.
So the behavior without -m seems incorrect, and at least unhelpful.
* https://gitlab.inria.fr/mpfr/mpfr.git repository
at 74cb29f0908c2887dc8c3e6ba7a3c5a2f20710a3
"git log --reverse AUTHORS" shows only 2 commits:
bfa9d064e1cf7a736740c73b9773eabb11da6ed7
5a9521d1f305268e575c4a5c4de13614acef6321
where bfa9d064e1cf7a736740c73b9773eabb11da6ed7 corresponds to the file
creation and 5a9521d1f305268e575c4a5c4de13614acef6321 is unrelated to
the AUTHORS file:
git show 5a9521d1f305268e575c4a5c4de13614acef6321
contains nothing about AUTHORS, and "git log AUTHORS" does not list
this commit. But "git log AUTHORS" also lists
b28347ab59db2a99168a17c3e1804000069199aa
which had been done *before* the AUTHORS file was created!
Note: According to the git-log(1) man page, the --reverse option
is supposed to affect only the order, not the list of commits to
be shown:
--reverse
Output the commits chosen to be shown (see Commit Limiting section
above) in reverse order. Cannot be combined with --walk-reflogs.
--
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / Pascaline project (LIP, ENS-Lyon)
^ permalink raw reply
* Re: [PATCH v14 4/6] branch: add --prune-merged <branch>
From: Junio C Hamano @ 2026-06-19 15:42 UTC (permalink / raw)
To: Phillip Wood
Cc: Harald Nordgren, Harald Nordgren via GitGitGadget, git,
Kristoffer Haugsbakk, Johannes Sixt
In-Reply-To: <42ffcb36-7fff-4948-9b8d-2c54eb626e66@gmail.com>
Phillip Wood <phillip.wood123@gmail.com> writes:
> I was thinking that if I have feature1 with upstream origin/master and
> feature2 with upstream feautre1, then once feature1 is merged I'd still
> like "git log @{u}.." and "git rebase" without an explicit upstream to
> work when feature2 is checked out. If "git branch --prune-merged
> origin/master" deletes feautre1 then those commands stop working. Maybe
> it would be sensible to update feature2's upstream once feature1 is
> merged (which I think is what you're saying above) but do we really want
> to force the user to do that by deleting feature1?
Ahh, reference with @{upstream}. Yeah, that _does_ make sense.
^ permalink raw reply
* Re: [PATCH v2] Makefile: dedup archives in $(LIBS) so link recipes don't repeat them
From: Junio C Hamano @ 2026-06-19 15:41 UTC (permalink / raw)
To: Harald Nordgren; +Cc: Harald Nordgren via GitGitGadget, git
In-Reply-To: <CAHwyqnWBb65dC+qSYTw9SKdufjibUmTm065feM5D9906H5SQ4w@mail.gmail.com>
Harald Nordgren <haraldnordgren@gmail.com> writes:
> I think this would be quite nice to fix for all the macOS developers
> (I don't know how many we have who are active on this list), but when
> running repeated tests it does take up some space on the terminal:
>
> ````
> ❯ git rebase --keep-base -x 'make -s && cd t && prove -j8
> t345?-history*.sh && echo'
>
> Executing: make -s && cd t && prove -j8 t345?-history*.sh && echo
> GIT_VERSION=2.55.0.rc1.20.g1e31474ef6
> ld: warning: ignoring duplicate libraries: 'libgit.a',
> 'target/release/libgitcore.a'
> ld: warning: ignoring duplicate libraries: 'libgit.a',
> 'target/release/libgitcore.a'
While I am very sympathetic that it may be annoying, I have to
wonder if that is ultimately the linker's job to accept the same
library listed twice on the same command line, deside when it can
ignore the second one, and *silently* ignore it.
Imagine this situation.
- There are two library archives, libA.a has a.o in it and libB.a
has b.o in it, respectively.
- The object file a.o defines a symbol that b.o needs, and b.o
defines a symbol a.o needs (i.e., mutually dependent). libA.a and
libB.a have other symbols in them. There are valid reasons why we
do not want to combine them into a single libAB.a.
- Now our program X uses both libraries and we build and try to link it this way:
$(CC) -c x.c # this builds x.o
$(CC) -o programX x.o libA.a libB.a # unfortunately does not work as-is
which fails because x.o uses symbol from libA.a that is not in
a.o (so a.o is not linked), and then x.o also uses something in
b.o that is picked up from libB.a. But b.o in turn needs a.o
that we already skipped. One way to make it work is to tweak the
final link phase to read like this:
$(CC) -o programX x.o libA.a libB.a libA.a
If your linker complains because we list libA.a twice, it would be
annoying.
I guess if we can assume GNU ld (e.g., gcc/clang), we can use
$(CC) -o programX x.o -Wl,--start-group libA.a libB.a -Wl,end-group
to tell the linker that they need to be processed for circular
dependencies, but listing them twice is more portable and harmless
(i.e., if all the symbols are resolved by the time the linker sees
the second libA.a, then it would not pick up anything extra from
there) way to achieve the same thing.
So from future-proofing and portability perspective (which is
another way to say maintainability we care about), I would very much
prefer to see this solved at the linker level, allowing the build
procedure to list the same library twice on the command line.
It seems that on the Internet various folks, including masonbuild
and CMake, have heard complaints from users enough and fixed the
linker by using -no_warn_duplicate_libraries option. Their approach
translates to something like the following in our build environment.
config.mak.uname | 4 ++++
1 file changed, 4 insertions(+)
diff --git i/config.mak.uname w/config.mak.uname
index 8719e09f66..e29eaaf3fd 100644
--- i/config.mak.uname
+++ w/config.mak.uname
@@ -149,6 +149,10 @@ ifeq ($(uname_S),Darwin)
ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 20 && echo 1),1)
OPEN_RETURNS_EINTR = UnfortunatelyYes
endif
+
+ # NEEDSWORK: do this only for XCode 15 or later
+ BASIC_LDFLAGS += -Wl,-no_warn_duplicate_libraries
+
NO_MEMMEM = YesPlease
USE_ST_TIMESPEC = YesPlease
HAVE_DEV_TTY = YesPlease
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox