* Re: Suggetsions for collaboration workflows in large repos
From: Matthew Hughes @ 2026-06-02 18:35 UTC (permalink / raw)
To: Ben Knoble; +Cc: git
In-Reply-To: <82F556A1-A5C6-414E-8EFB-13F83FA30E44@gmail.com>
On Fri, May 29, 2026 at 01:56:02PM -0400, Ben Knoble wrote:
> My current advice is to enable git-maintenance on such a repo, where
> prefetches and commit graphs and so on will give you a nice perf boost. Then
> I keep the default fetch all heads config and don’t mind the noise too much.
Thanks, I do have maintenance activated (I believe `scalar` handled that for
me) and that does noticeable speed up some operations, and I have find the
performance in general for almost all operations to be much better than I
expected (having not worked in such a large repo before). My only real issue is
in fetching, since I really don't want to waste the time pulling down all the
other branches in the repo that I almost certainly will never need locally.
^ permalink raw reply
* Re: [PATCH v3 00/12] Improve git gui operation without a worktree
From: Johannes Sixt @ 2026-06-02 17:34 UTC (permalink / raw)
To: Mark Levedahl; +Cc: egg_mushroomcow, bootaina702, git
In-Reply-To: <20260531230225.126817-1-mlevedahl@gmail.com>
Am 01.06.26 um 01:02 schrieb Mark Levedahl:
> git gui has a number of inter-related problems that result in problems
> during startup from anything but a checked out worktree pointing at a
> valid git repository. Some of the symptoms are:
> - blame / browser subcommands, and launching gitk, are intended to be
> useful without a worktree, but fail to work.
> - unlike git, git-gui is supposed to use the parent directory as a
> worktree if started from the .git subdirectory in the very common
> single worktree + embedded git repository format. This does not
> work.
> - git-gui includes a repository picker allowing a user to select a
> worktree from a list and/or start a new repo+worktree: this dialog can
> appear at unexpected times, masking useful error feedback on
> configuration problems.
>
> This patch series addresses the above issues, substantially rewriting
> the initial repository/worktree process to rely upon git rev-parse so
> that git's knowledge of access rules, repository configuration, and use
> of GIT_DIR / GIT_WORK_TREE (or git --gitdir / --work-tree) is used
> throughout, replacing code largely based upon what git did in 2008. This
> also means that git gui will naturally gain any new rules implmented in
> git-core.
>
> With this, git-gui only exports GIT_WORK_TREE when non-empty.
> GIT_WORK_TREE is needed, and must be exported, if the user is overriding
> core.worktree in the git repository. But, GIT_WORK_TREE cannot be used
> to specify the lack of a worktree, so exporting an empty GIT_WORK_TREE
> is one of the problems fixed by this series.
>
> v3 of this series addresses j6t's review of v2, with some reordering of
> patches (1 from j6t added, patch #8 moved to #1), adds another rewrite
> of the browser / blame parser that eliminates the notion of path before
> rev on the command line, blame works correctly with a user modified file
> in the worktree. Clarification is added on the need for GTI_WORK_TREE,
> and the logic in finding a worktree from the gitdir is simplified.
>
> Johannes Sixt (1):
> git-gui: remove unnecessary 'cd $_gitworktree' from do_gitk
>
> Mark Levedahl (11):
> git-gui: use HEAD as current branch when detached
> git-gui: guard set/unset of GIT_DIR and GIT_WORK_TREE
> git-gui: do not change global vars in choose_repository::pick
> git-gui: use --absolute-git-dir
> git-gui: use rev-parse exclusively to find a repository
> git-gui: use git rev-parse for worktree discovery
> git-gui: simplify [is_bare] to report if a worktree is known
> git-gui: try harder to find worktree from gitdir
> git-gui: allow specifying path '.' to the browser
> git-gui: check browser/blame arguments carefully
> git-gui: add gui and pick as explicit subcommands
>
> git-gui.sh | 377 ++++++++++++++++++++++----------------
> lib/choose_repository.tcl | 21 +--
> 2 files changed, 223 insertions(+), 175 deletions(-)
This round looks excellent! Thank you very much!
While queuing, I applied the small fixup below to 03/12.
-- Hannes
diff --git a/git-gui.sh b/git-gui.sh
index 933e72c9b255..15dd2b3a84cc 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -2064,7 +2064,6 @@ proc incr_font_size {font {amt 1}} {
proc do_gitk {revs {is_submodule false}} {
global current_diff_path file_states current_diff_side ui_index
- global _gitworktree
# -- Always start gitk through whatever we were loaded with. This
# lets us bypass using shell process on Windows systems.
^ permalink raw reply related
* Re: [PATCH v9] revision.c: implement --max-count-oldest
From: Mirko Faina @ 2026-06-02 17:42 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, Jean-Noël Avila, Patrick Steinhardt,
Tian Yuchen, Ben Knoble, Johannes Sixt, Chris Torek, Mirko Faina
In-Reply-To: <xmqq4ijm3p2x.fsf@gitster.g>
On Tue, Jun 02, 2026 at 06:53:10AM +0900, Junio C Hamano wrote:
> It has been a while, and we saw no further comments by other
> reviewers.
>
> Perhaps we should declare a victory and mark the topic for 'next'.
Yes, if no one objects it might be time for 'next'.
^ permalink raw reply
* [PATCH v5 8/8] environment: move "warn_on_object_refname_ambiguity" into `struct repo_config_values`
From: Olamide Caleb Bello @ 2026-06-02 17:09 UTC (permalink / raw)
To: git
Cc: phillip.wood123, christian.couder, usmanakinyemi202,
kaartic.sivaraam, Olamide Caleb Bello
In-Reply-To: <20260602170921.35869-1-belkid98@gmail.com>
The `core.warnAmbiguousRefs` configuration was previously stored in a
global `int` variable, making it shared across repository instances
and risking cross‑repository state leakage.
Store it instead in `repo_config_values`, where eagerly‑parsed
repository configuration lives. This option is parsed eagerly because
ambiguity warnings influence how users interpret object references in
many commands; a lazy parse could cause these warnings to behave
inconsistently or to appear for the wrong repository, confusing users
and hindering libification. This preserves the existing behavior while
tying the value to the repository from which it was read, avoiding
cross‑repository state leakage and continuing the effort to reduce
reliance on global configuration state.
Update all references to use `repo_config_values()`.
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
builtin/cat-file.c | 7 ++++---
builtin/pack-objects.c | 7 ++++---
environment.c | 2 +-
environment.h | 2 +-
object-name.c | 3 ++-
revision.c | 7 ++++---
submodule.c | 7 ++++---
7 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index d9fbad5358..cfc5430186 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -901,6 +901,7 @@ static int batch_objects(struct batch_options *opt)
struct strbuf input = STRBUF_INIT;
struct strbuf output = STRBUF_INIT;
struct expand_data data = EXPAND_DATA_INIT;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
int save_warning;
int retval = 0;
@@ -973,8 +974,8 @@ static int batch_objects(struct batch_options *opt)
* warn) ends up dwarfing the actual cost of the object lookups
* themselves. We can work around it by just turning off the warning.
*/
- save_warning = warn_on_object_refname_ambiguity;
- warn_on_object_refname_ambiguity = 0;
+ save_warning = cfg->warn_on_object_refname_ambiguity;
+ cfg->warn_on_object_refname_ambiguity = 0;
if (opt->batch_mode == BATCH_MODE_QUEUE_AND_DISPATCH) {
batch_objects_command(opt, &output, &data);
@@ -1002,7 +1003,7 @@ static int batch_objects(struct batch_options *opt)
cleanup:
strbuf_release(&input);
strbuf_release(&output);
- warn_on_object_refname_ambiguity = save_warning;
+ cfg->warn_on_object_refname_ambiguity = save_warning;
return retval;
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 8ccbe7e178..7df75fe91e 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -4788,6 +4788,7 @@ static void get_object_list(struct rev_info *revs, struct strvec *argv)
struct setup_revision_opt s_r_opt = {
.allow_exclude_promisor_objects = 1,
};
+ struct repo_config_values *cfg = repo_config_values(the_repository);
char line[1000];
int flags = 0;
int save_warning;
@@ -4798,8 +4799,8 @@ static void get_object_list(struct rev_info *revs, struct strvec *argv)
/* make sure shallows are read */
is_repository_shallow(the_repository);
- save_warning = warn_on_object_refname_ambiguity;
- warn_on_object_refname_ambiguity = 0;
+ save_warning = cfg->warn_on_object_refname_ambiguity;
+ cfg->warn_on_object_refname_ambiguity = 0;
while (fgets(line, sizeof(line), stdin) != NULL) {
int len = strlen(line);
@@ -4827,7 +4828,7 @@ static void get_object_list(struct rev_info *revs, struct strvec *argv)
die(_("bad revision '%s'"), line);
}
- warn_on_object_refname_ambiguity = save_warning;
+ cfg->warn_on_object_refname_ambiguity = save_warning;
if (use_bitmap_index && !get_object_list_from_bitmap(revs))
return;
diff --git a/environment.c b/environment.c
index 57587ede56..ba2c60103f 100644
--- a/environment.c
+++ b/environment.c
@@ -47,7 +47,6 @@ 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;
char *git_commit_encoding;
char *git_log_output_encoding;
char *apply_default_whitespace;
@@ -725,4 +724,5 @@ void repo_config_values_init(struct repo_config_values *cfg)
cfg->precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
cfg->core_sparse_checkout_cone = 0;
cfg->sparse_expect_files_outside_of_patterns = 0;
+ cfg->warn_on_object_refname_ambiguity = 1;
}
diff --git a/environment.h b/environment.h
index 609cdaa07f..1ff0a7ba8b 100644
--- a/environment.h
+++ b/environment.h
@@ -97,6 +97,7 @@ struct repo_config_values {
int pack_compression_level;
int precomposed_unicode;
int core_sparse_checkout_cone;
+ int warn_on_object_refname_ambiguity;
/* section "sparse" config values */
int sparse_expect_files_outside_of_patterns;
@@ -174,7 +175,6 @@ 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;
extern char *apply_default_ignorewhitespace;
extern unsigned long pack_size_limit_cfg;
diff --git a/object-name.c b/object-name.c
index 21dcdc4a0e..319d3db01d 100644
--- a/object-name.c
+++ b/object-name.c
@@ -684,11 +684,12 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
int refs_found = 0;
int at, reflog_len, nth_prior = 0;
int fatal = !(flags & GET_OID_QUIETLY);
+ struct repo_config_values *cfg = repo_config_values(the_repository);
if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
if (!(flags & GET_OID_SKIP_AMBIGUITY_CHECK) &&
repo_settings_get_warn_ambiguous_refs(r) &&
- warn_on_object_refname_ambiguity) {
+ cfg->warn_on_object_refname_ambiguity) {
refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref, 0);
if (refs_found > 0) {
warning(warn_msg, len, str);
diff --git a/revision.c b/revision.c
index 599b3a66c3..4e7faa7eb1 100644
--- a/revision.c
+++ b/revision.c
@@ -2922,9 +2922,10 @@ static void read_revisions_from_stdin(struct rev_info *revs,
int seen_end_of_options = 0;
int save_warning;
int flags = 0;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
- save_warning = warn_on_object_refname_ambiguity;
- warn_on_object_refname_ambiguity = 0;
+ save_warning = cfg->warn_on_object_refname_ambiguity;
+ cfg->warn_on_object_refname_ambiguity = 0;
strbuf_init(&sb, 1000);
while (strbuf_getline(&sb, stdin) != EOF) {
@@ -2958,7 +2959,7 @@ static void read_revisions_from_stdin(struct rev_info *revs,
read_pathspec_from_stdin(&sb, prune);
strbuf_release(&sb);
- warn_on_object_refname_ambiguity = save_warning;
+ cfg->warn_on_object_refname_ambiguity = save_warning;
}
static void NORETURN diagnose_missing_default(const char *def)
diff --git a/submodule.c b/submodule.c
index b1a0363f9d..f26235bbb7 100644
--- a/submodule.c
+++ b/submodule.c
@@ -898,12 +898,13 @@ static void collect_changed_submodules(struct repository *r,
struct setup_revision_opt s_r_opt = {
.assume_dashdash = 1,
};
+ struct repo_config_values *cfg = repo_config_values(the_repository);
- save_warning = warn_on_object_refname_ambiguity;
- warn_on_object_refname_ambiguity = 0;
+ save_warning = cfg->warn_on_object_refname_ambiguity;
+ cfg->warn_on_object_refname_ambiguity = 0;
repo_init_revisions(r, &rev, NULL);
setup_revisions_from_strvec(argv, &rev, &s_r_opt);
- warn_on_object_refname_ambiguity = save_warning;
+ cfg->warn_on_object_refname_ambiguity = save_warning;
if (prepare_revision_walk(&rev))
die(_("revision walk setup failed"));
--
2.53.0.155.g9f36b15afa
^ permalink raw reply related
* [PATCH v5 7/8] environment: move "sparse_expect_files_outside_of_patterns" into `struct repo_config_values`
From: Olamide Caleb Bello @ 2026-06-02 17:09 UTC (permalink / raw)
To: git
Cc: phillip.wood123, christian.couder, usmanakinyemi202,
kaartic.sivaraam, Olamide Caleb Bello
In-Reply-To: <20260602170921.35869-1-belkid98@gmail.com>
The `core.sparseCheckoutExpectFilesOutsideOfPatterns` configuration was
previously stored in a global `int` variable, making it shared across
repository instances and risking cross‑repository state leakage.
Store it instead in `repo_config_values`, where eagerly‑parsed
repository configuration lives. This option is parsed eagerly because
it controls how sparse‑checkout paths are interpreted – a fundamental
behavior that many commands rely on; a lazy parse could cause
inconsistent sparse‑checkout handling and complicate libification.
This preserves the existing behavior while tying the value to the
repository from which it was read, avoiding cross‑repository state
leakage and continuing the effort to reduce reliance on global
configuration state.
Update all references to use `repo_config_values()`.
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
environment.c | 6 ++++--
environment.h | 5 +++--
sparse-index.c | 2 +-
3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/environment.c b/environment.c
index b0e873e9f5..57587ede56 100644
--- a/environment.c
+++ b/environment.c
@@ -70,7 +70,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
#endif
enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
int grafts_keep_true_parents;
-int sparse_expect_files_outside_of_patterns;
unsigned long pack_size_limit_cfg;
#ifndef PROTECT_HFS_DEFAULT
@@ -550,8 +549,10 @@ int git_default_core_config(const char *var, const char *value,
static int git_default_sparse_config(const char *var, const char *value)
{
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+
if (!strcmp(var, "sparse.expectfilesoutsideofpatterns")) {
- sparse_expect_files_outside_of_patterns = git_config_bool(var, value);
+ cfg->sparse_expect_files_outside_of_patterns = git_config_bool(var, value);
return 0;
}
@@ -723,4 +724,5 @@ void repo_config_values_init(struct repo_config_values *cfg)
cfg->pack_compression_level = Z_DEFAULT_COMPRESSION;
cfg->precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
cfg->core_sparse_checkout_cone = 0;
+ cfg->sparse_expect_files_outside_of_patterns = 0;
}
diff --git a/environment.h b/environment.h
index befad9a388..609cdaa07f 100644
--- a/environment.h
+++ b/environment.h
@@ -98,6 +98,9 @@ struct repo_config_values {
int precomposed_unicode;
int core_sparse_checkout_cone;
+ /* section "sparse" config values */
+ int sparse_expect_files_outside_of_patterns;
+
/* section "branch" config values */
enum branch_track branch_track;
};
@@ -179,8 +182,6 @@ extern unsigned long pack_size_limit_cfg;
extern int protect_hfs;
extern int protect_ntfs;
-extern int sparse_expect_files_outside_of_patterns;
-
enum rebase_setup_type {
AUTOREBASE_NEVER = 0,
AUTOREBASE_LOCAL,
diff --git a/sparse-index.c b/sparse-index.c
index 53cb8d64fc..1ed769b78d 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -675,7 +675,7 @@ void clear_skip_worktree_from_present_files(struct index_state *istate)
struct repo_config_values *cfg = repo_config_values(the_repository);
if (!cfg->apply_sparse_checkout ||
- sparse_expect_files_outside_of_patterns)
+ cfg->sparse_expect_files_outside_of_patterns)
return;
if (clear_skip_worktree_from_present_files_sparse(istate)) {
--
2.53.0.155.g9f36b15afa
^ permalink raw reply related
* [PATCH v5 6/8] environment: move "core_sparse_checkout_cone" into `struct repo_config_values`
From: Olamide Caleb Bello @ 2026-06-02 17:09 UTC (permalink / raw)
To: git
Cc: phillip.wood123, christian.couder, usmanakinyemi202,
kaartic.sivaraam, Olamide Caleb Bello
In-Reply-To: <20260602170921.35869-1-belkid98@gmail.com>
The `core.sparseCheckoutCone` configuration was previously stored in an
uninitialized global `int` variable, risking cross‑repository state
leakage.
Move it into `repo_config_values`, where eagerly‑parsed repository
configuration lives. `core.sparseCheckoutCone` is parsed eagerly
because it determines the fundamental sparse‑checkout mode and is
consulted very early during repository setup; a lazy parse could
leave the sparse‑checkout state undefined and complicate
libification. This preserves the existing behavior while tying the
value to the repository from which it was read, avoiding cross‑
repository state leakage and continuing the effort to reduce reliance
on global configuration state.
Update all references to use `repo_config_values()`.
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
builtin/mv.c | 2 +-
builtin/sparse-checkout.c | 37 ++++++++++++++++++++++---------------
dir.c | 3 ++-
environment.c | 4 ++--
environment.h | 2 +-
sparse-index.c | 2 +-
6 files changed, 29 insertions(+), 21 deletions(-)
diff --git a/builtin/mv.c b/builtin/mv.c
index 2215d34e31..ef3a326c90 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -574,7 +574,7 @@ int cmd_mv(int argc,
if (ignore_sparse &&
cfg->apply_sparse_checkout &&
- core_sparse_checkout_cone) {
+ cfg->core_sparse_checkout_cone) {
/*
* NEEDSWORK: we are *not* paying attention to
* "out-to-out" move (<source> is out-of-cone and
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index f4aa405da9..92d017b81f 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -73,7 +73,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
memset(&pl, 0, sizeof(pl));
- pl.use_cone_patterns = core_sparse_checkout_cone;
+ pl.use_cone_patterns = cfg->core_sparse_checkout_cone;
sparse_filename = get_sparse_checkout_filename();
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0);
@@ -334,6 +334,7 @@ static int write_patterns_and_update(struct repository *repo,
FILE *fp;
struct lock_file lk = LOCK_INIT;
int result;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
sparse_filename = get_sparse_checkout_filename();
@@ -353,7 +354,7 @@ static int write_patterns_and_update(struct repository *repo,
if (!fp)
die_errno(_("unable to fdopen %s"), get_lock_file_path(&lk));
- if (core_sparse_checkout_cone)
+ if (cfg->core_sparse_checkout_cone)
write_cone_to_file(fp, pl);
else
write_patterns_to_file(fp, pl);
@@ -402,15 +403,15 @@ static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
/* If not specified, use previous definition of cone mode */
if (*cone_mode == -1 && cfg->apply_sparse_checkout)
- *cone_mode = core_sparse_checkout_cone;
+ *cone_mode = cfg->core_sparse_checkout_cone;
/* Set cone/non-cone mode appropriately */
cfg->apply_sparse_checkout = 1;
if (*cone_mode == 1 || *cone_mode == -1) {
- core_sparse_checkout_cone = 1;
+ cfg->core_sparse_checkout_cone = 1;
return MODE_CONE_PATTERNS;
}
- core_sparse_checkout_cone = 0;
+ cfg->core_sparse_checkout_cone = 0;
return MODE_ALL_PATTERNS;
}
@@ -577,7 +578,9 @@ static void add_patterns_from_input(struct pattern_list *pl,
FILE *file)
{
int i;
- if (core_sparse_checkout_cone) {
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+
+ if (cfg->core_sparse_checkout_cone) {
struct strbuf line = STRBUF_INIT;
hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0);
@@ -636,13 +639,14 @@ static void add_patterns_cone_mode(int argc, const char **argv,
struct pattern_entry *pe;
struct hashmap_iter iter;
struct pattern_list existing;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
char *sparse_filename = get_sparse_checkout_filename();
add_patterns_from_input(pl, argc, argv,
use_stdin ? stdin : NULL);
memset(&existing, 0, sizeof(existing));
- existing.use_cone_patterns = core_sparse_checkout_cone;
+ existing.use_cone_patterns = cfg->core_sparse_checkout_cone;
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
&existing, NULL, 0))
@@ -690,7 +694,7 @@ static int modify_pattern_list(struct repository *repo,
switch (m) {
case ADD:
- if (core_sparse_checkout_cone)
+ if (cfg->core_sparse_checkout_cone)
add_patterns_cone_mode(args->nr, args->v, pl, use_stdin);
else
add_patterns_literal(args->nr, args->v, pl, use_stdin);
@@ -723,11 +727,12 @@ static void sanitize_paths(struct repository *repo,
const char *prefix, int skip_checks)
{
int i;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
if (!args->nr)
return;
- if (prefix && *prefix && core_sparse_checkout_cone) {
+ if (prefix && *prefix && cfg->core_sparse_checkout_cone) {
/*
* The args are not pathspecs, so unfortunately we
* cannot imitate how cmd_add() uses parse_pathspec().
@@ -744,10 +749,10 @@ static void sanitize_paths(struct repository *repo,
if (skip_checks)
return;
- if (prefix && *prefix && !core_sparse_checkout_cone)
+ if (prefix && *prefix && !cfg->core_sparse_checkout_cone)
die(_("please run from the toplevel directory in non-cone mode"));
- if (core_sparse_checkout_cone) {
+ if (cfg->core_sparse_checkout_cone) {
for (i = 0; i < args->nr; i++) {
if (args->v[i][0] == '/')
die(_("specify directories rather than patterns (no leading slash)"));
@@ -769,7 +774,7 @@ static void sanitize_paths(struct repository *repo,
if (S_ISSPARSEDIR(ce->ce_mode))
continue;
- if (core_sparse_checkout_cone)
+ if (cfg->core_sparse_checkout_cone)
die(_("'%s' is not a directory; to treat it as a directory anyway, rerun with --skip-checks"), args->v[i]);
else
warning(_("pass a leading slash before paths such as '%s' if you want a single file (see NON-CONE PROBLEMS in the git-sparse-checkout manual)."), args->v[i]);
@@ -836,6 +841,7 @@ static struct sparse_checkout_set_opts {
static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
struct repository *repo)
{
+ struct repo_config_values *cfg = repo_config_values(the_repository);
int default_patterns_nr = 2;
const char *default_patterns[] = {"/*", "!/*/", NULL};
@@ -873,7 +879,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
* non-cone mode, if nothing is specified, manually select just the
* top-level directory (much as 'init' would do).
*/
- if (!core_sparse_checkout_cone && !set_opts.use_stdin && argc == 0) {
+ if (!cfg->core_sparse_checkout_cone && !set_opts.use_stdin && argc == 0) {
for (int i = 0; i < default_patterns_nr; i++)
strvec_push(&patterns, default_patterns[i]);
} else {
@@ -977,7 +983,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
setup_work_tree();
if (!cfg->apply_sparse_checkout)
die(_("must be in a sparse-checkout to clean directories"));
- if (!core_sparse_checkout_cone)
+ if (!cfg->core_sparse_checkout_cone)
die(_("must be in a cone-mode sparse-checkout to clean directories"));
argc = parse_options(argc, argv, prefix,
@@ -1141,6 +1147,7 @@ static int sparse_checkout_check_rules(int argc, const char **argv, const char *
FILE *fp;
int ret;
struct pattern_list pl = {0};
+ struct repo_config_values *cfg = repo_config_values(the_repository);
char *sparse_filename;
check_rules_opts.cone_mode = -1;
@@ -1152,7 +1159,7 @@ static int sparse_checkout_check_rules(int argc, const char **argv, const char *
check_rules_opts.cone_mode = 1;
update_cone_mode(&check_rules_opts.cone_mode);
- pl.use_cone_patterns = core_sparse_checkout_cone;
+ pl.use_cone_patterns = cfg->core_sparse_checkout_cone;
if (check_rules_opts.rules_file) {
fp = xfopen(check_rules_opts.rules_file, "r");
add_patterns_from_input(&pl, argc, argv, fp);
diff --git a/dir.c b/dir.c
index fcb8f6dd2a..4f493b64c6 100644
--- a/dir.c
+++ b/dir.c
@@ -3508,8 +3508,9 @@ int get_sparse_checkout_patterns(struct pattern_list *pl)
{
int res;
char *sparse_filename = get_sparse_checkout_filename();
+ struct repo_config_values *cfg = repo_config_values(the_repository);
- pl->use_cone_patterns = core_sparse_checkout_cone;
+ pl->use_cone_patterns = cfg->core_sparse_checkout_cone;
res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL, 0);
free(sparse_filename);
diff --git a/environment.c b/environment.c
index 739b647ebe..b0e873e9f5 100644
--- a/environment.c
+++ b/environment.c
@@ -70,7 +70,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
#endif
enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
int grafts_keep_true_parents;
-int core_sparse_checkout_cone;
int sparse_expect_files_outside_of_patterns;
unsigned long pack_size_limit_cfg;
@@ -526,7 +525,7 @@ int git_default_core_config(const char *var, const char *value,
}
if (!strcmp(var, "core.sparsecheckoutcone")) {
- core_sparse_checkout_cone = git_config_bool(var, value);
+ cfg->core_sparse_checkout_cone = git_config_bool(var, value);
return 0;
}
@@ -723,4 +722,5 @@ void repo_config_values_init(struct repo_config_values *cfg)
cfg->zlib_compression_level = Z_BEST_SPEED;
cfg->pack_compression_level = Z_DEFAULT_COMPRESSION;
cfg->precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
+ cfg->core_sparse_checkout_cone = 0;
}
diff --git a/environment.h b/environment.h
index 508cb1afbc..befad9a388 100644
--- a/environment.h
+++ b/environment.h
@@ -96,6 +96,7 @@ struct repo_config_values {
int zlib_compression_level;
int pack_compression_level;
int precomposed_unicode;
+ int core_sparse_checkout_cone;
/* section "branch" config values */
enum branch_track branch_track;
@@ -178,7 +179,6 @@ extern unsigned long pack_size_limit_cfg;
extern int protect_hfs;
extern int protect_ntfs;
-extern int core_sparse_checkout_cone;
extern int sparse_expect_files_outside_of_patterns;
enum rebase_setup_type {
diff --git a/sparse-index.c b/sparse-index.c
index 13629c075d..53cb8d64fc 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -154,7 +154,7 @@ int is_sparse_index_allowed(struct index_state *istate, int flags)
{
struct repo_config_values *cfg = repo_config_values(the_repository);
- if (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)
+ if (!cfg->apply_sparse_checkout || !cfg->core_sparse_checkout_cone)
return 0;
if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
--
2.53.0.155.g9f36b15afa
^ permalink raw reply related
* [PATCH v5 5/8] environment: move "precomposed_unicode" into `struct repo_config_values`
From: Olamide Caleb Bello @ 2026-06-02 17:09 UTC (permalink / raw)
To: git
Cc: phillip.wood123, christian.couder, usmanakinyemi202,
kaartic.sivaraam, Olamide Caleb Bello
In-Reply-To: <20260602170921.35869-1-belkid98@gmail.com>
The `core.precomposeunicode` configuration is currently stored in the
global variable `precomposed_unicode`, which makes it shared across
repository instances within a single process.
Store it instead in `repo_config_values`, where eagerly‑parsed
repository configuration lives. `core.precomposeunicode` is parsed
eagerly because it controls Unicode path normalization on macOS,
a fundamental filesystem‑level behavior that many operations depend
on; a lazy parse could lead to inconsistent results and hamper
libification. This preserves the existing behavior while tying the
value to the repository from which it was read, avoiding cross‑
repository state leakage and continuing the effort to reduce reliance
on global configuration state.
Update all references to use `repo_config_values()`.
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
compat/precompose_utf8.c | 20 +++++++++++++-------
environment.c | 4 ++--
environment.h | 2 +-
upload-pack.c | 3 ++-
4 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index 43b3be0114..0e94dbd862 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -48,16 +48,18 @@ void probe_utf8_pathname_composition(void)
static const char *auml_nfc = "\xc3\xa4";
static const char *auml_nfd = "\x61\xcc\x88";
int output_fd;
- if (precomposed_unicode != -1)
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+
+ if (cfg->precomposed_unicode != -1)
return; /* We found it defined in the global config, respect it */
repo_git_path_replace(the_repository, &path, "%s", auml_nfc);
output_fd = open(path.buf, O_CREAT|O_EXCL|O_RDWR, 0600);
if (output_fd >= 0) {
close(output_fd);
repo_git_path_replace(the_repository, &path, "%s", auml_nfd);
- precomposed_unicode = access(path.buf, R_OK) ? 0 : 1;
+ cfg->precomposed_unicode = access(path.buf, R_OK) ? 0 : 1;
repo_config_set(the_repository, "core.precomposeunicode",
- precomposed_unicode ? "true" : "false");
+ cfg->precomposed_unicode ? "true" : "false");
repo_git_path_replace(the_repository, &path, "%s", auml_nfc);
if (unlink(path.buf))
die_errno(_("failed to unlink '%s'"), path.buf);
@@ -69,14 +71,16 @@ const char *precompose_string_if_needed(const char *in)
{
size_t inlen;
size_t outlen;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+
if (!in)
return NULL;
if (has_non_ascii(in, (size_t)-1, &inlen)) {
iconv_t ic_prec;
char *out;
- if (precomposed_unicode < 0)
- repo_config_get_bool(the_repository, "core.precomposeunicode", &precomposed_unicode);
- if (precomposed_unicode != 1)
+ if (cfg->precomposed_unicode < 0)
+ repo_config_get_bool(the_repository, "core.precomposeunicode", &cfg->precomposed_unicode);
+ if (cfg->precomposed_unicode != 1)
return in;
ic_prec = iconv_open(repo_encoding, path_encoding);
if (ic_prec == (iconv_t) -1)
@@ -130,7 +134,9 @@ PREC_DIR *precompose_utf8_opendir(const char *dirname)
struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *prec_dir)
{
+ struct repo_config_values *cfg = repo_config_values(the_repository);
struct dirent *res;
+
res = readdir(prec_dir->dirp);
if (res) {
size_t namelenz = strlen(res->d_name) + 1; /* \0 */
@@ -149,7 +155,7 @@ struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *prec_dir)
prec_dir->dirent_nfc->d_ino = res->d_ino;
prec_dir->dirent_nfc->d_type = res->d_type;
- if ((precomposed_unicode == 1) && has_non_ascii(res->d_name, (size_t)-1, NULL)) {
+ if ((cfg->precomposed_unicode == 1) && has_non_ascii(res->d_name, (size_t)-1, NULL)) {
if (prec_dir->ic_precompose == (iconv_t)-1) {
die("iconv_open(%s,%s) failed, but needed:\n"
" precomposed unicode is not supported.\n"
diff --git a/environment.c b/environment.c
index d0d3a4b7d2..739b647ebe 100644
--- a/environment.c
+++ b/environment.c
@@ -72,7 +72,6 @@ enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
int grafts_keep_true_parents;
int core_sparse_checkout_cone;
int sparse_expect_files_outside_of_patterns;
-int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
unsigned long pack_size_limit_cfg;
#ifndef PROTECT_HFS_DEFAULT
@@ -532,7 +531,7 @@ int git_default_core_config(const char *var, const char *value,
}
if (!strcmp(var, "core.precomposeunicode")) {
- precomposed_unicode = git_config_bool(var, value);
+ cfg->precomposed_unicode = git_config_bool(var, value);
return 0;
}
@@ -723,4 +722,5 @@ void repo_config_values_init(struct repo_config_values *cfg)
cfg->check_stat = 1;
cfg->zlib_compression_level = Z_BEST_SPEED;
cfg->pack_compression_level = Z_DEFAULT_COMPRESSION;
+ cfg->precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
}
diff --git a/environment.h b/environment.h
index 514576b67a..508cb1afbc 100644
--- a/environment.h
+++ b/environment.h
@@ -95,6 +95,7 @@ struct repo_config_values {
int check_stat;
int zlib_compression_level;
int pack_compression_level;
+ int precomposed_unicode;
/* section "branch" config values */
enum branch_track branch_track;
@@ -174,7 +175,6 @@ extern char *apply_default_whitespace;
extern char *apply_default_ignorewhitespace;
extern unsigned long pack_size_limit_cfg;
-extern int precomposed_unicode;
extern int protect_hfs;
extern int protect_ntfs;
diff --git a/upload-pack.c b/upload-pack.c
index 9f6d6fe48c..3a52237134 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1336,6 +1336,7 @@ static int upload_pack_config(const char *var, const char *value,
void *cb_data)
{
struct upload_pack_data *data = cb_data;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
if (!strcmp("uploadpack.allowtipsha1inwant", var)) {
if (git_config_bool(var, value))
@@ -1366,7 +1367,7 @@ static int upload_pack_config(const char *var, const char *value,
if (value)
data->allow_packfile_uris = 1;
} else if (!strcmp("core.precomposeunicode", var)) {
- precomposed_unicode = git_config_bool(var, value);
+ cfg->precomposed_unicode = git_config_bool(var, value);
} else if (!strcmp("transfer.advertisesid", var)) {
data->advertise_sid = git_config_bool(var, value);
}
--
2.53.0.155.g9f36b15afa
^ permalink raw reply related
* [PATCH v5 4/8] environment: move "pack_compression_level" into `struct repo_config_values`
From: Olamide Caleb Bello @ 2026-06-02 17:09 UTC (permalink / raw)
To: git
Cc: phillip.wood123, christian.couder, usmanakinyemi202,
kaartic.sivaraam, Olamide Caleb Bello
In-Reply-To: <20260602170921.35869-1-belkid98@gmail.com>
The `pack_compression_level` configuration is currently stored in the
global variable `pack_compression_level`, which makes it shared across
repository instances within a single process.
Store it instead in `repo_config_values`, where eagerly‑parsed
repository configuration lives. `pack_compression_level` is parsed
eagerly because it influences packfile compression, a core operation
where a lazy parse could cause inconsistent behavior and hamper
libification. This preserves the existing eager‑parsing behavior while
tying the value to the repository from which it was read, avoiding
cross‑repository state leakage and continuing the effort to reduce
reliance on global configuration state.
Update all references to use `repo_config_values()`.
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
builtin/fast-import.c | 8 +++++---
builtin/pack-objects.c | 17 ++++++++++-------
environment.c | 8 +++++---
environment.h | 2 +-
object-file.c | 3 ++-
5 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 82bc6dcc00..070a5af3e4 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -965,6 +965,7 @@ static int store_object(
unsigned long hdrlen, deltalen;
struct git_hash_ctx c;
git_zstream s;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
hdrlen = format_object_header((char *)hdr, sizeof(hdr), type,
dat->len);
@@ -1005,7 +1006,7 @@ static int store_object(
} else
delta = NULL;
- git_deflate_init(&s, pack_compression_level);
+ git_deflate_init(&s, cfg->pack_compression_level);
if (delta) {
s.next_in = delta;
s.avail_in = deltalen;
@@ -1032,7 +1033,7 @@ static int store_object(
if (delta) {
FREE_AND_NULL(delta);
- git_deflate_init(&s, pack_compression_level);
+ git_deflate_init(&s, cfg->pack_compression_level);
s.next_in = (void *)dat->buf;
s.avail_in = dat->len;
s.avail_out = git_deflate_bound(&s, s.avail_in);
@@ -1115,6 +1116,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
struct git_hash_ctx c;
git_zstream s;
struct hashfile_checkpoint checkpoint;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
int status = Z_OK;
/* Determine if we should auto-checkpoint. */
@@ -1134,7 +1136,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
crc32_begin(pack_file);
- git_deflate_init(&s, pack_compression_level);
+ git_deflate_init(&s, cfg->pack_compression_level);
hdrlen = encode_in_pack_object_header(out_buf, out_sz, OBJ_BLOB, len);
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index dd2480a73d..8ccbe7e178 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -386,8 +386,9 @@ static unsigned long do_compress(void **pptr, unsigned long size)
git_zstream stream;
void *in, *out;
unsigned long maxsize;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
- git_deflate_init(&stream, pack_compression_level);
+ git_deflate_init(&stream, cfg->pack_compression_level);
maxsize = git_deflate_bound(&stream, size);
in = *pptr;
@@ -413,8 +414,9 @@ static unsigned long write_large_blob_data(struct odb_read_stream *st, struct ha
unsigned char ibuf[1024 * 16];
unsigned char obuf[1024 * 16];
unsigned long olen = 0;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
- git_deflate_init(&stream, pack_compression_level);
+ git_deflate_init(&stream, cfg->pack_compression_level);
for (;;) {
ssize_t readlen;
@@ -5003,6 +5005,7 @@ int cmd_pack_objects(int argc,
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
struct list_objects_filter_options filter_options =
LIST_OBJECTS_FILTER_INIT;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
struct option pack_objects_options[] = {
OPT_CALLBACK_F('q', "quiet", &progress, NULL,
@@ -5084,7 +5087,7 @@ int cmd_pack_objects(int argc,
N_("ignore packs that have companion .keep file")),
OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
N_("ignore this pack")),
- OPT_INTEGER(0, "compression", &pack_compression_level,
+ OPT_INTEGER(0, "compression", &cfg->pack_compression_level,
N_("pack compression level")),
OPT_BOOL(0, "keep-true-parents", &grafts_keep_true_parents,
N_("do not hide commits by grafts")),
@@ -5243,10 +5246,10 @@ int cmd_pack_objects(int argc,
if (!reuse_object)
reuse_delta = 0;
- if (pack_compression_level == -1)
- pack_compression_level = Z_DEFAULT_COMPRESSION;
- else if (pack_compression_level < 0 || pack_compression_level > Z_BEST_COMPRESSION)
- die(_("bad pack compression level %d"), pack_compression_level);
+ if (cfg->pack_compression_level == -1)
+ cfg->pack_compression_level = Z_DEFAULT_COMPRESSION;
+ else if (cfg->pack_compression_level < 0 || cfg->pack_compression_level > Z_BEST_COMPRESSION)
+ die(_("bad pack compression level %d"), cfg->pack_compression_level);
if (!delta_search_threads) /* --threads=0 means autodetect */
delta_search_threads = online_cpus();
diff --git a/environment.c b/environment.c
index 5b0e88b65c..d0d3a4b7d2 100644
--- a/environment.c
+++ b/environment.c
@@ -52,7 +52,6 @@ char *git_commit_encoding;
char *git_log_output_encoding;
char *apply_default_whitespace;
char *apply_default_ignorewhitespace;
-int pack_compression_level = Z_DEFAULT_COMPRESSION;
int fsync_object_files = -1;
int use_fsync = -1;
enum fsync_method fsync_method = FSYNC_METHOD_DEFAULT;
@@ -390,7 +389,7 @@ int git_default_core_config(const char *var, const char *value,
if (!zlib_compression_seen)
cfg->zlib_compression_level = level;
if (!pack_compression_seen)
- pack_compression_level = level;
+ cfg->pack_compression_level = level;
return 0;
}
@@ -662,6 +661,8 @@ static int git_default_attr_config(const char *var, const char *value)
int git_default_config(const char *var, const char *value,
const struct config_context *ctx, void *cb)
{
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+
if (starts_with(var, "core."))
return git_default_core_config(var, value, ctx, cb);
@@ -701,7 +702,7 @@ int git_default_config(const char *var, const char *value,
level = Z_DEFAULT_COMPRESSION;
else if (level < 0 || level > Z_BEST_COMPRESSION)
die(_("bad pack compression level %d"), level);
- pack_compression_level = level;
+ cfg->pack_compression_level = level;
pack_compression_seen = 1;
return 0;
}
@@ -721,4 +722,5 @@ void repo_config_values_init(struct repo_config_values *cfg)
cfg->trust_ctime = 1;
cfg->check_stat = 1;
cfg->zlib_compression_level = Z_BEST_SPEED;
+ cfg->pack_compression_level = Z_DEFAULT_COMPRESSION;
}
diff --git a/environment.h b/environment.h
index 93201620af..514576b67a 100644
--- a/environment.h
+++ b/environment.h
@@ -94,6 +94,7 @@ struct repo_config_values {
int trust_ctime;
int check_stat;
int zlib_compression_level;
+ int pack_compression_level;
/* section "branch" config values */
enum branch_track branch_track;
@@ -171,7 +172,6 @@ extern int assume_unchanged;
extern int warn_on_object_refname_ambiguity;
extern char *apply_default_whitespace;
extern char *apply_default_ignorewhitespace;
-extern int pack_compression_level;
extern unsigned long pack_size_limit_cfg;
extern int precomposed_unicode;
diff --git a/object-file.c b/object-file.c
index 7c122ac419..37def5cc59 100644
--- a/object-file.c
+++ b/object-file.c
@@ -1437,8 +1437,9 @@ static int stream_blob_to_pack(struct transaction_packfile *state,
int status = Z_OK;
int write_object = (flags & INDEX_WRITE_OBJECT);
off_t offset = 0;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
- git_deflate_init(&s, pack_compression_level);
+ git_deflate_init(&s, cfg->pack_compression_level);
hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), OBJ_BLOB, size);
s.next_out = obuf + hdrlen;
--
2.53.0.155.g9f36b15afa
^ permalink raw reply related
* [PATCH v5 3/8] environment: move `zlib_compression_level` into `struct repo_config_values`
From: Olamide Caleb Bello @ 2026-06-02 17:09 UTC (permalink / raw)
To: git
Cc: phillip.wood123, christian.couder, usmanakinyemi202,
kaartic.sivaraam, Olamide Caleb Bello
In-Reply-To: <20260602170921.35869-1-belkid98@gmail.com>
The `zlib_compression_level` configuration is currently stored in the
global variable `zlib_compression_level`, which makes it shared across
repository instances within a single process.
Store it instead in `repo_config_values`, where eagerly‑parsed
repository configuration lives. `zlib_compression_level` is parsed
eagerly because it determines compression behaviour for objects and
packs – core operations where a lazy parse could lead to unpredictable
results and hinder libification. This preserves the existing
eager‑parsing behavior while tying the value to the repository it
was read from, avoiding cross‑repository state leakage and continuing
the effort to reduce reliance on global configuration state.
Update all references to use `repo_config_values()`.
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
builtin/index-pack.c | 3 ++-
diff.c | 3 ++-
environment.c | 6 +++---
environment.h | 2 +-
http-push.c | 3 ++-
object-file.c | 3 ++-
6 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index ca7784dc2c..3942d3e0d0 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1416,8 +1416,9 @@ static int write_compressed(struct hashfile *f, void *in, unsigned int size)
git_zstream stream;
int status;
unsigned char outbuf[4096];
+ struct repo_config_values *cfg = repo_config_values(the_repository);
- git_deflate_init(&stream, zlib_compression_level);
+ git_deflate_init(&stream, cfg->zlib_compression_level);
stream.next_in = in;
stream.avail_in = size;
diff --git a/diff.c b/diff.c
index 397e38b41c..7d17b0bf3f 100644
--- a/diff.c
+++ b/diff.c
@@ -3589,8 +3589,9 @@ static unsigned char *deflate_it(char *data,
int bound;
unsigned char *deflated;
git_zstream stream;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
- git_deflate_init(&stream, zlib_compression_level);
+ git_deflate_init(&stream, cfg->zlib_compression_level);
bound = git_deflate_bound(&stream, size);
deflated = xmalloc(bound);
stream.next_out = deflated;
diff --git a/environment.c b/environment.c
index 8542ac3141..5b0e88b65c 100644
--- a/environment.c
+++ b/environment.c
@@ -52,7 +52,6 @@ char *git_commit_encoding;
char *git_log_output_encoding;
char *apply_default_whitespace;
char *apply_default_ignorewhitespace;
-int zlib_compression_level = Z_BEST_SPEED;
int pack_compression_level = Z_DEFAULT_COMPRESSION;
int fsync_object_files = -1;
int use_fsync = -1;
@@ -377,7 +376,7 @@ int git_default_core_config(const char *var, const char *value,
level = Z_DEFAULT_COMPRESSION;
else if (level < 0 || level > Z_BEST_COMPRESSION)
die(_("bad zlib compression level %d"), level);
- zlib_compression_level = level;
+ cfg->zlib_compression_level = level;
zlib_compression_seen = 1;
return 0;
}
@@ -389,7 +388,7 @@ int git_default_core_config(const char *var, const char *value,
else if (level < 0 || level > Z_BEST_COMPRESSION)
die(_("bad zlib compression level %d"), level);
if (!zlib_compression_seen)
- zlib_compression_level = level;
+ cfg->zlib_compression_level = level;
if (!pack_compression_seen)
pack_compression_level = level;
return 0;
@@ -721,4 +720,5 @@ void repo_config_values_init(struct repo_config_values *cfg)
cfg->branch_track = BRANCH_TRACK_REMOTE;
cfg->trust_ctime = 1;
cfg->check_stat = 1;
+ cfg->zlib_compression_level = Z_BEST_SPEED;
}
diff --git a/environment.h b/environment.h
index 1d3e2e4f23..93201620af 100644
--- a/environment.h
+++ b/environment.h
@@ -93,6 +93,7 @@ struct repo_config_values {
int apply_sparse_checkout;
int trust_ctime;
int check_stat;
+ int zlib_compression_level;
/* section "branch" config values */
enum branch_track branch_track;
@@ -170,7 +171,6 @@ extern int assume_unchanged;
extern int warn_on_object_refname_ambiguity;
extern char *apply_default_whitespace;
extern char *apply_default_ignorewhitespace;
-extern int zlib_compression_level;
extern int pack_compression_level;
extern unsigned long pack_size_limit_cfg;
diff --git a/http-push.c b/http-push.c
index d143fe2845..8ac107a56e 100644
--- a/http-push.c
+++ b/http-push.c
@@ -369,13 +369,14 @@ static void start_put(struct transfer_request *request)
int hdrlen;
ssize_t size;
git_zstream stream;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
unpacked = odb_read_object(the_repository->objects, &request->obj->oid,
&type, &len);
hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
/* Set it up */
- git_deflate_init(&stream, zlib_compression_level);
+ git_deflate_init(&stream, cfg->zlib_compression_level);
size = git_deflate_bound(&stream, len + hdrlen);
strbuf_grow(&request->buffer.buf, size);
request->buffer.posn = 0;
diff --git a/object-file.c b/object-file.c
index 2acc9522df..7c122ac419 100644
--- a/object-file.c
+++ b/object-file.c
@@ -906,6 +906,7 @@ static int start_loose_object_common(struct odb_source *source,
const struct git_hash_algo *algo = source->odb->repo->hash_algo;
const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
int fd;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
fd = create_tmpfile(source->odb->repo, tmp_file, filename);
if (fd < 0) {
@@ -921,7 +922,7 @@ static int start_loose_object_common(struct odb_source *source,
}
/* Setup zlib stream for compression */
- git_deflate_init(stream, zlib_compression_level);
+ git_deflate_init(stream, cfg->zlib_compression_level);
stream->next_out = buf;
stream->avail_out = buflen;
algo->init_fn(c);
--
2.53.0.155.g9f36b15afa
^ permalink raw reply related
* [PATCH v5 2/8] environment: move "check_stat" into `struct repo_config_values`
From: Olamide Caleb Bello @ 2026-06-02 17:09 UTC (permalink / raw)
To: git
Cc: phillip.wood123, christian.couder, usmanakinyemi202,
kaartic.sivaraam, Olamide Caleb Bello
In-Reply-To: <20260602170921.35869-1-belkid98@gmail.com>
The `core.checkstat` configuration is currently stored in the global
variable `check_stat`, which makes it shared across repository
instances within a single process.
Store it instead in `repo_config_values`, where eagerly‑parsed
repository configuration lives. `core.checkstat` is parsed eagerly
because it controls how `match_stat_data()` and related functions
decide file freshness; a lazy parse could lead to unexpected
behavior or complicate libification. This preserves the existing
eager‑parsing behavior while tying the value to the repository it
was read from, avoiding cross‑repository state leakage, and
continuing the effort to reduce reliance on global configuration
state.
Update all references to use `repo_config_values()`.
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
entry.c | 3 ++-
environment.c | 6 +++---
environment.h | 2 +-
statinfo.c | 10 +++++-----
4 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/entry.c b/entry.c
index 7817aee362..c55e867d8a 100644
--- a/entry.c
+++ b/entry.c
@@ -443,7 +443,8 @@ static int check_path(const char *path, int len, struct stat *st, int skiplen)
static void mark_colliding_entries(const struct checkout *state,
struct cache_entry *ce, struct stat *st)
{
- int trust_ino = check_stat;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+ int trust_ino = cfg->check_stat;
#if defined(GIT_WINDOWS_NATIVE) || defined(__CYGWIN__)
trust_ino = 0;
diff --git a/environment.c b/environment.c
index 0a9067729e..8542ac3141 100644
--- a/environment.c
+++ b/environment.c
@@ -42,7 +42,6 @@ static int pack_compression_seen;
static int zlib_compression_seen;
int trust_executable_bit = 1;
-int check_stat = 1;
int has_symlinks = 1;
int minimum_abbrev = 4, default_abbrev = -1;
int ignore_case;
@@ -315,9 +314,9 @@ int git_default_core_config(const char *var, const char *value,
if (!value)
return config_error_nonbool(var);
if (!strcasecmp(value, "default"))
- check_stat = 1;
+ cfg->check_stat = 1;
else if (!strcasecmp(value, "minimal"))
- check_stat = 0;
+ cfg->check_stat = 0;
else
return error(_("invalid value for '%s': '%s'"),
var, value);
@@ -721,4 +720,5 @@ void repo_config_values_init(struct repo_config_values *cfg)
cfg->apply_sparse_checkout = 0;
cfg->branch_track = BRANCH_TRACK_REMOTE;
cfg->trust_ctime = 1;
+ cfg->check_stat = 1;
}
diff --git a/environment.h b/environment.h
index 64d537686e..1d3e2e4f23 100644
--- a/environment.h
+++ b/environment.h
@@ -92,6 +92,7 @@ struct repo_config_values {
char *attributes_file;
int apply_sparse_checkout;
int trust_ctime;
+ int check_stat;
/* section "branch" config values */
enum branch_track branch_track;
@@ -162,7 +163,6 @@ extern char *git_work_tree_cfg;
/* Environment bits from configuration mechanism */
extern int trust_executable_bit;
-extern int check_stat;
extern int has_symlinks;
extern int minimum_abbrev, default_abbrev;
extern int ignore_case;
diff --git a/statinfo.c b/statinfo.c
index 4fc12053f4..5e00af127d 100644
--- a/statinfo.c
+++ b/statinfo.c
@@ -68,19 +68,19 @@ int match_stat_data(const struct stat_data *sd, struct stat *st)
if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
changed |= MTIME_CHANGED;
- if (cfg->trust_ctime && check_stat &&
+ if (cfg->trust_ctime && cfg->check_stat &&
sd->sd_ctime.sec != (unsigned int)st->st_ctime)
changed |= CTIME_CHANGED;
#ifdef USE_NSEC
- if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
+ if (cfg->check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
changed |= MTIME_CHANGED;
- if (cfg->trust_ctime && check_stat &&
+ if (cfg->trust_ctime && cfg->check_stat &&
sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
changed |= CTIME_CHANGED;
#endif
- if (check_stat) {
+ if (cfg->check_stat) {
if (sd->sd_uid != (unsigned int) st->st_uid ||
sd->sd_gid != (unsigned int) st->st_gid)
changed |= OWNER_CHANGED;
@@ -94,7 +94,7 @@ int match_stat_data(const struct stat_data *sd, struct stat *st)
* clients will have different views of what "device"
* the filesystem is on
*/
- if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
+ if (cfg->check_stat && sd->sd_dev != (unsigned int) st->st_dev)
changed |= INODE_CHANGED;
#endif
--
2.53.0.155.g9f36b15afa
^ permalink raw reply related
* [PATCH v5 1/8] environment: move "trust_ctime" into `struct repo_config_values`
From: Olamide Caleb Bello @ 2026-06-02 17:09 UTC (permalink / raw)
To: git
Cc: phillip.wood123, christian.couder, usmanakinyemi202,
kaartic.sivaraam, Olamide Caleb Bello
In-Reply-To: <20260602170921.35869-1-belkid98@gmail.com>
The `core.trustctime` configuration is currently stored in the global
variable `trust_ctime`, which makes it shared across repository
instances in a single process.
Store it instead in `repo_config_values`, where eagerly‑parsed
repository configuration lives. `core.trustctime` is parsed eagerly
because it is used in low‑level stat‑matching functions
(`match_stat_data()`), where a lazy parse could cause unexpected
fatal errors, result in a performance regression and complicate
libification efforts. This preserves that behavior while tying the
value to the repository from which it was read, avoiding cross‑repository
state leakage and continuing the effort to reduce reliance on global
configuration state.
Update all references to use repo_config_values().
Mentored-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
environment.c | 4 ++--
environment.h | 2 +-
statinfo.c | 6 ++++--
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/environment.c b/environment.c
index fc3ed8bb1c..0a9067729e 100644
--- a/environment.c
+++ b/environment.c
@@ -42,7 +42,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;
int minimum_abbrev = 4, default_abbrev = -1;
@@ -309,7 +308,7 @@ int git_default_core_config(const char *var, const char *value,
return 0;
}
if (!strcmp(var, "core.trustctime")) {
- trust_ctime = git_config_bool(var, value);
+ cfg->trust_ctime = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "core.checkstat")) {
@@ -721,4 +720,5 @@ void repo_config_values_init(struct repo_config_values *cfg)
cfg->attributes_file = NULL;
cfg->apply_sparse_checkout = 0;
cfg->branch_track = BRANCH_TRACK_REMOTE;
+ cfg->trust_ctime = 1;
}
diff --git a/environment.h b/environment.h
index 123a71cdc8..64d537686e 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_ctime;
/* section "branch" config values */
enum branch_track branch_track;
@@ -161,7 +162,6 @@ 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;
extern int minimum_abbrev, default_abbrev;
diff --git a/statinfo.c b/statinfo.c
index 30a164b0e6..4fc12053f4 100644
--- a/statinfo.c
+++ b/statinfo.c
@@ -3,6 +3,7 @@
#include "git-compat-util.h"
#include "environment.h"
#include "statinfo.h"
+#include "repository.h"
/*
* Munge st_size into an unsigned int.
@@ -63,17 +64,18 @@ void fake_lstat_data(const struct stat_data *sd, struct stat *st)
int match_stat_data(const struct stat_data *sd, struct stat *st)
{
int changed = 0;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
changed |= MTIME_CHANGED;
- if (trust_ctime && check_stat &&
+ if (cfg->trust_ctime && check_stat &&
sd->sd_ctime.sec != (unsigned int)st->st_ctime)
changed |= CTIME_CHANGED;
#ifdef USE_NSEC
if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
changed |= MTIME_CHANGED;
- if (trust_ctime && check_stat &&
+ if (cfg->trust_ctime && check_stat &&
sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
changed |= CTIME_CHANGED;
#endif
--
2.53.0.155.g9f36b15afa
^ permalink raw reply related
* [PATCH v5 0/8] repo_config_values: migrate more globals variables
From: Olamide Caleb Bello @ 2026-06-02 17:09 UTC (permalink / raw)
To: git
Cc: phillip.wood123, christian.couder, usmanakinyemi202,
kaartic.sivaraam, Olamide Caleb Bello
In-Reply-To: <20260601154211.82370-1-belkid98@gmail.com>
Changes since v4:
- environment: move "precomposed_unicode" into `struct repo_config_values`:
remove incorrect mention of changing `int` to `bool`
Olamide Caleb Bello (8):
environment: move "trust_ctime" into `struct repo_config_values`
environment: move "check_stat" into `struct repo_config_values`
environment: move `zlib_compression_level` into `struct
repo_config_values`
environment: move "pack_compression_level" into `struct
repo_config_values`
environment: move "precomposed_unicode" into `struct
repo_config_values`
environment: move "core_sparse_checkout_cone" into `struct
repo_config_values`
environment: move "sparse_expect_files_outside_of_patterns" into
`struct repo_config_values`
environment: move "warn_on_object_refname_ambiguity" into `struct
repo_config_values`
builtin/cat-file.c | 7 ++++---
builtin/fast-import.c | 8 +++++---
builtin/index-pack.c | 3 ++-
builtin/mv.c | 2 +-
builtin/pack-objects.c | 24 +++++++++++++----------
builtin/sparse-checkout.c | 37 +++++++++++++++++++++---------------
compat/precompose_utf8.c | 20 +++++++++++++-------
diff.c | 3 ++-
dir.c | 3 ++-
entry.c | 3 ++-
environment.c | 40 +++++++++++++++++++++------------------
environment.h | 19 ++++++++++---------
http-push.c | 3 ++-
object-file.c | 6 ++++--
object-name.c | 3 ++-
revision.c | 7 ++++---
sparse-index.c | 4 ++--
statinfo.c | 12 +++++++-----
submodule.c | 7 ++++---
upload-pack.c | 3 ++-
20 files changed, 126 insertions(+), 88 deletions(-)
--
2.53.0.155.g9f36b15afa
^ permalink raw reply
* Re: [PATCH 1/2] b4: introduce configuration for the Git project
From: Tuomas Ahola @ 2026-06-02 17:09 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Junio C Hamano
In-Reply-To: <20260602-pks-b4-v1-1-a7ae5a49e9cf@pks.im>
Patrick Steinhardt <ps@pks.im> wrote:
> We're about to extend our documentation to recommend b4 for sending
> patch series ot the mailing list. Prepare for this by introducing a b4
s/ot/to/
> configuration so that the tool knows to honor our preferences. For now,
> this configuration does two things:
>
> - It configures "send-same-thread = shallow", which tells b4 to always
> send subsequent versions of the same patch series as a reply to the
> cover letter of the first version.
>
Huh? Doesn't MyFirstContribution speak *against* shallow threading?
[...] make sure to replace it with the correct Message-ID for your
**previous cover letter** - that is, if you're sending v2, use the Message-ID
from v1; if you're sending v3, use the Message-ID from v2.
Besides, GitGitGadget also employs that kind of nested threading, if I'm
not mistaken.
Thanks.
^ permalink raw reply
* Re: [PATCH v2] prio-queue: use cascade-down for faster extract-min
From: René Scharfe @ 2026-06-02 16:36 UTC (permalink / raw)
To: Kristofer Karlsson via GitGitGadget, git; +Cc: Kristofer Karlsson
In-Reply-To: <pull.2132.v2.git.1780301856444.gitgitgadget@gmail.com>
On 6/1/26 10:17 AM, Kristofer Karlsson via GitGitGadget wrote:
>
> Changes since v1:
>
> * Kept sift_down_root() and prio_queue_replace() completely unchanged,
> preserving René's optimization that avoids the get+put overhead for
> replace. The cascade approach now only applies to prio_queue_get().
The prospect of no longer needing prio_queue_replace() had me excited in
round 1. The benchmarks from commits that added its callers [1][2][3]
did show performance regressions with your patch 1 plus changes to
revert prio_queue_peek()+prio_queue_replace() to prio_queue_get()+
prio_queue_put(), but for two of them low enough to be in the noise.
'git describe $(git rev-list v2.41.0..v2.47.0)' took a 50%+ hit, though.
[1] a79e3519d6 (commit: use prio_queue_replace() in pop_most_recent_commit(), 2025-07-18)
[2] 08bb69d70f (describe: use prio_queue_replace(), 2025-08-03)
[3] abf05d856f (show-branch: use prio_queue, 2025-12-26)
> * Extracted the new logic into a separate sift_up_rebalance() function
> rather than inlining it in prio_queue_get().
>
> * Updated benchmark numbers for ascending, descending and random
> insertion ordering. No regressions in any scenario.
I don't see any regression for the benchmarks mentioned above with
patch 2 alone, unsurprisingly. The describe command still takes that
50%+ performance hit after reverting [2] on top.
Would you be interested in benchmarking the following patch for making
prio_queue_replace() unnecessary by doing its optimization
automatically? I get a 1% performance hit for the describe command
that I can't explain. And it leaves the heap unbalanced after a
prio_queue_get(), which complicates things, so I found it lacking.
But I wonder how it stacks up against your cascade approach for your
use case and if there's anything to salvage.
René
---
prio-queue.c | 60 +++++++++++++++++++++++++++++++++++++++++-------------------
prio-queue.h | 1 +
2 files changed, 42 insertions(+), 19 deletions(-)
diff --git a/prio-queue.c b/prio-queue.c
index 9748528ce6..ba6b460a46 100644
--- a/prio-queue.c
+++ b/prio-queue.c
@@ -34,12 +34,46 @@ void clear_prio_queue(struct prio_queue *queue)
queue->nr = 0;
queue->alloc = 0;
queue->insertion_ctr = 0;
+ queue->sift_down_root_pending = false;
+}
+
+static void sift_down_root(struct prio_queue *queue)
+{
+ size_t ix, child;
+
+ /* Push down the one at the root */
+ for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) {
+ child = ix * 2 + 1; /* left */
+ if (child + 1 < queue->nr &&
+ compare(queue, child, child + 1) >= 0)
+ child++; /* use right child */
+
+ if (compare(queue, ix, child) <= 0)
+ break;
+
+ swap(queue, child, ix);
+ }
+ queue->sift_down_root_pending = false;
}
void prio_queue_put(struct prio_queue *queue, void *thing)
{
size_t ix, parent;
+ if (queue->sift_down_root_pending) {
+ /*
+ * Restore the original heap size. The last item is
+ * still in the right place.
+ */
+ queue->nr++;
+
+ /* Now fill the hole at the root with the new item. */
+ queue->array[0].ctr = queue->insertion_ctr++;
+ queue->array[0].data = thing;
+ sift_down_root(queue);
+ return;
+ }
+
/* Append at the end */
ALLOC_GROW(queue->array, queue->nr + 1, queue->alloc);
queue->array[queue->nr].ctr = queue->insertion_ctr++;
@@ -58,24 +92,6 @@ void prio_queue_put(struct prio_queue *queue, void *thing)
}
}
-static void sift_down_root(struct prio_queue *queue)
-{
- size_t ix, child;
-
- /* Push down the one at the root */
- for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) {
- child = ix * 2 + 1; /* left */
- if (child + 1 < queue->nr &&
- compare(queue, child, child + 1) >= 0)
- child++; /* use right child */
-
- if (compare(queue, ix, child) <= 0)
- break;
-
- swap(queue, child, ix);
- }
-}
-
void *prio_queue_get(struct prio_queue *queue)
{
void *result;
@@ -85,12 +101,14 @@ void *prio_queue_get(struct prio_queue *queue)
if (!queue->compare)
return queue->array[--queue->nr].data; /* LIFO */
+ if (queue->sift_down_root_pending)
+ sift_down_root(queue);
result = queue->array[0].data;
if (!--queue->nr)
return result;
queue->array[0] = queue->array[queue->nr];
- sift_down_root(queue);
+ queue->sift_down_root_pending = true;
return result;
}
@@ -100,6 +118,8 @@ void *prio_queue_peek(struct prio_queue *queue)
return NULL;
if (!queue->compare)
return queue->array[queue->nr - 1].data;
+ if (queue->sift_down_root_pending)
+ sift_down_root(queue);
return queue->array[0].data;
}
@@ -111,6 +131,8 @@ void prio_queue_replace(struct prio_queue *queue, void *thing)
queue->array[queue->nr - 1].ctr = queue->insertion_ctr++;
queue->array[queue->nr - 1].data = thing;
} else {
+ if (queue->sift_down_root_pending)
+ sift_down_root(queue);
queue->array[0].ctr = queue->insertion_ctr++;
queue->array[0].data = thing;
sift_down_root(queue);
diff --git a/prio-queue.h b/prio-queue.h
index da7fad2f1f..5977fba438 100644
--- a/prio-queue.h
+++ b/prio-queue.h
@@ -32,6 +32,7 @@ struct prio_queue {
void *cb_data;
size_t alloc, nr;
struct prio_queue_entry *array;
+ bool sift_down_root_pending;
};
/*
^ permalink raw reply related
* Re: [PATCH v2] doc: document and test `@` prefix for raw timestamps
From: Luna Schwalbe @ 2026-06-02 16:35 UTC (permalink / raw)
To: Junio C Hamano, ps; +Cc: git
In-Reply-To: <xmqqmrxdxq1r.fsf@gitster.g>
Thanks to both of you for pointing out my mistakes! I'll make sure to do
it right next time.
^ permalink raw reply
* Re: [PATCH v5 1/2] config: let git_config_parse_key() validate quietly
From: Harald Nordgren @ 2026-06-02 16:31 UTC (permalink / raw)
To: Junio C Hamano
Cc: Harald Nordgren via GitGitGadget, git, Kristoffer Haugsbakk
In-Reply-To: <xmqqtsrlujah.fsf@gitster.g>
> Perhaps the updated "git_config_parse_key()" in this patch should be
> renamed to be a file-scape static internal helper, and the existing
> "git_config_parse_key()" should become a thin wrapper around that
> new helper function, retaining the current external interface,
> requiring no changes to existing callers.
I want to remember a discussion on one of my earlier topics, a few
months back, where someone else suggested instead of introducing two
thin wrappers over a helper, we should update the callers instead.
But for me either way is fine, maybe here it makes more sense, because
of the repeated NULL/0/1 parameters.
Harald
^ permalink raw reply
* Re: [PATCH 1/2] b4: introduce configuration for the Git project
From: Ramsay Jones @ 2026-06-02 16:23 UTC (permalink / raw)
To: Junio C Hamano, Patrick Steinhardt; +Cc: git
In-Reply-To: <xmqqldcxvziw.fsf@gitster.g>
On 02/06/2026 2:32 pm, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
>> We're about to extend our documentation to recommend b4 for sending
>> patch series ot the mailing list. Prepare for this by introducing a b4
>> configuration so that the tool knows to honor our preferences. For now,
>> this configuration does two things:
>>
>> - It configures "send-same-thread = shallow", which tells b4 to always
>> send subsequent versions of the same patch series as a reply to the
>> cover letter of the first version.
>>
>> - It configures "prep-cover-template", which tells b4 to use a custom
>> template for the cover letter. The most important change compared to
>> the default template is that our custom template also includes a
>> range-diff.
>>
>> There's potentially more things that we may want to configure going
>> forward, like for example auto-configuration of folks to Cc on certain
>> patches. But these two tweaks feel like a good place to start.
>>
>> Signed-off-by: Patrick Steinhardt <ps@pks.im>
>> ---
>> .b4-config | 3 +++
>> .b4-cover-template | 11 +++++++++++
>> 2 files changed, 14 insertions(+)
>
> Shipping a sample like ".b4-config.sample" that users who opt-in can
> copy-and-edit into the final name ".b4-config" is OK, but I'd rather
> not to ship the configuration files that the users would want to edit
> (hence making the tree dirty).
>
Hmm, for those of us not in the know, perhaps mention the b4 documentation
at 'b4.docs.kernel.org' (which includes how to install b4 ... ;) ).
ATB,
Ramsay Jones
^ permalink raw reply
* [PATCH] http: preserve wwwauth_headers across redirects
From: Aaron Plattner @ 2026-06-02 16:11 UTC (permalink / raw)
To: git; +Cc: Aaron Plattner, Rahul Rameshbabu
When cURL follows a redirect, it calls the CURLOPT_HEADERFUNCTION for
each header received including ones from a redirect. http_request() sets
fwrite_wwwauth() as the header function, which will record the wwwauth[]
entries for the last step in the redirection chain.
However, when http_request_recoverable() sees that cURL followed a
redirect, it attempts to update the credentials for the request from the
new URL using credential_from_url(). The first thing that does is call
credential_clear(), which clears everything including wwwauth_headers.
If the new URL should use a credential helper rather than credentials
embedded in the URL, this loses the list of authentication methods that
the server provided in the redirect.
For example, I have a server that supports HTTP but always redirects to
HTTPS before handling requests. This redirect breaks OAuth
authentication:
$ git ls-remote http://server/git
=> Send header: GET /git/info/refs?service=git-upload-pack HTTP/1.1
<= Recv header: HTTP/1.1 302 Found
<= Recv header: Location: https://server.nvidia.com/git/info/refs?service=git-upload-pack
== Info: Issue another request to this URL: 'https://server.nvidia.com/git/info/refs?service=git-upload-pack'
=> Send header: GET /git/info/refs?service=git-upload-pack HTTP/1.1
<= Recv header: HTTP/1.1 401 Unauthorized
<= Recv header: WWW-Authenticate: Bearer error="invalid_request", error_description="No bearer token found in the request", msal-tenant-id="<tenant>", msal-client-id="<client>"
trace: run_command: 'git credential-cache --timeout 7200 get'
trace: start_command: /bin/sh -c 'git credential-cache --timeout 7200 get' 'git credential-cache --timeout 7200 get'
trace: built-in: git credential-cache --timeout 7200 get
trace: run_command: 'git credential-msal get'
trace: start_command: /bin/sh -c 'git credential-msal get' 'git credential-msal get'
trace: exec: git-credential-msal get
trace: run_command: git-credential-msal get
trace: start_command: /usr/bin/git-credential-msal get
Username for 'https://server.nvidia.com': ^C
When git invokes the credential helper, it doesn't include the wwwauth[]
array, so git-credential-msal doesn't think that OAuth is supported [1].
Fix the problem by preserving the wwwauth_headers strvec across the call
to credential_from_url().
[1] https://github.com/Binary-Eater/git-credential-msal/blob/trunk/src/git_credential_msal/main.py#L69
Signed-off-by: Aaron Plattner <aplattner@nvidia.com>
---
http.c | 14 ++++++++++++
t/lib-httpd/apache.conf | 1 +
t/t5563-simple-http-auth.sh | 45 +++++++++++++++++++++++++++++++++++++
3 files changed, 60 insertions(+)
diff --git a/http.c b/http.c
index ea9b16861b..cac8c9bfc9 100644
--- a/http.c
+++ b/http.c
@@ -2425,7 +2425,21 @@ static int http_request_recoverable(const char *url,
if (options->effective_url && options->base_url) {
if (update_url_from_redirect(options->base_url,
url, options->effective_url)) {
+ struct strvec wwwauth_headers = STRVEC_INIT;
+
+ /*
+ * Preserve wwwauth_headers across the call to
+ * credential_from_url(): if the effective URL doesn't
+ * specify its own credentials, a credential helper
+ * might need the wwwauth[] array from the server's
+ * redirect response in order to authenticate.
+ */
+ strvec_pushv(&wwwauth_headers,
+ http_auth.wwwauth_headers.v);
credential_from_url(&http_auth, options->base_url->buf);
+ strvec_pushv(&http_auth.wwwauth_headers,
+ wwwauth_headers.v);
+ strvec_clear(&wwwauth_headers);
url = options->effective_url->buf;
}
}
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 40a690b0bb..664f23fc6c 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -202,6 +202,7 @@ RewriteRule ^/dumb-redir/(.*)$ /dumb/$1 [R=301]
RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301]
RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302]
RewriteRule ^/smart-redir-auth/(.*)$ /auth/smart/$1 [R=301]
+RewriteRule ^/custom_auth_redir/(.*)$ /custom_auth/$1 [R=302]
RewriteRule ^/smart-redir-limited/(.*)/info/refs$ /smart/$1/info/refs [R=301]
RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302]
diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh
index a7d475dd68..349ae4ab39 100755
--- a/t/t5563-simple-http-auth.sh
+++ b/t/t5563-simple-http-auth.sh
@@ -557,6 +557,51 @@ test_expect_success 'access using bearer auth' '
EOF
'
+test_expect_success 'bearer auth after redirect preserves wwwauth headers' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ capability[]=authtype
+ authtype=Bearer
+ credential=YS1naXQtdG9rZW4=
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Bearer YS1naXQtdG9rZW4=
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=200
+ id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
+ id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ id=default response=WWW-Authenticate: Basic realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ test_config_global credential.useHttpPath true &&
+ git ls-remote "$HTTPD_URL/custom_auth_redir/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ path=custom_auth/repo.git
+ wwwauth[]=FooBar param1="value1" param2="value2"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query store <<-EOF
+ capability[]=authtype
+ authtype=Bearer
+ credential=YS1naXQtdG9rZW4=
+ protocol=http
+ host=$HTTPD_DEST
+ path=custom_auth/repo.git
+ EOF
+'
+
test_expect_success 'access using bearer auth with invalid credentials' '
test_when_finished "per_test_cleanup" &&
--
2.54.0
^ permalink raw reply related
* Re: [PATCH 2/2] Documentation/MyFirstContribution: recommend the use of b4
From: Weijie Yuan @ 2026-06-02 16:09 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Junio C Hamano
In-Reply-To: <20260602-pks-b4-v1-2-a7ae5a49e9cf@pks.im>
Hi Patrick and Junio,
Just so happens that I just submitted my first patch. At this point, I
may or should be the target audience for this document.
I personally watched Patrick's videos with Scott Chacon[1] first, and I
reviewed them many times until I could do those "manual" git operations.
> +Contributors are encouraged to use `b4`, which automates much of the
> +bookkeeping that is otherwise done by hand.
So for statement like this and with my personal experience, I would say
b4 is a more suitable option for senior contributors, as they already
know, for example, what Message-ID and range-diffs are. But apparently,
whose who use forges may not know.
Back to the patch, I think regarding b4 as a more advanced contribution
way for those who had contributed via mailing lists for more than one
time is a better expression or formulation. Here I mean "b4 prep", other
usage like "b4 mbox" and "b4 am" are of course more basic, and be
mentioned as tips when interacting with Git mailing list.
A bit too wordy, in conclusion: Suggest that new contributors master
classic git operations first. When they are familiar with those process,
b4 might be a good option.
Thanks!
--
[1] https://www.youtube.com/watch?v=mjYac9SwIK0&t=1s (Part 1)
^ permalink raw reply
* Re: [PATCH v2 0/2] Small updates to SubmittingPatches
From: Derrick Stolee @ 2026-06-02 15:24 UTC (permalink / raw)
To: Junio C Hamano, git
In-Reply-To: <20260602144304.3341000-1-gitster@pobox.com>
On 6/2/2026 10:43 AM, Junio C Hamano wrote:
> Recently I gave some advice on how a cover letter should
> try to sell the idea to widest possible audience, and then
> I realized that we do not seem to teach how in our guides.
>
> Here is a small series to do so.
>
> In this round, a few typos have been corrected, and improvements are
> made thanks to help from Christian, Stolee, and Patrick.
This version LGTM.
Thanks,
-Stolee
^ permalink raw reply
* Re: [PATCH 2/2] builtin/init-db: deprecate alias for git-init(1)
From: SZEDER Gábor @ 2026-06-02 15:24 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: Junio C Hamano, Kristoffer Haugsbakk, Phillip Wood, git
In-Reply-To: <ah7N5bKAiAORtNkp@pks.im>
On Tue, Jun 02, 2026 at 02:34:45PM +0200, Patrick Steinhardt wrote:
> On Tue, Jun 02, 2026 at 05:27:41PM +0900, Junio C Hamano wrote:
> > Patrick Steinhardt <ps@pks.im> writes:
> >
> > > I wouldn't mind that outcome much, either. What triggered this series is
> > > that I'm always annoyed that it's "builtin/init-db.c" instead of
> > > "builtin/init.c", and the same for `cmd_init_db()`. But I intentionally
> > > constructed the series in a way that the first commit can be picked
> > > as-is, so that we can adjust our code to the modern world while not
> > > doing the deprecation dance.
> > >
> > > So I'd be equally happy if we just drop the second commit in this
> > > series.
> >
> > I'd actually find myself annoyed by such a rename when looking for
> > builtin/init-db.c only to find it gone---much like how a previous
> > rename made ll-merge difficult to locate.
> >
> > My point is that while static names may annoy some, renaming them
> > does not resolve the annoyance; it merely shifts it to someone else.
> >
> > So, if the primary motivation is just the first patch, I would be
> > less inclined to support this series.
>
> That's entirely fair. My take on this is a bit different, as I think
> it's beneficial to accept a short-term adjustment for core contributors
> in favor of making stuff easier to discover/maintain going forward.
That's not a short-term adjustment, but it will be an ongoing
annoyance, because 'git log builtin/init.c' will cut off at the rename
barrier.
^ permalink raw reply
* Re: [PATCH 1/2] SubmittingPatches: separate typofixes section
From: Christian Couder @ 2026-06-02 15:16 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <xmqqbjdtuidp.fsf@gitster.g>
On Tue, Jun 2, 2026 at 4:28 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Christian Couder <christian.couder@gmail.com> writes:
> > s/typofies/typofixes/
> >
> > Thanks.
>
> Thanks. It is amusing to see I cannot say typofixes when I talk
> about them ;-)
I wondered a bit if it was a case of satiric or ironical misspelling,
but it looks like in English the usual words for them are "grammer"
and "speling", like in:
"I am an expert in English grammer and speling." ;-)
^ permalink raw reply
* Re: [PATCH 2/2] builtin/init-db: deprecate alias for git-init(1)
From: Patrick Steinhardt @ 2026-06-02 14:58 UTC (permalink / raw)
To: phillip.wood; +Cc: Junio C Hamano, Kristoffer Haugsbakk, git
In-Reply-To: <e6e225e7-f915-4ed0-900d-03a7767fb36f@gmail.com>
On Tue, Jun 02, 2026 at 02:12:58PM +0100, Phillip Wood wrote:
> On 02/06/2026 13:34, Patrick Steinhardt wrote:
> >
> > That's entirely fair. My take on this is a bit different, as I think
> > it's beneficial to accept a short-term adjustment for core contributors
> > in favor of making stuff easier to discover/maintain going forward.
> > > A new contributor would probably be quick to learn that every
> > `cmd_foo()` entry point is named exactly the same as the subcommand
> > name, but they will then eventually trip over the few exceptions like
> > `cmd_init_db()` where that assumption doesn't hold.
>
> Yes, those exceptions to the rule are annoying. Though they mostly exist for
> a good reason (code sharing between builtin commands), it would be nice to
> minimize them where we can.
Either minimize, or if we don't want to (or can't) the next best thing
is to pick the variant that most folks will know about. Which would be
the case if we just picked the first patch in this series.
Patrick
^ permalink raw reply
* Re: [PATCH 1/2] b4: introduce configuration for the Git project
From: Patrick Steinhardt @ 2026-06-02 14:56 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <xmqqldcxvziw.fsf@gitster.g>
On Tue, Jun 02, 2026 at 10:32:23PM +0900, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > We're about to extend our documentation to recommend b4 for sending
> > patch series ot the mailing list. Prepare for this by introducing a b4
> > configuration so that the tool knows to honor our preferences. For now,
> > this configuration does two things:
> >
> > - It configures "send-same-thread = shallow", which tells b4 to always
> > send subsequent versions of the same patch series as a reply to the
> > cover letter of the first version.
> >
> > - It configures "prep-cover-template", which tells b4 to use a custom
> > template for the cover letter. The most important change compared to
> > the default template is that our custom template also includes a
> > range-diff.
> >
> > There's potentially more things that we may want to configure going
> > forward, like for example auto-configuration of folks to Cc on certain
> > patches. But these two tweaks feel like a good place to start.
> >
> > Signed-off-by: Patrick Steinhardt <ps@pks.im>
> > ---
> > .b4-config | 3 +++
> > .b4-cover-template | 11 +++++++++++
> > 2 files changed, 14 insertions(+)
>
> Shipping a sample like ".b4-config.sample" that users who opt-in can
> copy-and-edit into the final name ".b4-config" is OK, but I'd rather
> not to ship the configuration files that the users would want to edit
> (hence making the tree dirty).
I think shipping this as-is makes sense though, as it allows us to make
b4 behave the way we want it to without the user having to do anything.
If users actually want to reconfigure those values they can by saying
`git config set b4.<foobar>`, as the repository-local configuration will
override whatever `.b4-config` has.
Patrick
^ permalink raw reply
* [ANNOUNCE] Git Rev News edition 135
From: Christian Couder @ 2026-06-02 14:49 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., JAYATHEERTH K, Lucas Seiki Oshiro, Justin Tobler,
Pablo, Chandra Pratap, karthik nayak, Siddharth Asthana,
Siddharth Shrimali, Tian Yuchen, Ayush Chandekar, Bello Olamide,
lwn
Hi everyone,
The 135th edition of Git Rev News is now published:
https://git.github.io/rev_news/2026/05/31/edition-135/
Thanks a lot to Matthias Aßhauer and Štěpán Němec who helped this month!
Enjoy,
Christian, Jakub, Markus and Kaartic.
PS: An issue for the next edition is already opened and contributions
are welcome:
https://github.com/git/git.github.io/issues/846
^ 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