public inbox for git@vger.kernel.org
 help / color / mirror / Atom feed
* [Outreachy PATCH RFC 0/3] store git_default_config() parsed values in new config struct
@ 2026-01-12 12:59 Olamide Caleb Bello
  2026-01-12 12:59 ` [Outreachy PATCH RFC 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
                   ` (3 more replies)
  0 siblings, 4 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-12 12:59 UTC (permalink / raw)
  To: git
  Cc: gitster, christian.couder, usmanakinyemi202, kaartic.sivaraam, me,
	karthik.188, Olamide Caleb Bello

Hi Git Community,
Over the course of my ongoing internship, which focused on moving global
variables in environment.h into local scope, I have attempted to move some
variables into the struct repo-settings.
However there have been some design concerns as regards the use of
`prepare_repo_settings()` with respect to when and where to call the
function, and also the change in behaviours when the variable is lazily
loaded as discussed in [1] and [2].

After different deliberations, Phillip Wood proposed creating a new config
struct [3], adding it to the repository struct and passing the repo struct to
`git_default_config()` to store the parsed config values per repo.
This would mean we would not need to remove code from
`git_default_config()`, and hence the current behaviours will be retained.

I have experimented with this approach for some values and I would
appreciate feedbacks about this approach before we can move forward
and use it for more variables related to `git_default_config()`.

For now, the parsed value is stored in `the_repository` in
`git_default_*_config()` and further efforts to pass the repository
parameter to `git_default_config()` as the callback parameter will
be looked into later on.

1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/
2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/
3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/

Olamide Caleb Bello (3):
  environment: stop storing `core.attributesFile` globally
  environment: stop using core.sparseCheckout globally
  environment: move "branch.autoSetupMerge" into `struct config_values`

 attr.c                      |  8 +++++---
 builtin/backfill.c          |  3 +--
 builtin/branch.c            |  2 +-
 builtin/checkout.c          |  2 +-
 builtin/clone.c             |  2 +-
 builtin/mv.c                |  2 +-
 builtin/push.c              |  2 +-
 builtin/sparse-checkout.c   | 22 +++++++++++-----------
 builtin/submodule--helper.c |  2 +-
 builtin/worktree.c          |  2 +-
 config.c                    | 10 ++++++++++
 config.h                    | 13 +++++++++++++
 dir.c                       |  2 +-
 environment.c               | 20 ++++++++++----------
 environment.h               |  2 --
 repository.c                |  7 +++++++
 repository.h                |  4 ++++
 sparse-index.c              |  4 ++--
 unpack-trees.c              |  2 +-
 wt-status.c                 |  3 ++-
 20 files changed, 74 insertions(+), 40 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [Outreachy PATCH RFC 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-12 12:59 [Outreachy PATCH RFC 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
@ 2026-01-12 12:59 ` Olamide Caleb Bello
  2026-01-12 14:29   ` Phillip Wood
  2026-01-12 12:59 ` [Outreachy PATCH RFC 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-12 12:59 UTC (permalink / raw)
  To: git
  Cc: gitster, christian.couder, usmanakinyemi202, kaartic.sivaraam, me,
	karthik.188, Olamide Caleb Bello, Phillip Wood

The config value parsed in git_default_core_config() is loaded eagerly
and stored in the global variable `git_attributes_file`.
Storing this value in a global variable can lead to unexpected
behaviours when more than one Git repository run in the same Git process.

Move this value into a `struct config_values` which holds all the values
parsed by `git_default_config()` and can be accessed per
repository via `git_default_config()`. This will prevent us from
moving any code from git_default_core_config(), ensuring the current
behaviour remains the same while also enabling the libification of Git.

It is important to note that `git_default_config()` is a wrapper to other
`git_default_*_config()` such as `git_default_core_config()`.
Therefore to access and modify this global variable,
the change has to be made in the function which parses and
stores the value in the global variable.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 attr.c        | 8 +++++---
 config.c      | 5 +++++
 config.h      | 8 ++++++++
 environment.c | 7 ++++---
 environment.h | 1 -
 repository.c  | 6 ++++++
 repository.h  | 4 ++++
 7 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/attr.c b/attr.c
index 4999b7e09d..eb7b82707d 100644
--- a/attr.c
+++ b/attr.c
@@ -881,10 +881,12 @@ const char *git_attr_system_file(void)
 
 const char *git_attr_global_file(void)
 {
-	if (!git_attributes_file)
-		git_attributes_file = xdg_config_home("attributes");
+	struct config_values *cfg = the_repository->cfg_values;
 
-	return git_attributes_file;
+	if (!cfg->attributes_file_path)
+		cfg->attributes_file_path = xdg_config_home("attributes");
+
+	return cfg->attributes_file_path;
 }
 
 int git_attr_system_is_enabled(void)
diff --git a/config.c b/config.c
index 7f6d53b473..8b882f64ae 100644
--- a/config.c
+++ b/config.c
@@ -1761,6 +1761,11 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,
 	return strcmp(e1->key, e2->key);
 }
 
+void config_values_clear(struct config_values *cfg)
+{
+	free(cfg->attributes_file_path);
+}
+
 void git_configset_init(struct config_set *set)
 {
 	hashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);
diff --git a/config.h b/config.h
index ba426a960a..1652d315e2 100644
--- a/config.h
+++ b/config.h
@@ -135,6 +135,13 @@ struct config_context {
 	/* Config source metadata for key and value. */
 	const struct key_value_info *kvi;
 };
+
+/* Holds values parsed by git_default_config() */
+struct config_values {
+	/* core config values */
+	char *attributes_file_path;
+
+};
 #define CONFIG_CONTEXT_INIT { 0 }
 
 /**
@@ -187,6 +194,7 @@ int git_config_from_blob_oid(config_fn_t fn, const char *name,
 void git_config_push_parameter(const char *text);
 void git_config_push_env(const char *spec);
 int git_config_from_parameters(config_fn_t fn, void *data);
+void config_values_clear(struct config_values *cfg);
 
 /*
  * Read config when the Git directory has not yet been set up. In case
diff --git a/environment.c b/environment.c
index a770b5921d..d633b0405b 100644
--- a/environment.c
+++ b/environment.c
@@ -53,7 +53,6 @@ char *git_commit_encoding;
 char *git_log_output_encoding;
 char *apply_default_whitespace;
 char *apply_default_ignorewhitespace;
-char *git_attributes_file;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files = -1;
@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
 static int git_default_core_config(const char *var, const char *value,
 				   const struct config_context *ctx, void *cb)
 {
+	struct config_values *cfg = the_repository->cfg_values;
+
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
 		trust_executable_bit = git_config_bool(var, value);
@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.attributesfile")) {
-		FREE_AND_NULL(git_attributes_file);
-		return git_config_pathname(&git_attributes_file, var, value);
+		FREE_AND_NULL(cfg->attributes_file_path);
+		return git_config_pathname(&cfg->attributes_file_path, var, value);
 	}
 
 	if (!strcmp(var, "core.bare")) {
diff --git a/environment.h b/environment.h
index 51898c99cd..3512a7072e 100644
--- a/environment.h
+++ b/environment.h
@@ -152,7 +152,6 @@ extern int assume_unchanged;
 extern int warn_on_object_refname_ambiguity;
 extern char *apply_default_whitespace;
 extern char *apply_default_ignorewhitespace;
-extern char *git_attributes_file;
 extern int zlib_compression_level;
 extern int pack_compression_level;
 extern unsigned long pack_size_limit_cfg;
diff --git a/repository.c b/repository.c
index c7e75215ac..3ad944e71c 100644
--- a/repository.c
+++ b/repository.c
@@ -55,6 +55,7 @@ void initialize_repository(struct repository *repo)
 	repo->remote_state = remote_state_new();
 	repo->parsed_objects = parsed_object_pool_new(repo);
 	ALLOC_ARRAY(repo->index, 1);
+	CALLOC_ARRAY(repo->cfg_values, 1);
 	index_state_init(repo->index, repo);
 	repo->check_deprecated_config = true;
 
@@ -403,6 +404,11 @@ void repo_clear(struct repository *repo)
 		FREE_AND_NULL(repo->remote_state);
 	}
 
+	if (repo->cfg_values) {
+		config_values_clear(repo->cfg_values);
+		FREE_AND_NULL(repo->cfg_values);
+	}
+
 	strmap_for_each_entry(&repo->submodule_ref_stores, &iter, e)
 		ref_store_release(e->value);
 	strmap_clear(&repo->submodule_ref_stores, 1);
diff --git a/repository.h b/repository.h
index 6063c4b846..5fb825f799 100644
--- a/repository.h
+++ b/repository.h
@@ -13,6 +13,7 @@ struct object_database;
 struct submodule_cache;
 struct promisor_remote_config;
 struct remote_state;
+struct config_values;
 
 enum ref_storage_format {
 	REF_STORAGE_FORMAT_UNKNOWN,
@@ -171,6 +172,9 @@ struct repository {
 
 	/* Should repo_config() check for deprecated settings */
 	bool check_deprecated_config;
+
+	/* Repository's config values parsed by git_default_config() */
+	struct config_values *cfg_values;
 };
 
 #ifdef USE_THE_REPOSITORY_VARIABLE
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH RFC 2/3] environment: stop using core.sparseCheckout globally
  2026-01-12 12:59 [Outreachy PATCH RFC 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
  2026-01-12 12:59 ` [Outreachy PATCH RFC 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-01-12 12:59 ` Olamide Caleb Bello
  2026-01-12 12:59 ` [Outreachy PATCH RFC 3/3] environment: move "branch.autoSetupMerge" into `struct config_values` Olamide Caleb Bello
  2026-01-13 16:43 ` [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
  3 siblings, 0 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-12 12:59 UTC (permalink / raw)
  To: git
  Cc: gitster, christian.couder, usmanakinyemi202, kaartic.sivaraam, me,
	karthik.188, Olamide Caleb Bello, Phillip Wood

The config value `core.sparseCheckout` is parsed in
`git_default_core_config()` and stored globally in
`core_appy_sparse_checkout`. This could cause unintended behaviours
when different Git repositories running in the same process access this
variable.

Move the parsed value into `struct config_values` which holds all the
values parsed by `git_default_config()` and can be accessed
per repo via `git_default_config()`. This would mean we do not need to
remove code from `git_default_core_config()`, thereby retaining current
behaviours.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
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/backfill.c        |  3 +--
 builtin/clone.c           |  2 +-
 builtin/mv.c              |  2 +-
 builtin/sparse-checkout.c | 22 +++++++++++-----------
 builtin/worktree.c        |  2 +-
 config.h                  |  1 +
 dir.c                     |  2 +-
 environment.c             |  3 +--
 environment.h             |  1 -
 sparse-index.c            |  4 ++--
 unpack-trees.c            |  2 +-
 wt-status.c               |  3 ++-
 12 files changed, 23 insertions(+), 24 deletions(-)

diff --git a/builtin/backfill.c b/builtin/backfill.c
index e80fc1b694..7745aa6bf9 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -1,4 +1,3 @@
-/* We need this macro to access core_apply_sparse_checkout */
 #define USE_THE_REPOSITORY_VARIABLE
 
 #include "builtin.h"
@@ -139,7 +138,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 	repo_config(repo, git_default_config, NULL);
 
 	if (ctx.sparse < 0)
-		ctx.sparse = core_apply_sparse_checkout;
+		ctx.sparse = repo->cfg_values->sparse_checkout;
 
 	result = do_backfill(&ctx);
 	backfill_context_clear(&ctx);
diff --git a/builtin/clone.c b/builtin/clone.c
index b19b302b06..568e76a135 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
 	 * We must apply the setting in the current process
 	 * for the later checkout to use the sparse-checkout file.
 	 */
-	core_apply_sparse_checkout = 1;
+	the_repository->cfg_values->sparse_checkout = 1;
 
 	cmd.git_cmd = 1;
 	if (run_command(&cmd)) {
diff --git a/builtin/mv.c b/builtin/mv.c
index d43925097b..e95c1dfb91 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -572,7 +572,7 @@ int cmd_mv(int argc,
 		rename_index_entry_at(the_repository->index, pos, dst);
 
 		if (ignore_sparse &&
-		    core_apply_sparse_checkout &&
+		    the_repository->cfg_values->sparse_checkout &&
 		    core_sparse_checkout_cone) {
 			/*
 			 * NEEDSWORK: we are *not* paying attention to
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 15d51e60a8..0eb81b2188 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
 	int res;
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->cfg_values->sparse_checkout)
 		die(_("this worktree is not sparse"));
 
 	argc = parse_options(argc, argv, prefix,
@@ -400,11 +400,11 @@ static int set_config(struct repository *repo,
 
 static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
 	/* If not specified, use previous definition of cone mode */
-	if (*cone_mode == -1 && core_apply_sparse_checkout)
+	if (*cone_mode == -1 && the_repository->cfg_values->sparse_checkout)
 		*cone_mode = core_sparse_checkout_cone;
 
 	/* Set cone/non-cone mode appropriately */
-	core_apply_sparse_checkout = 1;
+	the_repository->cfg_values->sparse_checkout = 1;
 	if (*cone_mode == 1 || *cone_mode == -1) {
 		core_sparse_checkout_cone = 1;
 		return MODE_CONE_PATTERNS;
@@ -418,7 +418,7 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind
 	int mode, record_mode;
 
 	/* Determine if we need to record the mode; ensure sparse checkout on */
-	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+	record_mode = (*cone_mode != -1) || !repo->cfg_values->sparse_checkout;
 
 	mode = update_cone_mode(cone_mode);
 	if (record_mode && set_config(repo, mode))
@@ -699,9 +699,9 @@ static int modify_pattern_list(struct repository *repo,
 		break;
 	}
 
-	if (!core_apply_sparse_checkout) {
+	if (!repo->cfg_values->sparse_checkout) {
 		set_config(repo, MODE_ALL_PATTERNS);
-		core_apply_sparse_checkout = 1;
+		repo->cfg_values->sparse_checkout = 1;
 		changed_config = 1;
 	}
 
@@ -798,7 +798,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
 	int ret;
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!repo->cfg_values->sparse_checkout)
 		die(_("no sparse-checkout to add to"));
 
 	repo_read_index(repo);
@@ -907,7 +907,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!repo->cfg_values->sparse_checkout)
 		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
 
 	reapply_opts.cone_mode = -1;
@@ -969,7 +969,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!repo->cfg_values->sparse_checkout)
 		die(_("must be in a sparse-checkout to clean directories"));
 	if (!core_sparse_checkout_cone)
 		die(_("must be in a cone-mode sparse-checkout to clean directories"));
@@ -1035,7 +1035,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	struct pattern_list pl;
 
 	/*
-	 * We do not exit early if !core_apply_sparse_checkout; due to the
+	 * We do not exit early if !repo->cfg_values->sparse_checkout; due to the
 	 * ability for users to manually muck things up between
 	 *   direct editing of .git/info/sparse-checkout
 	 *   running read-tree -m u HEAD or update-index --skip-worktree
@@ -1061,7 +1061,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
 	hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
 	pl.use_cone_patterns = 0;
-	core_apply_sparse_checkout = 1;
+	repo->cfg_values->sparse_checkout = 1;
 
 	add_pattern("/*", empty_base, 0, &pl, 0);
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index fbdaf2eb2e..921b4a8ca2 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,
 	 * If the current worktree has sparse-checkout enabled, then copy
 	 * the sparse-checkout patterns from the current worktree.
 	 */
-	if (core_apply_sparse_checkout)
+	if (the_repository->cfg_values->sparse_checkout)
 		copy_sparse_checkout(sb_repo.buf);
 
 	/*
diff --git a/config.h b/config.h
index 1652d315e2..21d65c440e 100644
--- a/config.h
+++ b/config.h
@@ -140,6 +140,7 @@ struct config_context {
 struct config_values {
 	/* core config values */
 	char *attributes_file_path;
+	int sparse_checkout;
 
 };
 #define CONFIG_CONTEXT_INIT { 0 }
diff --git a/dir.c b/dir.c
index b00821f294..d23f057f95 100644
--- a/dir.c
+++ b/dir.c
@@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(
 
 int init_sparse_checkout_patterns(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout)
+	if (!istate->repo->cfg_values->sparse_checkout)
 		return 1;
 	if (istate->sparse_checkout_patterns)
 		return 0;
diff --git a/environment.c b/environment.c
index d633b0405b..15b63cdc4a 100644
--- a/environment.c
+++ b/environment.c
@@ -74,7 +74,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_apply_sparse_checkout;
 int core_sparse_checkout_cone;
 int sparse_expect_files_outside_of_patterns;
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.sparsecheckout")) {
-		core_apply_sparse_checkout = git_config_bool(var, value);
+		cfg->sparse_checkout = git_config_bool(var, value);
 		return 0;
 	}
 
diff --git a/environment.h b/environment.h
index 3512a7072e..60d5933cc2 100644
--- a/environment.h
+++ b/environment.h
@@ -161,7 +161,6 @@ extern int precomposed_unicode;
 extern int protect_hfs;
 extern int protect_ntfs;
 
-extern int core_apply_sparse_checkout;
 extern int core_sparse_checkout_cone;
 extern int sparse_expect_files_outside_of_patterns;
 
diff --git a/sparse-index.c b/sparse-index.c
index 76f90da5f5..14f3f07fe9 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -152,7 +152,7 @@ static int index_has_unmerged_entries(struct index_state *istate)
 
 int is_sparse_index_allowed(struct index_state *istate, int flags)
 {
-	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
+	if (!istate->repo->cfg_values->sparse_checkout || !core_sparse_checkout_cone)
 		return 0;
 
 	if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
@@ -670,7 +670,7 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
 
 void clear_skip_worktree_from_present_files(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout ||
+	if (!istate->repo->cfg_values->sparse_checkout ||
 	    sparse_expect_files_outside_of_patterns)
 		return;
 
diff --git a/unpack-trees.c b/unpack-trees.c
index f38c761ab9..ffab104bfb 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 	if (o->prefix)
 		update_sparsity_for_prefix(o->prefix, o->src_index);
 
-	if (!core_apply_sparse_checkout || !o->update)
+	if (!repo->cfg_values->sparse_checkout || !o->update)
 		o->skip_sparse_checkout = 1;
 	if (!o->skip_sparse_checkout) {
 		memset(&pl, 0, sizeof(pl));
diff --git a/wt-status.c b/wt-status.c
index e12adb26b9..de527bd620 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -35,6 +35,7 @@
 #include "lockfile.h"
 #include "sequencer.h"
 #include "fsmonitor-settings.h"
+#include "config.h"
 
 #define AB_DELAY_WARNING_IN_MS (2 * 1000)
 #define UF_DELAY_WARNING_IN_MS (2 * 1000)
@@ -1764,7 +1765,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,
 	int skip_worktree = 0;
 	int i;
 
-	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
+	if (!r->cfg_values->sparse_checkout || r->index->cache_nr == 0) {
 		/*
 		 * Don't compute percentage of checked out files if we
 		 * aren't in a sparse checkout or would get division by 0.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH RFC 3/3] environment: move "branch.autoSetupMerge" into `struct config_values`
  2026-01-12 12:59 [Outreachy PATCH RFC 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
  2026-01-12 12:59 ` [Outreachy PATCH RFC 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
  2026-01-12 12:59 ` [Outreachy PATCH RFC 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-01-12 12:59 ` Olamide Caleb Bello
  2026-01-13 16:43 ` [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
  3 siblings, 0 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-12 12:59 UTC (permalink / raw)
  To: git
  Cc: gitster, christian.couder, usmanakinyemi202, kaartic.sivaraam, me,
	karthik.188, Olamide Caleb Bello, Phillip Wood

The config value `brach.autoSetupMerge` is parsed in
`git_default_branch_config()` and stored in the global variable
`git_branch_track`. This global variable can cause unexpected behaviours
when multiple Git repos run in the the same process.

Move this value into `struct config_values` which holds all values
parsed by `git_default_config()` and can be accessed per
repo via `git_default_config()`. This would mean we do not have to remove
code from `git_default_branch_config()`, thereby retaining the same
behaviour.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
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/branch.c            |  2 +-
 builtin/checkout.c          |  2 +-
 builtin/push.c              |  2 +-
 builtin/submodule--helper.c |  2 +-
 config.c                    |  5 +++++
 config.h                    |  4 ++++
 environment.c               | 10 +++++-----
 repository.c                |  1 +
 8 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/builtin/branch.c b/builtin/branch.c
index c577b5d20f..6dde426e90 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -795,7 +795,7 @@ int cmd_branch(int argc,
 	if (!sorting_options.nr)
 		string_list_append(&sorting_options, "refname");
 
-	track = git_branch_track;
+	track = the_repository->cfg_values->git_branch_track;
 
 	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
 				   0, &head_oid, NULL);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 261699e2f5..0b8aa6b45a 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1631,7 +1631,7 @@ static int checkout_branch(struct checkout_opts *opts,
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
 	} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
-		opts->track = git_branch_track;
+		opts->track = the_repository->cfg_values->git_branch_track;
 
 	if (new_branch_info->name && !new_branch_info->commit)
 		die(_("Cannot switch branch to a non-commit '%s'"),
diff --git a/builtin/push.c b/builtin/push.c
index 5b6cebbb85..14967002ae 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -162,7 +162,7 @@ static NORETURN void die_push_simple(struct branch *branch,
 		advice_pushdefault_maybe = _("\n"
 				 "To choose either option permanently, "
 				 "see push.default in 'git help config'.\n");
-	if (git_branch_track != BRANCH_TRACK_SIMPLE)
+	if (the_repository->cfg_values->git_branch_track != BRANCH_TRACK_SIMPLE)
 		advice_automergesimple_maybe = _("\n"
 				 "To avoid automatically configuring "
 				 "an upstream branch when its name\n"
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d537ab087a..0e2226b3c8 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3128,7 +3128,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,
 	};
 
 	repo_config(the_repository, git_default_config, NULL);
-	track = git_branch_track;
+	track = the_repository->cfg_values->git_branch_track;
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
 	if (argc != 3)
diff --git a/config.c b/config.c
index 8b882f64ae..c19df32ad6 100644
--- a/config.c
+++ b/config.c
@@ -1766,6 +1766,11 @@ void config_values_clear(struct config_values *cfg)
 	free(cfg->attributes_file_path);
 }
 
+void config_values_init_defaults(struct config_values *cfg)
+{
+	cfg->git_branch_track = BRANCH_TRACK_REMOTE;
+}
+
 void git_configset_init(struct config_set *set)
 {
 	hashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);
diff --git a/config.h b/config.h
index 21d65c440e..62032346e3 100644
--- a/config.h
+++ b/config.h
@@ -5,6 +5,7 @@
 #include "string-list.h"
 #include "repository.h"
 #include "parse.h"
+#include "branch.h"
 
 /**
  * The config API gives callers a way to access Git configuration files
@@ -142,6 +143,8 @@ struct config_values {
 	char *attributes_file_path;
 	int sparse_checkout;
 
+	/* branch config values */
+	enum branch_track git_branch_track;
 };
 #define CONFIG_CONTEXT_INIT { 0 }
 
@@ -195,6 +198,7 @@ int git_config_from_blob_oid(config_fn_t fn, const char *name,
 void git_config_push_parameter(const char *text);
 void git_config_push_env(const char *spec);
 int git_config_from_parameters(config_fn_t fn, void *data);
+void config_values_init_defaults(struct config_values *cfg);
 void config_values_clear(struct config_values *cfg);
 
 /*
diff --git a/environment.c b/environment.c
index 15b63cdc4a..207e63f519 100644
--- a/environment.c
+++ b/environment.c
@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 enum eol core_eol = EOL_UNSET;
 int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
 char *check_roundtrip_encoding;
-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #ifndef OBJECT_CREATION_MODE
@@ -607,18 +606,19 @@ static int git_default_i18n_config(const char *var, const char *value)
 
 static int git_default_branch_config(const char *var, const char *value)
 {
+	struct config_values *cfg = the_repository->cfg_values;
 	if (!strcmp(var, "branch.autosetupmerge")) {
 		if (value && !strcmp(value, "always")) {
-			git_branch_track = BRANCH_TRACK_ALWAYS;
+			cfg->git_branch_track = BRANCH_TRACK_ALWAYS;
 			return 0;
 		} else if (value && !strcmp(value, "inherit")) {
-			git_branch_track = BRANCH_TRACK_INHERIT;
+			cfg->git_branch_track = BRANCH_TRACK_INHERIT;
 			return 0;
 		} else if (value && !strcmp(value, "simple")) {
-			git_branch_track = BRANCH_TRACK_SIMPLE;
+			cfg->git_branch_track = BRANCH_TRACK_SIMPLE;
 			return 0;
 		}
-		git_branch_track = git_config_bool(var, value);
+		cfg->git_branch_track = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "branch.autosetuprebase")) {
diff --git a/repository.c b/repository.c
index 3ad944e71c..98237eceaa 100644
--- a/repository.c
+++ b/repository.c
@@ -56,6 +56,7 @@ void initialize_repository(struct repository *repo)
 	repo->parsed_objects = parsed_object_pool_new(repo);
 	ALLOC_ARRAY(repo->index, 1);
 	CALLOC_ARRAY(repo->cfg_values, 1);
+	config_values_init_defaults(repo->cfg_values);
 	index_state_init(repo->index, repo);
 	repo->check_deprecated_config = true;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH RFC 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-12 12:59 ` [Outreachy PATCH RFC 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-01-12 14:29   ` Phillip Wood
  2026-01-12 15:05     ` Bello Olamide
  0 siblings, 1 reply; 79+ messages in thread
From: Phillip Wood @ 2026-01-12 14:29 UTC (permalink / raw)
  To: Olamide Caleb Bello, git
  Cc: gitster, christian.couder, usmanakinyemi202, kaartic.sivaraam, me,
	karthik.188

Hi Olamide

On 12/01/2026 12:59, Olamide Caleb Bello wrote:
> The config value parsed in git_default_core_config() is loaded eagerly
> and stored in the global variable `git_attributes_file`.
> Storing this value in a global variable can lead to unexpected
> behaviours when more than one Git repository run in the same Git process.
> 
> Move this value into a `struct config_values` which holds all the values
> parsed by `git_default_config()` and can be accessed per
> repository via `git_default_config()`. This will prevent us from
> moving any code from git_default_core_config(), ensuring the current
> behaviour remains the same while also enabling the libification of Git.

The important thing is that we're not changing when the config is 
parsed, not that we're not removing code from git_default_core_config().

Looking at the changes below, I think it would be simpler to embed 
`struct config_values` in `struct repository` as we do for `struct 
repo_settings`. That would simplify things as we wouldn't have to mess 
about allocating an instance on the heap and freeing it in repo_clear(). 
I'd be tempted to call the new struct `repo_config` rather than 
`config_values` which is rather non-specific. I'm also not sure 
config.[ch] is the best home for it, maybe it should live in 
environment.[ch] for now - we might want to move it to it's own file at 
some point.

Thanks

Phillip

> It is important to note that `git_default_config()` is a wrapper to other
> `git_default_*_config()` such as `git_default_core_config()`.
> Therefore to access and modify this global variable,
> the change has to be made in the function which parses and
> stores the value in the global variable.
> 
> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
> ---
>   attr.c        | 8 +++++---
>   config.c      | 5 +++++
>   config.h      | 8 ++++++++
>   environment.c | 7 ++++---
>   environment.h | 1 -
>   repository.c  | 6 ++++++
>   repository.h  | 4 ++++
>   7 files changed, 32 insertions(+), 7 deletions(-)
> 
> diff --git a/attr.c b/attr.c
> index 4999b7e09d..eb7b82707d 100644
> --- a/attr.c
> +++ b/attr.c
> @@ -881,10 +881,12 @@ const char *git_attr_system_file(void)
>   
>   const char *git_attr_global_file(void)
>   {
> -	if (!git_attributes_file)
> -		git_attributes_file = xdg_config_home("attributes");
> +	struct config_values *cfg = the_repository->cfg_values;
>   
> -	return git_attributes_file;
> +	if (!cfg->attributes_file_path)
> +		cfg->attributes_file_path = xdg_config_home("attributes");
> +
> +	return cfg->attributes_file_path;
>   }
>   
>   int git_attr_system_is_enabled(void)
> diff --git a/config.c b/config.c
> index 7f6d53b473..8b882f64ae 100644
> --- a/config.c
> +++ b/config.c
> @@ -1761,6 +1761,11 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,
>   	return strcmp(e1->key, e2->key);
>   }
>   
> +void config_values_clear(struct config_values *cfg)
> +{
> +	free(cfg->attributes_file_path);
> +}
> +
>   void git_configset_init(struct config_set *set)
>   {
>   	hashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);
> diff --git a/config.h b/config.h
> index ba426a960a..1652d315e2 100644
> --- a/config.h
> +++ b/config.h
> @@ -135,6 +135,13 @@ struct config_context {
>   	/* Config source metadata for key and value. */
>   	const struct key_value_info *kvi;
>   };
> +
> +/* Holds values parsed by git_default_config() */
> +struct config_values {
> +	/* core config values */
> +	char *attributes_file_path;
> +
> +};
>   #define CONFIG_CONTEXT_INIT { 0 }
>   
>   /**
> @@ -187,6 +194,7 @@ int git_config_from_blob_oid(config_fn_t fn, const char *name,
>   void git_config_push_parameter(const char *text);
>   void git_config_push_env(const char *spec);
>   int git_config_from_parameters(config_fn_t fn, void *data);
> +void config_values_clear(struct config_values *cfg);
>   
>   /*
>    * Read config when the Git directory has not yet been set up. In case
> diff --git a/environment.c b/environment.c
> index a770b5921d..d633b0405b 100644
> --- a/environment.c
> +++ b/environment.c
> @@ -53,7 +53,6 @@ char *git_commit_encoding;
>   char *git_log_output_encoding;
>   char *apply_default_whitespace;
>   char *apply_default_ignorewhitespace;
> -char *git_attributes_file;
>   int zlib_compression_level = Z_BEST_SPEED;
>   int pack_compression_level = Z_DEFAULT_COMPRESSION;
>   int fsync_object_files = -1;
> @@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
>   static int git_default_core_config(const char *var, const char *value,
>   				   const struct config_context *ctx, void *cb)
>   {
> +	struct config_values *cfg = the_repository->cfg_values;
> +
>   	/* This needs a better name */
>   	if (!strcmp(var, "core.filemode")) {
>   		trust_executable_bit = git_config_bool(var, value);
> @@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,
>   	}
>   
>   	if (!strcmp(var, "core.attributesfile")) {
> -		FREE_AND_NULL(git_attributes_file);
> -		return git_config_pathname(&git_attributes_file, var, value);
> +		FREE_AND_NULL(cfg->attributes_file_path);
> +		return git_config_pathname(&cfg->attributes_file_path, var, value);
>   	}
>   
>   	if (!strcmp(var, "core.bare")) {
> diff --git a/environment.h b/environment.h
> index 51898c99cd..3512a7072e 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -152,7 +152,6 @@ extern int assume_unchanged;
>   extern int warn_on_object_refname_ambiguity;
>   extern char *apply_default_whitespace;
>   extern char *apply_default_ignorewhitespace;
> -extern char *git_attributes_file;
>   extern int zlib_compression_level;
>   extern int pack_compression_level;
>   extern unsigned long pack_size_limit_cfg;
> diff --git a/repository.c b/repository.c
> index c7e75215ac..3ad944e71c 100644
> --- a/repository.c
> +++ b/repository.c
> @@ -55,6 +55,7 @@ void initialize_repository(struct repository *repo)
>   	repo->remote_state = remote_state_new();
>   	repo->parsed_objects = parsed_object_pool_new(repo);
>   	ALLOC_ARRAY(repo->index, 1);
> +	CALLOC_ARRAY(repo->cfg_values, 1);
>   	index_state_init(repo->index, repo);
>   	repo->check_deprecated_config = true;
>   
> @@ -403,6 +404,11 @@ void repo_clear(struct repository *repo)
>   		FREE_AND_NULL(repo->remote_state);
>   	}
>   
> +	if (repo->cfg_values) {
> +		config_values_clear(repo->cfg_values);
> +		FREE_AND_NULL(repo->cfg_values);
> +	}
> +
>   	strmap_for_each_entry(&repo->submodule_ref_stores, &iter, e)
>   		ref_store_release(e->value);
>   	strmap_clear(&repo->submodule_ref_stores, 1);
> diff --git a/repository.h b/repository.h
> index 6063c4b846..5fb825f799 100644
> --- a/repository.h
> +++ b/repository.h
> @@ -13,6 +13,7 @@ struct object_database;
>   struct submodule_cache;
>   struct promisor_remote_config;
>   struct remote_state;
> +struct config_values;
>   
>   enum ref_storage_format {
>   	REF_STORAGE_FORMAT_UNKNOWN,
> @@ -171,6 +172,9 @@ struct repository {
>   
>   	/* Should repo_config() check for deprecated settings */
>   	bool check_deprecated_config;
> +
> +	/* Repository's config values parsed by git_default_config() */
> +	struct config_values *cfg_values;
>   };
>   
>   #ifdef USE_THE_REPOSITORY_VARIABLE


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH RFC 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-12 14:29   ` Phillip Wood
@ 2026-01-12 15:05     ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-12 15:05 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Mon, 12 Jan 2026 at 15:29, Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Olamide

Hellp Phillip

>
> On 12/01/2026 12:59, Olamide Caleb Bello wrote:
> > The config value parsed in git_default_core_config() is loaded eagerly
> > and stored in the global variable `git_attributes_file`.
> > Storing this value in a global variable can lead to unexpected
> > behaviours when more than one Git repository run in the same Git process.
> >
> > Move this value into a `struct config_values` which holds all the values
> > parsed by `git_default_config()` and can be accessed per
> > repository via `git_default_config()`. This will prevent us from
> > moving any code from git_default_core_config(), ensuring the current
> > behaviour remains the same while also enabling the libification of Git.
>
> The important thing is that we're not changing when the config is
> parsed, not that we're not removing code from git_default_core_config().

Okay thanks for clarifying

>
> Looking at the changes below, I think it would be simpler to embed
> `struct config_values` in `struct repository` as we do for `struct
> repo_settings`. That would simplify things as we wouldn't have to mess
> about allocating an instance on the heap and freeing it in repo_clear().

Okay I will try this approach

> I'd be tempted to call the new struct `repo_config` rather than
> `config_values` which is rather non-specific.

I initially considered `repo_config`, but a struct with the name already exists

> I'm also not sure
> config.[ch] is the best home for it, maybe it should live in
> environment.[ch] for now - we might want to move it to it's own file at
> some point.

Okay this is noted.
Thanks

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct
  2026-01-12 12:59 [Outreachy PATCH RFC 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
                   ` (2 preceding siblings ...)
  2026-01-12 12:59 ` [Outreachy PATCH RFC 3/3] environment: move "branch.autoSetupMerge" into `struct config_values` Olamide Caleb Bello
@ 2026-01-13 16:43 ` Olamide Caleb Bello
  2026-01-13 16:44   ` [Outreachy PATCH v2 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
                     ` (4 more replies)
  3 siblings, 5 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-13 16:43 UTC (permalink / raw)
  To: git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Olamide Caleb Bello

Hi Git Community,
Over the course of my ongoing internship, which focused on moving global
variables in environment.h into local scope, I have attempted to move some
variables into the struct repo-settings.
However there have been some design concerns as regards the use of
`prepare_repo_settings()` with respect to when and where to call the
function, and also the change in behaviours when the variable is lazily
loaded as discussed in [1] and [2].

After different deliberations, Phillip Wood proposed creating a new config
struct [3], adding it to the repository struct and passing the repo struct to
`git_default_config()` to store the parsed config values per repo.
This ensures the current behaviours will be retained.

I have experimented with this approach for some values and I would
appreciate feedbacks about this approach before we can move forward
and use it for more variables related to `git_default_config()`.

For now, the parsed value is stored in `the_repository` in
`git_default_*_config()` and further efforts to pass the repository
parameter to `git_default_config()` as the callback parameter will
be looked into later on.
The link to the CI tests can be see in [4]

1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/
2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/
3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/
4. https://github.com/git/git/actions/runs/20953059862

Changes in v2:
==============
- Renamed new struct to repo_config_values
- Moved struct and functions declaration and definition to
  environment.[ch]
- embedded the new struct in the repository struct thereby removing the
  need to allocate memory on the heap

Olamide Caleb Bello (3):
  environment: stop storing `core.attributesFile` globally
  environment: environment: stop using core.sparseCheckout globally
  environment: move "branch.autoSetupMerge" into `struct
    repo_config_values`

 attr.c                      |  7 ++++---
 branch.h                    |  2 --
 builtin/backfill.c          |  3 +--
 builtin/branch.c            |  2 +-
 builtin/checkout.c          |  2 +-
 builtin/clone.c             |  2 +-
 builtin/grep.c              |  2 +-
 builtin/mv.c                |  2 +-
 builtin/push.c              |  2 +-
 builtin/sparse-checkout.c   | 22 +++++++++++-----------
 builtin/submodule--helper.c |  2 +-
 builtin/worktree.c          |  2 +-
 dir.c                       |  2 +-
 environment.c               | 26 ++++++++++++++++----------
 environment.h               | 15 +++++++++++++--
 repository.c                |  1 +
 repository.h                |  4 ++++
 sparse-index.c              |  6 ++++--
 unpack-trees.c              |  2 +-
 wt-status.c                 |  2 +-
 20 files changed, 65 insertions(+), 43 deletions(-)

 Range diff versus v2
 ====================
1:  25fc10854e < -:  ---------- environment: stop storing `core.attributesFile` globally
-:  ---------- > 1:  b6f8deaa40 environment: stop storing `core.attributesFile` globally
2:  f25afc6f68 ! 2:  1e83c077f2 environment: stop using core.sparseCheckout globally
    @@ Metadata
     Author: Olamide Caleb Bello <belkid98@gmail.com>
     
      ## Commit message ##
    -    environment: stop using core.sparseCheckout globally
    +    environment: environment: stop using core.sparseCheckout globally
     
         The config value `core.sparseCheckout` is parsed in
         `git_default_core_config()` and stored globally in
    @@ Commit message
         when different Git repositories running in the same process access this
         variable.
     
    -    Move the parsed value into `struct config_values` which can be accessed
    -    per repo via `git_default_config()`. This would mean we do not need to
    -    remove code from `git_default_core_config()`, thereby retaining current
    -    behaviours.
    +    Move the parsed value into `struct repo_config_values` which holds all the
    +    values parsed by `git_default_config()` and can be accessed
    +    per repo via `git_default_config()`. This retains current
    +    behaviours while achieving the repository scoped access.
     
         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
         Mentored-by: Christian Couder <christian.couder@gmail.com>
    @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *pr
      
      	if (ctx.sparse < 0)
     -		ctx.sparse = core_apply_sparse_checkout;
    -+		ctx.sparse = repo->cfg_values->sparse_checkout;
    ++		ctx.sparse = repo->config_values.sparse_checkout;
      
      	result = do_backfill(&ctx);
      	backfill_context_clear(&ctx);
    @@ builtin/clone.c: static int git_sparse_checkout_init(const char *repo)
      	 * for the later checkout to use the sparse-checkout file.
      	 */
     -	core_apply_sparse_checkout = 1;
    -+	the_repository->cfg_values->sparse_checkout = 1;
    ++	the_repository->config_values.sparse_checkout = 1;
      
      	cmd.git_cmd = 1;
      	if (run_command(&cmd)) {
     
    + ## builtin/grep.c ##
    +@@ builtin/grep.c: static int grep_submodule(struct grep_opt *opt,
    + 	 *	"forget" the sparse-index feature switch. As a result, the index
    + 	 *	of these submodules are expanded unexpectedly.
    + 	 *
    +-	 * 2. "core_apply_sparse_checkout"
    ++	 * 2. "sparse_checkout"
    + 	 *	When running `grep` in the superproject, this setting is
    + 	 *	populated using the superproject's configs. However, once
    + 	 *	initialized, this config is globally accessible and is read by
    +
      ## builtin/mv.c ##
     @@ builtin/mv.c: int cmd_mv(int argc,
      		rename_index_entry_at(the_repository->index, pos, dst);
      
      		if (ignore_sparse &&
     -		    core_apply_sparse_checkout &&
    -+			the_repository->cfg_values->sparse_checkout &&
    ++		    the_repository->config_values.sparse_checkout &&
      		    core_sparse_checkout_cone) {
      			/*
      			 * NEEDSWORK: we are *not* paying attention to
    @@ builtin/sparse-checkout.c: static int sparse_checkout_list(int argc, const char
      
      	setup_work_tree();
     -	if (!core_apply_sparse_checkout)
    -+	if (!the_repository->cfg_values->sparse_checkout)
    ++	if (!the_repository->config_values.sparse_checkout)
      		die(_("this worktree is not sparse"));
      
      	argc = parse_options(argc, argv, prefix,
    @@ builtin/sparse-checkout.c: static int set_config(struct repository *repo,
      static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
      	/* If not specified, use previous definition of cone mode */
     -	if (*cone_mode == -1 && core_apply_sparse_checkout)
    -+	if (*cone_mode == -1 && the_repository->cfg_values->sparse_checkout)
    ++	if (*cone_mode == -1 && the_repository->config_values.sparse_checkout)
      		*cone_mode = core_sparse_checkout_cone;
      
      	/* Set cone/non-cone mode appropriately */
     -	core_apply_sparse_checkout = 1;
    -+	the_repository->cfg_values->sparse_checkout = 1;
    ++	the_repository->config_values.sparse_checkout = 1;
      	if (*cone_mode == 1 || *cone_mode == -1) {
      		core_sparse_checkout_cone = 1;
      		return MODE_CONE_PATTERNS;
    @@ builtin/sparse-checkout.c: static int update_modes(struct repository *repo, int
      
      	/* Determine if we need to record the mode; ensure sparse checkout on */
     -	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
    -+	record_mode = (*cone_mode != -1) || !repo->cfg_values->sparse_checkout;
    ++	record_mode = (*cone_mode != -1) || !repo->config_values.sparse_checkout;
      
      	mode = update_cone_mode(cone_mode);
      	if (record_mode && set_config(repo, mode))
    @@ builtin/sparse-checkout.c: static int modify_pattern_list(struct repository *rep
      	}
      
     -	if (!core_apply_sparse_checkout) {
    -+	if (!repo->cfg_values->sparse_checkout) {
    ++	if (!repo->config_values.sparse_checkout) {
      		set_config(repo, MODE_ALL_PATTERNS);
     -		core_apply_sparse_checkout = 1;
    -+		repo->cfg_values->sparse_checkout = 1;
    ++		repo->config_values.sparse_checkout = 1;
      		changed_config = 1;
      	}
      
    @@ builtin/sparse-checkout.c: static int sparse_checkout_add(int argc, const char *
      
      	setup_work_tree();
     -	if (!core_apply_sparse_checkout)
    -+	if (!repo->cfg_values->sparse_checkout)
    ++	if (!repo->config_values.sparse_checkout)
      		die(_("no sparse-checkout to add to"));
      
      	repo_read_index(repo);
    @@ builtin/sparse-checkout.c: static int sparse_checkout_reapply(int argc, const ch
      
      	setup_work_tree();
     -	if (!core_apply_sparse_checkout)
    -+	if (!repo->cfg_values->sparse_checkout)
    ++	if (!repo->config_values.sparse_checkout)
      		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
      
      	reapply_opts.cone_mode = -1;
    @@ builtin/sparse-checkout.c: static int sparse_checkout_clean(int argc, const char
      
      	setup_work_tree();
     -	if (!core_apply_sparse_checkout)
    -+	if (!repo->cfg_values->sparse_checkout)
    ++	if (!repo->config_values.sparse_checkout)
      		die(_("must be in a sparse-checkout to clean directories"));
      	if (!core_sparse_checkout_cone)
      		die(_("must be in a cone-mode sparse-checkout to clean directories"));
    @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const ch
      
      	/*
     -	 * We do not exit early if !core_apply_sparse_checkout; due to the
    -+	 * We do not exit early if !repo->cfg_values->sparse_checkout; due to the
    ++	 * We do not exit early if !repo->config_values.sparse_checkout; due to the
      	 * ability for users to manually muck things up between
      	 *   direct editing of .git/info/sparse-checkout
      	 *   running read-tree -m u HEAD or update-index --skip-worktree
    @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const ch
      	hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
      	pl.use_cone_patterns = 0;
     -	core_apply_sparse_checkout = 1;
    -+	repo->cfg_values->sparse_checkout = 1;
    ++	repo->config_values.sparse_checkout = 1;
      
      	add_pattern("/*", empty_base, 0, &pl, 0);
      
    @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam
      	 * the sparse-checkout patterns from the current worktree.
      	 */
     -	if (core_apply_sparse_checkout)
    -+	if (the_repository->cfg_values->sparse_checkout)
    ++	if (wt->repo->config_values.sparse_checkout)
      		copy_sparse_checkout(sb_repo.buf);
      
      	/*
     
    - ## config.h ##
    -@@ config.h: struct config_context {
    - struct config_values {
    - 	/* core config values */
    - 	char *attributes_file_path;
    -+	int sparse_checkout;
    - 
    - };
    - #define CONFIG_CONTEXT_INIT { 0 }
    -
      ## dir.c ##
     @@ dir.c: enum pattern_match_result path_matches_pattern_list(
      
      int init_sparse_checkout_patterns(struct index_state *istate)
      {
     -	if (!core_apply_sparse_checkout)
    -+	if (!istate->repo->cfg_values->sparse_checkout)
    ++	if (!istate->repo->config_values.sparse_checkout)
      		return 1;
      	if (istate->sparse_checkout_patterns)
      		return 0;
    @@ environment.c: static int git_default_core_config(const char *var, const char *v
      
     
      ## environment.h ##
    +@@ environment.h: struct strvec;
    + struct repo_config_values {
    + 	/* core config values */
    + 	char *attributes_file_path;
    ++	int sparse_checkout;
    + };
    + 
    + /*
     @@ environment.h: extern int precomposed_unicode;
      extern int protect_hfs;
      extern int protect_ntfs;
    @@ sparse-index.c: static int index_has_unmerged_entries(struct index_state *istate
      int is_sparse_index_allowed(struct index_state *istate, int flags)
      {
     -	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
    -+	if (!istate->repo->cfg_values->sparse_checkout || !core_sparse_checkout_cone)
    ++	struct repo_config_values *cfg = &istate->repo->config_values;
    ++	if (!cfg->sparse_checkout || !core_sparse_checkout_cone)
      		return 0;
      
      	if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
    @@ sparse-index.c: static void clear_skip_worktree_from_present_files_full(struct i
      void clear_skip_worktree_from_present_files(struct index_state *istate)
      {
     -	if (!core_apply_sparse_checkout ||
    -+	if (!istate->repo->cfg_values->sparse_checkout ||
    ++	struct repo_config_values *cfg = &istate->repo->config_values;
    ++	if (!cfg->sparse_checkout ||
      	    sparse_expect_files_outside_of_patterns)
      		return;
      
    @@ unpack-trees.c: int unpack_trees(unsigned len, struct tree_desc *t, struct unpac
      		update_sparsity_for_prefix(o->prefix, o->src_index);
      
     -	if (!core_apply_sparse_checkout || !o->update)
    -+	if (!repo->cfg_values->sparse_checkout || !o->update)
    ++	if (!repo->config_values.sparse_checkout || !o->update)
      		o->skip_sparse_checkout = 1;
      	if (!o->skip_sparse_checkout) {
      		memset(&pl, 0, sizeof(pl));
     
      ## wt-status.c ##
    -@@
    - #include "lockfile.h"
    - #include "sequencer.h"
    - #include "fsmonitor-settings.h"
    -+#include "config.h"
    - 
    - #define AB_DELAY_WARNING_IN_MS (2 * 1000)
    - #define UF_DELAY_WARNING_IN_MS (2 * 1000)
     @@ wt-status.c: static void wt_status_check_sparse_checkout(struct repository *r,
      	int skip_worktree = 0;
      	int i;
      
     -	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
    -+	if (!r->cfg_values->sparse_checkout || r->index->cache_nr == 0) {
    ++	if (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {
      		/*
      		 * Don't compute percentage of checked out files if we
      		 * aren't in a sparse checkout or would get division by 0.
3:  1c86c72f2d < -:  ---------- environment: move "branch.autoSetupMerge" into `struct config_values`
-:  ---------- > 3:  6e54e22ac7 environment: move "branch.autoSetupMerge" into `struct repo_config_values`

-- 
2.34.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v2 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-13 16:43 ` [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
@ 2026-01-13 16:44   ` Olamide Caleb Bello
  2026-01-13 19:26     ` Junio C Hamano
  2026-01-13 16:44   ` [Outreachy PATCH v2 2/3] environment: environment: stop using core.sparseCheckout globally Olamide Caleb Bello
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-13 16:44 UTC (permalink / raw)
  To: git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Olamide Caleb Bello

The config value parsed in git_default_core_config() is loaded eagerly
and stored in the global variable `git_attributes_file`.
Storing this value in a global variable can lead to unexpected
behaviours when more than one Git repository run in the same Git process.

Move this value into a `struct repo_config_values` which holds all the
values parsed by `git_default_config()` and can be accessed per
repository via `git_default_config()`. This will ensure the current
behaviour remains the same while also enabling the libification of Git.

It is important to note that `git_default_config()` is a wrapper to other
`git_default_*_config()` such as `git_default_core_config()`.
Therefore to access and modify this global variable,
the change has to be made in the function which parses and
stores the value in the global variable.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 attr.c        | 7 ++++---
 environment.c | 7 ++++---
 environment.h | 7 ++++++-
 repository.h  | 4 ++++
 4 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/attr.c b/attr.c
index 4999b7e09d..fbb9eaffaf 100644
--- a/attr.c
+++ b/attr.c
@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)
 
 const char *git_attr_global_file(void)
 {
-	if (!git_attributes_file)
-		git_attributes_file = xdg_config_home("attributes");
+	struct repo_config_values *cfg = &the_repository->config_values;
+	if (!cfg->attributes_file_path)
+		cfg->attributes_file_path = xdg_config_home("attributes");
 
-	return git_attributes_file;
+	return cfg->attributes_file_path;
 }
 
 int git_attr_system_is_enabled(void)
diff --git a/environment.c b/environment.c
index a770b5921d..2789c3514a 100644
--- a/environment.c
+++ b/environment.c
@@ -53,7 +53,6 @@ char *git_commit_encoding;
 char *git_log_output_encoding;
 char *apply_default_whitespace;
 char *apply_default_ignorewhitespace;
-char *git_attributes_file;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files = -1;
@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
 static int git_default_core_config(const char *var, const char *value,
 				   const struct config_context *ctx, void *cb)
 {
+	struct repo_config_values *cfg = &the_repository->config_values;
+
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
 		trust_executable_bit = git_config_bool(var, value);
@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.attributesfile")) {
-		FREE_AND_NULL(git_attributes_file);
-		return git_config_pathname(&git_attributes_file, var, value);
+		FREE_AND_NULL(cfg->attributes_file_path);
+		return git_config_pathname(&cfg->attributes_file_path, var, value);
 	}
 
 	if (!strcmp(var, "core.bare")) {
diff --git a/environment.h b/environment.h
index 51898c99cd..8c7803425e 100644
--- a/environment.h
+++ b/environment.h
@@ -84,6 +84,12 @@ extern const char * const local_repo_env[];
 
 struct strvec;
 
+/* Config values parsed by git_default_config() */
+struct repo_config_values {
+	/* core config values */
+	char *attributes_file_path;
+};
+
 /*
  * Wrapper of getenv() that returns a strdup value. This value is kept
  * in argv to be freed later.
@@ -152,7 +158,6 @@ extern int assume_unchanged;
 extern int warn_on_object_refname_ambiguity;
 extern char *apply_default_whitespace;
 extern char *apply_default_ignorewhitespace;
-extern char *git_attributes_file;
 extern int zlib_compression_level;
 extern int pack_compression_level;
 extern unsigned long pack_size_limit_cfg;
diff --git a/repository.h b/repository.h
index 6063c4b846..638a142577 100644
--- a/repository.h
+++ b/repository.h
@@ -3,6 +3,7 @@
 
 #include "strmap.h"
 #include "repo-settings.h"
+#include "environment.h"
 
 struct config_set;
 struct git_hash_algo;
@@ -148,6 +149,9 @@ struct repository {
 	/* Repository's compatibility hash algorithm. */
 	const struct git_hash_algo *compat_hash_algo;
 
+	/* Repository's config values parsed by git_default_config() */
+	struct repo_config_values config_values;
+
 	/* Repository's reference storage format, as serialized on disk. */
 	enum ref_storage_format ref_storage_format;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v2 2/3] environment: environment: stop using core.sparseCheckout globally
  2026-01-13 16:43 ` [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
  2026-01-13 16:44   ` [Outreachy PATCH v2 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-01-13 16:44   ` Olamide Caleb Bello
  2026-01-13 19:38     ` Junio C Hamano
  2026-01-13 16:44   ` [Outreachy PATCH v2 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-13 16:44 UTC (permalink / raw)
  To: git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Olamide Caleb Bello

The config value `core.sparseCheckout` is parsed in
`git_default_core_config()` and stored globally in
`core_appy_sparse_checkout`. This could cause unintended behaviours
when different Git repositories running in the same process access this
variable.

Move the parsed value into `struct repo_config_values` which holds all the
values parsed by `git_default_config()` and can be accessed
per repo via `git_default_config()`. This retains current
behaviours while achieving the repository scoped access.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
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/backfill.c        |  3 +--
 builtin/clone.c           |  2 +-
 builtin/grep.c            |  2 +-
 builtin/mv.c              |  2 +-
 builtin/sparse-checkout.c | 22 +++++++++++-----------
 builtin/worktree.c        |  2 +-
 dir.c                     |  2 +-
 environment.c             |  3 +--
 environment.h             |  2 +-
 sparse-index.c            |  6 ++++--
 unpack-trees.c            |  2 +-
 wt-status.c               |  2 +-
 12 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/builtin/backfill.c b/builtin/backfill.c
index e80fc1b694..90d5312240 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -1,4 +1,3 @@
-/* We need this macro to access core_apply_sparse_checkout */
 #define USE_THE_REPOSITORY_VARIABLE
 
 #include "builtin.h"
@@ -139,7 +138,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 	repo_config(repo, git_default_config, NULL);
 
 	if (ctx.sparse < 0)
-		ctx.sparse = core_apply_sparse_checkout;
+		ctx.sparse = repo->config_values.sparse_checkout;
 
 	result = do_backfill(&ctx);
 	backfill_context_clear(&ctx);
diff --git a/builtin/clone.c b/builtin/clone.c
index b19b302b06..b6b19e83d1 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
 	 * We must apply the setting in the current process
 	 * for the later checkout to use the sparse-checkout file.
 	 */
-	core_apply_sparse_checkout = 1;
+	the_repository->config_values.sparse_checkout = 1;
 
 	cmd.git_cmd = 1;
 	if (run_command(&cmd)) {
diff --git a/builtin/grep.c b/builtin/grep.c
index 53cccf2d25..525edb5e9c 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,
 	 *	"forget" the sparse-index feature switch. As a result, the index
 	 *	of these submodules are expanded unexpectedly.
 	 *
-	 * 2. "core_apply_sparse_checkout"
+	 * 2. "sparse_checkout"
 	 *	When running `grep` in the superproject, this setting is
 	 *	populated using the superproject's configs. However, once
 	 *	initialized, this config is globally accessible and is read by
diff --git a/builtin/mv.c b/builtin/mv.c
index d43925097b..511620747b 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -572,7 +572,7 @@ int cmd_mv(int argc,
 		rename_index_entry_at(the_repository->index, pos, dst);
 
 		if (ignore_sparse &&
-		    core_apply_sparse_checkout &&
+		    the_repository->config_values.sparse_checkout &&
 		    core_sparse_checkout_cone) {
 			/*
 			 * NEEDSWORK: we are *not* paying attention to
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 15d51e60a8..1c2c39b968 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
 	int res;
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.sparse_checkout)
 		die(_("this worktree is not sparse"));
 
 	argc = parse_options(argc, argv, prefix,
@@ -400,11 +400,11 @@ static int set_config(struct repository *repo,
 
 static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
 	/* If not specified, use previous definition of cone mode */
-	if (*cone_mode == -1 && core_apply_sparse_checkout)
+	if (*cone_mode == -1 && the_repository->config_values.sparse_checkout)
 		*cone_mode = core_sparse_checkout_cone;
 
 	/* Set cone/non-cone mode appropriately */
-	core_apply_sparse_checkout = 1;
+	the_repository->config_values.sparse_checkout = 1;
 	if (*cone_mode == 1 || *cone_mode == -1) {
 		core_sparse_checkout_cone = 1;
 		return MODE_CONE_PATTERNS;
@@ -418,7 +418,7 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind
 	int mode, record_mode;
 
 	/* Determine if we need to record the mode; ensure sparse checkout on */
-	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+	record_mode = (*cone_mode != -1) || !repo->config_values.sparse_checkout;
 
 	mode = update_cone_mode(cone_mode);
 	if (record_mode && set_config(repo, mode))
@@ -699,9 +699,9 @@ static int modify_pattern_list(struct repository *repo,
 		break;
 	}
 
-	if (!core_apply_sparse_checkout) {
+	if (!repo->config_values.sparse_checkout) {
 		set_config(repo, MODE_ALL_PATTERNS);
-		core_apply_sparse_checkout = 1;
+		repo->config_values.sparse_checkout = 1;
 		changed_config = 1;
 	}
 
@@ -798,7 +798,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
 	int ret;
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!repo->config_values.sparse_checkout)
 		die(_("no sparse-checkout to add to"));
 
 	repo_read_index(repo);
@@ -907,7 +907,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!repo->config_values.sparse_checkout)
 		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
 
 	reapply_opts.cone_mode = -1;
@@ -969,7 +969,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!repo->config_values.sparse_checkout)
 		die(_("must be in a sparse-checkout to clean directories"));
 	if (!core_sparse_checkout_cone)
 		die(_("must be in a cone-mode sparse-checkout to clean directories"));
@@ -1035,7 +1035,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	struct pattern_list pl;
 
 	/*
-	 * We do not exit early if !core_apply_sparse_checkout; due to the
+	 * We do not exit early if !repo->config_values.sparse_checkout; due to the
 	 * ability for users to manually muck things up between
 	 *   direct editing of .git/info/sparse-checkout
 	 *   running read-tree -m u HEAD or update-index --skip-worktree
@@ -1061,7 +1061,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
 	hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
 	pl.use_cone_patterns = 0;
-	core_apply_sparse_checkout = 1;
+	repo->config_values.sparse_checkout = 1;
 
 	add_pattern("/*", empty_base, 0, &pl, 0);
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index fbdaf2eb2e..e401b8253e 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,
 	 * If the current worktree has sparse-checkout enabled, then copy
 	 * the sparse-checkout patterns from the current worktree.
 	 */
-	if (core_apply_sparse_checkout)
+	if (wt->repo->config_values.sparse_checkout)
 		copy_sparse_checkout(sb_repo.buf);
 
 	/*
diff --git a/dir.c b/dir.c
index b00821f294..56b412a6d2 100644
--- a/dir.c
+++ b/dir.c
@@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(
 
 int init_sparse_checkout_patterns(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout)
+	if (!istate->repo->config_values.sparse_checkout)
 		return 1;
 	if (istate->sparse_checkout_patterns)
 		return 0;
diff --git a/environment.c b/environment.c
index 2789c3514a..65919a6c52 100644
--- a/environment.c
+++ b/environment.c
@@ -74,7 +74,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_apply_sparse_checkout;
 int core_sparse_checkout_cone;
 int sparse_expect_files_outside_of_patterns;
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.sparsecheckout")) {
-		core_apply_sparse_checkout = git_config_bool(var, value);
+		cfg->sparse_checkout = git_config_bool(var, value);
 		return 0;
 	}
 
diff --git a/environment.h b/environment.h
index 8c7803425e..5caf73b4b8 100644
--- a/environment.h
+++ b/environment.h
@@ -88,6 +88,7 @@ struct strvec;
 struct repo_config_values {
 	/* core config values */
 	char *attributes_file_path;
+	int sparse_checkout;
 };
 
 /*
@@ -167,7 +168,6 @@ extern int precomposed_unicode;
 extern int protect_hfs;
 extern int protect_ntfs;
 
-extern int core_apply_sparse_checkout;
 extern int core_sparse_checkout_cone;
 extern int sparse_expect_files_outside_of_patterns;
 
diff --git a/sparse-index.c b/sparse-index.c
index 76f90da5f5..6dd8dd679d 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)
 
 int is_sparse_index_allowed(struct index_state *istate, int flags)
 {
-	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
+	struct repo_config_values *cfg = &istate->repo->config_values;
+	if (!cfg->sparse_checkout || !core_sparse_checkout_cone)
 		return 0;
 
 	if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
@@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
 
 void clear_skip_worktree_from_present_files(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout ||
+	struct repo_config_values *cfg = &istate->repo->config_values;
+	if (!cfg->sparse_checkout ||
 	    sparse_expect_files_outside_of_patterns)
 		return;
 
diff --git a/unpack-trees.c b/unpack-trees.c
index f38c761ab9..2bdfa1334c 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 	if (o->prefix)
 		update_sparsity_for_prefix(o->prefix, o->src_index);
 
-	if (!core_apply_sparse_checkout || !o->update)
+	if (!repo->config_values.sparse_checkout || !o->update)
 		o->skip_sparse_checkout = 1;
 	if (!o->skip_sparse_checkout) {
 		memset(&pl, 0, sizeof(pl));
diff --git a/wt-status.c b/wt-status.c
index e12adb26b9..a2e388606f 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,
 	int skip_worktree = 0;
 	int i;
 
-	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
+	if (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {
 		/*
 		 * Don't compute percentage of checked out files if we
 		 * aren't in a sparse checkout or would get division by 0.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v2 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-01-13 16:43 ` [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
  2026-01-13 16:44   ` [Outreachy PATCH v2 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
  2026-01-13 16:44   ` [Outreachy PATCH v2 2/3] environment: environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-01-13 16:44   ` Olamide Caleb Bello
  2026-01-13 19:53     ` Junio C Hamano
  2026-01-15 22:17   ` [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct Bello Olamide
  2026-01-17 20:59   ` [Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values` Olamide Caleb Bello
  4 siblings, 1 reply; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-13 16:44 UTC (permalink / raw)
  To: git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Olamide Caleb Bello

The config value `brach.autoSetupMerge` is parsed in
`git_default_branch_config()` and stored in the global variable
`git_branch_track`. This global variable can cause unexpected behaviours
when multiple Git repos run in the the same process.

Move this value into `struct repo_config_values` which holds all values
parsed by `git_default_config()` and can be accessed per
repo via `git_default_config()`. This would retain the same
behaviours while achieving repository scoped access.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 branch.h                    |  2 --
 builtin/branch.c            |  2 +-
 builtin/checkout.c          |  2 +-
 builtin/push.c              |  2 +-
 builtin/submodule--helper.c |  2 +-
 environment.c               | 16 +++++++++++-----
 environment.h               |  6 ++++++
 repository.c                |  1 +
 8 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/branch.h b/branch.h
index ec2f35fda4..3dc6e2a0ff 100644
--- a/branch.h
+++ b/branch.h
@@ -15,8 +15,6 @@ enum branch_track {
 	BRANCH_TRACK_SIMPLE,
 };
 
-extern enum branch_track git_branch_track;
-
 /* Functions for acting on the information about branches. */
 
 /**
diff --git a/builtin/branch.c b/builtin/branch.c
index c577b5d20f..d75114b41c 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -795,7 +795,7 @@ int cmd_branch(int argc,
 	if (!sorting_options.nr)
 		string_list_append(&sorting_options, "refname");
 
-	track = git_branch_track;
+	track = the_repository->config_values.git_branch_track;
 
 	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
 				   0, &head_oid, NULL);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 261699e2f5..6c1cb9713c 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1631,7 +1631,7 @@ static int checkout_branch(struct checkout_opts *opts,
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
 	} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
-		opts->track = git_branch_track;
+		opts->track = the_repository->config_values.git_branch_track;
 
 	if (new_branch_info->name && !new_branch_info->commit)
 		die(_("Cannot switch branch to a non-commit '%s'"),
diff --git a/builtin/push.c b/builtin/push.c
index 5b6cebbb85..68105ecb2d 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -162,7 +162,7 @@ static NORETURN void die_push_simple(struct branch *branch,
 		advice_pushdefault_maybe = _("\n"
 				 "To choose either option permanently, "
 				 "see push.default in 'git help config'.\n");
-	if (git_branch_track != BRANCH_TRACK_SIMPLE)
+	if (the_repository->config_values.git_branch_track != BRANCH_TRACK_SIMPLE)
 		advice_automergesimple_maybe = _("\n"
 				 "To avoid automatically configuring "
 				 "an upstream branch when its name\n"
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d537ab087a..7239722e48 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3128,7 +3128,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,
 	};
 
 	repo_config(the_repository, git_default_config, NULL);
-	track = git_branch_track;
+	track = the_repository->config_values.git_branch_track;
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
 	if (argc != 3)
diff --git a/environment.c b/environment.c
index 65919a6c52..437d14e1ae 100644
--- a/environment.c
+++ b/environment.c
@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 enum eol core_eol = EOL_UNSET;
 int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
 char *check_roundtrip_encoding;
-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #ifndef OBJECT_CREATION_MODE
@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)
 
 static int git_default_branch_config(const char *var, const char *value)
 {
+	struct repo_config_values *cfg = &the_repository->config_values;
+
 	if (!strcmp(var, "branch.autosetupmerge")) {
 		if (value && !strcmp(value, "always")) {
-			git_branch_track = BRANCH_TRACK_ALWAYS;
+			cfg->git_branch_track = BRANCH_TRACK_ALWAYS;
 			return 0;
 		} else if (value && !strcmp(value, "inherit")) {
-			git_branch_track = BRANCH_TRACK_INHERIT;
+			cfg->git_branch_track = BRANCH_TRACK_INHERIT;
 			return 0;
 		} else if (value && !strcmp(value, "simple")) {
-			git_branch_track = BRANCH_TRACK_SIMPLE;
+			cfg->git_branch_track = BRANCH_TRACK_SIMPLE;
 			return 0;
 		}
-		git_branch_track = git_config_bool(var, value);
+		cfg->git_branch_track = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "branch.autosetuprebase")) {
@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,
 	/* Add other config variables here and to Documentation/config.adoc. */
 	return 0;
 }
+
+void repo_config_values_init(struct repo_config_values *cfg)
+{
+	cfg->git_branch_track = BRANCH_TRACK_REMOTE;
+}
diff --git a/environment.h b/environment.h
index 5caf73b4b8..bfcdffe836 100644
--- a/environment.h
+++ b/environment.h
@@ -2,6 +2,7 @@
 #define ENVIRONMENT_H
 
 #include "repo-settings.h"
+#include "branch.h"
 
 /* Double-check local_repo_env below if you add to this list. */
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
@@ -89,6 +90,9 @@ struct repo_config_values {
 	/* core config values */
 	char *attributes_file_path;
 	int sparse_checkout;
+
+	/* branch config values */
+	enum branch_track git_branch_track;
 };
 
 /*
@@ -114,6 +118,8 @@ const char *strip_namespace(const char *namespaced_ref);
 int git_default_config(const char *, const char *,
 		       const struct config_context *, void *);
 
+void repo_config_values_init(struct repo_config_values *cfg);
+
 /*
  * TODO: All the below state either explicitly or implicitly relies on
  * `the_repository`. We should eventually get rid of these and make the
diff --git a/repository.c b/repository.c
index c7e75215ac..d308cd78bf 100644
--- a/repository.c
+++ b/repository.c
@@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)
 	ALLOC_ARRAY(repo->index, 1);
 	index_state_init(repo->index, repo);
 	repo->check_deprecated_config = true;
+	repo_config_values_init(&repo->config_values);
 
 	/*
 	 * When a command runs inside a repository, it learns what
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v2 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-13 16:44   ` [Outreachy PATCH v2 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-01-13 19:26     ` Junio C Hamano
  2026-01-14  6:59       ` Bello Olamide
  0 siblings, 1 reply; 79+ messages in thread
From: Junio C Hamano @ 2026-01-13 19:26 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Olamide Caleb Bello <belkid98@gmail.com> writes:

> The config value parsed in git_default_core_config() is loaded eagerly
> and stored in the global variable `git_attributes_file`.
> Storing this value in a global variable can lead to unexpected
> behaviours when more than one Git repository run in the same Git process.

There are quite a many global singleton variables that are accessed
by git_default_core_config(), and this patch addresses only one of
them.  Are most of these variables per repository, or are there some
variables that are inherently just one for a user or for a system?

>  extern int warn_on_object_refname_ambiguity;
>  extern char *apply_default_whitespace;
>  extern char *apply_default_ignorewhitespace;
> -extern char *git_attributes_file;
>  extern int zlib_compression_level;
>  extern int pack_compression_level;
>  extern unsigned long pack_size_limit_cfg;

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v2 2/3] environment: environment: stop using core.sparseCheckout globally
  2026-01-13 16:44   ` [Outreachy PATCH v2 2/3] environment: environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-01-13 19:38     ` Junio C Hamano
  2026-01-14  7:16       ` Bello Olamide
  0 siblings, 1 reply; 79+ messages in thread
From: Junio C Hamano @ 2026-01-13 19:38 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Olamide Caleb Bello <belkid98@gmail.com> writes:

> diff --git a/builtin/backfill.c b/builtin/backfill.c
> index e80fc1b694..90d5312240 100644
> --- a/builtin/backfill.c
> +++ b/builtin/backfill.c
> @@ -1,4 +1,3 @@
> -/* We need this macro to access core_apply_sparse_checkout */

Why this removal?  You'll need to be able to access the_repository
because ...

>  #define USE_THE_REPOSITORY_VARIABLE
>  
>  #include "builtin.h"
> @@ -139,7 +138,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
>  	repo_config(repo, git_default_config, NULL);
>  
>  	if (ctx.sparse < 0)
> -		ctx.sparse = core_apply_sparse_checkout;
> +		ctx.sparse = repo->config_values.sparse_checkout;
>  
>  	result = do_backfill(&ctx);
>  	backfill_context_clear(&ctx);
> diff --git a/builtin/clone.c b/builtin/clone.c
> index b19b302b06..b6b19e83d1 100644
> --- a/builtin/clone.c
> +++ b/builtin/clone.c
> @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
>  	 * We must apply the setting in the current process
>  	 * for the later checkout to use the sparse-checkout file.
>  	 */
> -	core_apply_sparse_checkout = 1;
> +	the_repository->config_values.sparse_checkout = 1;

... you'd need to access this, even if it is now called slightly
differently, no?

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v2 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-01-13 16:44   ` [Outreachy PATCH v2 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
@ 2026-01-13 19:53     ` Junio C Hamano
  2026-01-14  7:40       ` Bello Olamide
  0 siblings, 1 reply; 79+ messages in thread
From: Junio C Hamano @ 2026-01-13 19:53 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Olamide Caleb Bello <belkid98@gmail.com> writes:

> The config value `brach.autoSetupMerge` is parsed in
> `git_default_branch_config()` and stored in the global variable
> `git_branch_track`. This global variable can cause unexpected behaviours
> when multiple Git repos run in the the same process.
>
> Move this value into `struct repo_config_values` which holds all values
> parsed by `git_default_config()` and can be accessed per
> repo via `git_default_config()`. This would retain the same
> behaviours while achieving repository scoped access.
>
> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
> ---

Currently the code flow is for git_config(git_default_config) to be
called fairly early in the program, updating the singleton globals
that are independent from individual repository.  This moves these
global variables to be stored in the config_values structure that is
tied to the_repository.

The claim in the cover letter was that this will make it possible
for us to later work on more than one repositories at once and each
repository can keep its own independent value.  While the updated
data structure may make it _possible_, I am not sure if this is a
safe approach to get to the final state, without seeing how the
config_values structure in the second "repo" structure is populated.

And before moving all these globals into config_values, it is not
possible to safely populate the config_values structure in the
second "repo" structure, if git_config(git_default_config) is what
we plan to use.  The settings that are still stored in globals will
then get overwritten.

That is why my first question for this round of patches was "are all
these settings inherently per repository?", because the scheme would
not work if there are globals that cannot be moved to config_value
structure to be per-repo.


> diff --git a/repository.c b/repository.c
> index c7e75215ac..d308cd78bf 100644
> --- a/repository.c
> +++ b/repository.c
> @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)
>  	ALLOC_ARRAY(repo->index, 1);
>  	index_state_init(repo->index, repo);
>  	repo->check_deprecated_config = true;
> +	repo_config_values_init(&repo->config_values);

Having a call to repo_config_values_init() when initializing an
in-core repository instance is a reasonable design, and I see this
step has an initialization of git_branch_track in that function.
Shouldn't we be doing similar initialization in the same
config_values_init() function for other members of the structure,
namely, attributes_file_path and sparse_checkout?

The function also may be a good place to do an equivalent of calling
git_config(git_default_config) there to grab values that are suitable
for the given repository, but I didn't think things through.

Thanks.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v2 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-13 19:26     ` Junio C Hamano
@ 2026-01-14  6:59       ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-14  6:59 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Tue, 13 Jan 2026 at 20:26, Junio C Hamano <gitster@pobox.com> wrote:
>
> Olamide Caleb Bello <belkid98@gmail.com> writes:
>
> > The config value parsed in git_default_core_config() is loaded eagerly
> > and stored in the global variable `git_attributes_file`.
> > Storing this value in a global variable can lead to unexpected
> > behaviours when more than one Git repository run in the same Git process.
>
> There are quite a many global singleton variables that are accessed
> by git_default_core_config(), and this patch addresses only one of
> them.  Are most of these variables per repository, or are there some
> variables that are inherently just one for a user or for a system?

Hello Junio,
From my understanding of the code in `git_default_config()`, some
config are just one for a user such
as the ones in `git_default_ident_config()`, `git_default_mailmap_config()`.
However a majority of them are repository specific such as the ones in
`git_default_core_config()`,
`git_default_branch_config()`, `git_default_push_config()`.
These are the ones that the movement to struct repo_config_values targets.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v2 2/3] environment: environment: stop using core.sparseCheckout globally
  2026-01-13 19:38     ` Junio C Hamano
@ 2026-01-14  7:16       ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-14  7:16 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Tue, 13 Jan 2026 at 20:38, Junio C Hamano <gitster@pobox.com> wrote:
>
> Olamide Caleb Bello <belkid98@gmail.com> writes:
>
> > diff --git a/builtin/backfill.c b/builtin/backfill.c
> > index e80fc1b694..90d5312240 100644
> > --- a/builtin/backfill.c
> > +++ b/builtin/backfill.c
> > @@ -1,4 +1,3 @@
> > -/* We need this macro to access core_apply_sparse_checkout */
>
> Why this removal?  You'll need to be able to access the_repository
> because ...
>
> >  #define USE_THE_REPOSITORY_VARIABLE
> >
> >  #include "builtin.h"
> > @@ -139,7 +138,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
> >       repo_config(repo, git_default_config, NULL);
> >
> >       if (ctx.sparse < 0)
> > -             ctx.sparse = core_apply_sparse_checkout;
> > +             ctx.sparse = repo->config_values.sparse_checkout;
> >
> >       result = do_backfill(&ctx);
> >       backfill_context_clear(&ctx);
> > diff --git a/builtin/clone.c b/builtin/clone.c
> > index b19b302b06..b6b19e83d1 100644
> > --- a/builtin/clone.c
> > +++ b/builtin/clone.c
> > @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
> >        * We must apply the setting in the current process
> >        * for the later checkout to use the sparse-checkout file.
> >        */
> > -     core_apply_sparse_checkout = 1;
> > +     the_repository->config_values.sparse_checkout = 1;
>
> ... you'd need to access this, even if it is now called slightly
> differently, no?

Oh yes you are right.
I will return it.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v2 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-01-13 19:53     ` Junio C Hamano
@ 2026-01-14  7:40       ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-14  7:40 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Tue, 13 Jan 2026 at 20:53, Junio C Hamano <gitster@pobox.com> wrote:
>
> Olamide Caleb Bello <belkid98@gmail.com> writes:
>
> > The config value `brach.autoSetupMerge` is parsed in
> > `git_default_branch_config()` and stored in the global variable
> > `git_branch_track`. This global variable can cause unexpected behaviours
> > when multiple Git repos run in the the same process.
> >
> > Move this value into `struct repo_config_values` which holds all values
> > parsed by `git_default_config()` and can be accessed per
> > repo via `git_default_config()`. This would retain the same
> > behaviours while achieving repository scoped access.
> >
> > Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
> > Mentored-by: Christian Couder <christian.couder@gmail.com>
> > Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
> > Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
> > ---
>
> Currently the code flow is for git_config(git_default_config) to be
> called fairly early in the program, updating the singleton globals
> that are independent from individual repository.  This moves these
> global variables to be stored in the config_values structure that is
> tied to the_repository.

This will move those global variables that are repository dependent into the
struct repo_config_values.
I admit the mistake is from my end. I should have been clear on that in the
commit message rather than say '...holds all variables parsed by
git_default_config()'.

Sorry
>
> The claim in the cover letter was that this will make it possible
> for us to later work on more than one repositories at once and each
> repository can keep its own independent value.  While the updated
> data structure may make it _possible_, I am not sure if this is a
> safe approach to get to the final state, without seeing how the
> config_values structure in the second "repo" structure is populated.
>
> And before moving all these globals into config_values, it is not
> possible to safely populate the config_values structure in the
> second "repo" structure, if git_config(git_default_config) is what
> we plan to use.  The settings that are still stored in globals will
> then get overwritten.

The idea proposed by Phillip is that we pass the repository parameter
as the call back to `git_default_config()`.
But since that will be quite invasive, you proposed we use `the_repository`
for now. Then later we can pass the repository parameter as the call back
and handle the invasiveness by simply checking in git_default_config()
struct repository *r =  cb ? cb : the_repository

>
> That is why my first question for this round of patches was "are all
> these settings inherently per repository?", because the scheme would
> not work if there are globals that cannot be moved to config_value
> structure to be per-repo.

The movement does not target all the global variables, but only those
that are dependent on a repository.

>
>
> > diff --git a/repository.c b/repository.c
> > index c7e75215ac..d308cd78bf 100644
> > --- a/repository.c
> > +++ b/repository.c
> > @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)
> >       ALLOC_ARRAY(repo->index, 1);
> >       index_state_init(repo->index, repo);
> >       repo->check_deprecated_config = true;
> > +     repo_config_values_init(&repo->config_values);
>
> Having a call to repo_config_values_init() when initializing an
> in-core repository instance is a reasonable design, and I see this
> step has an initialization of git_branch_track in that function.
> Shouldn't we be doing similar initialization in the same
> config_values_init() function for other members of the structure,
> namely, attributes_file_path and sparse_checkout?

I opted to initialize only the git_branch_track because in the original code,
it is set to a default value BRANCH_TRACK_REMOTE before the call to
git_default_config()
But attributes_file_path and sparse_checkout were only declared and
not initialized
before the call to 'git_default_config'.

I just tried to replicate the current behaviour be initializing
git_branch_track and those default
values early before the call to `git_default_config`

>
> The function also may be a good place to do an equivalent of calling
> git_config(git_default_config) there to grab values that are suitable
> for the given repository, but I didn't think things through.
>

Thanks

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct
  2026-01-13 16:43 ` [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
                     ` (2 preceding siblings ...)
  2026-01-13 16:44   ` [Outreachy PATCH v2 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
@ 2026-01-15 22:17   ` Bello Olamide
  2026-01-17 20:59   ` [Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values` Olamide Caleb Bello
  4 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-15 22:17 UTC (permalink / raw)
  To: git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Tue, 13 Jan 2026 at 18:25, Olamide Caleb Bello <belkid98@gmail.com> wrote:
>
> Hi Git Community,
> Over the course of my ongoing internship, which focused on moving global
> variables in environment.h into local scope, I have attempted to move some
> variables into the struct repo-settings.
> However there have been some design concerns as regards the use of
> `prepare_repo_settings()` with respect to when and where to call the
> function, and also the change in behaviours when the variable is lazily
> loaded as discussed in [1] and [2].
>
> After different deliberations, Phillip Wood proposed creating a new config
> struct [3], adding it to the repository struct and passing the repo struct to
> `git_default_config()` to store the parsed config values per repo.
> This ensures the current behaviours will be retained.
>
> I have experimented with this approach for some values and I would
> appreciate feedbacks about this approach before we can move forward
> and use it for more variables related to `git_default_config()`.
>
> For now, the parsed value is stored in `the_repository` in
> `git_default_*_config()` and further efforts to pass the repository
> parameter to `git_default_config()` as the callback parameter will
> be looked into later on.
> The link to the CI tests can be see in [4]
>
> 1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/
> 2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/
> 3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/
> 4. https://github.com/git/git/actions/runs/20953059862
>
> Changes in v2:
> ==============
> - Renamed new struct to repo_config_values
> - Moved struct and functions declaration and definition to
>   environment.[ch]
> - embedded the new struct in the repository struct thereby removing the
>   need to allocate memory on the heap
>

Hello,
I would like to know if there are new comments.
I want to send a new patch version.

Thanks

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values`
  2026-01-13 16:43 ` [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
                     ` (3 preceding siblings ...)
  2026-01-15 22:17   ` [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct Bello Olamide
@ 2026-01-17 20:59   ` Olamide Caleb Bello
  2026-01-17 20:59     ` [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
                       ` (4 more replies)
  4 siblings, 5 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-17 20:59 UTC (permalink / raw)
  To: git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Olamide Caleb Bello

Hi Git Community,
Over the course of my ongoing internship, which focused on moving repo specific
global variables in environment.h into local scope, I have attempted to move some
variables into the struct repo-settings.
However there has been some design concerns as regards the use of
`prepare_repo_settings()` with respect to when and where to call the
function, and also the change in behaviours when the variable is lazily
loaded as discussed in [1] and [2].

After different deliberations, Phillip Wood proposed creating a new config
struct [3], adding it to the repository struct and passing the repo struct to
`git_default_config()` to store parsed repo specific config values per repo.
This ensures the current behaviours will be retained.

I have experimented with this approach for some values and I would
appreciate feedbacks about this approach before we can move forward
and use it for more variables related to `git_default_config()`.

For now, the parsed value is stored in `the_repository` in
`git_default_*_config()` and further efforts to pass the repository
parameter to `git_default_config()` as the callback parameter will
be looked into later on.
The link to the CI tests can be see in [4]

1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/
2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/
3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/
4. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2266020513

Changes in v3:
==============
- Moved declaration and definition of repo_config_values_init into patch 1
- Returned commit message in line 1 of builtin/backfill.c

Olamide Caleb Bello (3):
  environment: stop storing `core.attributesFile` globally
  environment: environment: stop using core.sparseCheckout globally
  environment: move "branch.autoSetupMerge" into `struct
    repo_config_values`

 attr.c                      |  7 ++++---
 branch.h                    |  2 --
 builtin/backfill.c          |  2 +-
 builtin/branch.c            |  2 +-
 builtin/checkout.c          |  2 +-
 builtin/clone.c             |  2 +-
 builtin/grep.c              |  2 +-
 builtin/mv.c                |  2 +-
 builtin/push.c              |  2 +-
 builtin/sparse-checkout.c   | 22 +++++++++++-----------
 builtin/submodule--helper.c |  2 +-
 builtin/worktree.c          |  2 +-
 dir.c                       |  2 +-
 environment.c               | 28 ++++++++++++++++++----------
 environment.h               | 15 +++++++++++++--
 repository.c                |  1 +
 repository.h                |  4 ++++
 sparse-index.c              |  6 ++++--
 unpack-trees.c              |  2 +-
 wt-status.c                 |  2 +-
 20 files changed, 67 insertions(+), 42 deletions(-)

 Range diff versus v2:
 =====================

 1:  b6f8deaa40 ! 1:  1aa41da833 environment: stop storing `core.attributesFile` globally
    @@ Metadata
      ## Commit message ##
         environment: stop storing `core.attributesFile` globally

    -    The config value parsed in git_default_core_config() is loaded eagerly
    +    The config value is parsed in git_default_core_config(), loaded eagerly
         and stored in the global variable `git_attributes_file`.
         Storing this value in a global variable can lead to unexpected
         behaviours when more than one Git repository run in the same Git process.

    -    Move this value into a `struct repo_config_values` which holds all the
    -    values parsed by `git_default_config()` and can be accessed per
    -    repository via `git_default_config()`. This will ensure the current
    -    behaviour remains the same while also enabling the libification of Git.
    +    Create a new struct `repo_config_values` to hold this value and
    +    other repository dependent values parsed by `git_default_config()` and
    +    can be accessed per repository via `git_default_config()`.
    +    This will ensure the current behaviour remains the same while also
    +    enabling the libification of Git.

         It is important to note that `git_default_config()` is a wrapper to other
         `git_default_*_config()` such as `git_default_core_config()`.
    @@ environment.c: static int git_default_core_config(const char *var, const char *v
      	}

      	if (!strcmp(var, "core.bare")) {
    +@@ environment.c: int git_default_config(const char *var, const char *value,
    + 	/* Add other config variables here and to Documentation/config.adoc. */
    + 	return 0;
    + }
    ++
    ++void repo_config_values_init(struct repo_config_values *cfg)
    ++{
    ++	cfg->attributes_file_path = NULL;
    ++}

      ## environment.h ##
     @@ environment.h: extern const char * const local_repo_env[];
    @@ environment.h: extern const char * const local_repo_env[];
      /*
       * Wrapper of getenv() that returns a strdup value. This value is kept
       * in argv to be freed later.
    +@@ environment.h: const char *strip_namespace(const char *namespaced_ref);
    + int git_default_config(const char *, const char *,
    + 		       const struct config_context *, void *);
    +
    ++void repo_config_values_init(struct repo_config_values *cfg);
    ++
    + /*
    +  * TODO: All the below state either explicitly or implicitly relies on
    +  * `the_repository`. We should eventually get rid of these and make the
     @@ environment.h: extern int assume_unchanged;
      extern int warn_on_object_refname_ambiguity;
      extern char *apply_default_whitespace;
    @@ environment.h: extern int assume_unchanged;
      extern int pack_compression_level;
      extern unsigned long pack_size_limit_cfg;

    + ## repository.c ##
    +@@ repository.c: void initialize_repository(struct repository *repo)
    + 	ALLOC_ARRAY(repo->index, 1);
    + 	index_state_init(repo->index, repo);
    + 	repo->check_deprecated_config = true;
    ++	repo_config_values_init(&repo->config_values);
    +
    + 	/*
    + 	 * When a command runs inside a repository, it learns what
    +
      ## repository.h ##
     @@

2:  1e83c077f2 ! 2:  fd95169de4 environment: environment: stop using core.sparseCheckout globally
    @@ Commit message
         when different Git repositories running in the same process access this
         variable.

    -    Move the parsed value into `struct repo_config_values` which holds all the
    -    values parsed by `git_default_config()` and can be accessed
    -    per repo via `git_default_config()`. This retains current
    +    Move the parsed value into `struct repo_config_values` to retains current
         behaviours while achieving the repository scoped access.

         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
    @@ Commit message
         Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>

      ## builtin/backfill.c ##
    -@@
    --/* We need this macro to access core_apply_sparse_checkout */
    - #define USE_THE_REPOSITORY_VARIABLE
    -
    - #include "builtin.h"
     @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
      	repo_config(repo, git_default_config, NULL);

    @@ environment.c: static int git_default_core_config(const char *var, const char *v
      		return 0;
      	}

    +@@ environment.c: int git_default_config(const char *var, const char *value,
    + void repo_config_values_init(struct repo_config_values *cfg)
    + {
    + 	cfg->attributes_file_path = NULL;
    ++	cfg->sparse_checkout = 0;
    + }

      ## environment.h ##
     @@ environment.h: struct strvec;
3:  6e54e22ac7 ! 3:  9a411db9f8 environment: move "branch.autoSetupMerge" into `struct repo_config_values`
    @@ Commit message
         `git_branch_track`. This global variable can cause unexpected behaviours
         when multiple Git repos run in the the same process.

    -    Move this value into `struct repo_config_values` which holds all values
    -    parsed by `git_default_config()` and can be accessed per
    -    repo via `git_default_config()`. This would retain the same
    +    Move this value into `struct repo_config_values` to retain current
         behaviours while achieving repository scoped access.

         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
    @@ environment.c: static int git_default_i18n_config(const char *var, const char *v
      		return 0;
      	}
      	if (!strcmp(var, "branch.autosetuprebase")) {
    -@@ environment.c: int git_default_config(const char *var, const char *value,
    - 	/* Add other config variables here and to Documentation/config.adoc. */
    - 	return 0;
    - }
    -+
    -+void repo_config_values_init(struct repo_config_values *cfg)
    -+{
    +@@ environment.c: void repo_config_values_init(struct repo_config_values *cfg)
    + {
    + 	cfg->attributes_file_path = NULL;
    + 	cfg->sparse_checkout = 0;
     +	cfg->git_branch_track = BRANCH_TRACK_REMOTE;
    -+}
    + }

      ## environment.h ##
     @@
    @@ environment.h: struct repo_config_values {
      };

      /*
    -@@ environment.h: const char *strip_namespace(const char *namespaced_ref);
    - int git_default_config(const char *, const char *,
    - 		       const struct config_context *, void *);
    -
    -+void repo_config_values_init(struct repo_config_values *cfg);
    -+
    - /*
    -  * TODO: All the below state either explicitly or implicitly relies on
    -  * `the_repository`. We should eventually get rid of these and make the
    -
    - ## repository.c ##
    -@@ repository.c: void initialize_repository(struct repository *repo)
    - 	ALLOC_ARRAY(repo->index, 1);
    - 	index_state_init(repo->index, repo);
    - 	repo->check_deprecated_config = true;
    -+	repo_config_values_init(&repo->config_values);
    -
    - 	/*
    - 	 * When a command runs inside a repository, it learns what


-- 
2.34.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-17 20:59   ` [Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values` Olamide Caleb Bello
@ 2026-01-17 20:59     ` Olamide Caleb Bello
  2026-01-22 12:13       ` Toon Claes
  2026-01-22 14:40       ` Phillip Wood
  2026-01-17 20:59     ` [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally Olamide Caleb Bello
                       ` (3 subsequent siblings)
  4 siblings, 2 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-17 20:59 UTC (permalink / raw)
  To: git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Olamide Caleb Bello

The config value is parsed in git_default_core_config(), loaded eagerly
and stored in the global variable `git_attributes_file`.
Storing this value in a global variable can lead to unexpected
behaviours when more than one Git repository run in the same Git process.

Create a new struct `repo_config_values` to hold this value and
other repository dependent values parsed by `git_default_config()` and
can be accessed per repository via `git_default_config()`.
This will ensure the current behaviour remains the same while also
enabling the libification of Git.

It is important to note that `git_default_config()` is a wrapper to other
`git_default_*_config()` such as `git_default_core_config()`.
Therefore to access and modify this global variable,
the change has to be made in the function which parses and
stores the value in the global variable.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 attr.c        |  7 ++++---
 environment.c | 12 +++++++++---
 environment.h |  9 ++++++++-
 repository.c  |  1 +
 repository.h  |  4 ++++
 5 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/attr.c b/attr.c
index 4999b7e09d..fbb9eaffaf 100644
--- a/attr.c
+++ b/attr.c
@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)
 
 const char *git_attr_global_file(void)
 {
-	if (!git_attributes_file)
-		git_attributes_file = xdg_config_home("attributes");
+	struct repo_config_values *cfg = &the_repository->config_values;
+	if (!cfg->attributes_file_path)
+		cfg->attributes_file_path = xdg_config_home("attributes");
 
-	return git_attributes_file;
+	return cfg->attributes_file_path;
 }
 
 int git_attr_system_is_enabled(void)
diff --git a/environment.c b/environment.c
index a770b5921d..283db0a1a0 100644
--- a/environment.c
+++ b/environment.c
@@ -53,7 +53,6 @@ char *git_commit_encoding;
 char *git_log_output_encoding;
 char *apply_default_whitespace;
 char *apply_default_ignorewhitespace;
-char *git_attributes_file;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files = -1;
@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
 static int git_default_core_config(const char *var, const char *value,
 				   const struct config_context *ctx, void *cb)
 {
+	struct repo_config_values *cfg = &the_repository->config_values;
+
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
 		trust_executable_bit = git_config_bool(var, value);
@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.attributesfile")) {
-		FREE_AND_NULL(git_attributes_file);
-		return git_config_pathname(&git_attributes_file, var, value);
+		FREE_AND_NULL(cfg->attributes_file_path);
+		return git_config_pathname(&cfg->attributes_file_path, var, value);
 	}
 
 	if (!strcmp(var, "core.bare")) {
@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,
 	/* Add other config variables here and to Documentation/config.adoc. */
 	return 0;
 }
+
+void repo_config_values_init(struct repo_config_values *cfg)
+{
+	cfg->attributes_file_path = NULL;
+}
diff --git a/environment.h b/environment.h
index 51898c99cd..aea73ff25b 100644
--- a/environment.h
+++ b/environment.h
@@ -84,6 +84,12 @@ extern const char * const local_repo_env[];
 
 struct strvec;
 
+/* Config values parsed by git_default_config() */
+struct repo_config_values {
+	/* core config values */
+	char *attributes_file_path;
+};
+
 /*
  * Wrapper of getenv() that returns a strdup value. This value is kept
  * in argv to be freed later.
@@ -107,6 +113,8 @@ const char *strip_namespace(const char *namespaced_ref);
 int git_default_config(const char *, const char *,
 		       const struct config_context *, void *);
 
+void repo_config_values_init(struct repo_config_values *cfg);
+
 /*
  * TODO: All the below state either explicitly or implicitly relies on
  * `the_repository`. We should eventually get rid of these and make the
@@ -152,7 +160,6 @@ extern int assume_unchanged;
 extern int warn_on_object_refname_ambiguity;
 extern char *apply_default_whitespace;
 extern char *apply_default_ignorewhitespace;
-extern char *git_attributes_file;
 extern int zlib_compression_level;
 extern int pack_compression_level;
 extern unsigned long pack_size_limit_cfg;
diff --git a/repository.c b/repository.c
index c7e75215ac..d308cd78bf 100644
--- a/repository.c
+++ b/repository.c
@@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)
 	ALLOC_ARRAY(repo->index, 1);
 	index_state_init(repo->index, repo);
 	repo->check_deprecated_config = true;
+	repo_config_values_init(&repo->config_values);
 
 	/*
 	 * When a command runs inside a repository, it learns what
diff --git a/repository.h b/repository.h
index 6063c4b846..638a142577 100644
--- a/repository.h
+++ b/repository.h
@@ -3,6 +3,7 @@
 
 #include "strmap.h"
 #include "repo-settings.h"
+#include "environment.h"
 
 struct config_set;
 struct git_hash_algo;
@@ -148,6 +149,9 @@ struct repository {
 	/* Repository's compatibility hash algorithm. */
 	const struct git_hash_algo *compat_hash_algo;
 
+	/* Repository's config values parsed by git_default_config() */
+	struct repo_config_values config_values;
+
 	/* Repository's reference storage format, as serialized on disk. */
 	enum ref_storage_format ref_storage_format;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally
  2026-01-17 20:59   ` [Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values` Olamide Caleb Bello
  2026-01-17 20:59     ` [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-01-17 20:59     ` Olamide Caleb Bello
  2026-01-22 12:13       ` Toon Claes
  2026-01-22 14:41       ` Phillip Wood
  2026-01-17 20:59     ` [Outreachy PATCH v3 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
                       ` (2 subsequent siblings)
  4 siblings, 2 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-17 20:59 UTC (permalink / raw)
  To: git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Olamide Caleb Bello

The config value `core.sparseCheckout` is parsed in
`git_default_core_config()` and stored globally in
`core_appy_sparse_checkout`. This could cause unintended behaviours
when different Git repositories running in the same process access this
variable.

Move the parsed value into `struct repo_config_values` to retains current
behaviours while achieving the repository scoped access.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
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/backfill.c        |  2 +-
 builtin/clone.c           |  2 +-
 builtin/grep.c            |  2 +-
 builtin/mv.c              |  2 +-
 builtin/sparse-checkout.c | 22 +++++++++++-----------
 builtin/worktree.c        |  2 +-
 dir.c                     |  2 +-
 environment.c             |  4 ++--
 environment.h             |  2 +-
 sparse-index.c            |  6 ++++--
 unpack-trees.c            |  2 +-
 wt-status.c               |  2 +-
 12 files changed, 26 insertions(+), 24 deletions(-)

diff --git a/builtin/backfill.c b/builtin/backfill.c
index e80fc1b694..5fc8c51ed1 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 	repo_config(repo, git_default_config, NULL);
 
 	if (ctx.sparse < 0)
-		ctx.sparse = core_apply_sparse_checkout;
+		ctx.sparse = repo->config_values.sparse_checkout;
 
 	result = do_backfill(&ctx);
 	backfill_context_clear(&ctx);
diff --git a/builtin/clone.c b/builtin/clone.c
index b19b302b06..b6b19e83d1 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
 	 * We must apply the setting in the current process
 	 * for the later checkout to use the sparse-checkout file.
 	 */
-	core_apply_sparse_checkout = 1;
+	the_repository->config_values.sparse_checkout = 1;
 
 	cmd.git_cmd = 1;
 	if (run_command(&cmd)) {
diff --git a/builtin/grep.c b/builtin/grep.c
index 53cccf2d25..525edb5e9c 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,
 	 *	"forget" the sparse-index feature switch. As a result, the index
 	 *	of these submodules are expanded unexpectedly.
 	 *
-	 * 2. "core_apply_sparse_checkout"
+	 * 2. "sparse_checkout"
 	 *	When running `grep` in the superproject, this setting is
 	 *	populated using the superproject's configs. However, once
 	 *	initialized, this config is globally accessible and is read by
diff --git a/builtin/mv.c b/builtin/mv.c
index d43925097b..511620747b 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -572,7 +572,7 @@ int cmd_mv(int argc,
 		rename_index_entry_at(the_repository->index, pos, dst);
 
 		if (ignore_sparse &&
-		    core_apply_sparse_checkout &&
+		    the_repository->config_values.sparse_checkout &&
 		    core_sparse_checkout_cone) {
 			/*
 			 * NEEDSWORK: we are *not* paying attention to
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 15d51e60a8..1c2c39b968 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
 	int res;
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.sparse_checkout)
 		die(_("this worktree is not sparse"));
 
 	argc = parse_options(argc, argv, prefix,
@@ -400,11 +400,11 @@ static int set_config(struct repository *repo,
 
 static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
 	/* If not specified, use previous definition of cone mode */
-	if (*cone_mode == -1 && core_apply_sparse_checkout)
+	if (*cone_mode == -1 && the_repository->config_values.sparse_checkout)
 		*cone_mode = core_sparse_checkout_cone;
 
 	/* Set cone/non-cone mode appropriately */
-	core_apply_sparse_checkout = 1;
+	the_repository->config_values.sparse_checkout = 1;
 	if (*cone_mode == 1 || *cone_mode == -1) {
 		core_sparse_checkout_cone = 1;
 		return MODE_CONE_PATTERNS;
@@ -418,7 +418,7 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind
 	int mode, record_mode;
 
 	/* Determine if we need to record the mode; ensure sparse checkout on */
-	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+	record_mode = (*cone_mode != -1) || !repo->config_values.sparse_checkout;
 
 	mode = update_cone_mode(cone_mode);
 	if (record_mode && set_config(repo, mode))
@@ -699,9 +699,9 @@ static int modify_pattern_list(struct repository *repo,
 		break;
 	}
 
-	if (!core_apply_sparse_checkout) {
+	if (!repo->config_values.sparse_checkout) {
 		set_config(repo, MODE_ALL_PATTERNS);
-		core_apply_sparse_checkout = 1;
+		repo->config_values.sparse_checkout = 1;
 		changed_config = 1;
 	}
 
@@ -798,7 +798,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
 	int ret;
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!repo->config_values.sparse_checkout)
 		die(_("no sparse-checkout to add to"));
 
 	repo_read_index(repo);
@@ -907,7 +907,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!repo->config_values.sparse_checkout)
 		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
 
 	reapply_opts.cone_mode = -1;
@@ -969,7 +969,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!repo->config_values.sparse_checkout)
 		die(_("must be in a sparse-checkout to clean directories"));
 	if (!core_sparse_checkout_cone)
 		die(_("must be in a cone-mode sparse-checkout to clean directories"));
@@ -1035,7 +1035,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	struct pattern_list pl;
 
 	/*
-	 * We do not exit early if !core_apply_sparse_checkout; due to the
+	 * We do not exit early if !repo->config_values.sparse_checkout; due to the
 	 * ability for users to manually muck things up between
 	 *   direct editing of .git/info/sparse-checkout
 	 *   running read-tree -m u HEAD or update-index --skip-worktree
@@ -1061,7 +1061,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
 	hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
 	pl.use_cone_patterns = 0;
-	core_apply_sparse_checkout = 1;
+	repo->config_values.sparse_checkout = 1;
 
 	add_pattern("/*", empty_base, 0, &pl, 0);
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index fbdaf2eb2e..e401b8253e 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,
 	 * If the current worktree has sparse-checkout enabled, then copy
 	 * the sparse-checkout patterns from the current worktree.
 	 */
-	if (core_apply_sparse_checkout)
+	if (wt->repo->config_values.sparse_checkout)
 		copy_sparse_checkout(sb_repo.buf);
 
 	/*
diff --git a/dir.c b/dir.c
index b00821f294..56b412a6d2 100644
--- a/dir.c
+++ b/dir.c
@@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(
 
 int init_sparse_checkout_patterns(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout)
+	if (!istate->repo->config_values.sparse_checkout)
 		return 1;
 	if (istate->sparse_checkout_patterns)
 		return 0;
diff --git a/environment.c b/environment.c
index 283db0a1a0..6633542750 100644
--- a/environment.c
+++ b/environment.c
@@ -74,7 +74,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_apply_sparse_checkout;
 int core_sparse_checkout_cone;
 int sparse_expect_files_outside_of_patterns;
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.sparsecheckout")) {
-		core_apply_sparse_checkout = git_config_bool(var, value);
+		cfg->sparse_checkout = git_config_bool(var, value);
 		return 0;
 	}
 
@@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,
 void repo_config_values_init(struct repo_config_values *cfg)
 {
 	cfg->attributes_file_path = NULL;
+	cfg->sparse_checkout = 0;
 }
diff --git a/environment.h b/environment.h
index aea73ff25b..3b5ff7094a 100644
--- a/environment.h
+++ b/environment.h
@@ -88,6 +88,7 @@ struct strvec;
 struct repo_config_values {
 	/* core config values */
 	char *attributes_file_path;
+	int sparse_checkout;
 };
 
 /*
@@ -169,7 +170,6 @@ extern int precomposed_unicode;
 extern int protect_hfs;
 extern int protect_ntfs;
 
-extern int core_apply_sparse_checkout;
 extern int core_sparse_checkout_cone;
 extern int sparse_expect_files_outside_of_patterns;
 
diff --git a/sparse-index.c b/sparse-index.c
index 76f90da5f5..6dd8dd679d 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)
 
 int is_sparse_index_allowed(struct index_state *istate, int flags)
 {
-	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
+	struct repo_config_values *cfg = &istate->repo->config_values;
+	if (!cfg->sparse_checkout || !core_sparse_checkout_cone)
 		return 0;
 
 	if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
@@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
 
 void clear_skip_worktree_from_present_files(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout ||
+	struct repo_config_values *cfg = &istate->repo->config_values;
+	if (!cfg->sparse_checkout ||
 	    sparse_expect_files_outside_of_patterns)
 		return;
 
diff --git a/unpack-trees.c b/unpack-trees.c
index f38c761ab9..2bdfa1334c 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 	if (o->prefix)
 		update_sparsity_for_prefix(o->prefix, o->src_index);
 
-	if (!core_apply_sparse_checkout || !o->update)
+	if (!repo->config_values.sparse_checkout || !o->update)
 		o->skip_sparse_checkout = 1;
 	if (!o->skip_sparse_checkout) {
 		memset(&pl, 0, sizeof(pl));
diff --git a/wt-status.c b/wt-status.c
index e12adb26b9..a2e388606f 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,
 	int skip_worktree = 0;
 	int i;
 
-	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
+	if (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {
 		/*
 		 * Don't compute percentage of checked out files if we
 		 * aren't in a sparse checkout or would get division by 0.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v3 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-01-17 20:59   ` [Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values` Olamide Caleb Bello
  2026-01-17 20:59     ` [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
  2026-01-17 20:59     ` [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-01-17 20:59     ` Olamide Caleb Bello
  2026-01-22 14:41       ` Phillip Wood
  2026-01-20 15:19     ` [Outreachy PATCH v3 0/3] store repo specific config values in new " Bello Olamide
  2026-01-24 11:55     ` [Outreachy PATCH v4 " Olamide Caleb Bello
  4 siblings, 1 reply; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-17 20:59 UTC (permalink / raw)
  To: git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Olamide Caleb Bello

The config value `brach.autoSetupMerge` is parsed in
`git_default_branch_config()` and stored in the global variable
`git_branch_track`. This global variable can cause unexpected behaviours
when multiple Git repos run in the the same process.

Move this value into `struct repo_config_values` to retain current
behaviours while achieving repository scoped access.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 branch.h                    |  2 --
 builtin/branch.c            |  2 +-
 builtin/checkout.c          |  2 +-
 builtin/push.c              |  2 +-
 builtin/submodule--helper.c |  2 +-
 environment.c               | 12 +++++++-----
 environment.h               |  4 ++++
 7 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/branch.h b/branch.h
index ec2f35fda4..3dc6e2a0ff 100644
--- a/branch.h
+++ b/branch.h
@@ -15,8 +15,6 @@ enum branch_track {
 	BRANCH_TRACK_SIMPLE,
 };
 
-extern enum branch_track git_branch_track;
-
 /* Functions for acting on the information about branches. */
 
 /**
diff --git a/builtin/branch.c b/builtin/branch.c
index c577b5d20f..d75114b41c 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -795,7 +795,7 @@ int cmd_branch(int argc,
 	if (!sorting_options.nr)
 		string_list_append(&sorting_options, "refname");
 
-	track = git_branch_track;
+	track = the_repository->config_values.git_branch_track;
 
 	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
 				   0, &head_oid, NULL);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 261699e2f5..6c1cb9713c 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1631,7 +1631,7 @@ static int checkout_branch(struct checkout_opts *opts,
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
 	} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
-		opts->track = git_branch_track;
+		opts->track = the_repository->config_values.git_branch_track;
 
 	if (new_branch_info->name && !new_branch_info->commit)
 		die(_("Cannot switch branch to a non-commit '%s'"),
diff --git a/builtin/push.c b/builtin/push.c
index 5b6cebbb85..68105ecb2d 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -162,7 +162,7 @@ static NORETURN void die_push_simple(struct branch *branch,
 		advice_pushdefault_maybe = _("\n"
 				 "To choose either option permanently, "
 				 "see push.default in 'git help config'.\n");
-	if (git_branch_track != BRANCH_TRACK_SIMPLE)
+	if (the_repository->config_values.git_branch_track != BRANCH_TRACK_SIMPLE)
 		advice_automergesimple_maybe = _("\n"
 				 "To avoid automatically configuring "
 				 "an upstream branch when its name\n"
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d537ab087a..7239722e48 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3128,7 +3128,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,
 	};
 
 	repo_config(the_repository, git_default_config, NULL);
-	track = git_branch_track;
+	track = the_repository->config_values.git_branch_track;
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
 	if (argc != 3)
diff --git a/environment.c b/environment.c
index 6633542750..2092a5c3c6 100644
--- a/environment.c
+++ b/environment.c
@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 enum eol core_eol = EOL_UNSET;
 int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
 char *check_roundtrip_encoding;
-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #ifndef OBJECT_CREATION_MODE
@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)
 
 static int git_default_branch_config(const char *var, const char *value)
 {
+	struct repo_config_values *cfg = &the_repository->config_values;
+
 	if (!strcmp(var, "branch.autosetupmerge")) {
 		if (value && !strcmp(value, "always")) {
-			git_branch_track = BRANCH_TRACK_ALWAYS;
+			cfg->git_branch_track = BRANCH_TRACK_ALWAYS;
 			return 0;
 		} else if (value && !strcmp(value, "inherit")) {
-			git_branch_track = BRANCH_TRACK_INHERIT;
+			cfg->git_branch_track = BRANCH_TRACK_INHERIT;
 			return 0;
 		} else if (value && !strcmp(value, "simple")) {
-			git_branch_track = BRANCH_TRACK_SIMPLE;
+			cfg->git_branch_track = BRANCH_TRACK_SIMPLE;
 			return 0;
 		}
-		git_branch_track = git_config_bool(var, value);
+		cfg->git_branch_track = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "branch.autosetuprebase")) {
@@ -761,4 +762,5 @@ void repo_config_values_init(struct repo_config_values *cfg)
 {
 	cfg->attributes_file_path = NULL;
 	cfg->sparse_checkout = 0;
+	cfg->git_branch_track = BRANCH_TRACK_REMOTE;
 }
diff --git a/environment.h b/environment.h
index 3b5ff7094a..bfcdffe836 100644
--- a/environment.h
+++ b/environment.h
@@ -2,6 +2,7 @@
 #define ENVIRONMENT_H
 
 #include "repo-settings.h"
+#include "branch.h"
 
 /* Double-check local_repo_env below if you add to this list. */
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
@@ -89,6 +90,9 @@ struct repo_config_values {
 	/* core config values */
 	char *attributes_file_path;
 	int sparse_checkout;
+
+	/* branch config values */
+	enum branch_track git_branch_track;
 };
 
 /*
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values`
  2026-01-17 20:59   ` [Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values` Olamide Caleb Bello
                       ` (2 preceding siblings ...)
  2026-01-17 20:59     ` [Outreachy PATCH v3 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
@ 2026-01-20 15:19     ` Bello Olamide
  2026-01-24 11:55     ` [Outreachy PATCH v4 " Olamide Caleb Bello
  4 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-20 15:19 UTC (permalink / raw)
  To: git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Sat, 17 Jan 2026 at 22:02, Olamide Caleb Bello <belkid98@gmail.com> wrote:
>
> Hi Git Community,
> Over the course of my ongoing internship, which focused on moving repo specific
> global variables in environment.h into local scope, I have attempted to move some
> variables into the struct repo-settings.
> However there has been some design concerns as regards the use of
> `prepare_repo_settings()` with respect to when and where to call the
> function, and also the change in behaviours when the variable is lazily
> loaded as discussed in [1] and [2].
>
> After different deliberations, Phillip Wood proposed creating a new config
> struct [3], adding it to the repository struct and passing the repo struct to
> `git_default_config()` to store parsed repo specific config values per repo.
> This ensures the current behaviours will be retained.
>
> I have experimented with this approach for some values and I would
> appreciate feedbacks about this approach before we can move forward
> and use it for more variables related to `git_default_config()`.
>
> For now, the parsed value is stored in `the_repository` in
> `git_default_*_config()` and further efforts to pass the repository
> parameter to `git_default_config()` as the callback parameter will
> be looked into later on.
> The link to the CI tests can be see in [4]
>
> 1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/
> 2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/
> 3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/
> 4. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2266020513
>
> Changes in v3:
> ==============
> - Moved declaration and definition of repo_config_values_init into patch 1
> - Returned commit message in line 1 of builtin/backfill.c
>
> Olamide Caleb Bello (3):
>   environment: stop storing `core.attributesFile` globally
>   environment: environment: stop using core.sparseCheckout globally
>   environment: move "branch.autoSetupMerge" into `struct
>     repo_config_values`
>
>  attr.c                      |  7 ++++---
>  branch.h                    |  2 --
>  builtin/backfill.c          |  2 +-
>  builtin/branch.c            |  2 +-
>  builtin/checkout.c          |  2 +-
>  builtin/clone.c             |  2 +-
>  builtin/grep.c              |  2 +-
>  builtin/mv.c                |  2 +-
>  builtin/push.c              |  2 +-
>  builtin/sparse-checkout.c   | 22 +++++++++++-----------
>  builtin/submodule--helper.c |  2 +-
>  builtin/worktree.c          |  2 +-
>  dir.c                       |  2 +-
>  environment.c               | 28 ++++++++++++++++++----------
>  environment.h               | 15 +++++++++++++--
>  repository.c                |  1 +
>  repository.h                |  4 ++++
>  sparse-index.c              |  6 ++++--
>  unpack-trees.c              |  2 +-
>  wt-status.c                 |  2 +-
>  20 files changed, 67 insertions(+), 42 deletions(-)
>
>  Range diff versus v2:
>  =====================
>
>  1:  b6f8deaa40 ! 1:  1aa41da833 environment: stop storing `core.attributesFile` globally
>     @@ Metadata
>       ## Commit message ##
>          environment: stop storing `core.attributesFile` globally
>
>     -    The config value parsed in git_default_core_config() is loaded eagerly
>     +    The config value is parsed in git_default_core_config(), loaded eagerly
>          and stored in the global variable `git_attributes_file`.
>          Storing this value in a global variable can lead to unexpected
>          behaviours when more than one Git repository run in the same Git process.
>
>     -    Move this value into a `struct repo_config_values` which holds all the
>     -    values parsed by `git_default_config()` and can be accessed per
>     -    repository via `git_default_config()`. This will ensure the current
>     -    behaviour remains the same while also enabling the libification of Git.
>     +    Create a new struct `repo_config_values` to hold this value and
>     +    other repository dependent values parsed by `git_default_config()` and
>     +    can be accessed per repository via `git_default_config()`.
>     +    This will ensure the current behaviour remains the same while also
>     +    enabling the libification of Git.
>
>          It is important to note that `git_default_config()` is a wrapper to other
>          `git_default_*_config()` such as `git_default_core_config()`.
>     @@ environment.c: static int git_default_core_config(const char *var, const char *v
>         }
>
>         if (!strcmp(var, "core.bare")) {
>     +@@ environment.c: int git_default_config(const char *var, const char *value,
>     +   /* Add other config variables here and to Documentation/config.adoc. */
>     +   return 0;
>     + }
>     ++
>     ++void repo_config_values_init(struct repo_config_values *cfg)
>     ++{
>     ++  cfg->attributes_file_path = NULL;
>     ++}
>
>       ## environment.h ##
>      @@ environment.h: extern const char * const local_repo_env[];
>     @@ environment.h: extern const char * const local_repo_env[];
>       /*
>        * Wrapper of getenv() that returns a strdup value. This value is kept
>        * in argv to be freed later.
>     +@@ environment.h: const char *strip_namespace(const char *namespaced_ref);
>     + int git_default_config(const char *, const char *,
>     +                  const struct config_context *, void *);
>     +
>     ++void repo_config_values_init(struct repo_config_values *cfg);
>     ++
>     + /*
>     +  * TODO: All the below state either explicitly or implicitly relies on
>     +  * `the_repository`. We should eventually get rid of these and make the
>      @@ environment.h: extern int assume_unchanged;
>       extern int warn_on_object_refname_ambiguity;
>       extern char *apply_default_whitespace;
>     @@ environment.h: extern int assume_unchanged;
>       extern int pack_compression_level;
>       extern unsigned long pack_size_limit_cfg;
>
>     + ## repository.c ##
>     +@@ repository.c: void initialize_repository(struct repository *repo)
>     +   ALLOC_ARRAY(repo->index, 1);
>     +   index_state_init(repo->index, repo);
>     +   repo->check_deprecated_config = true;
>     ++  repo_config_values_init(&repo->config_values);
>     +
>     +   /*
>     +    * When a command runs inside a repository, it learns what
>     +
>       ## repository.h ##
>      @@
>
> 2:  1e83c077f2 ! 2:  fd95169de4 environment: environment: stop using core.sparseCheckout globally
>     @@ Commit message
>          when different Git repositories running in the same process access this
>          variable.
>
>     -    Move the parsed value into `struct repo_config_values` which holds all the
>     -    values parsed by `git_default_config()` and can be accessed
>     -    per repo via `git_default_config()`. This retains current
>     +    Move the parsed value into `struct repo_config_values` to retains current
>          behaviours while achieving the repository scoped access.
>
>          Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
>     @@ Commit message
>          Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
>
>       ## builtin/backfill.c ##
>     -@@
>     --/* We need this macro to access core_apply_sparse_checkout */
>     - #define USE_THE_REPOSITORY_VARIABLE
>     -
>     - #include "builtin.h"
>      @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
>         repo_config(repo, git_default_config, NULL);
>
>     @@ environment.c: static int git_default_core_config(const char *var, const char *v
>                 return 0;
>         }
>
>     +@@ environment.c: int git_default_config(const char *var, const char *value,
>     + void repo_config_values_init(struct repo_config_values *cfg)
>     + {
>     +   cfg->attributes_file_path = NULL;
>     ++  cfg->sparse_checkout = 0;
>     + }
>
>       ## environment.h ##
>      @@ environment.h: struct strvec;
> 3:  6e54e22ac7 ! 3:  9a411db9f8 environment: move "branch.autoSetupMerge" into `struct repo_config_values`
>     @@ Commit message
>          `git_branch_track`. This global variable can cause unexpected behaviours
>          when multiple Git repos run in the the same process.
>
>     -    Move this value into `struct repo_config_values` which holds all values
>     -    parsed by `git_default_config()` and can be accessed per
>     -    repo via `git_default_config()`. This would retain the same
>     +    Move this value into `struct repo_config_values` to retain current
>          behaviours while achieving repository scoped access.
>
>          Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
>     @@ environment.c: static int git_default_i18n_config(const char *var, const char *v
>                 return 0;
>         }
>         if (!strcmp(var, "branch.autosetuprebase")) {
>     -@@ environment.c: int git_default_config(const char *var, const char *value,
>     -   /* Add other config variables here and to Documentation/config.adoc. */
>     -   return 0;
>     - }
>     -+
>     -+void repo_config_values_init(struct repo_config_values *cfg)
>     -+{
>     +@@ environment.c: void repo_config_values_init(struct repo_config_values *cfg)
>     + {
>     +   cfg->attributes_file_path = NULL;
>     +   cfg->sparse_checkout = 0;
>      +  cfg->git_branch_track = BRANCH_TRACK_REMOTE;
>     -+}
>     + }
>
>       ## environment.h ##
>      @@
>     @@ environment.h: struct repo_config_values {
>       };
>
>       /*
>     -@@ environment.h: const char *strip_namespace(const char *namespaced_ref);
>     - int git_default_config(const char *, const char *,
>     -                  const struct config_context *, void *);
>     -
>     -+void repo_config_values_init(struct repo_config_values *cfg);
>     -+
>     - /*
>     -  * TODO: All the below state either explicitly or implicitly relies on
>     -  * `the_repository`. We should eventually get rid of these and make the
>     -
>     - ## repository.c ##
>     -@@ repository.c: void initialize_repository(struct repository *repo)
>     -   ALLOC_ARRAY(repo->index, 1);
>     -   index_state_init(repo->index, repo);
>     -   repo->check_deprecated_config = true;
>     -+  repo_config_values_init(&repo->config_values);
>     -
>     -   /*
>     -    * When a command runs inside a repository, it learns what
>
>

Hello
Replying to this to get reviews and feedback.

Thanks

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-17 20:59     ` [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-01-22 12:13       ` Toon Claes
  2026-01-22 15:08         ` Bello Olamide
  2026-01-22 14:40       ` Phillip Wood
  1 sibling, 1 reply; 79+ messages in thread
From: Toon Claes @ 2026-01-22 12:13 UTC (permalink / raw)
  To: Olamide Caleb Bello, git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Olamide Caleb Bello

Olamide Caleb Bello <belkid98@gmail.com> writes:

> The config value is parsed in git_default_core_config(), loaded

I assume you mean 'core.attributesFile' because it's in the title. But
personnally I don't mind seeing the name repeated in the body to make it
more clear.

> eagerly and stored in the global variable `git_attributes_file`.
> Storing this value in a global variable can lead to unexpected
> behaviours when more than one Git repository run in the same Git process.
>
> Create a new struct `repo_config_values` to hold this value and
> other repository dependent values parsed by `git_default_config()` and
> can be accessed per repository via `git_default_config()`.

I'd suggest to split off the part after the second 'and' into a new
sentence.

> This will ensure the current behaviour remains the same while also
> enabling the libification of Git.

How is this true? Was that value already accessible through
`git_default_config()`? 

> It is important to note that `git_default_config()` is a wrapper to other
> `git_default_*_config()` such as `git_default_core_config()`.

I'd suggest to insert 'functions' before 'such'.

> Therefore to access and modify this global variable,
> the change has to be made in the function which parses and
> stores the value in the global variable.

This doesn't clarify much for me. Do you mean 'git_attr_global_file()'
and 'git_default_core_config()'?

>
> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
> ---
>  attr.c        |  7 ++++---
>  environment.c | 12 +++++++++---
>  environment.h |  9 ++++++++-
>  repository.c  |  1 +
>  repository.h  |  4 ++++
>  5 files changed, 26 insertions(+), 7 deletions(-)
>
> diff --git a/attr.c b/attr.c
> index 4999b7e09d..fbb9eaffaf 100644
> --- a/attr.c
> +++ b/attr.c
> @@ -881,10 +881,11 @@ const char *git_attr_system_file(void)
>  
>  const char *git_attr_global_file(void)
>  {
> -	if (!git_attributes_file)
> -		git_attributes_file = xdg_config_home("attributes");
> +	struct repo_config_values *cfg = &the_repository->config_values;

Is 'cfg' guaranteed to be != NULL?

> +	if (!cfg->attributes_file_path)
> +		cfg->attributes_file_path = xdg_config_home("attributes");
>  
> -	return git_attributes_file;
> +	return cfg->attributes_file_path;
>  }
>  
>  int git_attr_system_is_enabled(void)
> diff --git a/environment.c b/environment.c
> index a770b5921d..283db0a1a0 100644
> --- a/environment.c
> +++ b/environment.c
> @@ -53,7 +53,6 @@ char *git_commit_encoding;
>  char *git_log_output_encoding;
>  char *apply_default_whitespace;
>  char *apply_default_ignorewhitespace;
> -char *git_attributes_file;
>  int zlib_compression_level = Z_BEST_SPEED;
>  int pack_compression_level = Z_DEFAULT_COMPRESSION;
>  int fsync_object_files = -1;
> @@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
>  static int git_default_core_config(const char *var, const char *value,
>  				   const struct config_context *ctx, void *cb)
>  {
> +	struct repo_config_values *cfg = &the_repository->config_values;
> +
>  	/* This needs a better name */
>  	if (!strcmp(var, "core.filemode")) {
>  		trust_executable_bit = git_config_bool(var, value);
> @@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,
>  	}
>  
>  	if (!strcmp(var, "core.attributesfile")) {
> -		FREE_AND_NULL(git_attributes_file);
> -		return git_config_pathname(&git_attributes_file, var, value);
> +		FREE_AND_NULL(cfg->attributes_file_path);
> +		return git_config_pathname(&cfg->attributes_file_path, var, value);
>  	}
>  
>  	if (!strcmp(var, "core.bare")) {
> @@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,
>  	/* Add other config variables here and to Documentation/config.adoc. */
>  	return 0;
>  }
> +
> +void repo_config_values_init(struct repo_config_values *cfg)
> +{
> +	cfg->attributes_file_path = NULL;
> +}

I assume the reason for adding this function becomes clear in a later
commit?

> diff --git a/environment.h b/environment.h
> index 51898c99cd..aea73ff25b 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -84,6 +84,12 @@ extern const char * const local_repo_env[];
>  
>  struct strvec;
>  
> +/* Config values parsed by git_default_config() */

Mentioning here they get filled from git_default_config() doesn't feel
really correct? Although I'm sure what comment would fit better, maybe
just drop the comment above the struct. I see you have a similar comment
in 'struct repository', where it *does* make sense.

> +struct repo_config_values {
> +	/* core config values */

I prefer emphasizing it's the "section 'core'" or something like that.

> +	char *attributes_file_path;

Would it be overkill to append: /* `core.attributesFile` */? This can
help when grepping through the codebase to find where some settings are
being parsed into. What do you think?

> +};
> +
>  /*
>   * Wrapper of getenv() that returns a strdup value. This value is kept
>   * in argv to be freed later.
> @@ -107,6 +113,8 @@ const char *strip_namespace(const char *namespaced_ref);
>  int git_default_config(const char *, const char *,
>  		       const struct config_context *, void *);
>  
> +void repo_config_values_init(struct repo_config_values *cfg);
> +
>  /*
>   * TODO: All the below state either explicitly or implicitly relies on
>   * `the_repository`. We should eventually get rid of these and make the
> @@ -152,7 +160,6 @@ extern int assume_unchanged;
>  extern int warn_on_object_refname_ambiguity;
>  extern char *apply_default_whitespace;
>  extern char *apply_default_ignorewhitespace;
> -extern char *git_attributes_file;
>  extern int zlib_compression_level;
>  extern int pack_compression_level;
>  extern unsigned long pack_size_limit_cfg;
> diff --git a/repository.c b/repository.c
> index c7e75215ac..d308cd78bf 100644
> --- a/repository.c
> +++ b/repository.c
> @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)
>  	ALLOC_ARRAY(repo->index, 1);
>  	index_state_init(repo->index, repo);
>  	repo->check_deprecated_config = true;
> +	repo_config_values_init(&repo->config_values);
>  
>  	/*
>  	 * When a command runs inside a repository, it learns what
> diff --git a/repository.h b/repository.h
> index 6063c4b846..638a142577 100644
> --- a/repository.h
> +++ b/repository.h
> @@ -3,6 +3,7 @@
>  
>  #include "strmap.h"
>  #include "repo-settings.h"
> +#include "environment.h"
>  
>  struct config_set;
>  struct git_hash_algo;
> @@ -148,6 +149,9 @@ struct repository {
>  	/* Repository's compatibility hash algorithm. */
>  	const struct git_hash_algo *compat_hash_algo;
>  
> +	/* Repository's config values parsed by git_default_config() */
> +	struct repo_config_values config_values;
> +
>  	/* Repository's reference storage format, as serialized on disk. */
>  	enum ref_storage_format ref_storage_format;
>  
> -- 
> 2.34.1
>
>

-- 
Cheers,
Toon

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally
  2026-01-17 20:59     ` [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-01-22 12:13       ` Toon Claes
  2026-01-22 15:17         ` Bello Olamide
  2026-01-22 14:41       ` Phillip Wood
  1 sibling, 1 reply; 79+ messages in thread
From: Toon Claes @ 2026-01-22 12:13 UTC (permalink / raw)
  To: Olamide Caleb Bello, git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Olamide Caleb Bello

Olamide Caleb Bello <belkid98@gmail.com> writes:

It seems you have 'environment:' twice in the title?

> The config value `core.sparseCheckout` is parsed in
> `git_default_core_config()` and stored globally in
> `core_appy_sparse_checkout`. This could cause unintended behaviours

s/core_appy_sparse_checkout/core_apply_sparse_checkout/ ?

> when different Git repositories running in the same process access this
> variable.
>
> Move the parsed value into `struct repo_config_values` to retains current
> behaviours while achieving the repository scoped access.
>
> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
> 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/backfill.c        |  2 +-
>  builtin/clone.c           |  2 +-
>  builtin/grep.c            |  2 +-
>  builtin/mv.c              |  2 +-
>  builtin/sparse-checkout.c | 22 +++++++++++-----------
>  builtin/worktree.c        |  2 +-
>  dir.c                     |  2 +-
>  environment.c             |  4 ++--
>  environment.h             |  2 +-
>  sparse-index.c            |  6 ++++--
>  unpack-trees.c            |  2 +-
>  wt-status.c               |  2 +-
>  12 files changed, 26 insertions(+), 24 deletions(-)
>
> diff --git a/builtin/backfill.c b/builtin/backfill.c
> index e80fc1b694..5fc8c51ed1 100644
> --- a/builtin/backfill.c
> +++ b/builtin/backfill.c
> @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
>  	repo_config(repo, git_default_config, NULL);
>  
>  	if (ctx.sparse < 0)
> -		ctx.sparse = core_apply_sparse_checkout;
> +		ctx.sparse = repo->config_values.sparse_checkout;
>  
>  	result = do_backfill(&ctx);
>  	backfill_context_clear(&ctx);
> diff --git a/builtin/clone.c b/builtin/clone.c
> index b19b302b06..b6b19e83d1 100644
> --- a/builtin/clone.c
> +++ b/builtin/clone.c
> @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
>  	 * We must apply the setting in the current process
>  	 * for the later checkout to use the sparse-checkout file.
>  	 */
> -	core_apply_sparse_checkout = 1;
> +	the_repository->config_values.sparse_checkout = 1;
>  
>  	cmd.git_cmd = 1;
>  	if (run_command(&cmd)) {
> diff --git a/builtin/grep.c b/builtin/grep.c
> index 53cccf2d25..525edb5e9c 100644
> --- a/builtin/grep.c
> +++ b/builtin/grep.c
> @@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,
>  	 *	"forget" the sparse-index feature switch. As a result, the index
>  	 *	of these submodules are expanded unexpectedly.
>  	 *
> -	 * 2. "core_apply_sparse_checkout"
> +	 * 2. "sparse_checkout"
>  	 *	When running `grep` in the superproject, this setting is
>  	 *	populated using the superproject's configs. However, once
>  	 *	initialized, this config is globally accessible and is read by
> diff --git a/builtin/mv.c b/builtin/mv.c
> index d43925097b..511620747b 100644
> --- a/builtin/mv.c
> +++ b/builtin/mv.c
> @@ -572,7 +572,7 @@ int cmd_mv(int argc,
>  		rename_index_entry_at(the_repository->index, pos, dst);
>  
>  		if (ignore_sparse &&
> -		    core_apply_sparse_checkout &&
> +		    the_repository->config_values.sparse_checkout &&
>  		    core_sparse_checkout_cone) {
>  			/*
>  			 * NEEDSWORK: we are *not* paying attention to
> diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
> index 15d51e60a8..1c2c39b968 100644
> --- a/builtin/sparse-checkout.c
> +++ b/builtin/sparse-checkout.c
> @@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
>  	int res;
>  
>  	setup_work_tree();
> -	if (!core_apply_sparse_checkout)
> +	if (!the_repository->config_values.sparse_checkout)
>  		die(_("this worktree is not sparse"));
>  
>  	argc = parse_options(argc, argv, prefix,
> @@ -400,11 +400,11 @@ static int set_config(struct repository *repo,
>  
>  static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
>  	/* If not specified, use previous definition of cone mode */
> -	if (*cone_mode == -1 && core_apply_sparse_checkout)
> +	if (*cone_mode == -1 && the_repository->config_values.sparse_checkout)
>  		*cone_mode = core_sparse_checkout_cone;
>  
>  	/* Set cone/non-cone mode appropriately */
> -	core_apply_sparse_checkout = 1;
> +	the_repository->config_values.sparse_checkout = 1;
>  	if (*cone_mode == 1 || *cone_mode == -1) {
>  		core_sparse_checkout_cone = 1;
>  		return MODE_CONE_PATTERNS;
> @@ -418,7 +418,7 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind
>  	int mode, record_mode;
>  
>  	/* Determine if we need to record the mode; ensure sparse checkout on */
> -	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
> +	record_mode = (*cone_mode != -1) || !repo->config_values.sparse_checkout;
>  
>  	mode = update_cone_mode(cone_mode);
>  	if (record_mode && set_config(repo, mode))
> @@ -699,9 +699,9 @@ static int modify_pattern_list(struct repository *repo,
>  		break;
>  	}
>  
> -	if (!core_apply_sparse_checkout) {
> +	if (!repo->config_values.sparse_checkout) {
>  		set_config(repo, MODE_ALL_PATTERNS);
> -		core_apply_sparse_checkout = 1;
> +		repo->config_values.sparse_checkout = 1;
>  		changed_config = 1;
>  	}
>  
> @@ -798,7 +798,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
>  	int ret;
>  
>  	setup_work_tree();
> -	if (!core_apply_sparse_checkout)
> +	if (!repo->config_values.sparse_checkout)
>  		die(_("no sparse-checkout to add to"));
>  
>  	repo_read_index(repo);
> @@ -907,7 +907,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,
>  	};
>  
>  	setup_work_tree();
> -	if (!core_apply_sparse_checkout)
> +	if (!repo->config_values.sparse_checkout)
>  		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
>  
>  	reapply_opts.cone_mode = -1;
> @@ -969,7 +969,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
>  	};
>  
>  	setup_work_tree();
> -	if (!core_apply_sparse_checkout)
> +	if (!repo->config_values.sparse_checkout)
>  		die(_("must be in a sparse-checkout to clean directories"));
>  	if (!core_sparse_checkout_cone)
>  		die(_("must be in a cone-mode sparse-checkout to clean directories"));
> @@ -1035,7 +1035,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
>  	struct pattern_list pl;
>  
>  	/*
> -	 * We do not exit early if !core_apply_sparse_checkout; due to the
> +	 * We do not exit early if !repo->config_values.sparse_checkout; due to the
>  	 * ability for users to manually muck things up between
>  	 *   direct editing of .git/info/sparse-checkout
>  	 *   running read-tree -m u HEAD or update-index --skip-worktree
> @@ -1061,7 +1061,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
>  	hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
>  	hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
>  	pl.use_cone_patterns = 0;
> -	core_apply_sparse_checkout = 1;
> +	repo->config_values.sparse_checkout = 1;
>  
>  	add_pattern("/*", empty_base, 0, &pl, 0);
>  
> diff --git a/builtin/worktree.c b/builtin/worktree.c
> index fbdaf2eb2e..e401b8253e 100644
> --- a/builtin/worktree.c
> +++ b/builtin/worktree.c
> @@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,
>  	 * If the current worktree has sparse-checkout enabled, then copy
>  	 * the sparse-checkout patterns from the current worktree.
>  	 */
> -	if (core_apply_sparse_checkout)
> +	if (wt->repo->config_values.sparse_checkout)
>  		copy_sparse_checkout(sb_repo.buf);
>  
>  	/*
> diff --git a/dir.c b/dir.c
> index b00821f294..56b412a6d2 100644
> --- a/dir.c
> +++ b/dir.c
> @@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(
>  
>  int init_sparse_checkout_patterns(struct index_state *istate)
>  {
> -	if (!core_apply_sparse_checkout)
> +	if (!istate->repo->config_values.sparse_checkout)
>  		return 1;
>  	if (istate->sparse_checkout_patterns)
>  		return 0;
> diff --git a/environment.c b/environment.c
> index 283db0a1a0..6633542750 100644
> --- a/environment.c
> +++ b/environment.c
> @@ -74,7 +74,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_apply_sparse_checkout;
>  int core_sparse_checkout_cone;
>  int sparse_expect_files_outside_of_patterns;
>  int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
> @@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,
>  	}
>  
>  	if (!strcmp(var, "core.sparsecheckout")) {
> -		core_apply_sparse_checkout = git_config_bool(var, value);
> +		cfg->sparse_checkout = git_config_bool(var, value);
>  		return 0;
>  	}
>  
> @@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,
>  void repo_config_values_init(struct repo_config_values *cfg)
>  {
>  	cfg->attributes_file_path = NULL;
> +	cfg->sparse_checkout = 0;
>  }
> diff --git a/environment.h b/environment.h
> index aea73ff25b..3b5ff7094a 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -88,6 +88,7 @@ struct strvec;
>  struct repo_config_values {
>  	/* core config values */
>  	char *attributes_file_path;
> +	int sparse_checkout;
>  };
>  
>  /*
> @@ -169,7 +170,6 @@ extern int precomposed_unicode;
>  extern int protect_hfs;
>  extern int protect_ntfs;
>  
> -extern int core_apply_sparse_checkout;

In the field you're adding to 'struct repo_config_values' you have
dropped the 'core_' prefix, what the reason for that? If I understand it
correctly also settings from other sections might end up in that struct,
so wouldn't it be better to keep the prefix?

>  extern int core_sparse_checkout_cone;
>  extern int sparse_expect_files_outside_of_patterns;
>  
> diff --git a/sparse-index.c b/sparse-index.c
> index 76f90da5f5..6dd8dd679d 100644
> --- a/sparse-index.c
> +++ b/sparse-index.c
> @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)
>  
>  int is_sparse_index_allowed(struct index_state *istate, int flags)
>  {
> -	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
> +	struct repo_config_values *cfg = &istate->repo->config_values;
> +	if (!cfg->sparse_checkout || !core_sparse_checkout_cone)
>  		return 0;
>  
>  	if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
> @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
>  
>  void clear_skip_worktree_from_present_files(struct index_state *istate)
>  {
> -	if (!core_apply_sparse_checkout ||
> +	struct repo_config_values *cfg = &istate->repo->config_values;
> +	if (!cfg->sparse_checkout ||
>  	    sparse_expect_files_outside_of_patterns)
>  		return;
>  
> diff --git a/unpack-trees.c b/unpack-trees.c
> index f38c761ab9..2bdfa1334c 100644
> --- a/unpack-trees.c
> +++ b/unpack-trees.c
> @@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
>  	if (o->prefix)
>  		update_sparsity_for_prefix(o->prefix, o->src_index);
>  
> -	if (!core_apply_sparse_checkout || !o->update)
> +	if (!repo->config_values.sparse_checkout || !o->update)
>  		o->skip_sparse_checkout = 1;
>  	if (!o->skip_sparse_checkout) {
>  		memset(&pl, 0, sizeof(pl));
> diff --git a/wt-status.c b/wt-status.c
> index e12adb26b9..a2e388606f 100644
> --- a/wt-status.c
> +++ b/wt-status.c
> @@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,
>  	int skip_worktree = 0;
>  	int i;
>  
> -	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
> +	if (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {
>  		/*
>  		 * Don't compute percentage of checked out files if we
>  		 * aren't in a sparse checkout or would get division by 0.
> -- 
> 2.34.1
>
>

-- 
Cheers,
Toon

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-17 20:59     ` [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
  2026-01-22 12:13       ` Toon Claes
@ 2026-01-22 14:40       ` Phillip Wood
  2026-01-22 15:11         ` Bello Olamide
  1 sibling, 1 reply; 79+ messages in thread
From: Phillip Wood @ 2026-01-22 14:40 UTC (permalink / raw)
  To: Olamide Caleb Bello, git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Toon Claes

Hi Olamide

On 17/01/2026 20:59, Olamide Caleb Bello wrote:
> The config value is parsed in git_default_core_config(), loaded eagerly
> and stored in the global variable `git_attributes_file`.
> Storing this value in a global variable can lead to unexpected
> behaviours when more than one Git repository run in the same Git process.

It would maybe be helpful to explain what the unexpected behavior is and 
how it is caused.

> diff --git a/environment.h b/environment.h
> index 51898c99cd..aea73ff25b 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -84,6 +84,12 @@ extern const char * const local_repo_env[];
>   
>   struct strvec;
>   
> +/* Config values parsed by git_default_config() */
> +struct repo_config_values {
> +	/* core config values */
> +	char *attributes_file_path;

The variable we're converting is called "attributes_file", do we really 
need to add a "_path" suffix?

Apart from that everything here looks good

Thanks

Phillip

> +};
> +
>   /*
>    * Wrapper of getenv() that returns a strdup value. This value is kept
>    * in argv to be freed later.
> @@ -107,6 +113,8 @@ const char *strip_namespace(const char *namespaced_ref);
>   int git_default_config(const char *, const char *,
>   		       const struct config_context *, void *);
>   
> +void repo_config_values_init(struct repo_config_values *cfg);
> +
>   /*
>    * TODO: All the below state either explicitly or implicitly relies on
>    * `the_repository`. We should eventually get rid of these and make the
> @@ -152,7 +160,6 @@ extern int assume_unchanged;
>   extern int warn_on_object_refname_ambiguity;
>   extern char *apply_default_whitespace;
>   extern char *apply_default_ignorewhitespace;
> -extern char *git_attributes_file;
>   extern int zlib_compression_level;
>   extern int pack_compression_level;
>   extern unsigned long pack_size_limit_cfg;
> diff --git a/repository.c b/repository.c
> index c7e75215ac..d308cd78bf 100644
> --- a/repository.c
> +++ b/repository.c
> @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)
>   	ALLOC_ARRAY(repo->index, 1);
>   	index_state_init(repo->index, repo);
>   	repo->check_deprecated_config = true;
> +	repo_config_values_init(&repo->config_values);
>   
>   	/*
>   	 * When a command runs inside a repository, it learns what
> diff --git a/repository.h b/repository.h
> index 6063c4b846..638a142577 100644
> --- a/repository.h
> +++ b/repository.h
> @@ -3,6 +3,7 @@
>   
>   #include "strmap.h"
>   #include "repo-settings.h"
> +#include "environment.h"
>   
>   struct config_set;
>   struct git_hash_algo;
> @@ -148,6 +149,9 @@ struct repository {
>   	/* Repository's compatibility hash algorithm. */
>   	const struct git_hash_algo *compat_hash_algo;
>   
> +	/* Repository's config values parsed by git_default_config() */
> +	struct repo_config_values config_values;
> +
>   	/* Repository's reference storage format, as serialized on disk. */
>   	enum ref_storage_format ref_storage_format;
>   


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally
  2026-01-17 20:59     ` [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally Olamide Caleb Bello
  2026-01-22 12:13       ` Toon Claes
@ 2026-01-22 14:41       ` Phillip Wood
  2026-01-22 15:29         ` Bello Olamide
  1 sibling, 1 reply; 79+ messages in thread
From: Phillip Wood @ 2026-01-22 14:41 UTC (permalink / raw)
  To: Olamide Caleb Bello, git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Toon Claes

Hi Olamide

On 17/01/2026 20:59, Olamide Caleb Bello wrote:
> The config value `core.sparseCheckout` is parsed in
> `git_default_core_config()` and stored globally in
> `core_appy_sparse_checkout`. This could cause unintended behaviours
> when different Git repositories running in the same process access this
> variable.
> 
> Move the parsed value into `struct repo_config_values` to retains current
> behaviours while achieving the repository scoped access.

It doesn't achieve repository scoped access though because we only ever 
populate the values in "the_repository", all other instances of "struct 
repository" are initialized by config_values_init() but not the config 
settings.

> diff --git a/builtin/backfill.c b/builtin/backfill.c
> index e80fc1b694..5fc8c51ed1 100644
> --- a/builtin/backfill.c
> +++ b/builtin/backfill.c
> @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
>   	repo_config(repo, git_default_config, NULL);
>   
>   	if (ctx.sparse < 0)
> -		ctx.sparse = core_apply_sparse_checkout;
> +		ctx.sparse = repo->config_values.sparse_checkout;

Using "repo" rather than "the_repository" here is dangerous because only 
"the_repository" contains the parsed config. This applies throughout 
this patch.

>   
>   	result = do_backfill(&ctx);
>   	backfill_context_clear(&ctx);
> diff --git a/builtin/clone.c b/builtin/clone.c
> index b19b302b06..b6b19e83d1 100644
> --- a/builtin/clone.c
> +++ b/builtin/clone.c
> @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
>   	 * We must apply the setting in the current process
>   	 * for the later checkout to use the sparse-checkout file.
>   	 */
> -	core_apply_sparse_checkout = 1;
> +	the_repository->config_values.sparse_checkout = 1;
>   
>   	cmd.git_cmd = 1;
>   	if (run_command(&cmd)) {
> diff --git a/builtin/grep.c b/builtin/grep.c
> index 53cccf2d25..525edb5e9c 100644
> --- a/builtin/grep.c
> +++ b/builtin/grep.c
> @@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,
>   	 *	"forget" the sparse-index feature switch. As a result, the index
>   	 *	of these submodules are expanded unexpectedly.
>   	 *
> -	 * 2. "core_apply_sparse_checkout"
> +	 * 2. "sparse_checkout"

That should be something like config_values.sparse_checkout to make it 
clear that "sparse_checkout" is the name of a member of a struct, not 
the name of a variable.

> diff --git a/environment.h b/environment.h
> index aea73ff25b..3b5ff7094a 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -88,6 +88,7 @@ struct strvec;
>   struct repo_config_values {
>   	/* core config values */
>   	char *attributes_file_path;
> +	int sparse_checkout;

There are several other sparse checkout variables like 
core_sparse_checkout_cone that we'll need to convert in the future so 
"apply_sparse_checkout" or "sparse_checkout_apply" would be better names.

Thanks

Phillip


>   };
>   
>   /*
> @@ -169,7 +170,6 @@ extern int precomposed_unicode;
>   extern int protect_hfs;
>   extern int protect_ntfs;
>   
> -extern int core_apply_sparse_checkout;
>   extern int core_sparse_checkout_cone;
>   extern int sparse_expect_files_outside_of_patterns;
>   
> diff --git a/sparse-index.c b/sparse-index.c
> index 76f90da5f5..6dd8dd679d 100644
> --- a/sparse-index.c
> +++ b/sparse-index.c
> @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)
>   
>   int is_sparse_index_allowed(struct index_state *istate, int flags)
>   {
> -	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
> +	struct repo_config_values *cfg = &istate->repo->config_values;
> +	if (!cfg->sparse_checkout || !core_sparse_checkout_cone)
>   		return 0;
>   
>   	if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
> @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
>   
>   void clear_skip_worktree_from_present_files(struct index_state *istate)
>   {
> -	if (!core_apply_sparse_checkout ||
> +	struct repo_config_values *cfg = &istate->repo->config_values;
> +	if (!cfg->sparse_checkout ||
>   	    sparse_expect_files_outside_of_patterns)
>   		return;
>   
> diff --git a/unpack-trees.c b/unpack-trees.c
> index f38c761ab9..2bdfa1334c 100644
> --- a/unpack-trees.c
> +++ b/unpack-trees.c
> @@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
>   	if (o->prefix)
>   		update_sparsity_for_prefix(o->prefix, o->src_index);
>   
> -	if (!core_apply_sparse_checkout || !o->update)
> +	if (!repo->config_values.sparse_checkout || !o->update)
>   		o->skip_sparse_checkout = 1;
>   	if (!o->skip_sparse_checkout) {
>   		memset(&pl, 0, sizeof(pl));
> diff --git a/wt-status.c b/wt-status.c
> index e12adb26b9..a2e388606f 100644
> --- a/wt-status.c
> +++ b/wt-status.c
> @@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,
>   	int skip_worktree = 0;
>   	int i;
>   
> -	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
> +	if (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {
>   		/*
>   		 * Don't compute percentage of checked out files if we
>   		 * aren't in a sparse checkout or would get division by 0.


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-01-17 20:59     ` [Outreachy PATCH v3 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
@ 2026-01-22 14:41       ` Phillip Wood
  2026-01-22 15:29         ` Bello Olamide
  0 siblings, 1 reply; 79+ messages in thread
From: Phillip Wood @ 2026-01-22 14:41 UTC (permalink / raw)
  To: Olamide Caleb Bello, git
  Cc: phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Toon Claes

Hi Olamide

On 17/01/2026 20:59, Olamide Caleb Bello wrote:
> The config value `brach.autoSetupMerge` is parsed in
> `git_default_branch_config()` and stored in the global variable
> `git_branch_track`. This global variable can cause unexpected behaviours
> when multiple Git repos run in the the same process.
> 
> Move this value into `struct repo_config_values` to retain current
> behaviours while achieving repository scoped access.

Same comment as the previous patch about repository scoped access.

> diff --git a/environment.h b/environment.h
> index 3b5ff7094a..bfcdffe836 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -2,6 +2,7 @@
>   #define ENVIRONMENT_H
>   
>   #include "repo-settings.h"
> +#include "branch.h"
>   
>   /* Double-check local_repo_env below if you add to this list. */
>   #define GIT_DIR_ENVIRONMENT "GIT_DIR"
> @@ -89,6 +90,9 @@ struct repo_config_values {
>   	/* core config values */
>   	char *attributes_file_path;
>   	int sparse_checkout;
> +
> +	/* branch config values */
> +	enum branch_track git_branch_track;

We could probably drop the "git_" prefix now that it is not a global 
variable.

Thanks

Phillip


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-22 12:13       ` Toon Claes
@ 2026-01-22 15:08         ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-22 15:08 UTC (permalink / raw)
  To: Toon Claes
  Cc: git, phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Thu, 22 Jan 2026 at 13:13, Toon Claes <toon@iotcl.com> wrote:
>
> Olamide Caleb Bello <belkid98@gmail.com> writes:
>
> > The config value is parsed in git_default_core_config(), loaded
>
> I assume you mean 'core.attributesFile' because it's in the title. But
> personnally I don't mind seeing the name repeated in the body to make it
> more clear.
Hello Toon,

Okay thank you for your review.
I will take note of this.

>
> > eagerly and stored in the global variable `git_attributes_file`.
> > Storing this value in a global variable can lead to unexpected
> > behaviours when more than one Git repository run in the same Git process.
> >
> > Create a new struct `repo_config_values` to hold this value and
> > other repository dependent values parsed by `git_default_config()` and
> > can be accessed per repository via `git_default_config()`.
>
> I'd suggest to split off the part after the second 'and' into a new
> sentence.

Okay

>
> > This will ensure the current behaviour remains the same while also
> > enabling the libification of Git.
>
> How is this true? Was that value already accessible through
> `git_default_config()`?
>
> > It is important to note that `git_default_config()` is a wrapper to other
> > `git_default_*_config()` such as `git_default_core_config()`.
>
> I'd suggest to insert 'functions' before 'such'.

Alright, noted.

>
> > Therefore to access and modify this global variable,
> > the change has to be made in the function which parses and
> > stores the value in the global variable.
>
> This doesn't clarify much for me. Do you mean 'git_attr_global_file()'
> and 'git_default_core_config()'?

Okay I meant git_default_core_config().
I will modify it.

>
> >
> > Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
> > Mentored-by: Christian Couder <christian.couder@gmail.com>
> > Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
> > Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
> > ---
> >  attr.c        |  7 ++++---
> >  environment.c | 12 +++++++++---
> >  environment.h |  9 ++++++++-
> >  repository.c  |  1 +
> >  repository.h  |  4 ++++
> >  5 files changed, 26 insertions(+), 7 deletions(-)
> >
> > diff --git a/attr.c b/attr.c
> > index 4999b7e09d..fbb9eaffaf 100644
> > --- a/attr.c
> > +++ b/attr.c
> > @@ -881,10 +881,11 @@ const char *git_attr_system_file(void)
> >
> >  const char *git_attr_global_file(void)
> >  {
> > -     if (!git_attributes_file)
> > -             git_attributes_file = xdg_config_home("attributes");
> > +     struct repo_config_values *cfg = &the_repository->config_values;
>
> Is 'cfg' guaranteed to be != NULL?
>
> > +     if (!cfg->attributes_file_path)
> > +             cfg->attributes_file_path = xdg_config_home("attributes");
> >
> > -     return git_attributes_file;
> > +     return cfg->attributes_file_path;
> >  }
> >
> >  int git_attr_system_is_enabled(void)
> > diff --git a/environment.c b/environment.c
> > index a770b5921d..283db0a1a0 100644
> > --- a/environment.c
> > +++ b/environment.c
> > @@ -53,7 +53,6 @@ char *git_commit_encoding;
> >  char *git_log_output_encoding;
> >  char *apply_default_whitespace;
> >  char *apply_default_ignorewhitespace;
> > -char *git_attributes_file;
> >  int zlib_compression_level = Z_BEST_SPEED;
> >  int pack_compression_level = Z_DEFAULT_COMPRESSION;
> >  int fsync_object_files = -1;
> > @@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
> >  static int git_default_core_config(const char *var, const char *value,
> >                                  const struct config_context *ctx, void *cb)
> >  {
> > +     struct repo_config_values *cfg = &the_repository->config_values;
> > +
> >       /* This needs a better name */
> >       if (!strcmp(var, "core.filemode")) {
> >               trust_executable_bit = git_config_bool(var, value);
> > @@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,
> >       }
> >
> >       if (!strcmp(var, "core.attributesfile")) {
> > -             FREE_AND_NULL(git_attributes_file);
> > -             return git_config_pathname(&git_attributes_file, var, value);
> > +             FREE_AND_NULL(cfg->attributes_file_path);
> > +             return git_config_pathname(&cfg->attributes_file_path, var, value);
> >       }
> >
> >       if (!strcmp(var, "core.bare")) {
> > @@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,
> >       /* Add other config variables here and to Documentation/config.adoc. */
> >       return 0;
> >  }
> > +
> > +void repo_config_values_init(struct repo_config_values *cfg)
> > +{
> > +     cfg->attributes_file_path = NULL;
> > +}
>
> I assume the reason for adding this function becomes clear in a later
> commit?

Yes.

>
> > diff --git a/environment.h b/environment.h
> > index 51898c99cd..aea73ff25b 100644
> > --- a/environment.h
> > +++ b/environment.h
> > @@ -84,6 +84,12 @@ extern const char * const local_repo_env[];
> >
> >  struct strvec;
> >
> > +/* Config values parsed by git_default_config() */
>
> Mentioning here they get filled from git_default_config() doesn't feel
> really correct? Although I'm sure what comment would fit better, maybe
> just drop the comment above the struct. I see you have a similar comment
> in 'struct repository', where it *does* make sense.

Okay thank you

>
> > +struct repo_config_values {
> > +     /* core config values */
>
> I prefer emphasizing it's the "section 'core'" or something like that.

Noted

>
> > +     char *attributes_file_path;
>
> Would it be overkill to append: /* `core.attributesFile` */? This can
> help when grepping through the codebase to find where some settings are
> being parsed into. What do you think?

Alright

>
> > +};
> > +
> >  /*
> >   * Wrapper of getenv() that returns a strdup value. This value is kept
> >   * in argv to be freed later.
> > @@ -107,6 +113,8 @@ const char *strip_namespace(const char *namespaced_ref);
> >  int git_default_config(const char *, const char *,
> >                      const struct config_context *, void *);
> >
> > +void repo_config_values_init(struct repo_config_values *cfg);
> > +
> >  /*
> >   * TODO: All the below state either explicitly or implicitly relies on
> >   * `the_repository`. We should eventually get rid of these and make the
> > @@ -152,7 +160,6 @@ extern int assume_unchanged;
> >  extern int warn_on_object_refname_ambiguity;
> >  extern char *apply_default_whitespace;
> >  extern char *apply_default_ignorewhitespace;
> > -extern char *git_attributes_file;
> >  extern int zlib_compression_level;
> >  extern int pack_compression_level;
> >  extern unsigned long pack_size_limit_cfg;
> > diff --git a/repository.c b/repository.c
> > index c7e75215ac..d308cd78bf 100644
> > --- a/repository.c
> > +++ b/repository.c
> > @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)
> >       ALLOC_ARRAY(repo->index, 1);
> >       index_state_init(repo->index, repo);
> >       repo->check_deprecated_config = true;
> > +     repo_config_values_init(&repo->config_values);
> >
> >       /*
> >        * When a command runs inside a repository, it learns what
> > diff --git a/repository.h b/repository.h
> > index 6063c4b846..638a142577 100644
> > --- a/repository.h
> > +++ b/repository.h
> > @@ -3,6 +3,7 @@
> >
> >  #include "strmap.h"
> >  #include "repo-settings.h"
> > +#include "environment.h"
> >
> >  struct config_set;
> >  struct git_hash_algo;
> > @@ -148,6 +149,9 @@ struct repository {
> >       /* Repository's compatibility hash algorithm. */
> >       const struct git_hash_algo *compat_hash_algo;
> >
> > +     /* Repository's config values parsed by git_default_config() */
> > +     struct repo_config_values config_values;
> > +
> >       /* Repository's reference storage format, as serialized on disk. */
> >       enum ref_storage_format ref_storage_format;
> >
> > --
> > 2.34.1
> >
> >
>
> --
> Cheers,
> Toon

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-22 14:40       ` Phillip Wood
@ 2026-01-22 15:11         ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-22 15:11 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Toon Claes

On Thu, 22 Jan 2026 at 15:40, Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Olamide

Hello Phillip

>
> On 17/01/2026 20:59, Olamide Caleb Bello wrote:
> > The config value is parsed in git_default_core_config(), loaded eagerly
> > and stored in the global variable `git_attributes_file`.
> > Storing this value in a global variable can lead to unexpected
> > behaviours when more than one Git repository run in the same Git process.
>
> It would maybe be helpful to explain what the unexpected behavior is and
> how it is caused.

Okay thank you.

>
> > diff --git a/environment.h b/environment.h
> > index 51898c99cd..aea73ff25b 100644
> > --- a/environment.h
> > +++ b/environment.h
> > @@ -84,6 +84,12 @@ extern const char * const local_repo_env[];
> >
> >   struct strvec;
> >
> > +/* Config values parsed by git_default_config() */
> > +struct repo_config_values {
> > +     /* core config values */
> > +     char *attributes_file_path;
>
> The variable we're converting is called "attributes_file", do we really
> need to add a "_path" suffix?

Okay I will remove it

>
> Apart from that everything here looks good
>
> Thanks
>
> Phillip

Thank you

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally
  2026-01-22 12:13       ` Toon Claes
@ 2026-01-22 15:17         ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-22 15:17 UTC (permalink / raw)
  To: Toon Claes
  Cc: git, phillip.wood123, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Thu, 22 Jan 2026 at 13:13, Toon Claes <toon@iotcl.com> wrote:
>
> Olamide Caleb Bello <belkid98@gmail.com> writes:
>
> It seems you have 'environment:' twice in the title?

Arh! Thank you for spotting it

>
> > The config value `core.sparseCheckout` is parsed in
> > `git_default_core_config()` and stored globally in
> > `core_appy_sparse_checkout`. This could cause unintended behaviours
>
> s/core_appy_sparse_checkout/core_apply_sparse_checkout/ ?

Thank you.

>
> > when different Git repositories running in the same process access this
> > variable.
> >
> > Move the parsed value into `struct repo_config_values` to retains current
> > behaviours while achieving the repository scoped access.
> >
> > Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
> > 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/backfill.c        |  2 +-
> >  builtin/clone.c           |  2 +-
> >  builtin/grep.c            |  2 +-
> >  builtin/mv.c              |  2 +-
> >  builtin/sparse-checkout.c | 22 +++++++++++-----------
> >  builtin/worktree.c        |  2 +-
> >  dir.c                     |  2 +-
> >  environment.c             |  4 ++--
> >  environment.h             |  2 +-
> >  sparse-index.c            |  6 ++++--
> >  unpack-trees.c            |  2 +-
> >  wt-status.c               |  2 +-
> >  12 files changed, 26 insertions(+), 24 deletions(-)
> >
> > diff --git a/builtin/backfill.c b/builtin/backfill.c
> > index e80fc1b694..5fc8c51ed1 100644
> > --- a/builtin/backfill.c
> > +++ b/builtin/backfill.c
> > @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
> >       repo_config(repo, git_default_config, NULL);
> >
> >       if (ctx.sparse < 0)
> > -             ctx.sparse = core_apply_sparse_checkout;
> > +             ctx.sparse = repo->config_values.sparse_checkout;
> >
> >       result = do_backfill(&ctx);
> >       backfill_context_clear(&ctx);
> > diff --git a/builtin/clone.c b/builtin/clone.c
> > index b19b302b06..b6b19e83d1 100644
> > --- a/builtin/clone.c
> > +++ b/builtin/clone.c
> > @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
> >        * We must apply the setting in the current process
> >        * for the later checkout to use the sparse-checkout file.
> >        */
> > -     core_apply_sparse_checkout = 1;
> > +     the_repository->config_values.sparse_checkout = 1;
> >
> >       cmd.git_cmd = 1;
> >       if (run_command(&cmd)) {
> > diff --git a/builtin/grep.c b/builtin/grep.c
> > index 53cccf2d25..525edb5e9c 100644
> > --- a/builtin/grep.c
> > +++ b/builtin/grep.c
> > @@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,
> >        *      "forget" the sparse-index feature switch. As a result, the index
> >        *      of these submodules are expanded unexpectedly.
> >        *
> > -      * 2. "core_apply_sparse_checkout"
> > +      * 2. "sparse_checkout"
> >        *      When running `grep` in the superproject, this setting is
> >        *      populated using the superproject's configs. However, once
> >        *      initialized, this config is globally accessible and is read by
> > diff --git a/builtin/mv.c b/builtin/mv.c
> > index d43925097b..511620747b 100644
> > --- a/builtin/mv.c
> > +++ b/builtin/mv.c
> > @@ -572,7 +572,7 @@ int cmd_mv(int argc,
> >               rename_index_entry_at(the_repository->index, pos, dst);
> >
> >               if (ignore_sparse &&
> > -                 core_apply_sparse_checkout &&
> > +                 the_repository->config_values.sparse_checkout &&
> >                   core_sparse_checkout_cone) {
> >                       /*
> >                        * NEEDSWORK: we are *not* paying attention to
> > diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
> > index 15d51e60a8..1c2c39b968 100644
> > --- a/builtin/sparse-checkout.c
> > +++ b/builtin/sparse-checkout.c
> > @@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
> >       int res;
> >
> >       setup_work_tree();
> > -     if (!core_apply_sparse_checkout)
> > +     if (!the_repository->config_values.sparse_checkout)
> >               die(_("this worktree is not sparse"));
> >
> >       argc = parse_options(argc, argv, prefix,
> > @@ -400,11 +400,11 @@ static int set_config(struct repository *repo,
> >
> >  static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
> >       /* If not specified, use previous definition of cone mode */
> > -     if (*cone_mode == -1 && core_apply_sparse_checkout)
> > +     if (*cone_mode == -1 && the_repository->config_values.sparse_checkout)
> >               *cone_mode = core_sparse_checkout_cone;
> >
> >       /* Set cone/non-cone mode appropriately */
> > -     core_apply_sparse_checkout = 1;
> > +     the_repository->config_values.sparse_checkout = 1;
> >       if (*cone_mode == 1 || *cone_mode == -1) {
> >               core_sparse_checkout_cone = 1;
> >               return MODE_CONE_PATTERNS;
> > @@ -418,7 +418,7 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind
> >       int mode, record_mode;
> >
> >       /* Determine if we need to record the mode; ensure sparse checkout on */
> > -     record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
> > +     record_mode = (*cone_mode != -1) || !repo->config_values.sparse_checkout;
> >
> >       mode = update_cone_mode(cone_mode);
> >       if (record_mode && set_config(repo, mode))
> > @@ -699,9 +699,9 @@ static int modify_pattern_list(struct repository *repo,
> >               break;
> >       }
> >
> > -     if (!core_apply_sparse_checkout) {
> > +     if (!repo->config_values.sparse_checkout) {
> >               set_config(repo, MODE_ALL_PATTERNS);
> > -             core_apply_sparse_checkout = 1;
> > +             repo->config_values.sparse_checkout = 1;
> >               changed_config = 1;
> >       }
> >
> > @@ -798,7 +798,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
> >       int ret;
> >
> >       setup_work_tree();
> > -     if (!core_apply_sparse_checkout)
> > +     if (!repo->config_values.sparse_checkout)
> >               die(_("no sparse-checkout to add to"));
> >
> >       repo_read_index(repo);
> > @@ -907,7 +907,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,
> >       };
> >
> >       setup_work_tree();
> > -     if (!core_apply_sparse_checkout)
> > +     if (!repo->config_values.sparse_checkout)
> >               die(_("must be in a sparse-checkout to reapply sparsity patterns"));
> >
> >       reapply_opts.cone_mode = -1;
> > @@ -969,7 +969,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
> >       };
> >
> >       setup_work_tree();
> > -     if (!core_apply_sparse_checkout)
> > +     if (!repo->config_values.sparse_checkout)
> >               die(_("must be in a sparse-checkout to clean directories"));
> >       if (!core_sparse_checkout_cone)
> >               die(_("must be in a cone-mode sparse-checkout to clean directories"));
> > @@ -1035,7 +1035,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
> >       struct pattern_list pl;
> >
> >       /*
> > -      * We do not exit early if !core_apply_sparse_checkout; due to the
> > +      * We do not exit early if !repo->config_values.sparse_checkout; due to the
> >        * ability for users to manually muck things up between
> >        *   direct editing of .git/info/sparse-checkout
> >        *   running read-tree -m u HEAD or update-index --skip-worktree
> > @@ -1061,7 +1061,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
> >       hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
> >       hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
> >       pl.use_cone_patterns = 0;
> > -     core_apply_sparse_checkout = 1;
> > +     repo->config_values.sparse_checkout = 1;
> >
> >       add_pattern("/*", empty_base, 0, &pl, 0);
> >
> > diff --git a/builtin/worktree.c b/builtin/worktree.c
> > index fbdaf2eb2e..e401b8253e 100644
> > --- a/builtin/worktree.c
> > +++ b/builtin/worktree.c
> > @@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,
> >        * If the current worktree has sparse-checkout enabled, then copy
> >        * the sparse-checkout patterns from the current worktree.
> >        */
> > -     if (core_apply_sparse_checkout)
> > +     if (wt->repo->config_values.sparse_checkout)
> >               copy_sparse_checkout(sb_repo.buf);
> >
> >       /*
> > diff --git a/dir.c b/dir.c
> > index b00821f294..56b412a6d2 100644
> > --- a/dir.c
> > +++ b/dir.c
> > @@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(
> >
> >  int init_sparse_checkout_patterns(struct index_state *istate)
> >  {
> > -     if (!core_apply_sparse_checkout)
> > +     if (!istate->repo->config_values.sparse_checkout)
> >               return 1;
> >       if (istate->sparse_checkout_patterns)
> >               return 0;
> > diff --git a/environment.c b/environment.c
> > index 283db0a1a0..6633542750 100644
> > --- a/environment.c
> > +++ b/environment.c
> > @@ -74,7 +74,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_apply_sparse_checkout;
> >  int core_sparse_checkout_cone;
> >  int sparse_expect_files_outside_of_patterns;
> >  int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
> > @@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,
> >       }
> >
> >       if (!strcmp(var, "core.sparsecheckout")) {
> > -             core_apply_sparse_checkout = git_config_bool(var, value);
> > +             cfg->sparse_checkout = git_config_bool(var, value);
> >               return 0;
> >       }
> >
> > @@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,
> >  void repo_config_values_init(struct repo_config_values *cfg)
> >  {
> >       cfg->attributes_file_path = NULL;
> > +     cfg->sparse_checkout = 0;
> >  }
> > diff --git a/environment.h b/environment.h
> > index aea73ff25b..3b5ff7094a 100644
> > --- a/environment.h
> > +++ b/environment.h
> > @@ -88,6 +88,7 @@ struct strvec;
> >  struct repo_config_values {
> >       /* core config values */
> >       char *attributes_file_path;
> > +     int sparse_checkout;
> >  };
> >
> >  /*
> > @@ -169,7 +170,6 @@ extern int precomposed_unicode;
> >  extern int protect_hfs;
> >  extern int protect_ntfs;
> >
> > -extern int core_apply_sparse_checkout;
>
> In the field you're adding to 'struct repo_config_values' you have
> dropped the 'core_' prefix, what the reason for that? If I understand it
> correctly also settings from other sections might end up in that struct,
> so wouldn't it be better to keep the prefix?

Okay, noted

>
> >  extern int core_sparse_checkout_cone;
> >  extern int sparse_expect_files_outside_of_patterns;
> >
> > diff --git a/sparse-index.c b/sparse-index.c
> > index 76f90da5f5..6dd8dd679d 100644
> > --- a/sparse-index.c
> > +++ b/sparse-index.c
> > @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)
> >
> >  int is_sparse_index_allowed(struct index_state *istate, int flags)
> >  {
> > -     if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
> > +     struct repo_config_values *cfg = &istate->repo->config_values;
> > +     if (!cfg->sparse_checkout || !core_sparse_checkout_cone)
> >               return 0;
> >
> >       if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
> > @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
> >
> >  void clear_skip_worktree_from_present_files(struct index_state *istate)
> >  {
> > -     if (!core_apply_sparse_checkout ||
> > +     struct repo_config_values *cfg = &istate->repo->config_values;
> > +     if (!cfg->sparse_checkout ||
> >           sparse_expect_files_outside_of_patterns)
> >               return;
> >
> > diff --git a/unpack-trees.c b/unpack-trees.c
> > index f38c761ab9..2bdfa1334c 100644
> > --- a/unpack-trees.c
> > +++ b/unpack-trees.c
> > @@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
> >       if (o->prefix)
> >               update_sparsity_for_prefix(o->prefix, o->src_index);
> >
> > -     if (!core_apply_sparse_checkout || !o->update)
> > +     if (!repo->config_values.sparse_checkout || !o->update)
> >               o->skip_sparse_checkout = 1;
> >       if (!o->skip_sparse_checkout) {
> >               memset(&pl, 0, sizeof(pl));
> > diff --git a/wt-status.c b/wt-status.c
> > index e12adb26b9..a2e388606f 100644
> > --- a/wt-status.c
> > +++ b/wt-status.c
> > @@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,
> >       int skip_worktree = 0;
> >       int i;
> >
> > -     if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
> > +     if (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {
> >               /*
> >                * Don't compute percentage of checked out files if we
> >                * aren't in a sparse checkout or would get division by 0.
> > --
> > 2.34.1
> >
> >
>
> --
> Cheers,
> Toon

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally
  2026-01-22 14:41       ` Phillip Wood
@ 2026-01-22 15:29         ` Bello Olamide
  2026-01-23 10:43           ` Phillip Wood
  0 siblings, 1 reply; 79+ messages in thread
From: Bello Olamide @ 2026-01-22 15:29 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Toon Claes

On Thu, 22 Jan 2026 at 15:41, Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Olamide
>
> On 17/01/2026 20:59, Olamide Caleb Bello wrote:
> > The config value `core.sparseCheckout` is parsed in
> > `git_default_core_config()` and stored globally in
> > `core_appy_sparse_checkout`. This could cause unintended behaviours
> > when different Git repositories running in the same process access this
> > variable.
> >
> > Move the parsed value into `struct repo_config_values` to retains current
> > behaviours while achieving the repository scoped access.
>
> It doesn't achieve repository scoped access though because we only ever
> populate the values in "the_repository", all other instances of "struct
> repository" are initialized by config_values_init() but not the config
> settings.

Okay I understand.
Thank you for clarifying.

>
> > diff --git a/builtin/backfill.c b/builtin/backfill.c
> > index e80fc1b694..5fc8c51ed1 100644
> > --- a/builtin/backfill.c
> > +++ b/builtin/backfill.c
> > @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
> >       repo_config(repo, git_default_config, NULL);
> >
> >       if (ctx.sparse < 0)
> > -             ctx.sparse = core_apply_sparse_checkout;
> > +             ctx.sparse = repo->config_values.sparse_checkout;
>
> Using "repo" rather than "the_repository" here is dangerous because only
> "the_repository" contains the parsed config. This applies throughout
> this patch.

Okay noted...
Sorry but I have a question.
I observed that the address of "repo" is passed to builtin/backfill.c,
is gotten from git.c:handle_builtin
which passed run_builtin "the_repository" as a parameter.

Won't the address of "repo" and "the_repository be the same"?

>
> >
> >       result = do_backfill(&ctx);
> >       backfill_context_clear(&ctx);
> > diff --git a/builtin/clone.c b/builtin/clone.c
> > index b19b302b06..b6b19e83d1 100644
> > --- a/builtin/clone.c
> > +++ b/builtin/clone.c
> > @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
> >        * We must apply the setting in the current process
> >        * for the later checkout to use the sparse-checkout file.
> >        */
> > -     core_apply_sparse_checkout = 1;
> > +     the_repository->config_values.sparse_checkout = 1;
> >
> >       cmd.git_cmd = 1;
> >       if (run_command(&cmd)) {
> > diff --git a/builtin/grep.c b/builtin/grep.c
> > index 53cccf2d25..525edb5e9c 100644
> > --- a/builtin/grep.c
> > +++ b/builtin/grep.c
> > @@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,
> >        *      "forget" the sparse-index feature switch. As a result, the index
> >        *      of these submodules are expanded unexpectedly.
> >        *
> > -      * 2. "core_apply_sparse_checkout"
> > +      * 2. "sparse_checkout"
>
> That should be something like config_values.sparse_checkout to make it
> clear that "sparse_checkout" is the name of a member of a struct, not
> the name of a variable.

Okay noted

>
> > diff --git a/environment.h b/environment.h
> > index aea73ff25b..3b5ff7094a 100644
> > --- a/environment.h
> > +++ b/environment.h
> > @@ -88,6 +88,7 @@ struct strvec;
> >   struct repo_config_values {
> >       /* core config values */
> >       char *attributes_file_path;
> > +     int sparse_checkout;
>
> There are several other sparse checkout variables like
> core_sparse_checkout_cone that we'll need to convert in the future so
> "apply_sparse_checkout" or "sparse_checkout_apply" would be better names.

Okay noted.

>
> Thanks
>
> Phillip
>
>
> >   };
> >
> >   /*
> > @@ -169,7 +170,6 @@ extern int precomposed_unicode;
> >   extern int protect_hfs;
> >   extern int protect_ntfs;
> >
> > -extern int core_apply_sparse_checkout;
> >   extern int core_sparse_checkout_cone;
> >   extern int sparse_expect_files_outside_of_patterns;
> >
> > diff --git a/sparse-index.c b/sparse-index.c
> > index 76f90da5f5..6dd8dd679d 100644
> > --- a/sparse-index.c
> > +++ b/sparse-index.c
> > @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)
> >
> >   int is_sparse_index_allowed(struct index_state *istate, int flags)
> >   {
> > -     if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
> > +     struct repo_config_values *cfg = &istate->repo->config_values;
> > +     if (!cfg->sparse_checkout || !core_sparse_checkout_cone)
> >               return 0;
> >
> >       if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
> > @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
> >
> >   void clear_skip_worktree_from_present_files(struct index_state *istate)
> >   {
> > -     if (!core_apply_sparse_checkout ||
> > +     struct repo_config_values *cfg = &istate->repo->config_values;
> > +     if (!cfg->sparse_checkout ||
> >           sparse_expect_files_outside_of_patterns)
> >               return;
> >
> > diff --git a/unpack-trees.c b/unpack-trees.c
> > index f38c761ab9..2bdfa1334c 100644
> > --- a/unpack-trees.c
> > +++ b/unpack-trees.c
> > @@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
> >       if (o->prefix)
> >               update_sparsity_for_prefix(o->prefix, o->src_index);
> >
> > -     if (!core_apply_sparse_checkout || !o->update)
> > +     if (!repo->config_values.sparse_checkout || !o->update)
> >               o->skip_sparse_checkout = 1;
> >       if (!o->skip_sparse_checkout) {
> >               memset(&pl, 0, sizeof(pl));
> > diff --git a/wt-status.c b/wt-status.c
> > index e12adb26b9..a2e388606f 100644
> > --- a/wt-status.c
> > +++ b/wt-status.c
> > @@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,
> >       int skip_worktree = 0;
> >       int i;
> >
> > -     if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
> > +     if (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {
> >               /*
> >                * Don't compute percentage of checked out files if we
> >                * aren't in a sparse checkout or would get division by 0.
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-01-22 14:41       ` Phillip Wood
@ 2026-01-22 15:29         ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-22 15:29 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Toon Claes

On Thu, 22 Jan 2026 at 15:41, Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Olamide
>
> On 17/01/2026 20:59, Olamide Caleb Bello wrote:
> > The config value `brach.autoSetupMerge` is parsed in
> > `git_default_branch_config()` and stored in the global variable
> > `git_branch_track`. This global variable can cause unexpected behaviours
> > when multiple Git repos run in the the same process.
> >
> > Move this value into `struct repo_config_values` to retain current
> > behaviours while achieving repository scoped access.
>
> Same comment as the previous patch about repository scoped access.

Okay thank you

>
> > diff --git a/environment.h b/environment.h
> > index 3b5ff7094a..bfcdffe836 100644
> > --- a/environment.h
> > +++ b/environment.h
> > @@ -2,6 +2,7 @@
> >   #define ENVIRONMENT_H
> >
> >   #include "repo-settings.h"
> > +#include "branch.h"
> >
> >   /* Double-check local_repo_env below if you add to this list. */
> >   #define GIT_DIR_ENVIRONMENT "GIT_DIR"
> > @@ -89,6 +90,9 @@ struct repo_config_values {
> >       /* core config values */
> >       char *attributes_file_path;
> >       int sparse_checkout;
> > +
> > +     /* branch config values */
> > +     enum branch_track git_branch_track;
>
> We could probably drop the "git_" prefix now that it is not a global
> variable.

Alright thank you for the review.
>
> Thanks
>
> Phillip
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally
  2026-01-22 15:29         ` Bello Olamide
@ 2026-01-23 10:43           ` Phillip Wood
  2026-01-23 13:24             ` Bello Olamide
  0 siblings, 1 reply; 79+ messages in thread
From: Phillip Wood @ 2026-01-23 10:43 UTC (permalink / raw)
  To: Bello Olamide
  Cc: git, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Toon Claes

On 22/01/2026 15:29, Bello Olamide wrote:
> On Thu, 22 Jan 2026 at 15:41, Phillip Wood <phillip.wood123@gmail.com> wrote:
>>
>>> diff --git a/builtin/backfill.c b/builtin/backfill.c
>>> index e80fc1b694..5fc8c51ed1 100644
>>> --- a/builtin/backfill.c
>>> +++ b/builtin/backfill.c
>>> @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
>>>        repo_config(repo, git_default_config, NULL);
>>>
>>>        if (ctx.sparse < 0)
>>> -             ctx.sparse = core_apply_sparse_checkout;
>>> +             ctx.sparse = repo->config_values.sparse_checkout;
>>
>> Using "repo" rather than "the_repository" here is dangerous because only
>> "the_repository" contains the parsed config. This applies throughout
>> this patch.
> 
> Okay noted...
> Sorry but I have a question.

You don't need to be sorry for having a question - it shows you have 
been thinking about the feedback you have received which is very good.

> I observed that the address of "repo" is passed to builtin/backfill.c,
> is gotten from git.c:handle_builtin
> which passed run_builtin "the_repository" as a parameter.
> 
> Won't the address of "repo" and "the_repository be the same"?

Yes, but I think it is safer to explicitly say "the_repository" so that
if any of the functions you convert here are ever passed another 
repository instance the code will keep working as expected. It also 
documents that the config value is only stored in "the_repository". Once 
we make these config values per-repository then we can use the 
repository instance passed to the function.

Thanks

Phillip

>>
>>>
>>>        result = do_backfill(&ctx);
>>>        backfill_context_clear(&ctx);
>>> diff --git a/builtin/clone.c b/builtin/clone.c
>>> index b19b302b06..b6b19e83d1 100644
>>> --- a/builtin/clone.c
>>> +++ b/builtin/clone.c
>>> @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
>>>         * We must apply the setting in the current process
>>>         * for the later checkout to use the sparse-checkout file.
>>>         */
>>> -     core_apply_sparse_checkout = 1;
>>> +     the_repository->config_values.sparse_checkout = 1;
>>>
>>>        cmd.git_cmd = 1;
>>>        if (run_command(&cmd)) {
>>> diff --git a/builtin/grep.c b/builtin/grep.c
>>> index 53cccf2d25..525edb5e9c 100644
>>> --- a/builtin/grep.c
>>> +++ b/builtin/grep.c
>>> @@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,
>>>         *      "forget" the sparse-index feature switch. As a result, the index
>>>         *      of these submodules are expanded unexpectedly.
>>>         *
>>> -      * 2. "core_apply_sparse_checkout"
>>> +      * 2. "sparse_checkout"
>>
>> That should be something like config_values.sparse_checkout to make it
>> clear that "sparse_checkout" is the name of a member of a struct, not
>> the name of a variable.
> 
> Okay noted
> 
>>
>>> diff --git a/environment.h b/environment.h
>>> index aea73ff25b..3b5ff7094a 100644
>>> --- a/environment.h
>>> +++ b/environment.h
>>> @@ -88,6 +88,7 @@ struct strvec;
>>>    struct repo_config_values {
>>>        /* core config values */
>>>        char *attributes_file_path;
>>> +     int sparse_checkout;
>>
>> There are several other sparse checkout variables like
>> core_sparse_checkout_cone that we'll need to convert in the future so
>> "apply_sparse_checkout" or "sparse_checkout_apply" would be better names.
> 
> Okay noted.
> 
>>
>> Thanks
>>
>> Phillip
>>
>>
>>>    };
>>>
>>>    /*
>>> @@ -169,7 +170,6 @@ extern int precomposed_unicode;
>>>    extern int protect_hfs;
>>>    extern int protect_ntfs;
>>>
>>> -extern int core_apply_sparse_checkout;
>>>    extern int core_sparse_checkout_cone;
>>>    extern int sparse_expect_files_outside_of_patterns;
>>>
>>> diff --git a/sparse-index.c b/sparse-index.c
>>> index 76f90da5f5..6dd8dd679d 100644
>>> --- a/sparse-index.c
>>> +++ b/sparse-index.c
>>> @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)
>>>
>>>    int is_sparse_index_allowed(struct index_state *istate, int flags)
>>>    {
>>> -     if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
>>> +     struct repo_config_values *cfg = &istate->repo->config_values;
>>> +     if (!cfg->sparse_checkout || !core_sparse_checkout_cone)
>>>                return 0;
>>>
>>>        if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
>>> @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
>>>
>>>    void clear_skip_worktree_from_present_files(struct index_state *istate)
>>>    {
>>> -     if (!core_apply_sparse_checkout ||
>>> +     struct repo_config_values *cfg = &istate->repo->config_values;
>>> +     if (!cfg->sparse_checkout ||
>>>            sparse_expect_files_outside_of_patterns)
>>>                return;
>>>
>>> diff --git a/unpack-trees.c b/unpack-trees.c
>>> index f38c761ab9..2bdfa1334c 100644
>>> --- a/unpack-trees.c
>>> +++ b/unpack-trees.c
>>> @@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
>>>        if (o->prefix)
>>>                update_sparsity_for_prefix(o->prefix, o->src_index);
>>>
>>> -     if (!core_apply_sparse_checkout || !o->update)
>>> +     if (!repo->config_values.sparse_checkout || !o->update)
>>>                o->skip_sparse_checkout = 1;
>>>        if (!o->skip_sparse_checkout) {
>>>                memset(&pl, 0, sizeof(pl));
>>> diff --git a/wt-status.c b/wt-status.c
>>> index e12adb26b9..a2e388606f 100644
>>> --- a/wt-status.c
>>> +++ b/wt-status.c
>>> @@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,
>>>        int skip_worktree = 0;
>>>        int i;
>>>
>>> -     if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
>>> +     if (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {
>>>                /*
>>>                 * Don't compute percentage of checked out files if we
>>>                 * aren't in a sparse checkout or would get division by 0.
>>


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally
  2026-01-23 10:43           ` Phillip Wood
@ 2026-01-23 13:24             ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-23 13:24 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, Toon Claes

On Fri, 23 Jan 2026 at 11:43, Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> On 22/01/2026 15:29, Bello Olamide wrote:
> > On Thu, 22 Jan 2026 at 15:41, Phillip Wood <phillip.wood123@gmail.com> wrote:
> >>
> >>> diff --git a/builtin/backfill.c b/builtin/backfill.c
> >>> index e80fc1b694..5fc8c51ed1 100644
> >>> --- a/builtin/backfill.c
> >>> +++ b/builtin/backfill.c
> >>> @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
> >>>        repo_config(repo, git_default_config, NULL);
> >>>
> >>>        if (ctx.sparse < 0)
> >>> -             ctx.sparse = core_apply_sparse_checkout;
> >>> +             ctx.sparse = repo->config_values.sparse_checkout;
> >>
> >> Using "repo" rather than "the_repository" here is dangerous because only
> >> "the_repository" contains the parsed config. This applies throughout
> >> this patch.
> >
> > Okay noted...
> > Sorry but I have a question.
>
> You don't need to be sorry for having a question - it shows you have
> been thinking about the feedback you have received which is very good.

Thank you

>
> > I observed that the address of "repo" is passed to builtin/backfill.c,
> > is gotten from git.c:handle_builtin
> > which passed run_builtin "the_repository" as a parameter.
> >
> > Won't the address of "repo" and "the_repository be the same"?
>
> Yes, but I think it is safer to explicitly say "the_repository" so that
> if any of the functions you convert here are ever passed another
> repository instance the code will keep working as expected. It also
> documents that the config value is only stored in "the_repository". Once
> we make these config values per-repository then we can use the
> repository instance passed to the function.

Okay thank you for clarifying.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v4 0/3] store repo specific config values in new `struct repo_config_values`
  2026-01-17 20:59   ` [Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values` Olamide Caleb Bello
                       ` (3 preceding siblings ...)
  2026-01-20 15:19     ` [Outreachy PATCH v3 0/3] store repo specific config values in new " Bello Olamide
@ 2026-01-24 11:55     ` Olamide Caleb Bello
  2026-01-24 11:55       ` [Outreachy PATCH v4 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
                         ` (3 more replies)
  4 siblings, 4 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-24 11:55 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

Hi Git Community,
Over the course of my ongoing internship, which focused on moving repo specific
global variables in environment.h into local scope, I have attempted to move some
variables into the struct repo-settings.
However there has been some design concerns as regards the use of
`prepare_repo_settings()` with respect to when and where to call the
function, and also the change in behaviours when the variable is lazily
loaded as discussed in [1] and [2].

After different deliberations, Phillip Wood proposed creating a new config
struct [3], adding it to the repository struct and passing the repo struct to
`git_default_config()` to store parsed repo specific config values per repo.
This ensures the current behaviours will be retained.

I have experimented with this approach for some values and I would
appreciate feedbacks about this approach before we can move forward
and use it for more variables related to `git_default_config()`.

For now, the parsed value is stored in `the_repository` in
`git_default_*_config()` and further efforts to pass the repository
parameter to `git_default_config()` as the callback parameter will
be looked into later on.
The link to the CI tests can be see in [4]

1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/
2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/
3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/
4. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2266020513

Changes in v4:
==============
- Changed variable name in the struct to attributes_file
- Modified commit messages in all patches to explain how the variable
  being global can be overwritten when the process has multiple
  repositories
- Changed the variable name in patch 2 to apply_sparse_checkout
- Changes the variable name in patch 3 to branch_track


Olamide Caleb Bello (3):
  environment: stop storing `core.attributesFile` globally
  environment: stop using core.sparseCheckout globally
  environment: move "branch.autoSetupMerge" into `struct
    repo_config_values`

 attr.c                      |  7 ++++---
 branch.h                    |  2 --
 builtin/backfill.c          |  2 +-
 builtin/branch.c            |  2 +-
 builtin/checkout.c          |  2 +-
 builtin/clone.c             |  2 +-
 builtin/grep.c              |  2 +-
 builtin/mv.c                |  2 +-
 builtin/push.c              |  2 +-
 builtin/sparse-checkout.c   | 23 ++++++++++++-----------
 builtin/submodule--helper.c |  2 +-
 builtin/worktree.c          |  2 +-
 dir.c                       |  2 +-
 environment.c               | 28 ++++++++++++++++++----------
 environment.h               | 14 ++++++++++++--
 repository.c                |  1 +
 repository.h                |  4 ++++
 sparse-index.c              |  6 ++++--
 unpack-trees.c              |  2 +-
 wt-status.c                 |  3 ++-
 20 files changed, 68 insertions(+), 42 deletions(-)

 Range diff versus v3:
 =====================
1:  1aa41da833 ! 1:  d28850bcdb environment: stop storing `core.attributesFile` globally
    @@ Metadata
      ## Commit message ##
         environment: stop storing `core.attributesFile` globally
     
    -    The config value is parsed in git_default_core_config(), loaded eagerly
    -    and stored in the global variable `git_attributes_file`.
    -    Storing this value in a global variable can lead to unexpected
    -    behaviours when more than one Git repository run in the same Git process.
    +    The `core.attributeFile` config value is parsed in
    +    git_default_core_config(), loaded eagerly and stored in the global
    +    variable `git_attributes_file`. Storing this value in a global variable
    +    can lead to it being overwritten by another repository when more than one
    +    Git repository run in the same Git process.
     
         Create a new struct `repo_config_values` to hold this value and
    -    other repository dependent values parsed by `git_default_config()` and
    -    can be accessed per repository via `git_default_config()`.
    +    other repository dependent values parsed by `git_default_config()`.
    +    For now the value can be accessed via the_repository in
    +    `git_default_config()`.
         This will ensure the current behaviour remains the same while also
         enabling the libification of Git.
     
         It is important to note that `git_default_config()` is a wrapper to other
    -    `git_default_*_config()` such as `git_default_core_config()`.
    +    `git_default_*_config()` functions such as `git_default_core_config()`.
         Therefore to access and modify this global variable,
    -    the change has to be made in the function which parses and
    -    stores the value in the global variable.
    +    the change has to be made `git_default_core_config()`.
     
         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
         Mentored-by: Christian Couder <christian.couder@gmail.com>
    @@ attr.c: const char *git_attr_system_file(void)
     -	if (!git_attributes_file)
     -		git_attributes_file = xdg_config_home("attributes");
     +	struct repo_config_values *cfg = &the_repository->config_values;
    -+	if (!cfg->attributes_file_path)
    -+		cfg->attributes_file_path = xdg_config_home("attributes");
    ++	if (!cfg->attributes_file)
    ++		cfg->attributes_file = xdg_config_home("attributes");
      
     -	return git_attributes_file;
    -+	return cfg->attributes_file_path;
    ++	return cfg->attributes_file;
      }
      
      int git_attr_system_is_enabled(void)
    @@ environment.c: static int git_default_core_config(const char *var, const char *v
      	if (!strcmp(var, "core.attributesfile")) {
     -		FREE_AND_NULL(git_attributes_file);
     -		return git_config_pathname(&git_attributes_file, var, value);
    -+		FREE_AND_NULL(cfg->attributes_file_path);
    -+		return git_config_pathname(&cfg->attributes_file_path, var, value);
    ++		FREE_AND_NULL(cfg->attributes_file);
    ++		return git_config_pathname(&cfg->attributes_file, var, value);
      	}
      
      	if (!strcmp(var, "core.bare")) {
    @@ environment.c: int git_default_config(const char *var, const char *value,
     +
     +void repo_config_values_init(struct repo_config_values *cfg)
     +{
    -+	cfg->attributes_file_path = NULL;
    ++	cfg->attributes_file = NULL;
     +}
     
      ## environment.h ##
    @@ environment.h: extern const char * const local_repo_env[];
      
      struct strvec;
      
    -+/* Config values parsed by git_default_config() */
     +struct repo_config_values {
    -+	/* core config values */
    -+	char *attributes_file_path;
    ++	/* section "core" config values */
    ++	char *attributes_file;
     +};
     +
      /*
2:  fd95169de4 ! 2:  e44ce7be3d environment: environment: stop using core.sparseCheckout globally
    @@ Metadata
     Author: Olamide Caleb Bello <belkid98@gmail.com>
     
      ## Commit message ##
    -    environment: environment: stop using core.sparseCheckout globally
    +    environment: stop using core.sparseCheckout globally
     
         The config value `core.sparseCheckout` is parsed in
         `git_default_core_config()` and stored globally in
    -    `core_appy_sparse_checkout`. This could cause unintended behaviours
    -    when different Git repositories running in the same process access this
    -    variable.
    +    `core_apply_sparse_checkout`. This could cause it to be overwritten
    +    by another repository when different Git repositories run in the same
    +    process.
     
    -    Move the parsed value into `struct repo_config_values` to retains current
    -    behaviours while achieving the repository scoped access.
    +    Move the parsed value into `struct repo_config_values` in the_repository
    +    to retain current behaviours and move towards libifying Git.
     
         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
         Mentored-by: Christian Couder <christian.couder@gmail.com>
    @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *pr
      
      	if (ctx.sparse < 0)
     -		ctx.sparse = core_apply_sparse_checkout;
    -+		ctx.sparse = repo->config_values.sparse_checkout;
    ++		ctx.sparse = the_repository->config_values.apply_sparse_checkout;
      
      	result = do_backfill(&ctx);
      	backfill_context_clear(&ctx);
    @@ builtin/clone.c: static int git_sparse_checkout_init(const char *repo)
      	 * for the later checkout to use the sparse-checkout file.
      	 */
     -	core_apply_sparse_checkout = 1;
    -+	the_repository->config_values.sparse_checkout = 1;
    ++	the_repository->config_values.apply_sparse_checkout = 1;
      
      	cmd.git_cmd = 1;
      	if (run_command(&cmd)) {
    @@ builtin/grep.c: static int grep_submodule(struct grep_opt *opt,
      	 *	of these submodules are expanded unexpectedly.
      	 *
     -	 * 2. "core_apply_sparse_checkout"
    -+	 * 2. "sparse_checkout"
    ++	 * 2. "config_values.apply_sparse_checkout"
      	 *	When running `grep` in the superproject, this setting is
      	 *	populated using the superproject's configs. However, once
      	 *	initialized, this config is globally accessible and is read by
    @@ builtin/mv.c: int cmd_mv(int argc,
      
      		if (ignore_sparse &&
     -		    core_apply_sparse_checkout &&
    -+		    the_repository->config_values.sparse_checkout &&
    ++		    the_repository->config_values.apply_sparse_checkout &&
      		    core_sparse_checkout_cone) {
      			/*
      			 * NEEDSWORK: we are *not* paying attention to
    @@ builtin/sparse-checkout.c: static int sparse_checkout_list(int argc, const char
      
      	setup_work_tree();
     -	if (!core_apply_sparse_checkout)
    -+	if (!the_repository->config_values.sparse_checkout)
    ++	if (!the_repository->config_values.apply_sparse_checkout)
      		die(_("this worktree is not sparse"));
      
      	argc = parse_options(argc, argv, prefix,
    @@ builtin/sparse-checkout.c: static int set_config(struct repository *repo,
      static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
      	/* If not specified, use previous definition of cone mode */
     -	if (*cone_mode == -1 && core_apply_sparse_checkout)
    -+	if (*cone_mode == -1 && the_repository->config_values.sparse_checkout)
    ++	if (*cone_mode == -1 && the_repository->config_values.apply_sparse_checkout)
      		*cone_mode = core_sparse_checkout_cone;
      
      	/* Set cone/non-cone mode appropriately */
     -	core_apply_sparse_checkout = 1;
    -+	the_repository->config_values.sparse_checkout = 1;
    ++	the_repository->config_values.apply_sparse_checkout = 1;
      	if (*cone_mode == 1 || *cone_mode == -1) {
      		core_sparse_checkout_cone = 1;


-- 
2.34.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v4 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-24 11:55     ` [Outreachy PATCH v4 " Olamide Caleb Bello
@ 2026-01-24 11:55       ` Olamide Caleb Bello
  2026-01-24 11:55       ` [Outreachy PATCH v4 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
                         ` (2 subsequent siblings)
  3 siblings, 0 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-24 11:55 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The `core.attributeFile` config value is parsed in
git_default_core_config(), loaded eagerly and stored in the global
variable `git_attributes_file`. Storing this value in a global variable
can lead to it being overwritten by another repository when more than one
Git repository run in the same Git process.

Create a new struct `repo_config_values` to hold this value and
other repository dependent values parsed by `git_default_config()`.
For now the value can be accessed via the_repository in
`git_default_config()`.
This will ensure the current behaviour remains the same while also
enabling the libification of Git.

It is important to note that `git_default_config()` is a wrapper to other
`git_default_*_config()` functions such as `git_default_core_config()`.
Therefore to access and modify this global variable,
the change has to be made `git_default_core_config()`.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 attr.c        |  7 ++++---
 environment.c | 12 +++++++++---
 environment.h |  8 +++++++-
 repository.c  |  1 +
 repository.h  |  4 ++++
 5 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/attr.c b/attr.c
index 4999b7e09d..b8b70e6dce 100644
--- a/attr.c
+++ b/attr.c
@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)
 
 const char *git_attr_global_file(void)
 {
-	if (!git_attributes_file)
-		git_attributes_file = xdg_config_home("attributes");
+	struct repo_config_values *cfg = &the_repository->config_values;
+	if (!cfg->attributes_file)
+		cfg->attributes_file = xdg_config_home("attributes");
 
-	return git_attributes_file;
+	return cfg->attributes_file;
 }
 
 int git_attr_system_is_enabled(void)
diff --git a/environment.c b/environment.c
index a770b5921d..72735d9e4b 100644
--- a/environment.c
+++ b/environment.c
@@ -53,7 +53,6 @@ char *git_commit_encoding;
 char *git_log_output_encoding;
 char *apply_default_whitespace;
 char *apply_default_ignorewhitespace;
-char *git_attributes_file;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files = -1;
@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
 static int git_default_core_config(const char *var, const char *value,
 				   const struct config_context *ctx, void *cb)
 {
+	struct repo_config_values *cfg = &the_repository->config_values;
+
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
 		trust_executable_bit = git_config_bool(var, value);
@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.attributesfile")) {
-		FREE_AND_NULL(git_attributes_file);
-		return git_config_pathname(&git_attributes_file, var, value);
+		FREE_AND_NULL(cfg->attributes_file);
+		return git_config_pathname(&cfg->attributes_file, var, value);
 	}
 
 	if (!strcmp(var, "core.bare")) {
@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,
 	/* Add other config variables here and to Documentation/config.adoc. */
 	return 0;
 }
+
+void repo_config_values_init(struct repo_config_values *cfg)
+{
+	cfg->attributes_file = NULL;
+}
diff --git a/environment.h b/environment.h
index 51898c99cd..0c0dcc6847 100644
--- a/environment.h
+++ b/environment.h
@@ -84,6 +84,11 @@ extern const char * const local_repo_env[];
 
 struct strvec;
 
+struct repo_config_values {
+	/* section "core" config values */
+	char *attributes_file;
+};
+
 /*
  * Wrapper of getenv() that returns a strdup value. This value is kept
  * in argv to be freed later.
@@ -107,6 +112,8 @@ const char *strip_namespace(const char *namespaced_ref);
 int git_default_config(const char *, const char *,
 		       const struct config_context *, void *);
 
+void repo_config_values_init(struct repo_config_values *cfg);
+
 /*
  * TODO: All the below state either explicitly or implicitly relies on
  * `the_repository`. We should eventually get rid of these and make the
@@ -152,7 +159,6 @@ extern int assume_unchanged;
 extern int warn_on_object_refname_ambiguity;
 extern char *apply_default_whitespace;
 extern char *apply_default_ignorewhitespace;
-extern char *git_attributes_file;
 extern int zlib_compression_level;
 extern int pack_compression_level;
 extern unsigned long pack_size_limit_cfg;
diff --git a/repository.c b/repository.c
index c7e75215ac..d308cd78bf 100644
--- a/repository.c
+++ b/repository.c
@@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)
 	ALLOC_ARRAY(repo->index, 1);
 	index_state_init(repo->index, repo);
 	repo->check_deprecated_config = true;
+	repo_config_values_init(&repo->config_values);
 
 	/*
 	 * When a command runs inside a repository, it learns what
diff --git a/repository.h b/repository.h
index 6063c4b846..638a142577 100644
--- a/repository.h
+++ b/repository.h
@@ -3,6 +3,7 @@
 
 #include "strmap.h"
 #include "repo-settings.h"
+#include "environment.h"
 
 struct config_set;
 struct git_hash_algo;
@@ -148,6 +149,9 @@ struct repository {
 	/* Repository's compatibility hash algorithm. */
 	const struct git_hash_algo *compat_hash_algo;
 
+	/* Repository's config values parsed by git_default_config() */
+	struct repo_config_values config_values;
+
 	/* Repository's reference storage format, as serialized on disk. */
 	enum ref_storage_format ref_storage_format;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v4 2/3] environment: stop using core.sparseCheckout globally
  2026-01-24 11:55     ` [Outreachy PATCH v4 " Olamide Caleb Bello
  2026-01-24 11:55       ` [Outreachy PATCH v4 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-01-24 11:55       ` Olamide Caleb Bello
  2026-01-24 11:55       ` [Outreachy PATCH v4 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
  2026-01-24 12:21       ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Olamide Caleb Bello
  3 siblings, 0 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-24 11:55 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The config value `core.sparseCheckout` is parsed in
`git_default_core_config()` and stored globally in
`core_apply_sparse_checkout`. This could cause it to be overwritten
by another repository when different Git repositories run in the same
process.

Move the parsed value into `struct repo_config_values` in the_repository
to retain current behaviours and move towards libifying Git.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
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/backfill.c        |  2 +-
 builtin/clone.c           |  2 +-
 builtin/grep.c            |  2 +-
 builtin/mv.c              |  2 +-
 builtin/sparse-checkout.c | 23 ++++++++++++-----------
 builtin/worktree.c        |  2 +-
 dir.c                     |  2 +-
 environment.c             |  4 ++--
 environment.h             |  2 +-
 sparse-index.c            |  6 ++++--
 unpack-trees.c            |  2 +-
 wt-status.c               |  3 ++-
 12 files changed, 28 insertions(+), 24 deletions(-)

diff --git a/builtin/backfill.c b/builtin/backfill.c
index e80fc1b694..d8cc13aaa4 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 	repo_config(repo, git_default_config, NULL);
 
 	if (ctx.sparse < 0)
-		ctx.sparse = core_apply_sparse_checkout;
+		ctx.sparse = the_repository->config_values.apply_sparse_checkout;
 
 	result = do_backfill(&ctx);
 	backfill_context_clear(&ctx);
diff --git a/builtin/clone.c b/builtin/clone.c
index b19b302b06..ee0736b634 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
 	 * We must apply the setting in the current process
 	 * for the later checkout to use the sparse-checkout file.
 	 */
-	core_apply_sparse_checkout = 1;
+	the_repository->config_values.apply_sparse_checkout = 1;
 
 	cmd.git_cmd = 1;
 	if (run_command(&cmd)) {
diff --git a/builtin/grep.c b/builtin/grep.c
index 53cccf2d25..4b5a96703a 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,
 	 *	"forget" the sparse-index feature switch. As a result, the index
 	 *	of these submodules are expanded unexpectedly.
 	 *
-	 * 2. "core_apply_sparse_checkout"
+	 * 2. "config_values.apply_sparse_checkout"
 	 *	When running `grep` in the superproject, this setting is
 	 *	populated using the superproject's configs. However, once
 	 *	initialized, this config is globally accessible and is read by
diff --git a/builtin/mv.c b/builtin/mv.c
index d43925097b..29801f5fe7 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -572,7 +572,7 @@ int cmd_mv(int argc,
 		rename_index_entry_at(the_repository->index, pos, dst);
 
 		if (ignore_sparse &&
-		    core_apply_sparse_checkout &&
+		    the_repository->config_values.apply_sparse_checkout &&
 		    core_sparse_checkout_cone) {
 			/*
 			 * NEEDSWORK: we are *not* paying attention to
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 15d51e60a8..c57793ebdc 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
 	int res;
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.apply_sparse_checkout)
 		die(_("this worktree is not sparse"));
 
 	argc = parse_options(argc, argv, prefix,
@@ -400,11 +400,11 @@ static int set_config(struct repository *repo,
 
 static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
 	/* If not specified, use previous definition of cone mode */
-	if (*cone_mode == -1 && core_apply_sparse_checkout)
+	if (*cone_mode == -1 && the_repository->config_values.apply_sparse_checkout)
 		*cone_mode = core_sparse_checkout_cone;
 
 	/* Set cone/non-cone mode appropriately */
-	core_apply_sparse_checkout = 1;
+	the_repository->config_values.apply_sparse_checkout = 1;
 	if (*cone_mode == 1 || *cone_mode == -1) {
 		core_sparse_checkout_cone = 1;
 		return MODE_CONE_PATTERNS;
@@ -418,7 +418,8 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind
 	int mode, record_mode;
 
 	/* Determine if we need to record the mode; ensure sparse checkout on */
-	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+	record_mode = (*cone_mode != -1) ||
+	              !the_repository->config_values.apply_sparse_checkout;
 
 	mode = update_cone_mode(cone_mode);
 	if (record_mode && set_config(repo, mode))
@@ -699,9 +700,9 @@ static int modify_pattern_list(struct repository *repo,
 		break;
 	}
 
-	if (!core_apply_sparse_checkout) {
+	if (!the_repository->config_values.apply_sparse_checkout) {
 		set_config(repo, MODE_ALL_PATTERNS);
-		core_apply_sparse_checkout = 1;
+		the_repository->config_values.apply_sparse_checkout = 1;
 		changed_config = 1;
 	}
 
@@ -798,7 +799,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
 	int ret;
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.apply_sparse_checkout)
 		die(_("no sparse-checkout to add to"));
 
 	repo_read_index(repo);
@@ -907,7 +908,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.apply_sparse_checkout)
 		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
 
 	reapply_opts.cone_mode = -1;
@@ -969,7 +970,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.apply_sparse_checkout)
 		die(_("must be in a sparse-checkout to clean directories"));
 	if (!core_sparse_checkout_cone)
 		die(_("must be in a cone-mode sparse-checkout to clean directories"));
@@ -1035,7 +1036,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	struct pattern_list pl;
 
 	/*
-	 * We do not exit early if !core_apply_sparse_checkout; due to the
+	 * We do not exit early if !repo->config_values.apply_sparse_checkout; due to the
 	 * ability for users to manually muck things up between
 	 *   direct editing of .git/info/sparse-checkout
 	 *   running read-tree -m u HEAD or update-index --skip-worktree
@@ -1061,7 +1062,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
 	hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
 	pl.use_cone_patterns = 0;
-	core_apply_sparse_checkout = 1;
+	the_repository->config_values.apply_sparse_checkout = 1;
 
 	add_pattern("/*", empty_base, 0, &pl, 0);
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index fbdaf2eb2e..4b0c3e09a9 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,
 	 * If the current worktree has sparse-checkout enabled, then copy
 	 * the sparse-checkout patterns from the current worktree.
 	 */
-	if (core_apply_sparse_checkout)
+	if (the_repository->config_values.apply_sparse_checkout)
 		copy_sparse_checkout(sb_repo.buf);
 
 	/*
diff --git a/dir.c b/dir.c
index b00821f294..bc59d8cd6a 100644
--- a/dir.c
+++ b/dir.c
@@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(
 
 int init_sparse_checkout_patterns(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.apply_sparse_checkout)
 		return 1;
 	if (istate->sparse_checkout_patterns)
 		return 0;
diff --git a/environment.c b/environment.c
index 72735d9e4b..269cac6d6e 100644
--- a/environment.c
+++ b/environment.c
@@ -74,7 +74,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_apply_sparse_checkout;
 int core_sparse_checkout_cone;
 int sparse_expect_files_outside_of_patterns;
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.sparsecheckout")) {
-		core_apply_sparse_checkout = git_config_bool(var, value);
+		cfg->apply_sparse_checkout = git_config_bool(var, value);
 		return 0;
 	}
 
@@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,
 void repo_config_values_init(struct repo_config_values *cfg)
 {
 	cfg->attributes_file = NULL;
+	cfg->apply_sparse_checkout = 0;
 }
diff --git a/environment.h b/environment.h
index 0c0dcc6847..ddce69c6ba 100644
--- a/environment.h
+++ b/environment.h
@@ -87,6 +87,7 @@ struct strvec;
 struct repo_config_values {
 	/* section "core" config values */
 	char *attributes_file;
+	int apply_sparse_checkout;
 };
 
 /*
@@ -168,7 +169,6 @@ extern int precomposed_unicode;
 extern int protect_hfs;
 extern int protect_ntfs;
 
-extern int core_apply_sparse_checkout;
 extern int core_sparse_checkout_cone;
 extern int sparse_expect_files_outside_of_patterns;
 
diff --git a/sparse-index.c b/sparse-index.c
index 76f90da5f5..f00c601986 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)
 
 int is_sparse_index_allowed(struct index_state *istate, int flags)
 {
-	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
+	struct repo_config_values *cfg = &the_repository->config_values;
+	if (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)
 		return 0;
 
 	if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
@@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
 
 void clear_skip_worktree_from_present_files(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout ||
+	struct repo_config_values *cfg = &the_repository->config_values;
+	if (!cfg->apply_sparse_checkout ||
 	    sparse_expect_files_outside_of_patterns)
 		return;
 
diff --git a/unpack-trees.c b/unpack-trees.c
index f38c761ab9..a2f4d568da 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 	if (o->prefix)
 		update_sparsity_for_prefix(o->prefix, o->src_index);
 
-	if (!core_apply_sparse_checkout || !o->update)
+	if (!the_repository->config_values.apply_sparse_checkout || !o->update)
 		o->skip_sparse_checkout = 1;
 	if (!o->skip_sparse_checkout) {
 		memset(&pl, 0, sizeof(pl));
diff --git a/wt-status.c b/wt-status.c
index e12adb26b9..ed93e219dc 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1764,7 +1764,8 @@ static void wt_status_check_sparse_checkout(struct repository *r,
 	int skip_worktree = 0;
 	int i;
 
-	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
+	if (!the_repository->config_values.apply_sparse_checkout ||
+	    r->index->cache_nr == 0) {
 		/*
 		 * Don't compute percentage of checked out files if we
 		 * aren't in a sparse checkout or would get division by 0.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v4 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-01-24 11:55     ` [Outreachy PATCH v4 " Olamide Caleb Bello
  2026-01-24 11:55       ` [Outreachy PATCH v4 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
  2026-01-24 11:55       ` [Outreachy PATCH v4 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-01-24 11:55       ` Olamide Caleb Bello
  2026-01-24 12:21       ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Olamide Caleb Bello
  3 siblings, 0 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-24 11:55 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The config value `brach.autoSetupMerge` is parsed in
`git_default_branch_config()` and stored in the global variable
`git_branch_track`. This global variable can be overwritten
by another repository when multiple Git repos run in the the same process.

Move this value into `struct repo_config_values` in the_repository to
retain current behaviours and move towards libifying Git.
Since the variable is no longer a global variable, it has been renamed to
`branch_track` in the struct `repo_config_values`.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 branch.h                    |  2 --
 builtin/branch.c            |  2 +-
 builtin/checkout.c          |  2 +-
 builtin/push.c              |  2 +-
 builtin/submodule--helper.c |  2 +-
 environment.c               | 12 +++++++-----
 environment.h               |  4 ++++
 7 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/branch.h b/branch.h
index ec2f35fda4..3dc6e2a0ff 100644
--- a/branch.h
+++ b/branch.h
@@ -15,8 +15,6 @@ enum branch_track {
 	BRANCH_TRACK_SIMPLE,
 };
 
-extern enum branch_track git_branch_track;
-
 /* Functions for acting on the information about branches. */
 
 /**
diff --git a/builtin/branch.c b/builtin/branch.c
index c577b5d20f..7d27951a7e 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -795,7 +795,7 @@ int cmd_branch(int argc,
 	if (!sorting_options.nr)
 		string_list_append(&sorting_options, "refname");
 
-	track = git_branch_track;
+	track = the_repository->config_values.branch_track;
 
 	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
 				   0, &head_oid, NULL);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 261699e2f5..ba6fea9aee 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1631,7 +1631,7 @@ static int checkout_branch(struct checkout_opts *opts,
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
 	} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
-		opts->track = git_branch_track;
+		opts->track = the_repository->config_values.branch_track;
 
 	if (new_branch_info->name && !new_branch_info->commit)
 		die(_("Cannot switch branch to a non-commit '%s'"),
diff --git a/builtin/push.c b/builtin/push.c
index 5b6cebbb85..7be20a1035 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -162,7 +162,7 @@ static NORETURN void die_push_simple(struct branch *branch,
 		advice_pushdefault_maybe = _("\n"
 				 "To choose either option permanently, "
 				 "see push.default in 'git help config'.\n");
-	if (git_branch_track != BRANCH_TRACK_SIMPLE)
+	if (the_repository->config_values.branch_track != BRANCH_TRACK_SIMPLE)
 		advice_automergesimple_maybe = _("\n"
 				 "To avoid automatically configuring "
 				 "an upstream branch when its name\n"
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d537ab087a..f2b6f027d7 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3128,7 +3128,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,
 	};
 
 	repo_config(the_repository, git_default_config, NULL);
-	track = git_branch_track;
+	track = the_repository->config_values.branch_track;
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
 	if (argc != 3)
diff --git a/environment.c b/environment.c
index 269cac6d6e..de8721657e 100644
--- a/environment.c
+++ b/environment.c
@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 enum eol core_eol = EOL_UNSET;
 int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
 char *check_roundtrip_encoding;
-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #ifndef OBJECT_CREATION_MODE
@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)
 
 static int git_default_branch_config(const char *var, const char *value)
 {
+	struct repo_config_values *cfg = &the_repository->config_values;
+
 	if (!strcmp(var, "branch.autosetupmerge")) {
 		if (value && !strcmp(value, "always")) {
-			git_branch_track = BRANCH_TRACK_ALWAYS;
+			cfg->branch_track = BRANCH_TRACK_ALWAYS;
 			return 0;
 		} else if (value && !strcmp(value, "inherit")) {
-			git_branch_track = BRANCH_TRACK_INHERIT;
+			cfg->branch_track = BRANCH_TRACK_INHERIT;
 			return 0;
 		} else if (value && !strcmp(value, "simple")) {
-			git_branch_track = BRANCH_TRACK_SIMPLE;
+			cfg->branch_track = BRANCH_TRACK_SIMPLE;
 			return 0;
 		}
-		git_branch_track = git_config_bool(var, value);
+		cfg->branch_track = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "branch.autosetuprebase")) {
@@ -761,4 +762,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;
 }
diff --git a/environment.h b/environment.h
index ddce69c6ba..27161d56ab 100644
--- a/environment.h
+++ b/environment.h
@@ -2,6 +2,7 @@
 #define ENVIRONMENT_H
 
 #include "repo-settings.h"
+#include "branch.h"
 
 /* Double-check local_repo_env below if you add to this list. */
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
@@ -88,6 +89,9 @@ struct repo_config_values {
 	/* section "core" config values */
 	char *attributes_file;
 	int apply_sparse_checkout;
+
+	/* section "branch" config values */
+	enum branch_track branch_track;
 };
 
 /*
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v5 0/3] store repo specific config values in new `struct repo_config_values`
  2026-01-24 11:55     ` [Outreachy PATCH v4 " Olamide Caleb Bello
                         ` (2 preceding siblings ...)
  2026-01-24 11:55       ` [Outreachy PATCH v4 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
@ 2026-01-24 12:21       ` Olamide Caleb Bello
  2026-01-24 12:21         ` [Outreachy PATCH v5 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
                           ` (5 more replies)
  3 siblings, 6 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-24 12:21 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

Hi Git Community,
Over the course of my ongoing internship, which focused on moving repo specific
global variables in environment.h into local scope, I have attempted to move some
variables into the struct repo-settings.
However there has been some design concerns as regards the use of
`prepare_repo_settings()` with respect to when and where to call the
function, and also the change in behaviours when the variable is lazily
loaded as discussed in [1] and [2].

After different deliberations, Phillip Wood proposed creating a new config
struct [3], adding it to the repository struct and passing the repo struct to
`git_default_config()` to store parsed repo specific config values per repo.
This ensures the current behaviours will be retained.

I have experimented with this approach for some values and I would
appreciate feedbacks about this approach before we can move forward
and use it for more variables related to `git_default_config()`.

For now, the parsed value is stored in `the_repository` in
`git_default_*_config()` and further efforts to pass the repository
parameter to `git_default_config()` as the callback parameter will
be looked into later on.
The link to the CI tests can be see in [4]

1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/
2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/
3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/
4. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2266020513

Changes in v5:
==============
- Corrected mistake in commit message of patch 3 which spelt branch
  wrongly in `branch.autoSetupmerge`


Olamide Caleb Bello (3):
  environment: stop storing `core.attributesFile` globally
  environment: stop using core.sparseCheckout globally
  environment: move "branch.autoSetupMerge" into `struct
    repo_config_values`

 attr.c                      |  7 ++++---
 branch.h                    |  2 --
 builtin/backfill.c          |  2 +-
 builtin/branch.c            |  2 +-
 builtin/checkout.c          |  2 +-
 builtin/clone.c             |  2 +-
 builtin/grep.c              |  2 +-
 builtin/mv.c                |  2 +-
 builtin/push.c              |  2 +-
 builtin/sparse-checkout.c   | 23 ++++++++++++-----------
 builtin/submodule--helper.c |  2 +-
 builtin/worktree.c          |  2 +-
 dir.c                       |  2 +-
 environment.c               | 28 ++++++++++++++++++----------
 environment.h               | 14 ++++++++++++--
 repository.c                |  1 +
 repository.h                |  4 ++++
 sparse-index.c              |  6 ++++--
 unpack-trees.c              |  2 +-
 wt-status.c                 |  3 ++-
 20 files changed, 68 insertions(+), 42 deletions(-)

Range diff versus v4:
====================
1:  d28850bcdb = 1:  d28850bcdb environment: stop storing `core.attributesFile` globally
2:  5e56e1cc41 = 2:  5e56e1cc41 environment: stop using core.sparseCheckout globally
3:  aed3183321 ! 3:  e7f37bac87 environment: move "branch.autoSetupMerge" into `struct repo_config_values`
    @@ Metadata
      ## Commit message ##
         environment: move "branch.autoSetupMerge" into `struct repo_config_values`

    -    The config value `brach.autoSetupMerge` is parsed in
    +    The config value `branch.autoSetupMerge` is parsed in
         `git_default_branch_config()` and stored in the global variable
         `git_branch_track`. This global variable can be overwritten
         by another repository when multiple Git repos run in the the same process.

-- 
2.34.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v5 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-24 12:21       ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Olamide Caleb Bello
@ 2026-01-24 12:21         ` Olamide Caleb Bello
  2026-01-29 18:01           ` Junio C Hamano
  2026-01-24 12:21         ` [Outreachy PATCH v5 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
                           ` (4 subsequent siblings)
  5 siblings, 1 reply; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-24 12:21 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The `core.attributeFile` config value is parsed in
git_default_core_config(), loaded eagerly and stored in the global
variable `git_attributes_file`. Storing this value in a global variable
can lead to it being overwritten by another repository when more than one
Git repository run in the same Git process.

Create a new struct `repo_config_values` to hold this value and
other repository dependent values parsed by `git_default_config()`.
For now the value can be accessed via the_repository in
`git_default_config()`.
This will ensure the current behaviour remains the same while also
enabling the libification of Git.

It is important to note that `git_default_config()` is a wrapper to other
`git_default_*_config()` functions such as `git_default_core_config()`.
Therefore to access and modify this global variable,
the change has to be made `git_default_core_config()`.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 attr.c        |  7 ++++---
 environment.c | 12 +++++++++---
 environment.h |  8 +++++++-
 repository.c  |  1 +
 repository.h  |  4 ++++
 5 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/attr.c b/attr.c
index 4999b7e09d..b8b70e6dce 100644
--- a/attr.c
+++ b/attr.c
@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)
 
 const char *git_attr_global_file(void)
 {
-	if (!git_attributes_file)
-		git_attributes_file = xdg_config_home("attributes");
+	struct repo_config_values *cfg = &the_repository->config_values;
+	if (!cfg->attributes_file)
+		cfg->attributes_file = xdg_config_home("attributes");
 
-	return git_attributes_file;
+	return cfg->attributes_file;
 }
 
 int git_attr_system_is_enabled(void)
diff --git a/environment.c b/environment.c
index a770b5921d..72735d9e4b 100644
--- a/environment.c
+++ b/environment.c
@@ -53,7 +53,6 @@ char *git_commit_encoding;
 char *git_log_output_encoding;
 char *apply_default_whitespace;
 char *apply_default_ignorewhitespace;
-char *git_attributes_file;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files = -1;
@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
 static int git_default_core_config(const char *var, const char *value,
 				   const struct config_context *ctx, void *cb)
 {
+	struct repo_config_values *cfg = &the_repository->config_values;
+
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
 		trust_executable_bit = git_config_bool(var, value);
@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.attributesfile")) {
-		FREE_AND_NULL(git_attributes_file);
-		return git_config_pathname(&git_attributes_file, var, value);
+		FREE_AND_NULL(cfg->attributes_file);
+		return git_config_pathname(&cfg->attributes_file, var, value);
 	}
 
 	if (!strcmp(var, "core.bare")) {
@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,
 	/* Add other config variables here and to Documentation/config.adoc. */
 	return 0;
 }
+
+void repo_config_values_init(struct repo_config_values *cfg)
+{
+	cfg->attributes_file = NULL;
+}
diff --git a/environment.h b/environment.h
index 51898c99cd..0c0dcc6847 100644
--- a/environment.h
+++ b/environment.h
@@ -84,6 +84,11 @@ extern const char * const local_repo_env[];
 
 struct strvec;
 
+struct repo_config_values {
+	/* section "core" config values */
+	char *attributes_file;
+};
+
 /*
  * Wrapper of getenv() that returns a strdup value. This value is kept
  * in argv to be freed later.
@@ -107,6 +112,8 @@ const char *strip_namespace(const char *namespaced_ref);
 int git_default_config(const char *, const char *,
 		       const struct config_context *, void *);
 
+void repo_config_values_init(struct repo_config_values *cfg);
+
 /*
  * TODO: All the below state either explicitly or implicitly relies on
  * `the_repository`. We should eventually get rid of these and make the
@@ -152,7 +159,6 @@ extern int assume_unchanged;
 extern int warn_on_object_refname_ambiguity;
 extern char *apply_default_whitespace;
 extern char *apply_default_ignorewhitespace;
-extern char *git_attributes_file;
 extern int zlib_compression_level;
 extern int pack_compression_level;
 extern unsigned long pack_size_limit_cfg;
diff --git a/repository.c b/repository.c
index c7e75215ac..d308cd78bf 100644
--- a/repository.c
+++ b/repository.c
@@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)
 	ALLOC_ARRAY(repo->index, 1);
 	index_state_init(repo->index, repo);
 	repo->check_deprecated_config = true;
+	repo_config_values_init(&repo->config_values);
 
 	/*
 	 * When a command runs inside a repository, it learns what
diff --git a/repository.h b/repository.h
index 6063c4b846..638a142577 100644
--- a/repository.h
+++ b/repository.h
@@ -3,6 +3,7 @@
 
 #include "strmap.h"
 #include "repo-settings.h"
+#include "environment.h"
 
 struct config_set;
 struct git_hash_algo;
@@ -148,6 +149,9 @@ struct repository {
 	/* Repository's compatibility hash algorithm. */
 	const struct git_hash_algo *compat_hash_algo;
 
+	/* Repository's config values parsed by git_default_config() */
+	struct repo_config_values config_values;
+
 	/* Repository's reference storage format, as serialized on disk. */
 	enum ref_storage_format ref_storage_format;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v5 2/3] environment: stop using core.sparseCheckout globally
  2026-01-24 12:21       ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Olamide Caleb Bello
  2026-01-24 12:21         ` [Outreachy PATCH v5 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-01-24 12:21         ` Olamide Caleb Bello
  2026-01-29 18:12           ` Junio C Hamano
  2026-01-24 12:21         ` [Outreachy PATCH v5 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
                           ` (3 subsequent siblings)
  5 siblings, 1 reply; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-24 12:21 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The config value `core.sparseCheckout` is parsed in
`git_default_core_config()` and stored globally in
`core_apply_sparse_checkout`. This could cause it to be overwritten
by another repository when different Git repositories run in the same
process.

Move the parsed value into `struct repo_config_values` in the_repository
to retain current behaviours and move towards libifying Git.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
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/backfill.c        |  2 +-
 builtin/clone.c           |  2 +-
 builtin/grep.c            |  2 +-
 builtin/mv.c              |  2 +-
 builtin/sparse-checkout.c | 23 ++++++++++++-----------
 builtin/worktree.c        |  2 +-
 dir.c                     |  2 +-
 environment.c             |  4 ++--
 environment.h             |  2 +-
 sparse-index.c            |  6 ++++--
 unpack-trees.c            |  2 +-
 wt-status.c               |  3 ++-
 12 files changed, 28 insertions(+), 24 deletions(-)

diff --git a/builtin/backfill.c b/builtin/backfill.c
index e80fc1b694..d8cc13aaa4 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 	repo_config(repo, git_default_config, NULL);
 
 	if (ctx.sparse < 0)
-		ctx.sparse = core_apply_sparse_checkout;
+		ctx.sparse = the_repository->config_values.apply_sparse_checkout;
 
 	result = do_backfill(&ctx);
 	backfill_context_clear(&ctx);
diff --git a/builtin/clone.c b/builtin/clone.c
index b19b302b06..ee0736b634 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)
 	 * We must apply the setting in the current process
 	 * for the later checkout to use the sparse-checkout file.
 	 */
-	core_apply_sparse_checkout = 1;
+	the_repository->config_values.apply_sparse_checkout = 1;
 
 	cmd.git_cmd = 1;
 	if (run_command(&cmd)) {
diff --git a/builtin/grep.c b/builtin/grep.c
index 53cccf2d25..4b5a96703a 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,
 	 *	"forget" the sparse-index feature switch. As a result, the index
 	 *	of these submodules are expanded unexpectedly.
 	 *
-	 * 2. "core_apply_sparse_checkout"
+	 * 2. "config_values.apply_sparse_checkout"
 	 *	When running `grep` in the superproject, this setting is
 	 *	populated using the superproject's configs. However, once
 	 *	initialized, this config is globally accessible and is read by
diff --git a/builtin/mv.c b/builtin/mv.c
index d43925097b..29801f5fe7 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -572,7 +572,7 @@ int cmd_mv(int argc,
 		rename_index_entry_at(the_repository->index, pos, dst);
 
 		if (ignore_sparse &&
-		    core_apply_sparse_checkout &&
+		    the_repository->config_values.apply_sparse_checkout &&
 		    core_sparse_checkout_cone) {
 			/*
 			 * NEEDSWORK: we are *not* paying attention to
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 15d51e60a8..cfd107db5e 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
 	int res;
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.apply_sparse_checkout)
 		die(_("this worktree is not sparse"));
 
 	argc = parse_options(argc, argv, prefix,
@@ -400,11 +400,11 @@ static int set_config(struct repository *repo,
 
 static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
 	/* If not specified, use previous definition of cone mode */
-	if (*cone_mode == -1 && core_apply_sparse_checkout)
+	if (*cone_mode == -1 && the_repository->config_values.apply_sparse_checkout)
 		*cone_mode = core_sparse_checkout_cone;
 
 	/* Set cone/non-cone mode appropriately */
-	core_apply_sparse_checkout = 1;
+	the_repository->config_values.apply_sparse_checkout = 1;
 	if (*cone_mode == 1 || *cone_mode == -1) {
 		core_sparse_checkout_cone = 1;
 		return MODE_CONE_PATTERNS;
@@ -418,7 +418,8 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind
 	int mode, record_mode;
 
 	/* Determine if we need to record the mode; ensure sparse checkout on */
-	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+	record_mode = (*cone_mode != -1) ||
+		      !the_repository->config_values.apply_sparse_checkout;
 
 	mode = update_cone_mode(cone_mode);
 	if (record_mode && set_config(repo, mode))
@@ -699,9 +700,9 @@ static int modify_pattern_list(struct repository *repo,
 		break;
 	}
 
-	if (!core_apply_sparse_checkout) {
+	if (!the_repository->config_values.apply_sparse_checkout) {
 		set_config(repo, MODE_ALL_PATTERNS);
-		core_apply_sparse_checkout = 1;
+		the_repository->config_values.apply_sparse_checkout = 1;
 		changed_config = 1;
 	}
 
@@ -798,7 +799,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
 	int ret;
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.apply_sparse_checkout)
 		die(_("no sparse-checkout to add to"));
 
 	repo_read_index(repo);
@@ -907,7 +908,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.apply_sparse_checkout)
 		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
 
 	reapply_opts.cone_mode = -1;
@@ -969,7 +970,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.apply_sparse_checkout)
 		die(_("must be in a sparse-checkout to clean directories"));
 	if (!core_sparse_checkout_cone)
 		die(_("must be in a cone-mode sparse-checkout to clean directories"));
@@ -1035,7 +1036,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	struct pattern_list pl;
 
 	/*
-	 * We do not exit early if !core_apply_sparse_checkout; due to the
+	 * We do not exit early if !repo->config_values.apply_sparse_checkout; due to the
 	 * ability for users to manually muck things up between
 	 *   direct editing of .git/info/sparse-checkout
 	 *   running read-tree -m u HEAD or update-index --skip-worktree
@@ -1061,7 +1062,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
 	hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
 	pl.use_cone_patterns = 0;
-	core_apply_sparse_checkout = 1;
+	the_repository->config_values.apply_sparse_checkout = 1;
 
 	add_pattern("/*", empty_base, 0, &pl, 0);
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index fbdaf2eb2e..4b0c3e09a9 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,
 	 * If the current worktree has sparse-checkout enabled, then copy
 	 * the sparse-checkout patterns from the current worktree.
 	 */
-	if (core_apply_sparse_checkout)
+	if (the_repository->config_values.apply_sparse_checkout)
 		copy_sparse_checkout(sb_repo.buf);
 
 	/*
diff --git a/dir.c b/dir.c
index b00821f294..bc59d8cd6a 100644
--- a/dir.c
+++ b/dir.c
@@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(
 
 int init_sparse_checkout_patterns(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout)
+	if (!the_repository->config_values.apply_sparse_checkout)
 		return 1;
 	if (istate->sparse_checkout_patterns)
 		return 0;
diff --git a/environment.c b/environment.c
index 72735d9e4b..269cac6d6e 100644
--- a/environment.c
+++ b/environment.c
@@ -74,7 +74,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_apply_sparse_checkout;
 int core_sparse_checkout_cone;
 int sparse_expect_files_outside_of_patterns;
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.sparsecheckout")) {
-		core_apply_sparse_checkout = git_config_bool(var, value);
+		cfg->apply_sparse_checkout = git_config_bool(var, value);
 		return 0;
 	}
 
@@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,
 void repo_config_values_init(struct repo_config_values *cfg)
 {
 	cfg->attributes_file = NULL;
+	cfg->apply_sparse_checkout = 0;
 }
diff --git a/environment.h b/environment.h
index 0c0dcc6847..ddce69c6ba 100644
--- a/environment.h
+++ b/environment.h
@@ -87,6 +87,7 @@ struct strvec;
 struct repo_config_values {
 	/* section "core" config values */
 	char *attributes_file;
+	int apply_sparse_checkout;
 };
 
 /*
@@ -168,7 +169,6 @@ extern int precomposed_unicode;
 extern int protect_hfs;
 extern int protect_ntfs;
 
-extern int core_apply_sparse_checkout;
 extern int core_sparse_checkout_cone;
 extern int sparse_expect_files_outside_of_patterns;
 
diff --git a/sparse-index.c b/sparse-index.c
index 76f90da5f5..f00c601986 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)
 
 int is_sparse_index_allowed(struct index_state *istate, int flags)
 {
-	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
+	struct repo_config_values *cfg = &the_repository->config_values;
+	if (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)
 		return 0;
 
 	if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
@@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
 
 void clear_skip_worktree_from_present_files(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout ||
+	struct repo_config_values *cfg = &the_repository->config_values;
+	if (!cfg->apply_sparse_checkout ||
 	    sparse_expect_files_outside_of_patterns)
 		return;
 
diff --git a/unpack-trees.c b/unpack-trees.c
index f38c761ab9..a2f4d568da 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 	if (o->prefix)
 		update_sparsity_for_prefix(o->prefix, o->src_index);
 
-	if (!core_apply_sparse_checkout || !o->update)
+	if (!the_repository->config_values.apply_sparse_checkout || !o->update)
 		o->skip_sparse_checkout = 1;
 	if (!o->skip_sparse_checkout) {
 		memset(&pl, 0, sizeof(pl));
diff --git a/wt-status.c b/wt-status.c
index e12adb26b9..4c66b47052 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1764,7 +1764,8 @@ static void wt_status_check_sparse_checkout(struct repository *r,
 	int skip_worktree = 0;
 	int i;
 
-	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
+	if (!the_repository->config_values.apply_sparse_checkout ||
+	    r->index->cache_nr == 0) {
 		/*
 		 * Don't compute percentage of checked out files if we
 		 * aren't in a sparse checkout or would get division by 0.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v5 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-01-24 12:21       ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Olamide Caleb Bello
  2026-01-24 12:21         ` [Outreachy PATCH v5 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
  2026-01-24 12:21         ` [Outreachy PATCH v5 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-01-24 12:21         ` Olamide Caleb Bello
  2026-01-29 18:37           ` Junio C Hamano
  2026-01-29  8:29         ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Bello Olamide
                           ` (2 subsequent siblings)
  5 siblings, 1 reply; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-01-24 12:21 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The config value `branch.autoSetupMerge` is parsed in
`git_default_branch_config()` and stored in the global variable
`git_branch_track`. This global variable can be overwritten
by another repository when multiple Git repos run in the the same process.

Move this value into `struct repo_config_values` in the_repository to
retain current behaviours and move towards libifying Git.
Since the variable is no longer a global variable, it has been renamed to
`branch_track` in the struct `repo_config_values`.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 branch.h                    |  2 --
 builtin/branch.c            |  2 +-
 builtin/checkout.c          |  2 +-
 builtin/push.c              |  2 +-
 builtin/submodule--helper.c |  2 +-
 environment.c               | 12 +++++++-----
 environment.h               |  4 ++++
 7 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/branch.h b/branch.h
index ec2f35fda4..3dc6e2a0ff 100644
--- a/branch.h
+++ b/branch.h
@@ -15,8 +15,6 @@ enum branch_track {
 	BRANCH_TRACK_SIMPLE,
 };
 
-extern enum branch_track git_branch_track;
-
 /* Functions for acting on the information about branches. */
 
 /**
diff --git a/builtin/branch.c b/builtin/branch.c
index c577b5d20f..7d27951a7e 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -795,7 +795,7 @@ int cmd_branch(int argc,
 	if (!sorting_options.nr)
 		string_list_append(&sorting_options, "refname");
 
-	track = git_branch_track;
+	track = the_repository->config_values.branch_track;
 
 	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
 				   0, &head_oid, NULL);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 261699e2f5..ba6fea9aee 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1631,7 +1631,7 @@ static int checkout_branch(struct checkout_opts *opts,
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
 	} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
-		opts->track = git_branch_track;
+		opts->track = the_repository->config_values.branch_track;
 
 	if (new_branch_info->name && !new_branch_info->commit)
 		die(_("Cannot switch branch to a non-commit '%s'"),
diff --git a/builtin/push.c b/builtin/push.c
index 5b6cebbb85..7be20a1035 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -162,7 +162,7 @@ static NORETURN void die_push_simple(struct branch *branch,
 		advice_pushdefault_maybe = _("\n"
 				 "To choose either option permanently, "
 				 "see push.default in 'git help config'.\n");
-	if (git_branch_track != BRANCH_TRACK_SIMPLE)
+	if (the_repository->config_values.branch_track != BRANCH_TRACK_SIMPLE)
 		advice_automergesimple_maybe = _("\n"
 				 "To avoid automatically configuring "
 				 "an upstream branch when its name\n"
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d537ab087a..f2b6f027d7 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3128,7 +3128,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,
 	};
 
 	repo_config(the_repository, git_default_config, NULL);
-	track = git_branch_track;
+	track = the_repository->config_values.branch_track;
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
 	if (argc != 3)
diff --git a/environment.c b/environment.c
index 269cac6d6e..de8721657e 100644
--- a/environment.c
+++ b/environment.c
@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 enum eol core_eol = EOL_UNSET;
 int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
 char *check_roundtrip_encoding;
-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #ifndef OBJECT_CREATION_MODE
@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)
 
 static int git_default_branch_config(const char *var, const char *value)
 {
+	struct repo_config_values *cfg = &the_repository->config_values;
+
 	if (!strcmp(var, "branch.autosetupmerge")) {
 		if (value && !strcmp(value, "always")) {
-			git_branch_track = BRANCH_TRACK_ALWAYS;
+			cfg->branch_track = BRANCH_TRACK_ALWAYS;
 			return 0;
 		} else if (value && !strcmp(value, "inherit")) {
-			git_branch_track = BRANCH_TRACK_INHERIT;
+			cfg->branch_track = BRANCH_TRACK_INHERIT;
 			return 0;
 		} else if (value && !strcmp(value, "simple")) {
-			git_branch_track = BRANCH_TRACK_SIMPLE;
+			cfg->branch_track = BRANCH_TRACK_SIMPLE;
 			return 0;
 		}
-		git_branch_track = git_config_bool(var, value);
+		cfg->branch_track = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "branch.autosetuprebase")) {
@@ -761,4 +762,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;
 }
diff --git a/environment.h b/environment.h
index ddce69c6ba..27161d56ab 100644
--- a/environment.h
+++ b/environment.h
@@ -2,6 +2,7 @@
 #define ENVIRONMENT_H
 
 #include "repo-settings.h"
+#include "branch.h"
 
 /* Double-check local_repo_env below if you add to this list. */
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
@@ -88,6 +89,9 @@ struct repo_config_values {
 	/* section "core" config values */
 	char *attributes_file;
 	int apply_sparse_checkout;
+
+	/* section "branch" config values */
+	enum branch_track branch_track;
 };
 
 /*
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v5 0/3] store repo specific config values in new `struct repo_config_values`
  2026-01-24 12:21       ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Olamide Caleb Bello
                           ` (2 preceding siblings ...)
  2026-01-24 12:21         ` [Outreachy PATCH v5 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
@ 2026-01-29  8:29         ` Bello Olamide
  2026-02-03 15:42         ` [Outreachy PATCH v6 " Olamide Caleb Bello
  2026-02-16 16:38         ` [Outreachy PATCH v7 " Olamide Caleb Bello
  5 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-01-29  8:29 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188

On Sat, 24 Jan 2026 at 13:21, Olamide Caleb Bello <belkid98@gmail.com> wrote:
>
> Hi Git Community,
> Over the course of my ongoing internship, which focused on moving repo specific
> global variables in environment.h into local scope, I have attempted to move some
> variables into the struct repo-settings.
> However there has been some design concerns as regards the use of
> `prepare_repo_settings()` with respect to when and where to call the
> function, and also the change in behaviours when the variable is lazily
> loaded as discussed in [1] and [2].
>
> After different deliberations, Phillip Wood proposed creating a new config
> struct [3], adding it to the repository struct and passing the repo struct to
> `git_default_config()` to store parsed repo specific config values per repo.
> This ensures the current behaviours will be retained.
>
> I have experimented with this approach for some values and I would
> appreciate feedbacks about this approach before we can move forward
> and use it for more variables related to `git_default_config()`.
>
> For now, the parsed value is stored in `the_repository` in
> `git_default_*_config()` and further efforts to pass the repository
> parameter to `git_default_config()` as the callback parameter will
> be looked into later on.
> The link to the CI tests can be see in [4]
>
> 1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/
> 2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/
> 3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/
> 4. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2266020513
>
> Changes in v5:
> ==============
> - Corrected mistake in commit message of patch 3 which spelt branch
>   wrongly in `branch.autoSetupmerge`

Hello,
Just a gentle nudge for a review
Thanks

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v5 1/3] environment: stop storing `core.attributesFile` globally
  2026-01-24 12:21         ` [Outreachy PATCH v5 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-01-29 18:01           ` Junio C Hamano
  0 siblings, 0 replies; 79+ messages in thread
From: Junio C Hamano @ 2026-01-29 18:01 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, toon, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Olamide Caleb Bello <belkid98@gmail.com> writes:

>  const char *git_attr_global_file(void)
>  {
> -	if (!git_attributes_file)
> -		git_attributes_file = xdg_config_home("attributes");
> +	struct repo_config_values *cfg = &the_repository->config_values;

Here, the_repository, being defined in repository.c as the address
of a singleton "the_repo" instance, cannot be NULL even outside a
repository, so taking the address of its config_values member is
always safe.  OK.

> +	if (!cfg->attributes_file)
> +		cfg->attributes_file = xdg_config_home("attributes");
>  
> -	return git_attributes_file;
> +	return cfg->attributes_file;
>  }

> diff --git a/repository.h b/repository.h
> index 6063c4b846..638a142577 100644
> --- a/repository.h
> +++ b/repository.h
> @@ -3,6 +3,7 @@
>  
>  #include "strmap.h"
>  #include "repo-settings.h"
> +#include "environment.h"
>  
>  struct config_set;
>  struct git_hash_algo;
> @@ -148,6 +149,9 @@ struct repository {
>  	/* Repository's compatibility hash algorithm. */
>  	const struct git_hash_algo *compat_hash_algo;
>  
> +	/* Repository's config values parsed by git_default_config() */
> +	struct repo_config_values config_values;
> +
>  	/* Repository's reference storage format, as serialized on disk. */
>  	enum ref_storage_format ref_storage_format;

And because this new config_values member is directly embedded in
the repository structure, and "the_repo" instance is a global in
BSS, its members are initialized exactly the same way as the
global variables like git_attributes_file were initialized.  Good.


> diff --git a/environment.c b/environment.c
> index a770b5921d..72735d9e4b 100644
> --- a/environment.c
> +++ b/environment.c
> @@ -53,7 +53,6 @@ char *git_commit_encoding;
>  char *git_log_output_encoding;
>  char *apply_default_whitespace;
>  char *apply_default_ignorewhitespace;
> -char *git_attributes_file;

And we lose this global, that used to be zero-initialized for being
in BSS.

> @@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
>  static int git_default_core_config(const char *var, const char *value,
>  				   const struct config_context *ctx, void *cb)
>  {
> +	struct repo_config_values *cfg = &the_repository->config_values;
> +
>  	/* This needs a better name */
>  	if (!strcmp(var, "core.filemode")) {
>  		trust_executable_bit = git_config_bool(var, value);
> @@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,
>  	}
>  
>  	if (!strcmp(var, "core.attributesfile")) {
> -		FREE_AND_NULL(git_attributes_file);
> -		return git_config_pathname(&git_attributes_file, var, value);
> +		FREE_AND_NULL(cfg->attributes_file);
> +		return git_config_pathname(&cfg->attributes_file, var, value);
>  	}
>  
>  	if (!strcmp(var, "core.bare")) {
> @@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,
>  	/* Add other config variables here and to Documentation/config.adoc. */
>  	return 0;
>  }

And instead of assigning to the global git_attributes_file, we
assign to the config_values.attributes_file member via the global
"the_repository".  No functional changes.  OK.

> +void repo_config_values_init(struct repo_config_values *cfg)
> +{
> +	cfg->attributes_file = NULL;
> +}

This is not strictly needed, as git_attributes_file is left to be
zero-initialized for being in BSS; its replacement, i.e.,
the_repo.config_values.attributes_file, will be zero-initialized the
same way.

But other members we may want add later to the struct may need a
place to initialize them.  Or we can do a static initialization for
the_repo in repository.c then we do not have to have this function
and we do not have to call it.  Either would work fine, as long as
everybody calls initialize_repository() function, which is the only
caller of this helper.

> diff --git a/environment.h b/environment.h
> index 51898c99cd..0c0dcc6847 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -84,6 +84,11 @@ extern const char * const local_repo_env[];
>  
>  struct strvec;
>  
> +struct repo_config_values {
> +	/* section "core" config values */
> +	char *attributes_file;
> +};

OK.

> diff --git a/repository.c b/repository.c
> index c7e75215ac..d308cd78bf 100644
> --- a/repository.c
> +++ b/repository.c
> @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)
>  	ALLOC_ARRAY(repo->index, 1);
>  	index_state_init(repo->index, repo);
>  	repo->check_deprecated_config = true;
> +	repo_config_values_init(&repo->config_values);
>  
>  	/*
>  	 * When a command runs inside a repository, it learns what

Continuing the discussion on repo_config_values_init(), currently,
initialize_repository() is called by init_git(), which is called
from "main()", so it should be fairly safe to assume that all in Git
codebase will call repo_config_values_init().

But those who replace "main()" for their own libified use of Git
code may not call init_git() hence initialize_repository() hence
your repo_config_values_init().  In that sense, this is less safe
than the other alternative.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v5 2/3] environment: stop using core.sparseCheckout globally
  2026-01-24 12:21         ` [Outreachy PATCH v5 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-01-29 18:12           ` Junio C Hamano
  0 siblings, 0 replies; 79+ messages in thread
From: Junio C Hamano @ 2026-01-29 18:12 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, toon, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Olamide Caleb Bello <belkid98@gmail.com> writes:

> The config value `core.sparseCheckout` is parsed in
> `git_default_core_config()` and stored globally in
> `core_apply_sparse_checkout`. This could cause it to be overwritten
> by another repository when different Git repositories run in the same
> process.
>
> Move the parsed value into `struct repo_config_values` in the_repository
> to retain current behaviours and move towards libifying Git.
>
> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
> ---

A lot of changes, almost all of which are mechanical

    s/core_apply_sparse_checkout/the_repository->config_values.apply_sparse_checkout/ 

changes, which is very straight-forward.

The same comment applies.  Do we want programs to require calling
repo_config_values_init(), or should we rely on program load-time
initialization, like all these globals that are moved to the
repo_config_values struct used to do?

I do not have a good answer to this question, but so far, these two
global variables that were both zero initialized for being in BSS do
not *need* initialization byh an explicit runtime assignment that
repo_config_values_init() allows us to do.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v5 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-01-24 12:21         ` [Outreachy PATCH v5 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
@ 2026-01-29 18:37           ` Junio C Hamano
  2026-01-30 16:20             ` Junio C Hamano
  0 siblings, 1 reply; 79+ messages in thread
From: Junio C Hamano @ 2026-01-29 18:37 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, toon, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Olamide Caleb Bello <belkid98@gmail.com> writes:

> -enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;

Unlike the other two, this global variable was not zero-initialized,
so we can do something like this to initialize it statically in the
new world order.  That way, we do not need to worry about the
repo_config_values_init() helper function, which (1) we can easily
forget to adjust, and (2) third-party may not be able to call.



diff --git i/repository.c w/repository.c
index d308cd78bf..b540e2ba01 100644
--- i/repository.c
+++ w/repository.c
@@ -25,7 +25,11 @@
 extern struct repository *the_repository;
 
 /* The main repository */
-static struct repository the_repo;
+static struct repository the_repo = {
+	.config_values = {
+		.branch_track = BRANCH_TRACK_REMOTE,
+	},
+};
 struct repository *the_repository = &the_repo;
 
 /*







>  enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
>  enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
>  #ifndef OBJECT_CREATION_MODE
> @@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)
>  
>  static int git_default_branch_config(const char *var, const char *value)
>  {
> +	struct repo_config_values *cfg = &the_repository->config_values;
> +
>  	if (!strcmp(var, "branch.autosetupmerge")) {
>  		if (value && !strcmp(value, "always")) {
> -			git_branch_track = BRANCH_TRACK_ALWAYS;
> +			cfg->branch_track = BRANCH_TRACK_ALWAYS;
>  			return 0;
>  		} else if (value && !strcmp(value, "inherit")) {
> -			git_branch_track = BRANCH_TRACK_INHERIT;
> +			cfg->branch_track = BRANCH_TRACK_INHERIT;
>  			return 0;
>  		} else if (value && !strcmp(value, "simple")) {
> -			git_branch_track = BRANCH_TRACK_SIMPLE;
> +			cfg->branch_track = BRANCH_TRACK_SIMPLE;
>  			return 0;
>  		}
> -		git_branch_track = git_config_bool(var, value);
> +		cfg->branch_track = git_config_bool(var, value);
>  		return 0;
>  	}
>  	if (!strcmp(var, "branch.autosetuprebase")) {
> @@ -761,4 +762,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;
>  }
> diff --git a/environment.h b/environment.h
> index ddce69c6ba..27161d56ab 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -2,6 +2,7 @@
>  #define ENVIRONMENT_H
>  
>  #include "repo-settings.h"
> +#include "branch.h"
>  
>  /* Double-check local_repo_env below if you add to this list. */
>  #define GIT_DIR_ENVIRONMENT "GIT_DIR"
> @@ -88,6 +89,9 @@ struct repo_config_values {
>  	/* section "core" config values */
>  	char *attributes_file;
>  	int apply_sparse_checkout;
> +
> +	/* section "branch" config values */
> +	enum branch_track branch_track;
>  };
>  
>  /*

^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v5 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-01-29 18:37           ` Junio C Hamano
@ 2026-01-30 16:20             ` Junio C Hamano
  2026-01-30 20:15               ` Junio C Hamano
  0 siblings, 1 reply; 79+ messages in thread
From: Junio C Hamano @ 2026-01-30 16:20 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, toon, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Junio C Hamano <gitster@pobox.com> writes:

> Olamide Caleb Bello <belkid98@gmail.com> writes:
>
>> -enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
>
> Unlike the other two, this global variable was not zero-initialized,
> so we can do something like this to initialize it statically in the
> new world order.  That way, we do not need to worry about the
> repo_config_values_init() helper function, which (1) we can easily
> forget to adjust, and (2) third-party may not be able to call.
>
> diff --git i/repository.c w/repository.c
> index d308cd78bf..b540e2ba01 100644
> --- i/repository.c
> +++ w/repository.c
> @@ -25,7 +25,11 @@
>  extern struct repository *the_repository;
>  
>  /* The main repository */
> -static struct repository the_repo;
> +static struct repository the_repo = {
> +	.config_values = {
> +		.branch_track = BRANCH_TRACK_REMOTE,
> +	},
> +};

After having slept over this one, I think a static initialization,
which mimicks what we have been doing with the global variables very
closely, is not what we want in the longer term.

The aim of these patches is to prepare for a new world order in
which we can add new Git subcommands that operates in two or more
repositories at the same time, and have these repositories use their
own versions of these "global" variables independently.  While the
static initialization has these advantages:

 * Once the code is written, no new command can forget to initialize
   them; initialization happens at the program load time.

 * No programming error can wipe what has already been read from the
   configuration by making a second repo_config_values_init() call
   by mistake.

neither of these advantages apply to second and subsequent
repositories.  We'd need to somehow initialize an instance of a
repository that is not "the_repo".

And for that, repo_config_values_init() is needed, even though it
adds the downsides that are opposite of the above two advantages of
not having to have an "initialization" step.

So, we need to take it as a given that repo_config_values_init()
needs to exist.  Under that condition, I wonder if we can somehow
have a cheap way to assert the following two things:

 * Before a repository instance is used, repo_config_values_init()
   has been called on it, as using an instance without initializing
   is a no-no.

 * repo_config_values_init() is never called twice on a repository
   instance, as the second call will wipe what the first call and
   subsequent reading of the configuration files have done.

Thanks.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v5 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-01-30 16:20             ` Junio C Hamano
@ 2026-01-30 20:15               ` Junio C Hamano
  0 siblings, 0 replies; 79+ messages in thread
From: Junio C Hamano @ 2026-01-30 20:15 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, toon, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Junio C Hamano <gitster@pobox.com> writes:

> So, we need to take it as a given that repo_config_values_init()
> needs to exist.  Under that condition, I wonder if we can somehow
> have a cheap way to assert the following two things:
>
>  * Before a repository instance is used, repo_config_values_init()
>    has been called on it, as using an instance without initializing
>    is a no-no.
>
>  * repo_config_values_init() is never called twice on a repository
>    instance, as the second call will wipe what the first call and
>    subsequent reading of the configuration files have done.

Something like this squashed into your [1/3] would give us these two
assertions, but I am not sure if it is a good idea or if I am overly
paranoid.

The main ideas are:

 * "struct repository" now knows if it has been initialized via its
   "bool initialized" member.

 * "initialize_repository()" detects double initialization of the
   repository struct itself.

 * "repo_config_values" member in "struct repository" has been
   renamed to make it clear it is "private", and there is an
   accessor function of the same name.  It barfs if you ask the
   address of repo_config_values in a repository instance that
   hasn't been initialized.

 * Any code outside what implement the above are supposed to call
   repo_config_values() on the repository they are working in, to
   request the address of the repo_config_values instance to use.

It didn't barf when I ran all the tests, which means there isn't
anybody who calls initialize_repository() twice in the current code.

I am not sure if this is being overly paranoid, or exercising a
reasonable caution, but anyway,...


 attr.c        |  3 ++-
 environment.c |  2 +-
 environment.h |  3 +++
 repository.c  | 13 ++++++++++++-
 repository.h  |  5 ++++-
 5 files changed, 22 insertions(+), 4 deletions(-)

diff --git c/attr.c w/attr.c
index b8b70e6dce..cd39e6d2bf 100644
--- c/attr.c
+++ w/attr.c
@@ -881,7 +881,8 @@ const char *git_attr_system_file(void)
 
 const char *git_attr_global_file(void)
 {
-	struct repo_config_values *cfg = &the_repository->config_values;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+
 	if (!cfg->attributes_file)
 		cfg->attributes_file = xdg_config_home("attributes");
 
diff --git c/environment.c w/environment.c
index c876589b05..208a52ce11 100644
--- c/environment.c
+++ w/environment.c
@@ -302,7 +302,7 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
 int git_default_core_config(const char *var, const char *value,
 			    const struct config_context *ctx, void *cb)
 {
-	struct repo_config_values *cfg = &the_repository->config_values;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
diff --git c/environment.h w/environment.h
index 2b861a61de..254fec6b7c 100644
--- c/environment.h
+++ w/environment.h
@@ -84,11 +84,14 @@ extern const char * const local_repo_env[];
 
 struct strvec;
 
+struct repository;
 struct repo_config_values {
 	/* section "core" config values */
 	char *attributes_file;
 };
 
+struct repo_config_values *repo_config_values(struct repository *);
+
 /*
  * Wrapper of getenv() that returns a strdup value. This value is kept
  * in argv to be freed later.
diff --git c/repository.c w/repository.c
index d308cd78bf..4cb487b5b2 100644
--- c/repository.c
+++ w/repository.c
@@ -50,14 +50,25 @@ static void set_default_hash_algo(struct repository *repo)
 	repo_set_hash_algo(repo, algo);
 }
 
+struct repo_config_values *repo_config_values(struct repository *repo)
+{
+	if (!repo->initialized)
+		BUG("config values from uninitialied repository?");
+	return &repo->config_values_private_;
+}
+
 void initialize_repository(struct repository *repo)
 {
+	if (repo->initialized)
+		BUG("repository initialized already!");
+	repo->initialized = true;
+
 	repo->remote_state = remote_state_new();
 	repo->parsed_objects = parsed_object_pool_new(repo);
 	ALLOC_ARRAY(repo->index, 1);
 	index_state_init(repo->index, repo);
 	repo->check_deprecated_config = true;
-	repo_config_values_init(&repo->config_values);
+	repo_config_values_init(repo_config_values(repo));
 
 	/*
 	 * When a command runs inside a repository, it learns what
diff --git c/repository.h w/repository.h
index 638a142577..9717e45000 100644
--- c/repository.h
+++ w/repository.h
@@ -150,7 +150,7 @@ struct repository {
 	const struct git_hash_algo *compat_hash_algo;
 
 	/* Repository's config values parsed by git_default_config() */
-	struct repo_config_values config_values;
+	struct repo_config_values config_values_private_;
 
 	/* Repository's reference storage format, as serialized on disk. */
 	enum ref_storage_format ref_storage_format;
@@ -175,6 +175,9 @@ struct repository {
 
 	/* Should repo_config() check for deprecated settings */
 	bool check_deprecated_config;
+
+	/* Has this repository instance been initialized? */
+	bool initialized;
 };
 
 #ifdef USE_THE_REPOSITORY_VARIABLE

^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v6 0/3] store repo specific config values in new `struct repo_config_values`
  2026-01-24 12:21       ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Olamide Caleb Bello
                           ` (3 preceding siblings ...)
  2026-01-29  8:29         ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Bello Olamide
@ 2026-02-03 15:42         ` Olamide Caleb Bello
  2026-02-03 15:42           ` [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
                             ` (3 more replies)
  2026-02-16 16:38         ` [Outreachy PATCH v7 " Olamide Caleb Bello
  5 siblings, 4 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-02-03 15:42 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

Hi Git Community,
Over the course of my ongoing internship, which focused on moving repo specific
global variables in environment.h into local scope, I have attempted to move some
variables into the struct repo-settings.
However there has been some design concerns as regards the use of
`prepare_repo_settings()` with respect to when and where to call the
function, and also the change in behaviours when the variable is lazily
loaded as discussed in [1] and [2].

After different deliberations, Phillip Wood proposed creating a new config
struct [3], adding it to the repository struct and passing the repo struct to
`git_default_config()` to store parsed repo specific config values per repo.
This ensures the current behaviours will be retained.

I have experimented with this approach for some values and I would
appreciate feedbacks about this approach before we can move forward
and use it for more variables related to `git_default_config()`.

For now, the parsed value is stored in `the_repository` in
`git_default_*_config()` and further efforts to pass the repository
parameter to `git_default_config()` as the callback parameter will
be looked into later on.
The link to the CI tests can be see in [4] and [5]
in the CI, some BUGs were gotten as a result of double initialization

1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/
2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/
3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com
4. https://github.com/git/git/actions/runs/21633462506/job/62352191148?pr=2166
5. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2303203992

Changes in v6:
==============
- Added a new accessor function 'repo_config_values()' which ensures we detect
  if a repository has been initialized already
- Changed the member name to 'config_values_private_' to indicate this member
  is private
- Modified commit message of patch 1 to reflect these changes

Olamide Caleb Bello (3):
  environment: stop storing `core.attributesFile` globally
  environment: stop using core.sparseCheckout globally
  environment: move "branch.autoSetupMerge" into `struct
    repo_config_values`

 attr.c                      |  7 ++++---
 branch.h                    |  2 --
 builtin/backfill.c          |  3 ++-
 builtin/branch.c            |  3 ++-
 builtin/checkout.c          |  3 ++-
 builtin/clone.c             |  3 ++-
 builtin/grep.c              |  2 +-
 builtin/mv.c                |  3 ++-
 builtin/push.c              |  3 ++-
 builtin/sparse-checkout.c   | 31 ++++++++++++++++++++-----------
 builtin/submodule--helper.c |  3 ++-
 builtin/worktree.c          |  3 ++-
 dir.c                       |  3 ++-
 environment.c               | 28 ++++++++++++++++++----------
 environment.h               | 17 +++++++++++++++--
 repository.c                | 12 ++++++++++++
 repository.h                |  7 +++++++
 sparse-index.c              |  6 ++++--
 unpack-trees.c              |  3 ++-
 wt-status.c                 |  4 +++-
 20 files changed, 104 insertions(+), 42 deletions(-)

 Range diff versus v5:
 =====================

 1:  d28850bcdb ! 1:  7e3082125d environment: stop storing `core.attributesFile` globally
    @@ Commit message

         Create a new struct `repo_config_values` to hold this value and
         other repository dependent values parsed by `git_default_config()`.
    -    For now the value can be accessed via the_repository in
    -    `git_default_config()`.
         This will ensure the current behaviour remains the same while also
         enabling the libification of Git.

    +    An accessor function 'repo_config_values()' is created and used to access
    +    the new struct member of the repository struct.
    +    This is to ensure that we detect if the struct repository has been
    +    initialized and also prevent double initialization of the repository.
    +
         It is important to note that `git_default_config()` is a wrapper to other
         `git_default_*_config()` functions such as `git_default_core_config()`.
         Therefore to access and modify this global variable,
    @@ Commit message
         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
         Mentored-by: Christian Couder <christian.couder@gmail.com>
         Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
    +    Helped-by: Junio C Hamano <gitster@pobox.com>
         Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>

      ## attr.c ##
    @@ attr.c: const char *git_attr_system_file(void)
      {
     -	if (!git_attributes_file)
     -		git_attributes_file = xdg_config_home("attributes");
    -+	struct repo_config_values *cfg = &the_repository->config_values;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
     +	if (!cfg->attributes_file)
     +		cfg->attributes_file = xdg_config_home("attributes");

    @@ environment.c: static enum fsync_component parse_fsync_components(const char *va
      static int git_default_core_config(const char *var, const char *value,
      				   const struct config_context *ctx, void *cb)
      {
    -+	struct repo_config_values *cfg = &the_repository->config_values;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
     +
      	/* This needs a better name */
      	if (!strcmp(var, "core.filemode")) {
    @@ environment.h: extern const char * const local_repo_env[];

      struct strvec;

    ++struct repository;
     +struct repo_config_values {
     +	/* section "core" config values */
     +	char *attributes_file;
     +};
    ++
    ++struct repo_config_values *repo_config_values(struct repository *repo);
     +
      /*
       * Wrapper of getenv() that returns a strdup value. This value is kept
    @@ environment.h: extern int assume_unchanged;
      extern unsigned long pack_size_limit_cfg;

      ## repository.c ##
    -@@ repository.c: void initialize_repository(struct repository *repo)
    +@@ repository.c: static void set_default_hash_algo(struct repository *repo)
    + 	repo_set_hash_algo(repo, algo);
    + }
    +
    ++struct repo_config_values *repo_config_values(struct repository *repo)
    ++{
    ++	if(!repo->initialized)
    ++		BUG("config values from uninitialized repository");
    ++	return &repo->config_values_private_;
    ++}
    ++
    + void initialize_repository(struct repository *repo)
    + {
    ++	if (repo->initialized)
    ++		BUG("repository initialized already");
    ++	repo->initialized = true;
    ++
    + 	repo->remote_state = remote_state_new();
    + 	repo->parsed_objects = parsed_object_pool_new(repo);
      	ALLOC_ARRAY(repo->index, 1);
      	index_state_init(repo->index, repo);
      	repo->check_deprecated_config = true;
    -+	repo_config_values_init(&repo->config_values);
    ++	repo_config_values_init(repo_config_values(repo));

      	/*
      	 * When a command runs inside a repository, it learns what
    @@ repository.h: struct repository {
      	const struct git_hash_algo *compat_hash_algo;

     +	/* Repository's config values parsed by git_default_config() */
    -+	struct repo_config_values config_values;
    ++	struct repo_config_values config_values_private_;
     +
      	/* Repository's reference storage format, as serialized on disk. */
      	enum ref_storage_format ref_storage_format;

    +@@ repository.h: struct repository {
    +
    + 	/* Should repo_config() check for deprecated settings */
    + 	bool check_deprecated_config;
    ++
    ++	/* Has this repository instance been initialized? */
    ++	bool initialized;
    + };
    +
    + #ifdef USE_THE_REPOSITORY_VARIABLE
2:  5e56e1cc41 ! 2:  8645b4f595 environment: stop using core.sparseCheckout globally
    @@ Commit message
         Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>

      ## builtin/backfill.c ##
    +@@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
    + 			 N_("Restrict the missing objects to the current sparse-checkout")),
    + 		OPT_END(),
    + 	};
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    +
    + 	show_usage_with_options_if_asked(argc, argv,
    + 					 builtin_backfill_usage, options);
     @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
      	repo_config(repo, git_default_config, NULL);

      	if (ctx.sparse < 0)
     -		ctx.sparse = core_apply_sparse_checkout;
    -+		ctx.sparse = the_repository->config_values.apply_sparse_checkout;
    ++		ctx.sparse = cfg->apply_sparse_checkout;

      	result = do_backfill(&ctx);
      	backfill_context_clear(&ctx);

      ## builtin/clone.c ##
     @@ builtin/clone.c: static int git_sparse_checkout_init(const char *repo)
    + {
    + 	struct child_process cmd = CHILD_PROCESS_INIT;
    + 	int result = 0;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    + 	strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL);
    +
    + 	/*
      	 * We must apply the setting in the current process
      	 * for the later checkout to use the sparse-checkout file.
      	 */
     -	core_apply_sparse_checkout = 1;
    -+	the_repository->config_values.apply_sparse_checkout = 1;
    ++	cfg->apply_sparse_checkout = 1;

      	cmd.git_cmd = 1;
      	if (run_command(&cmd)) {
    @@ builtin/grep.c: static int grep_submodule(struct grep_opt *opt,
      	 *	of these submodules are expanded unexpectedly.
      	 *
     -	 * 2. "core_apply_sparse_checkout"
    -+	 * 2. "config_values.apply_sparse_checkout"
    ++	 * 2. "config_values_private_.apply_sparse_checkout"
      	 *	When running `grep` in the superproject, this setting is
      	 *	populated using the superproject's configs. However, once
      	 *	initialized, this config is globally accessible and is read by

      ## builtin/mv.c ##
    +@@ builtin/mv.c: int cmd_mv(int argc,
    + 	struct hashmap moved_dirs = HASHMAP_INIT(pathmap_cmp, NULL);
    + 	struct strbuf pathbuf = STRBUF_INIT;
    + 	int ret;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    +
    + 	repo_config(the_repository, git_default_config, NULL);
    +
     @@ builtin/mv.c: int cmd_mv(int argc,
      		rename_index_entry_at(the_repository->index, pos, dst);

      		if (ignore_sparse &&
     -		    core_apply_sparse_checkout &&
    -+		    the_repository->config_values.apply_sparse_checkout &&
    ++		    cfg->apply_sparse_checkout &&
      		    core_sparse_checkout_cone) {
      			/*
      			 * NEEDSWORK: we are *not* paying attention to

      ## builtin/sparse-checkout.c ##
     @@ builtin/sparse-checkout.c: static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
    + 	struct pattern_list pl;
    + 	char *sparse_filename;
      	int res;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);

      	setup_work_tree();
     -	if (!core_apply_sparse_checkout)
    -+	if (!the_repository->config_values.apply_sparse_checkout)
    ++	if (!cfg->apply_sparse_checkout)
      		die(_("this worktree is not sparse"));

      	argc = parse_options(argc, argv, prefix,
     @@ builtin/sparse-checkout.c: static int set_config(struct repository *repo,
    + }

      static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    ++
      	/* If not specified, use previous definition of cone mode */
     -	if (*cone_mode == -1 && core_apply_sparse_checkout)
    -+	if (*cone_mode == -1 && the_repository->config_values.apply_sparse_checkout)
    ++	if (*cone_mode == -1 && cfg->apply_sparse_checkout)
      		*cone_mode = core_sparse_checkout_cone;

      	/* Set cone/non-cone mode appropriately */
     -	core_apply_sparse_checkout = 1;
    -+	the_repository->config_values.apply_sparse_checkout = 1;
    ++	cfg->apply_sparse_checkout = 1;
      	if (*cone_mode == 1 || *cone_mode == -1) {
      		core_sparse_checkout_cone = 1;
      		return MODE_CONE_PATTERNS;
    -@@ builtin/sparse-checkout.c: static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind
    +@@ builtin/sparse-checkout.c: static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
    + static int update_modes(struct repository *repo, int *cone_mode, int *sparse_index)
    + {
      	int mode, record_mode;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);

      	/* Determine if we need to record the mode; ensure sparse checkout on */
     -	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
    -+	record_mode = (*cone_mode != -1) ||
    -+		      !the_repository->config_values.apply_sparse_checkout;
    ++	record_mode = (*cone_mode != -1) || !cfg->apply_sparse_checkout;

      	mode = update_cone_mode(cone_mode);
      	if (record_mode && set_config(repo, mode))
    +@@ builtin/sparse-checkout.c: static int modify_pattern_list(struct repository *repo,
    + 	int result;
    + 	int changed_config = 0;
    + 	struct pattern_list *pl = xcalloc(1, sizeof(*pl));
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    +
    + 	switch (m) {
    + 	case ADD:
     @@ builtin/sparse-checkout.c: static int modify_pattern_list(struct repository *repo,
      		break;
      	}

     -	if (!core_apply_sparse_checkout) {
    -+	if (!the_repository->config_values.apply_sparse_checkout) {
    ++	if (!cfg->apply_sparse_checkout) {
      		set_config(repo, MODE_ALL_PATTERNS);
     -		core_apply_sparse_checkout = 1;
    -+		the_repository->config_values.apply_sparse_checkout = 1;
    ++		cfg->apply_sparse_checkout = 1;
      		changed_config = 1;
      	}

     @@ builtin/sparse-checkout.c: static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
    + 	};
    + 	struct strvec patterns = STRVEC_INIT;
      	int ret;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);

      	setup_work_tree();
     -	if (!core_apply_sparse_checkout)
    -+	if (!the_repository->config_values.apply_sparse_checkout)
    ++	if (!cfg->apply_sparse_checkout)
      		die(_("no sparse-checkout to add to"));

      	repo_read_index(repo);
     @@ builtin/sparse-checkout.c: static int sparse_checkout_reapply(int argc, const char **argv,
    + 			 N_("toggle the use of a sparse index")),
    + 		OPT_END(),
      	};
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);

      	setup_work_tree();
     -	if (!core_apply_sparse_checkout)
    -+	if (!the_repository->config_values.apply_sparse_checkout)
    ++	if (!cfg->apply_sparse_checkout)
      		die(_("must be in a sparse-checkout to reapply sparsity patterns"));

      	reapply_opts.cone_mode = -1;
    +@@ builtin/sparse-checkout.c: static int sparse_checkout_clean(int argc, const char **argv,
    + 	size_t worktree_len;
    + 	int force = 0, dry_run = 0, verbose = 0;
    + 	int require_force = 1;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    +
    + 	struct option builtin_sparse_checkout_clean_options[] = {
    + 		OPT__DRY_RUN(&dry_run, N_("dry run")),
     @@ builtin/sparse-checkout.c: static int sparse_checkout_clean(int argc, const char **argv,
      	};

      	setup_work_tree();
     -	if (!core_apply_sparse_checkout)
    -+	if (!the_repository->config_values.apply_sparse_checkout)
    ++	if (!cfg->apply_sparse_checkout)
      		die(_("must be in a sparse-checkout to clean directories"));
      	if (!core_sparse_checkout_cone)
      		die(_("must be in a cone-mode sparse-checkout to clean directories"));
     @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const char **argv,
    + 		OPT_END(),
    + 	};
      	struct pattern_list pl;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);

      	/*
     -	 * We do not exit early if !core_apply_sparse_checkout; due to the
    @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const ch
      	hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
      	pl.use_cone_patterns = 0;
     -	core_apply_sparse_checkout = 1;
    -+	the_repository->config_values.apply_sparse_checkout = 1;
    ++	cfg->apply_sparse_checkout = 1;

      	add_pattern("/*", empty_base, 0, &pl, 0);


      ## builtin/worktree.c ##
    +@@ builtin/worktree.c: static int add_worktree(const char *path, const char *refname,
    + 	struct strbuf sb_name = STRBUF_INIT;
    + 	struct worktree **worktrees, *wt = NULL;
    + 	struct ref_store *wt_refs;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    +
    + 	worktrees = get_worktrees();
    + 	check_candidate_path(path, opts->force, worktrees, "add");
     @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refname,
      	 * If the current worktree has sparse-checkout enabled, then copy
      	 * the sparse-checkout patterns from the current worktree.
      	 */
     -	if (core_apply_sparse_checkout)
    -+	if (the_repository->config_values.apply_sparse_checkout)
    ++	if (cfg->apply_sparse_checkout)
      		copy_sparse_checkout(sb_repo.buf);

      	/*
    @@ dir.c: enum pattern_match_result path_matches_pattern_list(
      int init_sparse_checkout_patterns(struct index_state *istate)
      {
     -	if (!core_apply_sparse_checkout)
    -+	if (!the_repository->config_values.apply_sparse_checkout)
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    ++	if (!cfg->apply_sparse_checkout)
      		return 1;
      	if (istate->sparse_checkout_patterns)
      		return 0;
    @@ environment.c: int git_default_config(const char *var, const char *value,
      }

      ## environment.h ##
    -@@ environment.h: struct strvec;
    +@@ environment.h: struct repository;
      struct repo_config_values {
      	/* section "core" config values */
      	char *attributes_file;
     +	int apply_sparse_checkout;
      };

    - /*
    + struct repo_config_values *repo_config_values(struct repository *repo);
     @@ environment.h: extern int precomposed_unicode;
      extern int protect_hfs;
      extern int protect_ntfs;
    @@ sparse-index.c: static int index_has_unmerged_entries(struct index_state *istate
      int is_sparse_index_allowed(struct index_state *istate, int flags)
      {
     -	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
    -+	struct repo_config_values *cfg = &the_repository->config_values;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
     +	if (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)
      		return 0;

    @@ sparse-index.c: static void clear_skip_worktree_from_present_files_full(struct i
      void clear_skip_worktree_from_present_files(struct index_state *istate)
      {
     -	if (!core_apply_sparse_checkout ||
    -+	struct repo_config_values *cfg = &the_repository->config_values;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
     +	if (!cfg->apply_sparse_checkout ||
      	    sparse_expect_files_outside_of_patterns)
      		return;


      ## unpack-trees.c ##
    +@@ unpack-trees.c: int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
    + 	struct pattern_list pl;
    + 	int free_pattern_list = 0;
    + 	struct dir_struct dir = DIR_INIT;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    +
    + 	if (o->reset == UNPACK_RESET_INVALID)
    + 		BUG("o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED");
     @@ unpack-trees.c: int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
      	if (o->prefix)
      		update_sparsity_for_prefix(o->prefix, o->src_index);

     -	if (!core_apply_sparse_checkout || !o->update)
    -+	if (!the_repository->config_values.apply_sparse_checkout || !o->update)
    ++	if (!cfg->apply_sparse_checkout || !o->update)
      		o->skip_sparse_checkout = 1;
      	if (!o->skip_sparse_checkout) {
      		memset(&pl, 0, sizeof(pl));

      ## wt-status.c ##
     @@ wt-status.c: static void wt_status_check_sparse_checkout(struct repository *r,
    + {
      	int skip_worktree = 0;
      	int i;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);

     -	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
    -+	if (!the_repository->config_values.apply_sparse_checkout ||
    ++	if (!cfg->apply_sparse_checkout ||
     +	    r->index->cache_nr == 0) {
      		/*
      		 * Don't compute percentage of checked out files if we
3:  e7f37bac87 ! 3:  60451b93a5 environment: move "branch.autoSetupMerge" into `struct repo_config_values`
    @@ branch.h: enum branch_track {
      /**

      ## builtin/branch.c ##
    +@@ builtin/branch.c: int cmd_branch(int argc,
    + 	static struct ref_sorting *sorting;
    + 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
    + 	struct ref_format format = REF_FORMAT_INIT;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    + 	int ret;
    +
    + 	struct option options[] = {
     @@ builtin/branch.c: int cmd_branch(int argc,
      	if (!sorting_options.nr)
      		string_list_append(&sorting_options, "refname");

     -	track = git_branch_track;
    -+	track = the_repository->config_values.branch_track;
    ++	track = cfg->branch_track;

      	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
      				   0, &head_oid, NULL);

      ## builtin/checkout.c ##
    +@@ builtin/checkout.c: static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
    + static int checkout_branch(struct checkout_opts *opts,
    + 			   struct branch_info *new_branch_info)
    + {
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    + 	int noop_switch = (!new_branch_info->name &&
    + 			   !opts->new_branch &&
    + 			   !opts->force_detach);
     @@ builtin/checkout.c: static int checkout_branch(struct checkout_opts *opts,
      		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
      			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
      	} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
     -		opts->track = git_branch_track;
    -+		opts->track = the_repository->config_values.branch_track;
    ++		opts->track = cfg->branch_track;

      	if (new_branch_info->name && !new_branch_info->commit)
      		die(_("Cannot switch branch to a non-commit '%s'"),

      ## builtin/push.c ##
    +@@ builtin/push.c: static NORETURN void die_push_simple(struct branch *branch,
    + 	const char *advice_pushdefault_maybe = "";
    + 	const char *advice_automergesimple_maybe = "";
    + 	const char *short_upstream = branch->merge[0]->src;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
    +
    + 	skip_prefix(short_upstream, "refs/heads/", &short_upstream);
    +
     @@ builtin/push.c: static NORETURN void die_push_simple(struct branch *branch,
      		advice_pushdefault_maybe = _("\n"
      				 "To choose either option permanently, "
      				 "see push.default in 'git help config'.\n");
     -	if (git_branch_track != BRANCH_TRACK_SIMPLE)
    -+	if (the_repository->config_values.branch_track != BRANCH_TRACK_SIMPLE)
    ++	if (cfg->branch_track != BRANCH_TRACK_SIMPLE)
      		advice_automergesimple_maybe = _("\n"
      				 "To avoid automatically configuring "
      				 "an upstream branch when its name\n"

      ## builtin/submodule--helper.c ##
     @@ builtin/submodule--helper.c: static int module_create_branch(int argc, const char **argv, const char *prefix,
    + 		N_("git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] [-n|--dry-run] <name> <start-oid> <start-name>"),
    + 		NULL
      	};
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);

      	repo_config(the_repository, git_default_config, NULL);
     -	track = git_branch_track;
    -+	track = the_repository->config_values.branch_track;
    ++	track = cfg->branch_track;
      	argc = parse_options(argc, argv, prefix, options, usage, 0);

      	if (argc != 3)
    @@ environment.c: static int git_default_i18n_config(const char *var, const char *v

      static int git_default_branch_config(const char *var, const char *value)
      {
    -+	struct repo_config_values *cfg = &the_repository->config_values;
    ++	struct repo_config_values *cfg = repo_config_values(the_repository);
     +
      	if (!strcmp(var, "branch.autosetupmerge")) {
      		if (value && !strcmp(value, "always")) {
    @@ environment.h: struct repo_config_values {
     +	enum branch_track branch_track;
      };

    - /*
    + struct repo_config_values *repo_config_values(struct repository *repo);


-- 
2.34.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-03 15:42         ` [Outreachy PATCH v6 " Olamide Caleb Bello
@ 2026-02-03 15:42           ` Olamide Caleb Bello
  2026-02-04 16:39             ` Phillip Wood
  2026-02-07  1:14             ` Junio C Hamano
  2026-02-03 15:42           ` [Outreachy PATCH v6 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
                             ` (2 subsequent siblings)
  3 siblings, 2 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-02-03 15:42 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The `core.attributeFile` config value is parsed in
git_default_core_config(), loaded eagerly and stored in the global
variable `git_attributes_file`. Storing this value in a global variable
can lead to it being overwritten by another repository when more than one
Git repository run in the same Git process.

Create a new struct `repo_config_values` to hold this value and
other repository dependent values parsed by `git_default_config()`.
This will ensure the current behaviour remains the same while also
enabling the libification of Git.

An accessor function 'repo_config_values()' is created and used to access
the new struct member of the repository struct.
This is to ensure that we detect if the struct repository has been
initialized and also prevent double initialization of the repository.

It is important to note that `git_default_config()` is a wrapper to other
`git_default_*_config()` functions such as `git_default_core_config()`.
Therefore to access and modify this global variable,
the change has to be made `git_default_core_config()`.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 attr.c        |  7 ++++---
 environment.c | 12 +++++++++---
 environment.h | 11 ++++++++++-
 repository.c  | 12 ++++++++++++
 repository.h  |  7 +++++++
 5 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/attr.c b/attr.c
index 4999b7e09d..75369547b3 100644
--- a/attr.c
+++ b/attr.c
@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)
 
 const char *git_attr_global_file(void)
 {
-	if (!git_attributes_file)
-		git_attributes_file = xdg_config_home("attributes");
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+	if (!cfg->attributes_file)
+		cfg->attributes_file = xdg_config_home("attributes");
 
-	return git_attributes_file;
+	return cfg->attributes_file;
 }
 
 int git_attr_system_is_enabled(void)
diff --git a/environment.c b/environment.c
index a770b5921d..4b5c701e80 100644
--- a/environment.c
+++ b/environment.c
@@ -53,7 +53,6 @@ char *git_commit_encoding;
 char *git_log_output_encoding;
 char *apply_default_whitespace;
 char *apply_default_ignorewhitespace;
-char *git_attributes_file;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files = -1;
@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
 static int git_default_core_config(const char *var, const char *value,
 				   const struct config_context *ctx, void *cb)
 {
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
 		trust_executable_bit = git_config_bool(var, value);
@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.attributesfile")) {
-		FREE_AND_NULL(git_attributes_file);
-		return git_config_pathname(&git_attributes_file, var, value);
+		FREE_AND_NULL(cfg->attributes_file);
+		return git_config_pathname(&cfg->attributes_file, var, value);
 	}
 
 	if (!strcmp(var, "core.bare")) {
@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,
 	/* Add other config variables here and to Documentation/config.adoc. */
 	return 0;
 }
+
+void repo_config_values_init(struct repo_config_values *cfg)
+{
+	cfg->attributes_file = NULL;
+}
diff --git a/environment.h b/environment.h
index 51898c99cd..dfc31b794d 100644
--- a/environment.h
+++ b/environment.h
@@ -84,6 +84,14 @@ extern const char * const local_repo_env[];
 
 struct strvec;
 
+struct repository;
+struct repo_config_values {
+	/* section "core" config values */
+	char *attributes_file;
+};
+
+struct repo_config_values *repo_config_values(struct repository *repo);
+
 /*
  * Wrapper of getenv() that returns a strdup value. This value is kept
  * in argv to be freed later.
@@ -107,6 +115,8 @@ const char *strip_namespace(const char *namespaced_ref);
 int git_default_config(const char *, const char *,
 		       const struct config_context *, void *);
 
+void repo_config_values_init(struct repo_config_values *cfg);
+
 /*
  * TODO: All the below state either explicitly or implicitly relies on
  * `the_repository`. We should eventually get rid of these and make the
@@ -152,7 +162,6 @@ extern int assume_unchanged;
 extern int warn_on_object_refname_ambiguity;
 extern char *apply_default_whitespace;
 extern char *apply_default_ignorewhitespace;
-extern char *git_attributes_file;
 extern int zlib_compression_level;
 extern int pack_compression_level;
 extern unsigned long pack_size_limit_cfg;
diff --git a/repository.c b/repository.c
index c7e75215ac..a9b727540f 100644
--- a/repository.c
+++ b/repository.c
@@ -50,13 +50,25 @@ static void set_default_hash_algo(struct repository *repo)
 	repo_set_hash_algo(repo, algo);
 }
 
+struct repo_config_values *repo_config_values(struct repository *repo)
+{
+	if(!repo->initialized)
+		BUG("config values from uninitialized repository");
+	return &repo->config_values_private_;
+}
+
 void initialize_repository(struct repository *repo)
 {
+	if (repo->initialized)
+		BUG("repository initialized already");
+	repo->initialized = true;
+
 	repo->remote_state = remote_state_new();
 	repo->parsed_objects = parsed_object_pool_new(repo);
 	ALLOC_ARRAY(repo->index, 1);
 	index_state_init(repo->index, repo);
 	repo->check_deprecated_config = true;
+	repo_config_values_init(repo_config_values(repo));
 
 	/*
 	 * When a command runs inside a repository, it learns what
diff --git a/repository.h b/repository.h
index 6063c4b846..9717e45000 100644
--- a/repository.h
+++ b/repository.h
@@ -3,6 +3,7 @@
 
 #include "strmap.h"
 #include "repo-settings.h"
+#include "environment.h"
 
 struct config_set;
 struct git_hash_algo;
@@ -148,6 +149,9 @@ struct repository {
 	/* Repository's compatibility hash algorithm. */
 	const struct git_hash_algo *compat_hash_algo;
 
+	/* Repository's config values parsed by git_default_config() */
+	struct repo_config_values config_values_private_;
+
 	/* Repository's reference storage format, as serialized on disk. */
 	enum ref_storage_format ref_storage_format;
 
@@ -171,6 +175,9 @@ struct repository {
 
 	/* Should repo_config() check for deprecated settings */
 	bool check_deprecated_config;
+
+	/* Has this repository instance been initialized? */
+	bool initialized;
 };
 
 #ifdef USE_THE_REPOSITORY_VARIABLE
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v6 2/3] environment: stop using core.sparseCheckout globally
  2026-02-03 15:42         ` [Outreachy PATCH v6 " Olamide Caleb Bello
  2026-02-03 15:42           ` [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-02-03 15:42           ` Olamide Caleb Bello
  2026-02-04 16:55             ` Phillip Wood
  2026-02-03 15:42           ` [Outreachy PATCH v6 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
  2026-02-04 16:57           ` [Outreachy PATCH v6 0/3] store repo specific config values in new " Phillip Wood
  3 siblings, 1 reply; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-02-03 15:42 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The config value `core.sparseCheckout` is parsed in
`git_default_core_config()` and stored globally in
`core_apply_sparse_checkout`. This could cause it to be overwritten
by another repository when different Git repositories run in the same
process.

Move the parsed value into `struct repo_config_values` in the_repository
to retain current behaviours and move towards libifying Git.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
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/backfill.c        |  3 ++-
 builtin/clone.c           |  3 ++-
 builtin/grep.c            |  2 +-
 builtin/mv.c              |  3 ++-
 builtin/sparse-checkout.c | 31 ++++++++++++++++++++-----------
 builtin/worktree.c        |  3 ++-
 dir.c                     |  3 ++-
 environment.c             |  4 ++--
 environment.h             |  2 +-
 sparse-index.c            |  6 ++++--
 unpack-trees.c            |  3 ++-
 wt-status.c               |  4 +++-
 12 files changed, 43 insertions(+), 24 deletions(-)

diff --git a/builtin/backfill.c b/builtin/backfill.c
index e80fc1b694..9b2ca57b6a 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -129,6 +129,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 			 N_("Restrict the missing objects to the current sparse-checkout")),
 		OPT_END(),
 	};
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	show_usage_with_options_if_asked(argc, argv,
 					 builtin_backfill_usage, options);
@@ -139,7 +140,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 	repo_config(repo, git_default_config, NULL);
 
 	if (ctx.sparse < 0)
-		ctx.sparse = core_apply_sparse_checkout;
+		ctx.sparse = cfg->apply_sparse_checkout;
 
 	result = do_backfill(&ctx);
 	backfill_context_clear(&ctx);
diff --git a/builtin/clone.c b/builtin/clone.c
index b19b302b06..0005fd7118 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -617,13 +617,14 @@ static int git_sparse_checkout_init(const char *repo)
 {
 	struct child_process cmd = CHILD_PROCESS_INIT;
 	int result = 0;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 	strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL);
 
 	/*
 	 * We must apply the setting in the current process
 	 * for the later checkout to use the sparse-checkout file.
 	 */
-	core_apply_sparse_checkout = 1;
+	cfg->apply_sparse_checkout = 1;
 
 	cmd.git_cmd = 1;
 	if (run_command(&cmd)) {
diff --git a/builtin/grep.c b/builtin/grep.c
index 53cccf2d25..207d340548 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,
 	 *	"forget" the sparse-index feature switch. As a result, the index
 	 *	of these submodules are expanded unexpectedly.
 	 *
-	 * 2. "core_apply_sparse_checkout"
+	 * 2. "config_values_private_.apply_sparse_checkout"
 	 *	When running `grep` in the superproject, this setting is
 	 *	populated using the superproject's configs. However, once
 	 *	initialized, this config is globally accessible and is read by
diff --git a/builtin/mv.c b/builtin/mv.c
index d43925097b..2215d34e31 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -238,6 +238,7 @@ int cmd_mv(int argc,
 	struct hashmap moved_dirs = HASHMAP_INIT(pathmap_cmp, NULL);
 	struct strbuf pathbuf = STRBUF_INIT;
 	int ret;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	repo_config(the_repository, git_default_config, NULL);
 
@@ -572,7 +573,7 @@ int cmd_mv(int argc,
 		rename_index_entry_at(the_repository->index, pos, dst);
 
 		if (ignore_sparse &&
-		    core_apply_sparse_checkout &&
+		    cfg->apply_sparse_checkout &&
 		    core_sparse_checkout_cone) {
 			/*
 			 * NEEDSWORK: we are *not* paying attention to
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 15d51e60a8..7f3317841e 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -61,9 +61,10 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
 	struct pattern_list pl;
 	char *sparse_filename;
 	int res;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!cfg->apply_sparse_checkout)
 		die(_("this worktree is not sparse"));
 
 	argc = parse_options(argc, argv, prefix,
@@ -399,12 +400,14 @@ static int set_config(struct repository *repo,
 }
 
 static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+
 	/* If not specified, use previous definition of cone mode */
-	if (*cone_mode == -1 && core_apply_sparse_checkout)
+	if (*cone_mode == -1 && cfg->apply_sparse_checkout)
 		*cone_mode = core_sparse_checkout_cone;
 
 	/* Set cone/non-cone mode appropriately */
-	core_apply_sparse_checkout = 1;
+	cfg->apply_sparse_checkout = 1;
 	if (*cone_mode == 1 || *cone_mode == -1) {
 		core_sparse_checkout_cone = 1;
 		return MODE_CONE_PATTERNS;
@@ -416,9 +419,10 @@ static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
 static int update_modes(struct repository *repo, int *cone_mode, int *sparse_index)
 {
 	int mode, record_mode;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	/* Determine if we need to record the mode; ensure sparse checkout on */
-	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+	record_mode = (*cone_mode != -1) || !cfg->apply_sparse_checkout;
 
 	mode = update_cone_mode(cone_mode);
 	if (record_mode && set_config(repo, mode))
@@ -684,6 +688,7 @@ static int modify_pattern_list(struct repository *repo,
 	int result;
 	int changed_config = 0;
 	struct pattern_list *pl = xcalloc(1, sizeof(*pl));
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	switch (m) {
 	case ADD:
@@ -699,9 +704,9 @@ static int modify_pattern_list(struct repository *repo,
 		break;
 	}
 
-	if (!core_apply_sparse_checkout) {
+	if (!cfg->apply_sparse_checkout) {
 		set_config(repo, MODE_ALL_PATTERNS);
-		core_apply_sparse_checkout = 1;
+		cfg->apply_sparse_checkout = 1;
 		changed_config = 1;
 	}
 
@@ -796,9 +801,10 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
 	};
 	struct strvec patterns = STRVEC_INIT;
 	int ret;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!cfg->apply_sparse_checkout)
 		die(_("no sparse-checkout to add to"));
 
 	repo_read_index(repo);
@@ -905,9 +911,10 @@ static int sparse_checkout_reapply(int argc, const char **argv,
 			 N_("toggle the use of a sparse index")),
 		OPT_END(),
 	};
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!cfg->apply_sparse_checkout)
 		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
 
 	reapply_opts.cone_mode = -1;
@@ -960,6 +967,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
 	size_t worktree_len;
 	int force = 0, dry_run = 0, verbose = 0;
 	int require_force = 1;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	struct option builtin_sparse_checkout_clean_options[] = {
 		OPT__DRY_RUN(&dry_run, N_("dry run")),
@@ -969,7 +977,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!cfg->apply_sparse_checkout)
 		die(_("must be in a sparse-checkout to clean directories"));
 	if (!core_sparse_checkout_cone)
 		die(_("must be in a cone-mode sparse-checkout to clean directories"));
@@ -1033,9 +1041,10 @@ static int sparse_checkout_disable(int argc, const char **argv,
 		OPT_END(),
 	};
 	struct pattern_list pl;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	/*
-	 * We do not exit early if !core_apply_sparse_checkout; due to the
+	 * We do not exit early if !repo->config_values.apply_sparse_checkout; due to the
 	 * ability for users to manually muck things up between
 	 *   direct editing of .git/info/sparse-checkout
 	 *   running read-tree -m u HEAD or update-index --skip-worktree
@@ -1061,7 +1070,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
 	hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
 	pl.use_cone_patterns = 0;
-	core_apply_sparse_checkout = 1;
+	cfg->apply_sparse_checkout = 1;
 
 	add_pattern("/*", empty_base, 0, &pl, 0);
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index fbdaf2eb2e..90e56ab495 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -440,6 +440,7 @@ static int add_worktree(const char *path, const char *refname,
 	struct strbuf sb_name = STRBUF_INIT;
 	struct worktree **worktrees, *wt = NULL;
 	struct ref_store *wt_refs;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	worktrees = get_worktrees();
 	check_candidate_path(path, opts->force, worktrees, "add");
@@ -536,7 +537,7 @@ static int add_worktree(const char *path, const char *refname,
 	 * If the current worktree has sparse-checkout enabled, then copy
 	 * the sparse-checkout patterns from the current worktree.
 	 */
-	if (core_apply_sparse_checkout)
+	if (cfg->apply_sparse_checkout)
 		copy_sparse_checkout(sb_repo.buf);
 
 	/*
diff --git a/dir.c b/dir.c
index b00821f294..0e05b3a383 100644
--- a/dir.c
+++ b/dir.c
@@ -1551,7 +1551,8 @@ enum pattern_match_result path_matches_pattern_list(
 
 int init_sparse_checkout_patterns(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout)
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+	if (!cfg->apply_sparse_checkout)
 		return 1;
 	if (istate->sparse_checkout_patterns)
 		return 0;
diff --git a/environment.c b/environment.c
index 4b5c701e80..390af1ce54 100644
--- a/environment.c
+++ b/environment.c
@@ -74,7 +74,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_apply_sparse_checkout;
 int core_sparse_checkout_cone;
 int sparse_expect_files_outside_of_patterns;
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.sparsecheckout")) {
-		core_apply_sparse_checkout = git_config_bool(var, value);
+		cfg->apply_sparse_checkout = git_config_bool(var, value);
 		return 0;
 	}
 
@@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,
 void repo_config_values_init(struct repo_config_values *cfg)
 {
 	cfg->attributes_file = NULL;
+	cfg->apply_sparse_checkout = 0;
 }
diff --git a/environment.h b/environment.h
index dfc31b794d..2e24160322 100644
--- a/environment.h
+++ b/environment.h
@@ -88,6 +88,7 @@ struct repository;
 struct repo_config_values {
 	/* section "core" config values */
 	char *attributes_file;
+	int apply_sparse_checkout;
 };
 
 struct repo_config_values *repo_config_values(struct repository *repo);
@@ -171,7 +172,6 @@ extern int precomposed_unicode;
 extern int protect_hfs;
 extern int protect_ntfs;
 
-extern int core_apply_sparse_checkout;
 extern int core_sparse_checkout_cone;
 extern int sparse_expect_files_outside_of_patterns;
 
diff --git a/sparse-index.c b/sparse-index.c
index 76f90da5f5..cb4c99dcae 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)
 
 int is_sparse_index_allowed(struct index_state *istate, int flags)
 {
-	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+	if (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)
 		return 0;
 
 	if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
@@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
 
 void clear_skip_worktree_from_present_files(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout ||
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+	if (!cfg->apply_sparse_checkout ||
 	    sparse_expect_files_outside_of_patterns)
 		return;
 
diff --git a/unpack-trees.c b/unpack-trees.c
index f38c761ab9..998a1e6dc7 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1888,6 +1888,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 	struct pattern_list pl;
 	int free_pattern_list = 0;
 	struct dir_struct dir = DIR_INIT;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	if (o->reset == UNPACK_RESET_INVALID)
 		BUG("o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED");
@@ -1924,7 +1925,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 	if (o->prefix)
 		update_sparsity_for_prefix(o->prefix, o->src_index);
 
-	if (!core_apply_sparse_checkout || !o->update)
+	if (!cfg->apply_sparse_checkout || !o->update)
 		o->skip_sparse_checkout = 1;
 	if (!o->skip_sparse_checkout) {
 		memset(&pl, 0, sizeof(pl));
diff --git a/wt-status.c b/wt-status.c
index e12adb26b9..6609b37cad 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1763,8 +1763,10 @@ static void wt_status_check_sparse_checkout(struct repository *r,
 {
 	int skip_worktree = 0;
 	int i;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
-	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
+	if (!cfg->apply_sparse_checkout ||
+	    r->index->cache_nr == 0) {
 		/*
 		 * Don't compute percentage of checked out files if we
 		 * aren't in a sparse checkout or would get division by 0.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v6 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-02-03 15:42         ` [Outreachy PATCH v6 " Olamide Caleb Bello
  2026-02-03 15:42           ` [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
  2026-02-03 15:42           ` [Outreachy PATCH v6 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-02-03 15:42           ` Olamide Caleb Bello
  2026-02-04 16:57           ` [Outreachy PATCH v6 0/3] store repo specific config values in new " Phillip Wood
  3 siblings, 0 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-02-03 15:42 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The config value `branch.autoSetupMerge` is parsed in
`git_default_branch_config()` and stored in the global variable
`git_branch_track`. This global variable can be overwritten
by another repository when multiple Git repos run in the the same process.

Move this value into `struct repo_config_values` in the_repository to
retain current behaviours and move towards libifying Git.
Since the variable is no longer a global variable, it has been renamed to
`branch_track` in the struct `repo_config_values`.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 branch.h                    |  2 --
 builtin/branch.c            |  3 ++-
 builtin/checkout.c          |  3 ++-
 builtin/push.c              |  3 ++-
 builtin/submodule--helper.c |  3 ++-
 environment.c               | 12 +++++++-----
 environment.h               |  4 ++++
 7 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/branch.h b/branch.h
index ec2f35fda4..3dc6e2a0ff 100644
--- a/branch.h
+++ b/branch.h
@@ -15,8 +15,6 @@ enum branch_track {
 	BRANCH_TRACK_SIMPLE,
 };
 
-extern enum branch_track git_branch_track;
-
 /* Functions for acting on the information about branches. */
 
 /**
diff --git a/builtin/branch.c b/builtin/branch.c
index c577b5d20f..a1a43380d0 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -724,6 +724,7 @@ int cmd_branch(int argc,
 	static struct ref_sorting *sorting;
 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
 	struct ref_format format = REF_FORMAT_INIT;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 	int ret;
 
 	struct option options[] = {
@@ -795,7 +796,7 @@ int cmd_branch(int argc,
 	if (!sorting_options.nr)
 		string_list_append(&sorting_options, "refname");
 
-	track = git_branch_track;
+	track = cfg->branch_track;
 
 	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
 				   0, &head_oid, NULL);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 261699e2f5..ea728e733c 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1588,6 +1588,7 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
 static int checkout_branch(struct checkout_opts *opts,
 			   struct branch_info *new_branch_info)
 {
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 	int noop_switch = (!new_branch_info->name &&
 			   !opts->new_branch &&
 			   !opts->force_detach);
@@ -1631,7 +1632,7 @@ static int checkout_branch(struct checkout_opts *opts,
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
 	} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
-		opts->track = git_branch_track;
+		opts->track = cfg->branch_track;
 
 	if (new_branch_info->name && !new_branch_info->commit)
 		die(_("Cannot switch branch to a non-commit '%s'"),
diff --git a/builtin/push.c b/builtin/push.c
index 5b6cebbb85..7100ffba5d 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -151,6 +151,7 @@ static NORETURN void die_push_simple(struct branch *branch,
 	const char *advice_pushdefault_maybe = "";
 	const char *advice_automergesimple_maybe = "";
 	const char *short_upstream = branch->merge[0]->src;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	skip_prefix(short_upstream, "refs/heads/", &short_upstream);
 
@@ -162,7 +163,7 @@ static NORETURN void die_push_simple(struct branch *branch,
 		advice_pushdefault_maybe = _("\n"
 				 "To choose either option permanently, "
 				 "see push.default in 'git help config'.\n");
-	if (git_branch_track != BRANCH_TRACK_SIMPLE)
+	if (cfg->branch_track != BRANCH_TRACK_SIMPLE)
 		advice_automergesimple_maybe = _("\n"
 				 "To avoid automatically configuring "
 				 "an upstream branch when its name\n"
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d537ab087a..594cd107b3 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3126,9 +3126,10 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,
 		N_("git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] [-n|--dry-run] <name> <start-oid> <start-name>"),
 		NULL
 	};
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	repo_config(the_repository, git_default_config, NULL);
-	track = git_branch_track;
+	track = cfg->branch_track;
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
 	if (argc != 3)
diff --git a/environment.c b/environment.c
index 390af1ce54..1bc3adb75b 100644
--- a/environment.c
+++ b/environment.c
@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 enum eol core_eol = EOL_UNSET;
 int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
 char *check_roundtrip_encoding;
-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #ifndef OBJECT_CREATION_MODE
@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)
 
 static int git_default_branch_config(const char *var, const char *value)
 {
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+
 	if (!strcmp(var, "branch.autosetupmerge")) {
 		if (value && !strcmp(value, "always")) {
-			git_branch_track = BRANCH_TRACK_ALWAYS;
+			cfg->branch_track = BRANCH_TRACK_ALWAYS;
 			return 0;
 		} else if (value && !strcmp(value, "inherit")) {
-			git_branch_track = BRANCH_TRACK_INHERIT;
+			cfg->branch_track = BRANCH_TRACK_INHERIT;
 			return 0;
 		} else if (value && !strcmp(value, "simple")) {
-			git_branch_track = BRANCH_TRACK_SIMPLE;
+			cfg->branch_track = BRANCH_TRACK_SIMPLE;
 			return 0;
 		}
-		git_branch_track = git_config_bool(var, value);
+		cfg->branch_track = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "branch.autosetuprebase")) {
@@ -761,4 +762,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;
 }
diff --git a/environment.h b/environment.h
index 2e24160322..4bfd798757 100644
--- a/environment.h
+++ b/environment.h
@@ -2,6 +2,7 @@
 #define ENVIRONMENT_H
 
 #include "repo-settings.h"
+#include "branch.h"
 
 /* Double-check local_repo_env below if you add to this list. */
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
@@ -89,6 +90,9 @@ struct repo_config_values {
 	/* section "core" config values */
 	char *attributes_file;
 	int apply_sparse_checkout;
+
+	/* section "branch" config values */
+	enum branch_track branch_track;
 };
 
 struct repo_config_values *repo_config_values(struct repository *repo);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-03 15:42           ` [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-02-04 16:39             ` Phillip Wood
  2026-02-09  8:47               ` Bello Olamide
  2026-02-07  1:14             ` Junio C Hamano
  1 sibling, 1 reply; 79+ messages in thread
From: Phillip Wood @ 2026-02-04 16:39 UTC (permalink / raw)
  To: Olamide Caleb Bello, git
  Cc: toon, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On 03/02/2026 15:42, Olamide Caleb Bello wrote:
> The `core.attributeFile` config value is parsed in
> git_default_core_config(), loaded eagerly and stored in the global
> variable `git_attributes_file`. Storing this value in a global variable
> can lead to it being overwritten by another repository when more than one
> Git repository run in the same Git process.
> 
> Create a new struct `repo_config_values` to hold this value and
> other repository dependent values parsed by `git_default_config()`.
> This will ensure the current behaviour remains the same while also
> enabling the libification of Git.
> 
> An accessor function 'repo_config_values()' is created and used to access
> the new struct member of the repository struct.
> This is to ensure that we detect if the struct repository has been
> initialized and also prevent double initialization of the repository.

Sounds sensible. This paragraph could be reflowed.

> It is important to note that `git_default_config()` is a wrapper to other
> `git_default_*_config()` functions such as `git_default_core_config()`.
> Therefore to access and modify this global variable,
> the change has to be made `git_default_core_config()`.

I'm not sure what this paragraph is saying with regard to the changes in 
this patch.

> --- a/environment.c
> +++ b/environment.c
> @@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,
>   	/* Add other config variables here and to Documentation/config.adoc. */
>   	return 0;
>   }
> +
> +void repo_config_values_init(struct repo_config_values *cfg)
> +{
> +	cfg->attributes_file = NULL;
> +}

Should we be free()ing cfg->attributes_file when the repository instance 
is free()d? At the moment we're using "the_repository" which points to a 
static instance so it does not make any practical difference but once we 
start storing the config per-repository instance we will need to free 
the config when the repository instance is free()d.

> diff --git a/repository.c b/repository.c
> index c7e75215ac..a9b727540f 100644
> --- a/repository.c
> +++ b/repository.c
> @@ -50,13 +50,25 @@ static void set_default_hash_algo(struct repository *repo)
>   	repo_set_hash_algo(repo, algo);
>   }
>   
> +struct repo_config_values *repo_config_values(struct repository *repo)
> +{
> +	if(!repo->initialized)
> +		BUG("config values from uninitialized repository");

This check and the one in initialize_repository() below assume that the 
repository instance is zeroed out when it is created, that's a 
reasonable requirement but we should probably document it as our other 
data structures tend not to require that they're zeroed out before they 
are initialized. For example

	struct strbuf buf;
	strbuf_init(&buf, 0);

is perfectly fine as strbuf_init() does not assume the instance passed 
to it has been zeroed out.

As we only support retrieving values from "the_repository" at the moment 
we should perhaps add

	if (repo != the_repository)
		BUG("trying to read config from wrong repository instance");

Everything else looks fine to me

Thanks

Phillip

> +	return &repo->config_values_private_;
> +}
> +
>   void initialize_repository(struct repository *repo)
>   {
> +	if (repo->initialized)
> +		BUG("repository initialized already");
> +	repo->initialized = true;
> +
>   	repo->remote_state = remote_state_new();
>   	repo->parsed_objects = parsed_object_pool_new(repo);
>   	ALLOC_ARRAY(repo->index, 1);
>   	index_state_init(repo->index, repo);
>   	repo->check_deprecated_config = true;
> +	repo_config_values_init(repo_config_values(repo));
>   
>   	/*
>   	 * When a command runs inside a repository, it learns what
> diff --git a/repository.h b/repository.h
> index 6063c4b846..9717e45000 100644
> --- a/repository.h
> +++ b/repository.h
> @@ -3,6 +3,7 @@
>   
>   #include "strmap.h"
>   #include "repo-settings.h"
> +#include "environment.h"
>   
>   struct config_set;
>   struct git_hash_algo;
> @@ -148,6 +149,9 @@ struct repository {
>   	/* Repository's compatibility hash algorithm. */
>   	const struct git_hash_algo *compat_hash_algo;
>   
> +	/* Repository's config values parsed by git_default_config() */
> +	struct repo_config_values config_values_private_;
> +
>   	/* Repository's reference storage format, as serialized on disk. */
>   	enum ref_storage_format ref_storage_format;
>   
> @@ -171,6 +175,9 @@ struct repository {
>   
>   	/* Should repo_config() check for deprecated settings */
>   	bool check_deprecated_config;
> +
> +	/* Has this repository instance been initialized? */
> +	bool initialized;
>   };
>   
>   #ifdef USE_THE_REPOSITORY_VARIABLE


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 2/3] environment: stop using core.sparseCheckout globally
  2026-02-03 15:42           ` [Outreachy PATCH v6 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-02-04 16:55             ` Phillip Wood
  0 siblings, 0 replies; 79+ messages in thread
From: Phillip Wood @ 2026-02-04 16:55 UTC (permalink / raw)
  To: Olamide Caleb Bello, git
  Cc: toon, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On 03/02/2026 15:42, Olamide Caleb Bello wrote:
> The config value `core.sparseCheckout` is parsed in
> `git_default_core_config()` and stored globally in
> `core_apply_sparse_checkout`. This could cause it to be overwritten
> by another repository when different Git repositories run in the same
> process.
> 
> Move the parsed value into `struct repo_config_values` in the_repository
> to retain current behaviours and move towards libifying Git.

This looks good, I've left a small style comment below

> diff --git a/builtin/clone.c b/builtin/clone.c
> index b19b302b06..0005fd7118 100644
> --- a/builtin/clone.c
> +++ b/builtin/clone.c
> @@ -617,13 +617,14 @@ static int git_sparse_checkout_init(const char *repo)
>   {
>   	struct child_process cmd = CHILD_PROCESS_INIT;
>   	int result = 0;
> +	struct repo_config_values *cfg = repo_config_values(the_repository);

We normally have a blank line between the varibale declarations and 
statements. As you're adding a new declaration here it would be good to 
add a blank line as well. The same goes for dir.c and sparse-index.c below.

Thanks

Phillip

>   	strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL);

 > diff --git a/dir.c b/dir.c
 > index b00821f294..0e05b3a383 100644
 > --- a/dir.c
 > +++ b/dir.c
 > @@ -1551,7 +1551,8 @@ enum pattern_match_result 
path_matches_pattern_list(
 >
 >  int init_sparse_checkout_patterns(struct index_state *istate)
 >  {
 > -	if (!core_apply_sparse_checkout)
 > +	struct repo_config_values *cfg = repo_config_values(the_repository);
 > +	if (!cfg->apply_sparse_checkout)
 >  		return 1;
 >  	if (istate->sparse_checkout_patterns)
 >  		return 0;

> diff --git a/sparse-index.c b/sparse-index.c
> index 76f90da5f5..cb4c99dcae 100644
> --- a/sparse-index.c
> +++ b/sparse-index.c
> @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)
>   
>   int is_sparse_index_allowed(struct index_state *istate, int flags)
>   {
> -	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
> +	struct repo_config_values *cfg = repo_config_values(the_repository);
> +	if (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)
>   		return 0;
>   
>   	if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
> @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
>   
>   void clear_skip_worktree_from_present_files(struct index_state *istate)
>   {
> -	if (!core_apply_sparse_checkout ||
> +	struct repo_config_values *cfg = repo_config_values(the_repository);
> +	if (!cfg->apply_sparse_checkout ||
>   	    sparse_expect_files_outside_of_patterns)
>   		return;
>   

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 0/3] store repo specific config values in new `struct repo_config_values`
  2026-02-03 15:42         ` [Outreachy PATCH v6 " Olamide Caleb Bello
                             ` (2 preceding siblings ...)
  2026-02-03 15:42           ` [Outreachy PATCH v6 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
@ 2026-02-04 16:57           ` Phillip Wood
  3 siblings, 0 replies; 79+ messages in thread
From: Phillip Wood @ 2026-02-04 16:57 UTC (permalink / raw)
  To: Olamide Caleb Bello, git
  Cc: toon, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Hi Olamide

This version looks pretty good. I've left a couple of suggestions, but 
the changes since v4 look good.

Thanks

Phillip

On 03/02/2026 15:42, Olamide Caleb Bello wrote:
> Hi Git Community,
> Over the course of my ongoing internship, which focused on moving repo specific
> global variables in environment.h into local scope, I have attempted to move some
> variables into the struct repo-settings.
> However there has been some design concerns as regards the use of
> `prepare_repo_settings()` with respect to when and where to call the
> function, and also the change in behaviours when the variable is lazily
> loaded as discussed in [1] and [2].
> 
> After different deliberations, Phillip Wood proposed creating a new config
> struct [3], adding it to the repository struct and passing the repo struct to
> `git_default_config()` to store parsed repo specific config values per repo.
> This ensures the current behaviours will be retained.
> 
> I have experimented with this approach for some values and I would
> appreciate feedbacks about this approach before we can move forward
> and use it for more variables related to `git_default_config()`.
> 
> For now, the parsed value is stored in `the_repository` in
> `git_default_*_config()` and further efforts to pass the repository
> parameter to `git_default_config()` as the callback parameter will
> be looked into later on.
> The link to the CI tests can be see in [4] and [5]
> in the CI, some BUGs were gotten as a result of double initialization
> 
> 1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/
> 2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/
> 3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com
> 4. https://github.com/git/git/actions/runs/21633462506/job/62352191148?pr=2166
> 5. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2303203992
> 
> Changes in v6:
> ==============
> - Added a new accessor function 'repo_config_values()' which ensures we detect
>    if a repository has been initialized already
> - Changed the member name to 'config_values_private_' to indicate this member
>    is private
> - Modified commit message of patch 1 to reflect these changes
> 
> Olamide Caleb Bello (3):
>    environment: stop storing `core.attributesFile` globally
>    environment: stop using core.sparseCheckout globally
>    environment: move "branch.autoSetupMerge" into `struct
>      repo_config_values`
> 
>   attr.c                      |  7 ++++---
>   branch.h                    |  2 --
>   builtin/backfill.c          |  3 ++-
>   builtin/branch.c            |  3 ++-
>   builtin/checkout.c          |  3 ++-
>   builtin/clone.c             |  3 ++-
>   builtin/grep.c              |  2 +-
>   builtin/mv.c                |  3 ++-
>   builtin/push.c              |  3 ++-
>   builtin/sparse-checkout.c   | 31 ++++++++++++++++++++-----------
>   builtin/submodule--helper.c |  3 ++-
>   builtin/worktree.c          |  3 ++-
>   dir.c                       |  3 ++-
>   environment.c               | 28 ++++++++++++++++++----------
>   environment.h               | 17 +++++++++++++++--
>   repository.c                | 12 ++++++++++++
>   repository.h                |  7 +++++++
>   sparse-index.c              |  6 ++++--
>   unpack-trees.c              |  3 ++-
>   wt-status.c                 |  4 +++-
>   20 files changed, 104 insertions(+), 42 deletions(-)
> 
>   Range diff versus v5:
>   =====================
> 
>   1:  d28850bcdb ! 1:  7e3082125d environment: stop storing `core.attributesFile` globally
>      @@ Commit message
> 
>           Create a new struct `repo_config_values` to hold this value and
>           other repository dependent values parsed by `git_default_config()`.
>      -    For now the value can be accessed via the_repository in
>      -    `git_default_config()`.
>           This will ensure the current behaviour remains the same while also
>           enabling the libification of Git.
> 
>      +    An accessor function 'repo_config_values()' is created and used to access
>      +    the new struct member of the repository struct.
>      +    This is to ensure that we detect if the struct repository has been
>      +    initialized and also prevent double initialization of the repository.
>      +
>           It is important to note that `git_default_config()` is a wrapper to other
>           `git_default_*_config()` functions such as `git_default_core_config()`.
>           Therefore to access and modify this global variable,
>      @@ Commit message
>           Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
>           Mentored-by: Christian Couder <christian.couder@gmail.com>
>           Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
>      +    Helped-by: Junio C Hamano <gitster@pobox.com>
>           Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
> 
>        ## attr.c ##
>      @@ attr.c: const char *git_attr_system_file(void)
>        {
>       -	if (!git_attributes_file)
>       -		git_attributes_file = xdg_config_home("attributes");
>      -+	struct repo_config_values *cfg = &the_repository->config_values;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>       +	if (!cfg->attributes_file)
>       +		cfg->attributes_file = xdg_config_home("attributes");
> 
>      @@ environment.c: static enum fsync_component parse_fsync_components(const char *va
>        static int git_default_core_config(const char *var, const char *value,
>        				   const struct config_context *ctx, void *cb)
>        {
>      -+	struct repo_config_values *cfg = &the_repository->config_values;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>       +
>        	/* This needs a better name */
>        	if (!strcmp(var, "core.filemode")) {
>      @@ environment.h: extern const char * const local_repo_env[];
> 
>        struct strvec;
> 
>      ++struct repository;
>       +struct repo_config_values {
>       +	/* section "core" config values */
>       +	char *attributes_file;
>       +};
>      ++
>      ++struct repo_config_values *repo_config_values(struct repository *repo);
>       +
>        /*
>         * Wrapper of getenv() that returns a strdup value. This value is kept
>      @@ environment.h: extern int assume_unchanged;
>        extern unsigned long pack_size_limit_cfg;
> 
>        ## repository.c ##
>      -@@ repository.c: void initialize_repository(struct repository *repo)
>      +@@ repository.c: static void set_default_hash_algo(struct repository *repo)
>      + 	repo_set_hash_algo(repo, algo);
>      + }
>      +
>      ++struct repo_config_values *repo_config_values(struct repository *repo)
>      ++{
>      ++	if(!repo->initialized)
>      ++		BUG("config values from uninitialized repository");
>      ++	return &repo->config_values_private_;
>      ++}
>      ++
>      + void initialize_repository(struct repository *repo)
>      + {
>      ++	if (repo->initialized)
>      ++		BUG("repository initialized already");
>      ++	repo->initialized = true;
>      ++
>      + 	repo->remote_state = remote_state_new();
>      + 	repo->parsed_objects = parsed_object_pool_new(repo);
>        	ALLOC_ARRAY(repo->index, 1);
>        	index_state_init(repo->index, repo);
>        	repo->check_deprecated_config = true;
>      -+	repo_config_values_init(&repo->config_values);
>      ++	repo_config_values_init(repo_config_values(repo));
> 
>        	/*
>        	 * When a command runs inside a repository, it learns what
>      @@ repository.h: struct repository {
>        	const struct git_hash_algo *compat_hash_algo;
> 
>       +	/* Repository's config values parsed by git_default_config() */
>      -+	struct repo_config_values config_values;
>      ++	struct repo_config_values config_values_private_;
>       +
>        	/* Repository's reference storage format, as serialized on disk. */
>        	enum ref_storage_format ref_storage_format;
> 
>      +@@ repository.h: struct repository {
>      +
>      + 	/* Should repo_config() check for deprecated settings */
>      + 	bool check_deprecated_config;
>      ++
>      ++	/* Has this repository instance been initialized? */
>      ++	bool initialized;
>      + };
>      +
>      + #ifdef USE_THE_REPOSITORY_VARIABLE
> 2:  5e56e1cc41 ! 2:  8645b4f595 environment: stop using core.sparseCheckout globally
>      @@ Commit message
>           Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
> 
>        ## builtin/backfill.c ##
>      +@@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
>      + 			 N_("Restrict the missing objects to the current sparse-checkout")),
>      + 		OPT_END(),
>      + 	};
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      +
>      + 	show_usage_with_options_if_asked(argc, argv,
>      + 					 builtin_backfill_usage, options);
>       @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
>        	repo_config(repo, git_default_config, NULL);
> 
>        	if (ctx.sparse < 0)
>       -		ctx.sparse = core_apply_sparse_checkout;
>      -+		ctx.sparse = the_repository->config_values.apply_sparse_checkout;
>      ++		ctx.sparse = cfg->apply_sparse_checkout;
> 
>        	result = do_backfill(&ctx);
>        	backfill_context_clear(&ctx);
> 
>        ## builtin/clone.c ##
>       @@ builtin/clone.c: static int git_sparse_checkout_init(const char *repo)
>      + {
>      + 	struct child_process cmd = CHILD_PROCESS_INIT;
>      + 	int result = 0;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      + 	strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL);
>      +
>      + 	/*
>        	 * We must apply the setting in the current process
>        	 * for the later checkout to use the sparse-checkout file.
>        	 */
>       -	core_apply_sparse_checkout = 1;
>      -+	the_repository->config_values.apply_sparse_checkout = 1;
>      ++	cfg->apply_sparse_checkout = 1;
> 
>        	cmd.git_cmd = 1;
>        	if (run_command(&cmd)) {
>      @@ builtin/grep.c: static int grep_submodule(struct grep_opt *opt,
>        	 *	of these submodules are expanded unexpectedly.
>        	 *
>       -	 * 2. "core_apply_sparse_checkout"
>      -+	 * 2. "config_values.apply_sparse_checkout"
>      ++	 * 2. "config_values_private_.apply_sparse_checkout"
>        	 *	When running `grep` in the superproject, this setting is
>        	 *	populated using the superproject's configs. However, once
>        	 *	initialized, this config is globally accessible and is read by
> 
>        ## builtin/mv.c ##
>      +@@ builtin/mv.c: int cmd_mv(int argc,
>      + 	struct hashmap moved_dirs = HASHMAP_INIT(pathmap_cmp, NULL);
>      + 	struct strbuf pathbuf = STRBUF_INIT;
>      + 	int ret;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      +
>      + 	repo_config(the_repository, git_default_config, NULL);
>      +
>       @@ builtin/mv.c: int cmd_mv(int argc,
>        		rename_index_entry_at(the_repository->index, pos, dst);
> 
>        		if (ignore_sparse &&
>       -		    core_apply_sparse_checkout &&
>      -+		    the_repository->config_values.apply_sparse_checkout &&
>      ++		    cfg->apply_sparse_checkout &&
>        		    core_sparse_checkout_cone) {
>        			/*
>        			 * NEEDSWORK: we are *not* paying attention to
> 
>        ## builtin/sparse-checkout.c ##
>       @@ builtin/sparse-checkout.c: static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
>      + 	struct pattern_list pl;
>      + 	char *sparse_filename;
>        	int res;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
> 
>        	setup_work_tree();
>       -	if (!core_apply_sparse_checkout)
>      -+	if (!the_repository->config_values.apply_sparse_checkout)
>      ++	if (!cfg->apply_sparse_checkout)
>        		die(_("this worktree is not sparse"));
> 
>        	argc = parse_options(argc, argv, prefix,
>       @@ builtin/sparse-checkout.c: static int set_config(struct repository *repo,
>      + }
> 
>        static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      ++
>        	/* If not specified, use previous definition of cone mode */
>       -	if (*cone_mode == -1 && core_apply_sparse_checkout)
>      -+	if (*cone_mode == -1 && the_repository->config_values.apply_sparse_checkout)
>      ++	if (*cone_mode == -1 && cfg->apply_sparse_checkout)
>        		*cone_mode = core_sparse_checkout_cone;
> 
>        	/* Set cone/non-cone mode appropriately */
>       -	core_apply_sparse_checkout = 1;
>      -+	the_repository->config_values.apply_sparse_checkout = 1;
>      ++	cfg->apply_sparse_checkout = 1;
>        	if (*cone_mode == 1 || *cone_mode == -1) {
>        		core_sparse_checkout_cone = 1;
>        		return MODE_CONE_PATTERNS;
>      -@@ builtin/sparse-checkout.c: static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind
>      +@@ builtin/sparse-checkout.c: static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
>      + static int update_modes(struct repository *repo, int *cone_mode, int *sparse_index)
>      + {
>        	int mode, record_mode;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
> 
>        	/* Determine if we need to record the mode; ensure sparse checkout on */
>       -	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
>      -+	record_mode = (*cone_mode != -1) ||
>      -+		      !the_repository->config_values.apply_sparse_checkout;
>      ++	record_mode = (*cone_mode != -1) || !cfg->apply_sparse_checkout;
> 
>        	mode = update_cone_mode(cone_mode);
>        	if (record_mode && set_config(repo, mode))
>      +@@ builtin/sparse-checkout.c: static int modify_pattern_list(struct repository *repo,
>      + 	int result;
>      + 	int changed_config = 0;
>      + 	struct pattern_list *pl = xcalloc(1, sizeof(*pl));
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      +
>      + 	switch (m) {
>      + 	case ADD:
>       @@ builtin/sparse-checkout.c: static int modify_pattern_list(struct repository *repo,
>        		break;
>        	}
> 
>       -	if (!core_apply_sparse_checkout) {
>      -+	if (!the_repository->config_values.apply_sparse_checkout) {
>      ++	if (!cfg->apply_sparse_checkout) {
>        		set_config(repo, MODE_ALL_PATTERNS);
>       -		core_apply_sparse_checkout = 1;
>      -+		the_repository->config_values.apply_sparse_checkout = 1;
>      ++		cfg->apply_sparse_checkout = 1;
>        		changed_config = 1;
>        	}
> 
>       @@ builtin/sparse-checkout.c: static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
>      + 	};
>      + 	struct strvec patterns = STRVEC_INIT;
>        	int ret;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
> 
>        	setup_work_tree();
>       -	if (!core_apply_sparse_checkout)
>      -+	if (!the_repository->config_values.apply_sparse_checkout)
>      ++	if (!cfg->apply_sparse_checkout)
>        		die(_("no sparse-checkout to add to"));
> 
>        	repo_read_index(repo);
>       @@ builtin/sparse-checkout.c: static int sparse_checkout_reapply(int argc, const char **argv,
>      + 			 N_("toggle the use of a sparse index")),
>      + 		OPT_END(),
>        	};
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
> 
>        	setup_work_tree();
>       -	if (!core_apply_sparse_checkout)
>      -+	if (!the_repository->config_values.apply_sparse_checkout)
>      ++	if (!cfg->apply_sparse_checkout)
>        		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
> 
>        	reapply_opts.cone_mode = -1;
>      +@@ builtin/sparse-checkout.c: static int sparse_checkout_clean(int argc, const char **argv,
>      + 	size_t worktree_len;
>      + 	int force = 0, dry_run = 0, verbose = 0;
>      + 	int require_force = 1;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      +
>      + 	struct option builtin_sparse_checkout_clean_options[] = {
>      + 		OPT__DRY_RUN(&dry_run, N_("dry run")),
>       @@ builtin/sparse-checkout.c: static int sparse_checkout_clean(int argc, const char **argv,
>        	};
> 
>        	setup_work_tree();
>       -	if (!core_apply_sparse_checkout)
>      -+	if (!the_repository->config_values.apply_sparse_checkout)
>      ++	if (!cfg->apply_sparse_checkout)
>        		die(_("must be in a sparse-checkout to clean directories"));
>        	if (!core_sparse_checkout_cone)
>        		die(_("must be in a cone-mode sparse-checkout to clean directories"));
>       @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const char **argv,
>      + 		OPT_END(),
>      + 	};
>        	struct pattern_list pl;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
> 
>        	/*
>       -	 * We do not exit early if !core_apply_sparse_checkout; due to the
>      @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const ch
>        	hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
>        	pl.use_cone_patterns = 0;
>       -	core_apply_sparse_checkout = 1;
>      -+	the_repository->config_values.apply_sparse_checkout = 1;
>      ++	cfg->apply_sparse_checkout = 1;
> 
>        	add_pattern("/*", empty_base, 0, &pl, 0);
> 
> 
>        ## builtin/worktree.c ##
>      +@@ builtin/worktree.c: static int add_worktree(const char *path, const char *refname,
>      + 	struct strbuf sb_name = STRBUF_INIT;
>      + 	struct worktree **worktrees, *wt = NULL;
>      + 	struct ref_store *wt_refs;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      +
>      + 	worktrees = get_worktrees();
>      + 	check_candidate_path(path, opts->force, worktrees, "add");
>       @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refname,
>        	 * If the current worktree has sparse-checkout enabled, then copy
>        	 * the sparse-checkout patterns from the current worktree.
>        	 */
>       -	if (core_apply_sparse_checkout)
>      -+	if (the_repository->config_values.apply_sparse_checkout)
>      ++	if (cfg->apply_sparse_checkout)
>        		copy_sparse_checkout(sb_repo.buf);
> 
>        	/*
>      @@ dir.c: enum pattern_match_result path_matches_pattern_list(
>        int init_sparse_checkout_patterns(struct index_state *istate)
>        {
>       -	if (!core_apply_sparse_checkout)
>      -+	if (!the_repository->config_values.apply_sparse_checkout)
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      ++	if (!cfg->apply_sparse_checkout)
>        		return 1;
>        	if (istate->sparse_checkout_patterns)
>        		return 0;
>      @@ environment.c: int git_default_config(const char *var, const char *value,
>        }
> 
>        ## environment.h ##
>      -@@ environment.h: struct strvec;
>      +@@ environment.h: struct repository;
>        struct repo_config_values {
>        	/* section "core" config values */
>        	char *attributes_file;
>       +	int apply_sparse_checkout;
>        };
> 
>      - /*
>      + struct repo_config_values *repo_config_values(struct repository *repo);
>       @@ environment.h: extern int precomposed_unicode;
>        extern int protect_hfs;
>        extern int protect_ntfs;
>      @@ sparse-index.c: static int index_has_unmerged_entries(struct index_state *istate
>        int is_sparse_index_allowed(struct index_state *istate, int flags)
>        {
>       -	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
>      -+	struct repo_config_values *cfg = &the_repository->config_values;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>       +	if (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)
>        		return 0;
> 
>      @@ sparse-index.c: static void clear_skip_worktree_from_present_files_full(struct i
>        void clear_skip_worktree_from_present_files(struct index_state *istate)
>        {
>       -	if (!core_apply_sparse_checkout ||
>      -+	struct repo_config_values *cfg = &the_repository->config_values;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>       +	if (!cfg->apply_sparse_checkout ||
>        	    sparse_expect_files_outside_of_patterns)
>        		return;
> 
> 
>        ## unpack-trees.c ##
>      +@@ unpack-trees.c: int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
>      + 	struct pattern_list pl;
>      + 	int free_pattern_list = 0;
>      + 	struct dir_struct dir = DIR_INIT;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      +
>      + 	if (o->reset == UNPACK_RESET_INVALID)
>      + 		BUG("o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED");
>       @@ unpack-trees.c: int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
>        	if (o->prefix)
>        		update_sparsity_for_prefix(o->prefix, o->src_index);
> 
>       -	if (!core_apply_sparse_checkout || !o->update)
>      -+	if (!the_repository->config_values.apply_sparse_checkout || !o->update)
>      ++	if (!cfg->apply_sparse_checkout || !o->update)
>        		o->skip_sparse_checkout = 1;
>        	if (!o->skip_sparse_checkout) {
>        		memset(&pl, 0, sizeof(pl));
> 
>        ## wt-status.c ##
>       @@ wt-status.c: static void wt_status_check_sparse_checkout(struct repository *r,
>      + {
>        	int skip_worktree = 0;
>        	int i;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
> 
>       -	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
>      -+	if (!the_repository->config_values.apply_sparse_checkout ||
>      ++	if (!cfg->apply_sparse_checkout ||
>       +	    r->index->cache_nr == 0) {
>        		/*
>        		 * Don't compute percentage of checked out files if we
> 3:  e7f37bac87 ! 3:  60451b93a5 environment: move "branch.autoSetupMerge" into `struct repo_config_values`
>      @@ branch.h: enum branch_track {
>        /**
> 
>        ## builtin/branch.c ##
>      +@@ builtin/branch.c: int cmd_branch(int argc,
>      + 	static struct ref_sorting *sorting;
>      + 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
>      + 	struct ref_format format = REF_FORMAT_INIT;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      + 	int ret;
>      +
>      + 	struct option options[] = {
>       @@ builtin/branch.c: int cmd_branch(int argc,
>        	if (!sorting_options.nr)
>        		string_list_append(&sorting_options, "refname");
> 
>       -	track = git_branch_track;
>      -+	track = the_repository->config_values.branch_track;
>      ++	track = cfg->branch_track;
> 
>        	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
>        				   0, &head_oid, NULL);
> 
>        ## builtin/checkout.c ##
>      +@@ builtin/checkout.c: static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
>      + static int checkout_branch(struct checkout_opts *opts,
>      + 			   struct branch_info *new_branch_info)
>      + {
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      + 	int noop_switch = (!new_branch_info->name &&
>      + 			   !opts->new_branch &&
>      + 			   !opts->force_detach);
>       @@ builtin/checkout.c: static int checkout_branch(struct checkout_opts *opts,
>        		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
>        			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
>        	} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
>       -		opts->track = git_branch_track;
>      -+		opts->track = the_repository->config_values.branch_track;
>      ++		opts->track = cfg->branch_track;
> 
>        	if (new_branch_info->name && !new_branch_info->commit)
>        		die(_("Cannot switch branch to a non-commit '%s'"),
> 
>        ## builtin/push.c ##
>      +@@ builtin/push.c: static NORETURN void die_push_simple(struct branch *branch,
>      + 	const char *advice_pushdefault_maybe = "";
>      + 	const char *advice_automergesimple_maybe = "";
>      + 	const char *short_upstream = branch->merge[0]->src;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>      +
>      + 	skip_prefix(short_upstream, "refs/heads/", &short_upstream);
>      +
>       @@ builtin/push.c: static NORETURN void die_push_simple(struct branch *branch,
>        		advice_pushdefault_maybe = _("\n"
>        				 "To choose either option permanently, "
>        				 "see push.default in 'git help config'.\n");
>       -	if (git_branch_track != BRANCH_TRACK_SIMPLE)
>      -+	if (the_repository->config_values.branch_track != BRANCH_TRACK_SIMPLE)
>      ++	if (cfg->branch_track != BRANCH_TRACK_SIMPLE)
>        		advice_automergesimple_maybe = _("\n"
>        				 "To avoid automatically configuring "
>        				 "an upstream branch when its name\n"
> 
>        ## builtin/submodule--helper.c ##
>       @@ builtin/submodule--helper.c: static int module_create_branch(int argc, const char **argv, const char *prefix,
>      + 		N_("git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] [-n|--dry-run] <name> <start-oid> <start-name>"),
>      + 		NULL
>        	};
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
> 
>        	repo_config(the_repository, git_default_config, NULL);
>       -	track = git_branch_track;
>      -+	track = the_repository->config_values.branch_track;
>      ++	track = cfg->branch_track;
>        	argc = parse_options(argc, argv, prefix, options, usage, 0);
> 
>        	if (argc != 3)
>      @@ environment.c: static int git_default_i18n_config(const char *var, const char *v
> 
>        static int git_default_branch_config(const char *var, const char *value)
>        {
>      -+	struct repo_config_values *cfg = &the_repository->config_values;
>      ++	struct repo_config_values *cfg = repo_config_values(the_repository);
>       +
>        	if (!strcmp(var, "branch.autosetupmerge")) {
>        		if (value && !strcmp(value, "always")) {
>      @@ environment.h: struct repo_config_values {
>       +	enum branch_track branch_track;
>        };
> 
>      - /*
>      + struct repo_config_values *repo_config_values(struct repository *repo);
> 
> 


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-03 15:42           ` [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
  2026-02-04 16:39             ` Phillip Wood
@ 2026-02-07  1:14             ` Junio C Hamano
  2026-02-08 11:14               ` Phillip Wood
  1 sibling, 1 reply; 79+ messages in thread
From: Junio C Hamano @ 2026-02-07  1:14 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, toon, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Olamide Caleb Bello <belkid98@gmail.com> writes:

> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
> Helped-by: Junio C Hamano <gitster@pobox.com>
> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
> ---
>  attr.c        |  7 ++++---
>  environment.c | 12 +++++++++---
>  environment.h | 11 ++++++++++-
>  repository.c  | 12 ++++++++++++
>  repository.h  |  7 +++++++
>  5 files changed, 42 insertions(+), 7 deletions(-)

I bisected our recent CI failures that break fuzz smoke test down to
this change.

$ make -j32 \
            NO_CURL=NoThanks \
            CC=clang \
            FUZZ_CXX=clang++ \
            CFLAGS="-fsanitize=fuzzer-no-link,address" \
            LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
            fuzz-all >/dev/null &&
$ oss-fuzz/fuzz-commit-graph -verbosity=0 -runs=1

INFO: Running with entropic power schedule (0xFF, 100).
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
BUG: repository.c:63: repository initialized already
==2050473== ERROR: libFuzzer: deadly signal
    #0 0x56169aaf4065 in __sanitizer_print_stack_trace (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x32a065) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #1 0x56169aa4835c in fuzzer::PrintStackTrace() (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x27e35c) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #2 0x56169aa2d2d7 in fuzzer::Fuzzer::CrashCallback() (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x2632d7) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #3 0x7fb4c6f59def  (/lib/x86_64-linux-gnu/libc.so.6+0x3fdef) (BuildId: 61e1dea1f540b3b4b4d8ec76716e409cec096ece)
    #4 0x7fb4c6fae95b in __pthread_kill_implementation nptl/pthread_kill.c:43:17
    #5 0x7fb4c6f59cc1 in raise signal/../sysdeps/posix/raise.c:26:13
    #6 0x7fb4c6f424ab in abort stdlib/abort.c:73:3
    #7 0x56169ae91316 in BUG_vfl usage.c
    #8 0x56169ae8f527 in BUG_fl (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x6c5527) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #9 0x56169ad79d07 in initialize_repository (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x5afd07) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #10 0x56169ab2c4e2 in LLVMFuzzerTestOneInput (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x3624e2) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #11 0x56169aa2e9da in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x2649da) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #12 0x56169aa2dfe9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x263fe9) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #13 0x56169aa2fdaf in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x265daf) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #14 0x56169aa30390 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x266390) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #15 0x56169aa1cb65 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x252b65) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #16 0x56169aa48ec6 in main (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x27eec6) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
    #17 0x7fb4c6f43ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #18 0x7fb4c6f43d64 in __libc_start_main csu/../csu/libc-start.c:360:3
    #19 0x56169aa10ec0 in _start (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x246ec0) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)

NOTE: libFuzzer has rudimentary signal handlers.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-07  1:14             ` Junio C Hamano
@ 2026-02-08 11:14               ` Phillip Wood
  2026-02-09  8:54                 ` Bello Olamide
  2026-02-10  8:40                 ` Bello Olamide
  0 siblings, 2 replies; 79+ messages in thread
From: Phillip Wood @ 2026-02-08 11:14 UTC (permalink / raw)
  To: Junio C Hamano, Olamide Caleb Bello
  Cc: git, toon, christian.couder, usmanakinyemi202, kaartic.sivaraam,
	me, karthik.188



On 07/02/2026 01:14, Junio C Hamano wrote:
> Olamide Caleb Bello <belkid98@gmail.com> writes:
> 
>> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
>> Mentored-by: Christian Couder <christian.couder@gmail.com>
>> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
>> Helped-by: Junio C Hamano <gitster@pobox.com>
>> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
>> ---
>>   attr.c        |  7 ++++---
>>   environment.c | 12 +++++++++---
>>   environment.h | 11 ++++++++++-
>>   repository.c  | 12 ++++++++++++
>>   repository.h  |  7 +++++++
>>   5 files changed, 42 insertions(+), 7 deletions(-)
> 
> I bisected our recent CI failures that break fuzz smoke test down to
> this change.

The documentation for the LibFuzzer [1] notes

     * The fuzzing engine will execute the fuzz target many times with
       different inputs in the same process.

and the first thing that the callback in oss-fuzz/fuzz-commit-graph.c 
does is

	initialize_repository(the_repository);

so I think the problem is that the assumption that a process will only 
initialize "the_repository" once is incompatible with the way LibFuzzer 
works. Maybe we should add

	memset(the_repository, 0, sizeof(*the_repository));

before the call in initialize_repository()?

Thanks

Phillip

[1] https://llvm.org/docs/LibFuzzer.html

> $ make -j32 \
>              NO_CURL=NoThanks \
>              CC=clang \
>              FUZZ_CXX=clang++ \
>              CFLAGS="-fsanitize=fuzzer-no-link,address" \
>              LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
>              fuzz-all >/dev/null &&
> $ oss-fuzz/fuzz-commit-graph -verbosity=0 -runs=1
> 
> INFO: Running with entropic power schedule (0xFF, 100).
> INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
> INFO: A corpus is not provided, starting from an empty corpus
> BUG: repository.c:63: repository initialized already
> ==2050473== ERROR: libFuzzer: deadly signal
>      #0 0x56169aaf4065 in __sanitizer_print_stack_trace (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x32a065) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #1 0x56169aa4835c in fuzzer::PrintStackTrace() (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x27e35c) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #2 0x56169aa2d2d7 in fuzzer::Fuzzer::CrashCallback() (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x2632d7) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #3 0x7fb4c6f59def  (/lib/x86_64-linux-gnu/libc.so.6+0x3fdef) (BuildId: 61e1dea1f540b3b4b4d8ec76716e409cec096ece)
>      #4 0x7fb4c6fae95b in __pthread_kill_implementation nptl/pthread_kill.c:43:17
>      #5 0x7fb4c6f59cc1 in raise signal/../sysdeps/posix/raise.c:26:13
>      #6 0x7fb4c6f424ab in abort stdlib/abort.c:73:3
>      #7 0x56169ae91316 in BUG_vfl usage.c
>      #8 0x56169ae8f527 in BUG_fl (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x6c5527) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #9 0x56169ad79d07 in initialize_repository (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x5afd07) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #10 0x56169ab2c4e2 in LLVMFuzzerTestOneInput (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x3624e2) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #11 0x56169aa2e9da in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x2649da) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #12 0x56169aa2dfe9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x263fe9) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #13 0x56169aa2fdaf in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x265daf) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #14 0x56169aa30390 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x266390) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #15 0x56169aa1cb65 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x252b65) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #16 0x56169aa48ec6 in main (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x27eec6) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
>      #17 0x7fb4c6f43ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
>      #18 0x7fb4c6f43d64 in __libc_start_main csu/../csu/libc-start.c:360:3
>      #19 0x56169aa10ec0 in _start (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x246ec0) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)
> 
> NOTE: libFuzzer has rudimentary signal handlers.


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-04 16:39             ` Phillip Wood
@ 2026-02-09  8:47               ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-02-09  8:47 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, toon, gitster, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Wed, 4 Feb 2026 at 17:39, Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> On 03/02/2026 15:42, Olamide Caleb Bello wrote:
> > The `core.attributeFile` config value is parsed in
> > git_default_core_config(), loaded eagerly and stored in the global
> > variable `git_attributes_file`. Storing this value in a global variable
> > can lead to it being overwritten by another repository when more than one
> > Git repository run in the same Git process.
> >
> > Create a new struct `repo_config_values` to hold this value and
> > other repository dependent values parsed by `git_default_config()`.
> > This will ensure the current behaviour remains the same while also
> > enabling the libification of Git.
> >
> > An accessor function 'repo_config_values()' is created and used to access
> > the new struct member of the repository struct.
> > This is to ensure that we detect if the struct repository has been
> > initialized and also prevent double initialization of the repository.
>
> Sounds sensible. This paragraph could be reflowed.

Okay

>
> > It is important to note that `git_default_config()` is a wrapper to other
> > `git_default_*_config()` functions such as `git_default_core_config()`.
> > Therefore to access and modify this global variable,
> > the change has to be made `git_default_core_config()`.
>
> I'm not sure what this paragraph is saying with regard to the changes in
> this patch.

Okay I will fix it.

>
> > --- a/environment.c
> > +++ b/environment.c
> > @@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,
> >       /* Add other config variables here and to Documentation/config.adoc. */
> >       return 0;
> >   }
> > +
> > +void repo_config_values_init(struct repo_config_values *cfg)
> > +{
> > +     cfg->attributes_file = NULL;
> > +}
>
> Should we be free()ing cfg->attributes_file when the repository instance
> is free()d? At the moment we're using "the_repository" which points to a
> static instance so it does not make any practical difference but once we
> start storing the config per-repository instance we will need to free
> the config when the repository instance is free()d.

Okay I will keep this in mind.

>
> > diff --git a/repository.c b/repository.c
> > index c7e75215ac..a9b727540f 100644
> > --- a/repository.c
> > +++ b/repository.c
> > @@ -50,13 +50,25 @@ static void set_default_hash_algo(struct repository *repo)
> >       repo_set_hash_algo(repo, algo);
> >   }
> >
> > +struct repo_config_values *repo_config_values(struct repository *repo)
> > +{
> > +     if(!repo->initialized)
> > +             BUG("config values from uninitialized repository");
>
> This check and the one in initialize_repository() below assume that the
> repository instance is zeroed out when it is created, that's a
> reasonable requirement but we should probably document it as our other
> data structures tend not to require that they're zeroed out before they
> are initialized. For example
>
>         struct strbuf buf;
>         strbuf_init(&buf, 0);
>
> is perfectly fine as strbuf_init() does not assume the instance passed
> to it has been zeroed out.
>
> As we only support retrieving values from "the_repository" at the moment
> we should perhaps add
>
>         if (repo != the_repository)
>                 BUG("trying to read config from wrong repository instance");

Okay noted

>
> Everything else looks fine to me
>

Thanks

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-08 11:14               ` Phillip Wood
@ 2026-02-09  8:54                 ` Bello Olamide
  2026-02-10  8:40                 ` Bello Olamide
  1 sibling, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-02-09  8:54 UTC (permalink / raw)
  To: phillip.wood
  Cc: Junio C Hamano, git, toon, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Sun, 8 Feb 2026 at 12:14, Phillip Wood <phillip.wood123@gmail.com> wrote:
>
>
>
> On 07/02/2026 01:14, Junio C Hamano wrote:
> > Olamide Caleb Bello <belkid98@gmail.com> writes:
> >
> >> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
> >> Mentored-by: Christian Couder <christian.couder@gmail.com>
> >> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
> >> Helped-by: Junio C Hamano <gitster@pobox.com>
> >> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
> >> ---
> >>   attr.c        |  7 ++++---
> >>   environment.c | 12 +++++++++---
> >>   environment.h | 11 ++++++++++-
> >>   repository.c  | 12 ++++++++++++
> >>   repository.h  |  7 +++++++
> >>   5 files changed, 42 insertions(+), 7 deletions(-)
> >
> > I bisected our recent CI failures that break fuzz smoke test down to
> > this change.
>
> The documentation for the LibFuzzer [1] notes
>
>      * The fuzzing engine will execute the fuzz target many times with
>        different inputs in the same process.
>
> and the first thing that the callback in oss-fuzz/fuzz-commit-graph.c
> does is
>
>         initialize_repository(the_repository);
>
> so I think the problem is that the assumption that a process will only
> initialize "the_repository" once is incompatible with the way LibFuzzer
> works. Maybe we should add
>
>         memset(the_repository, 0, sizeof(*the_repository));
>
> before the call in initialize_repository()?
>
> Thanks
>
> Phillip
>
> [1] https://llvm.org/docs/LibFuzzer.html
>
Thank you Phillip, I will try this.
I noticed something like this.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-08 11:14               ` Phillip Wood
  2026-02-09  8:54                 ` Bello Olamide
@ 2026-02-10  8:40                 ` Bello Olamide
  1 sibling, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-02-10  8:40 UTC (permalink / raw)
  To: phillip.wood
  Cc: Junio C Hamano, git, toon, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Sun, 8 Feb 2026 at 12:14, Phillip Wood <phillip.wood123@gmail.com> wrote:
>
>
>
> On 07/02/2026 01:14, Junio C Hamano wrote:
> > Olamide Caleb Bello <belkid98@gmail.com> writes:
> >
> >> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
> >> Mentored-by: Christian Couder <christian.couder@gmail.com>
> >> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
> >> Helped-by: Junio C Hamano <gitster@pobox.com>
> >> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
> >> ---
> >>   attr.c        |  7 ++++---
> >>   environment.c | 12 +++++++++---
> >>   environment.h | 11 ++++++++++-
> >>   repository.c  | 12 ++++++++++++
> >>   repository.h  |  7 +++++++
> >>   5 files changed, 42 insertions(+), 7 deletions(-)
> >
> > I bisected our recent CI failures that break fuzz smoke test down to
> > this change.
>
> The documentation for the LibFuzzer [1] notes
>
>      * The fuzzing engine will execute the fuzz target many times with
>        different inputs in the same process.
>
> and the first thing that the callback in oss-fuzz/fuzz-commit-graph.c
> does is
>
>         initialize_repository(the_repository);
>
> so I think the problem is that the assumption that a process will only
> initialize "the_repository" once is incompatible with the way LibFuzzer
> works. Maybe we should add
>
>         memset(the_repository, 0, sizeof(*the_repository));
>
> before the call in initialize_repository()?
>
> Thanks
>
> Phillip
>
> [1] https://llvm.org/docs/LibFuzzer.html
>

Hello Phillip,
thank you for your reviews and assistance so far.

So I moved the code
 memset(the_repository, 0, sizeof(*the_repository))
into the fuzz-commit-graph.c Fuzzer test before the call to
initialize_repository().
This made the fuzzer smoke tests pass.

But the line below,
`if (repo != the_repository)
    BUG("trying to read config from wrong repository instance")`,
in the repo_config_values() accessor function
to make sure we are reading the config_values only for the_repository ,
makes many tests fail.

I believe this is because repo_init() calls initialize_repository() and
repo_init() is also called in repo_submodule_init() when creating a
subrepo from a super project, and this subrepo passed to repo_init() is
not the_repository.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
@ 2026-02-10 10:17 Bello Caleb Olamide
  2026-02-10 15:07 ` Phillip Wood
  2026-02-11  9:31 ` Phillip Wood
  0 siblings, 2 replies; 79+ messages in thread
From: Bello Caleb Olamide @ 2026-02-10 10:17 UTC (permalink / raw)
  To: Bello Olamide
  Cc: Junio C Hamano, git, toon, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, phillip.wood

This is how I implemented the suggestion

diff --git a/oss-fuzz/fuzz-commit-graph.c b/oss-fuzz/fuzz-commit-graph.c
index fb8b8787a4..59bbb849d1 100644
--- a/oss-fuzz/fuzz-commit-graph.c
+++ b/oss-fuzz/fuzz-commit-graph.c
@@ -10,6 +10,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
 {
 	struct commit_graph *g;
 
+	memset(the_repository, 0, sizeof(*the_repository));
 	initialize_repository(the_repository);
 
 	/*
diff --git a/repository.c b/repository.c
index c7e75215ac..0af40b016e 100644
--- a/repository.c
+++ b/repository.c
@@ -50,13 +50,27 @@ static void set_default_hash_algo(struct repository *repo)
 	repo_set_hash_algo(repo, algo);
 }
 
+struct repo_config_values *repo_config_values(struct repository *repo)
+{
+	if (repo != the_repository)
+		BUG("trying to read config from wrong repository instance");
+	if(!repo->initialized)
+		BUG("config values from uninitialized repository");
+	return &repo->config_values_private_;
+}
+
 void initialize_repository(struct repository *repo)
 {
+	if (repo->initialized)
+		BUG("repository initialized already");
+	repo->initialized = true;
+
 	repo->remote_state = remote_state_new();
 	repo->parsed_objects = parsed_object_pool_new(repo);
 	ALLOC_ARRAY(repo->index, 1);
 	index_state_init(repo->index, repo);
 	repo->check_deprecated_config = true;
+	repo_config_values_init(repo_config_values(repo));
 
 	/*
 	 * When a command runs inside a repository, it learns what

Some of the tests that fail are related to the submodule and a
couple output is shown below

./t7412-submodule-absorbgitdirs.sh  -i -v
...
Initialized empty Git repository in /home/ubuntu/Code/open_source/git/t/trash directory.t7412-submodule-absorbgitdirs/sub1/.git/
[master (root-commit) 50e526b] first
 Author: A U Thor <author@example.com>
 1 file changed, 1 insertion(+)
 create mode 100644 first.t
BUG: repository.c:56: trying to read config from wrong repository instance
Aborted (core dumped)
not ok 1 - setup a real submodule
#
# cwd="$(pwd)" &&
# git init sub1 &&
# test_commit -C sub1 first &&
# git submodule add ./sub1 &&
# test_tick &&
# git commit -m superproject
#
1..1

./t4027-diff-submodule.sh  -i -v
...
Initialized empty Git repository in /home/ubuntu/Code/open_source/git/t/trash directory.t4027-diff-submodule/sub/.git/
[master (root-commit) 4431e0b] submodule
 Author: A U Thor <author@example.com>
 1 file changed, 1 insertion(+)
 create mode 100644 world
BUG: repository.c:56: trying to read config from wrong repository instance
Aborted (core dumped)
not ok 1 - setup
#
# test_tick &&
# test_create_repo sub &&
# (
# cd sub &&
# echo hello >world &&
# git add world &&
# git commit -m submodule
# ) &&
#
# test_tick &&
# echo frotz >nitfol &&
# git add nitfol sub &&
# git commit -m superproject &&
#
# (
# cd sub &&
# echo goodbye >world &&
# git add world &&
# git commit -m "submodule #2"
# ) &&
#
# git -C sub rev-list HEAD >revs &&
# set x $(cat revs) &&
# echo ":160000 160000 $3 $ZERO_OID M sub" >expect &&
# subtip=$3 subprev=$2
#
1..1

Thanks

^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-10 10:17 [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally Bello Caleb Olamide
@ 2026-02-10 15:07 ` Phillip Wood
  2026-02-11  8:05   ` Bello Olamide
  2026-02-11  9:31 ` Phillip Wood
  1 sibling, 1 reply; 79+ messages in thread
From: Phillip Wood @ 2026-02-10 15:07 UTC (permalink / raw)
  To: Bello Caleb Olamide
  Cc: Junio C Hamano, git, toon, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, phillip.wood

On 10/02/2026 10:17, Bello Caleb Olamide wrote:

> Initialized empty Git repository in /home/ubuntu/Code/open_source/git/t/trash directory.t4027-diff-submodule/sub/.git/
> [master (root-commit) 4431e0b] submodule
>   Author: A U Thor <author@example.com>
>   1 file changed, 1 insertion(+)
>   create mode 100644 world
> BUG: repository.c:56: trying to read config from wrong repository instance
> Aborted (core dumped)

What does the backtrace show if you load the coredump into gdb? If 
you're using systemd you should be able to run

     coredumpctl gdb

to start gdb on the last coredump (you can list them with "coredumpctl 
list" if you need to select a different one) and then you can run

     bt full

in gdb to get a backtrace.

If you have an actual coredump file then you can just run "gdb 
path/to/coredump"

Thanks

Phillip

> not ok 1 - setup
> #
> # test_tick &&
> # test_create_repo sub &&
> # (
> # cd sub &&
> # echo hello >world &&
> # git add world &&
> # git commit -m submodule
> # ) &&
> #
> # test_tick &&
> # echo frotz >nitfol &&
> # git add nitfol sub &&
> # git commit -m superproject &&
> #
> # (
> # cd sub &&
> # echo goodbye >world &&
> # git add world &&
> # git commit -m "submodule #2"
> # ) &&
> #
> # git -C sub rev-list HEAD >revs &&
> # set x $(cat revs) &&
> # echo ":160000 160000 $3 $ZERO_OID M sub" >expect &&
> # subtip=$3 subprev=$2
> #
> 1..1
> 
> Thanks
> 


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-10 15:07 ` Phillip Wood
@ 2026-02-11  8:05   ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-02-11  8:05 UTC (permalink / raw)
  To: phillip.wood
  Cc: Junio C Hamano, git, toon, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Tue, 10 Feb 2026 at 16:07, Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> On 10/02/2026 10:17, Bello Caleb Olamide wrote:
>
> > Initialized empty Git repository in /home/ubuntu/Code/open_source/git/t/trash directory.t4027-diff-submodule/sub/.git/
> > [master (root-commit) 4431e0b] submodule
> >   Author: A U Thor <author@example.com>
> >   1 file changed, 1 insertion(+)
> >   create mode 100644 world
> > BUG: repository.c:56: trying to read config from wrong repository instance
> > Aborted (core dumped)
>
> What does the backtrace show if you load the coredump into gdb? If
> you're using systemd you should be able to run
>
>      coredumpctl gdb
>
> to start gdb on the last coredump (you can list them with "coredumpctl
> list" if you need to select a different one) and then you can run
>
>      bt full
>
> in gdb to get a backtrace.
>
> If you have an actual coredump file then you can just run "gdb
> path/to/coredump"
>

Thank you Phillip.
I have been able to generate the backtrace

coredumpctl gdb
           PID: 43422 (git)
           UID: 1000 (ubuntu)
           GID: 1000 (ubuntu)
        Signal: 6 (ABRT)
     Timestamp: Wed 2026-02-11 08:43:11 WAT (15s ago)
  Command Line: git submodule--helper add -- ./S
    Executable: /home/ubuntu/Code/open_source/git/git
 Control Group:
/user.slice/user-1000.slice/user@1000.service/app.slice/app-org.gnome.Terminal.slice/vte-spawn-29116a64-3a3d-4a87-b321-1dd9a0e862c7.scope
          Unit: user@1000.service
     User Unit: vte-spawn-29116a64-3a3d-4a87-b321-1dd9a0e862c7.scope
         Slice: user-1000.slice
     Owner UID: 1000 (ubuntu)
       Boot ID: a3430056a91d43f49b602796f4eafc8c
    Machine ID: 4fb1781ac9f64d5cad47e77f1ed4f268
      Hostname: ubuntu
       Storage:
/var/lib/systemd/coredump/core.git.1000.a3430056a91d43f49b602796f4eafc8c.43422.1770795791000000.zst
(present)
     Disk Size: 43.4K
       Message: Process 43422 (git) of user 1000 dumped core.

                Found module /home/ubuntu/Code/open_source/git/git
with build-id: d3afb1a6b38e0303f833747d30b120a5c520f40e
                Found module linux-vdso.so.1 with build-id:
c1c6868625bceb6f487c419392bd09e4edbfc5d9
                Found module libc.so.6 with build-id:
9f32d43c341bff10b9e7196738eedcfc4f3cc36c
                Found module libz.so.1 with build-id:
b781927da654e744ed29ff39815bef9c750eaf24
                Found module libc_malloc_debug.so.0 with build-id:
1e0d2faf0cfdf8b3b9940dc5937792836187f911
                Stack trace of thread 43422:
                #0  0x0000ffffb8802008 __pthread_kill_implementation
(libc.so.6 + 0x82008)
                #1  0x0000ffffb87ba83c __GI_raise (libc.so.6 + 0x3a83c)
                #2  0x0000ffffb87a7134 __GI_abort (libc.so.6 + 0x27134)
                #3  0x0000aaaab14af398 n/a
(/home/ubuntu/Code/open_source/git/git + 0x39f398)
                #4  0x0000aaaab14af398 n/a
(/home/ubuntu/Code/open_source/git/git + 0x39f398)
                #5  0x0000aaaab14af454 n/a
(/home/ubuntu/Code/open_source/git/git + 0x39f454)
                #6  0x0000aaaab14257ac n/a
(/home/ubuntu/Code/open_source/git/git + 0x3157ac)
                #7  0x0000aaaab1425890 n/a
(/home/ubuntu/Code/open_source/git/git + 0x315890)
                #8  0x0000aaaab142609c n/a
(/home/ubuntu/Code/open_source/git/git + 0x31609c)
                #9  0x0000aaaab142625c n/a
(/home/ubuntu/Code/open_source/git/git + 0x31625c)
                #10 0x0000aaaab13eeedc n/a
(/home/ubuntu/Code/open_source/git/git + 0x2deedc)
                #11 0x0000aaaab13eeab8 n/a
(/home/ubuntu/Code/open_source/git/git + 0x2deab8)
                #12 0x0000aaaab1249088 n/a
(/home/ubuntu/Code/open_source/git/git + 0x139088)
                #13 0x0000aaaab1249650 n/a
(/home/ubuntu/Code/open_source/git/git + 0x139650)
                #14 0x0000aaaab1249c84 n/a
(/home/ubuntu/Code/open_source/git/git + 0x139c84)
                #15 0x0000aaaab1132314 n/a
(/home/ubuntu/Code/open_source/git/git + 0x22314)
                #16 0x0000aaaab11328b0 n/a
(/home/ubuntu/Code/open_source/git/git + 0x228b0)
                #17 0x0000aaaab1132bd4 n/a
(/home/ubuntu/Code/open_source/git/git + 0x22bd4)
                #18 0x0000aaaab1133074 n/a
(/home/ubuntu/Code/open_source/git/git + 0x23074)
                #19 0x0000aaaab125e86c n/a
(/home/ubuntu/Code/open_source/git/git + 0x14e86c)
                #20 0x0000ffffb87a7400 __libc_start_call_main
(libc.so.6 + 0x27400)
                #21 0x0000ffffb89cf370 n/a (n/a + 0x0)
                #22 0x0000ffffb89cf370 n/a (n/a + 0x0)
                #23 0x3d455441445f524f n/a (n/a + 0x0)

GNU gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "aarch64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /home/ubuntu/Code/open_source/git/git...
[New LWP 43422]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
Core was generated by `git submodule--helper add -- ./S'.
Program terminated with signal SIGABRT, Aborted.
#0  __pthread_kill_implementation (threadid=281473778995232,
signo=signo@entry=6,
    no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
44 ./nptl/pthread_kill.c: No such file or directory.
(gdb) bt full
#0  __pthread_kill_implementation (threadid=281473778995232,
signo=signo@entry=6,
    no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
        tid = 43422
        ret = 0
        pd = 0xffffb89c5020
        old_mask = {__val = {16453191242184589568, 281474327489088,
187650095641156,
            281474327489568, 281474327489568, 281474327489520,
18446743528248704984,
            17298308644996116495, 17298308644996116495, 17298308644996116480,
            16453191242184589568, 281474327488672, 187650095637040,
281474327488752,
            18446743042917400560, 281474327489088}}
        ret = <optimized out>
#1  0x0000ffffb8802054 in __pthread_kill_internal (signo=6,
threadid=<optimized out>)
    at ./nptl/pthread_kill.c:78
No locals.
#2  0x0000ffffb87ba83c in __GI_raise (sig=sig@entry=6) at
../sysdeps/posix/raise.c:26
        ret = <optimized out>
#3  0x0000ffffb87a7134 in __GI_abort () at ./stdlib/abort.c:79
        save_stage = 1
        act = {__sigaction_handler = {sa_handler = 0xaaaad0012bfb,
            sa_sigaction = 0xaaaad0012bfb}, sa_mask = {__val =
{187650610900023, 0, 0,
              0, 0, 0, 4294967295, 0, 187651416064000, 0, 281473778608136,
              281474327489232, 187650095641436, 281474327489264,
281473778608284,
              187650610899888}}, sa_flags = -1, sa_restorer = 0x88}
        sigs = {__val = {32, 187650096456616, 243494161448, 187650096456672,
            281474327489824, 8099004987637978434, 3348833620946678639,
            18374721914061273699, 281474327488992, 281474904195073,
187650610899963,
            187650610899963, 187650610899963, 187650610899963, 187650610899964,
            187650610900023}}
#4  0x0000aaaab14af398 in BUG_vfl (file=0xaaaab15763e0 "repository.c", line=56,
    fmt=0xaaaab15763a8 "trying to read config from wrong repository instance",
    params=...) at usage.c:350
        params_copy = {__stack = 0xffffd94dac20, __gr_top = 0xffffd94dac20,
          __vr_top = 0xffffd94dabf0, __gr_offs = -40, __vr_offs = -128}
        in_bug = 1
#5  0x0000aaaab14af454 in BUG_fl (file=0xaaaab15763e0 "repository.c", line=56,
    fmt=0xaaaab15763a8 "trying to read config from wrong repository instance")
--Type <RET> for more, q to quit, c to continue without paging--
    at usage.c:360
        ap = {__stack = 0xffffd94dac20, __gr_top = 0xffffd94dac20,
          __vr_top = 0xffffd94dabf0, __gr_offs = -40, __vr_offs = -128}
#6  0x0000aaaab14257ac in repo_config_values (repo=0xaaaad0012900) at
repository.c:56
No locals.
#7  0x0000aaaab1425890 in initialize_repository (repo=0xaaaad0012900)
    at repository.c:73
No locals.
#8  0x0000aaaab142609c in repo_init (repo=0xaaaad0012900,
    gitdir=0xaaaad0012b10 "/home/ubuntu/Code/open_source/git/t/trash
directory.t7422-submodule-output/S/.git",
    worktree=0xaaaad0012bb0 "/home/ubuntu/Code/open_source/git/t/trash
directory.t7422-submodule-output/S") at repository.c:284
        format = {version = -1, precious_objects = 0, partial_clone = 0x0,
          worktree_config = 0, relative_worktrees = 0, is_bare = -1,
hash_algo = 1,
          compat_hash_algo = 0, ref_storage_format = REF_STORAGE_FORMAT_FILES,
          sparse_index = 0, work_tree = 0x0, unknown_extensions = {items = 0x0,
            nr = 0, alloc = 0, strdup_strings = 1, cmp = 0x0},
v1_only_extensions = {
            items = 0x0, nr = 0, alloc = 0, strdup_strings = 1, cmp = 0x0}}
#9  0x0000aaaab142625c in repo_submodule_init (subrepo=0xaaaad0012900,
    superproject=0xaaaab1656c98 <the_repo>, path=0xaaaad0012080 "S",
    treeish_name=0xaaaab1558cc8 <null_oid_sha1>) at repository.c:329
        gitdir = {alloc = 136, len = 81,
          buf = 0xaaaad0012b10
"/home/ubuntu/Code/open_source/git/t/trash
directory.t7422-submodule-output/S/.git"}
        worktree = {alloc = 136, len = 76,
          buf = 0xaaaad0012bb0
"/home/ubuntu/Code/open_source/git/t/trash
directory.t7422-submodule-output/S"}
        ret = 0
#10 0x0000aaaab13eeedc in repo_get_submodule_ref_store (
    repo=0xaaaab1656c98 <the_repo>, submodule=0xaaaad0012080 "S") at refs.c:2258
        submodule_sb = {alloc = 24, len = 6, buf = 0xaaaad0012500 "S/.git"}
        refs = 0x0
        to_free = 0x0
        len = 1
        subrepo = 0xaaaad0012900
--Type <RET> for more, q to quit, c to continue without paging--
#11 0x0000aaaab13eeab8 in repo_resolve_gitlink_ref (r=0xaaaab1656c98 <the_repo>,
    submodule=0xaaaad0012080 "S", refname=0xaaaab1537d50 "HEAD",
oid=0xffffd94dae80)
    at refs.c:2141
        refs = 0x1
        flags = 1
#12 0x0000aaaab1249088 in die_on_repo_without_commits (path=0xaaaad0012080 "S")
    at builtin/submodule--helper.c:3423
        oid = {hash = '\000' <repeats 24 times>, "\200
\001\u042a\252\000", algo = 0}
        sb = {alloc = 24, len = 1, buf = 0xaaaad00125f0 "S"}
#13 0x0000aaaab1249650 in module_add (argc=1, argv=0xaaaad0010ce0, prefix=0x0,
    repo=0xaaaab1656c98 <the_repo>) at builtin/submodule--helper.c:3522
        force = 0
        quiet = 0
        progress = 0
        dissociate = 0
        add_data = {prefix = 0x0, branch = 0x0, reference_path = 0x0,
          sm_path = 0xaaaad0012080 "S", sm_name = 0x0, repo =
0xaaaad0010ae0 "./S",
          realrepo = 0xaaaad0012590
"/home/ubuntu/Code/open_source/git/t/trash
directory.t7422-submodule-output/S", ref_storage_format =
REF_STORAGE_FORMAT_UNKNOWN,
          depth = -1, force = 0, quiet = 0, progress = 0, dissociate = 0}
        ref_storage_format = 0x0
        to_free = 0xaaaad0012590
"/home/ubuntu/Code/open_source/git/t/trash
directory.t7422-submodule-output/S"
        existing = 0xaaaab156aa08
        buf = {alloc = 0, len = 0, buf = 0xaaaab16572f8 <strbuf_slopbuf> ""}
        sm_name_to_free = 0x0
        options = {{type = OPTION_STRING, short_name = 98,
            long_name = 0xaaaab1539658 "branch", value = 0xffffd94daf60,
            precision = 0, argh = 0xaaaab1539658 "branch",
            help = 0xaaaab1539c80 "branch of repository to add as submodule",
            flags = 0, callback = 0x0, defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0x0}, {type = OPTION_COUNTUP, short_name = 102,
            long_name = 0xaaaab1538070 "force", value =
0xffffd94daee8, precision = 4,
            argh = 0x0,
            help = 0xaaaab1539cb0 "allow adding an otherwise ignored
submodule path",
            flags = (PARSE_OPT_NOARG | PARSE_OPT_NOCOMPLETE), callback = 0x0,
--Type <RET> for more, q to quit, c to continue without paging--
            defval = 0, ll_callback = 0x0, extra = 0, subcommand_fn = 0x0}, {
            type = OPTION_COUNTUP, short_name = 113,
            long_name = 0xaaaab15375e0 "quiet", value =
0xffffd94daeec, precision = 4,
            argh = 0x0, help = 0xaaaab1539748 "print only error messages",
            flags = PARSE_OPT_NOARG, callback = 0x0, defval = 0,
ll_callback = 0x0,
            extra = 0, subcommand_fn = 0x0}, {type = OPTION_SET_INT,
short_name = 0,
            long_name = 0xaaaab15387a0 "progress", value = 0xffffd94daef0,
            precision = 4, argh = 0x0, help = 0xaaaab15387b0 "force
cloning progress",
            flags = PARSE_OPT_NOARG, callback = 0x0, defval = 1,
ll_callback = 0x0,
            extra = 0, subcommand_fn = 0x0}, {type = OPTION_STRING,
short_name = 0,
            long_name = 0xaaaab15386a8 "reference", value = 0xffffd94daf68,
            precision = 0, argh = 0xaaaab1539ce8 "repository",
            help = 0xaaaab15386c0 "reference repository", flags = 0,
callback = 0x0,
            defval = 0, ll_callback = 0x0, extra = 0, subcommand_fn = 0x0}, {
            type = OPTION_STRING, short_name = 0,
            long_name = 0xaaaab15386d8 "ref-format", value = 0xffffd94daf00,
            precision = 0, argh = 0xaaaab15386e8 "format",
            help = 0xaaaab15386f0 "specify the reference format to
use", flags = 0,
            callback = 0x0, defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0x0}, {type = OPTION_SET_INT, short_name = 0,
            long_name = 0xaaaab1538718 "dissociate", value = 0xffffd94daef4,
            precision = 4, argh = 0x0,
            help = 0xaaaab1539cf8 "borrow the objects from reference
repositories",
            flags = PARSE_OPT_NOARG, callback = 0x0, defval = 1,
ll_callback = 0x0,
            extra = 0, subcommand_fn = 0x0}, {type = OPTION_STRING,
short_name = 0,
            long_name = 0xaaaab1538648 "name", value = 0xffffd94daf78,
precision = 0,
            argh = 0xaaaab1538648 "name",
            help = 0xaaaab1539d28 "sets the submodule's name to the
given string instead of defaulting to its path", flags = 0, callback =
0x0, defval = 0, ll_callback = 0x0,
            extra = 0, subcommand_fn = 0x0}, {type = OPTION_INTEGER,
short_name = 0,
            long_name = 0xaaaab1538750 "depth", value =
0xffffd94daf94, precision = 4,
            argh = 0xaaaab1537cf0 "n",
            help = 0xaaaab1538758 "depth for shallow clones", flags = 0,
            callback = 0x0, defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0x0}, {type = OPTION_END, short_name = 0,
long_name = 0x0,
            value = 0x0, precision = 0, argh = 0x0, help = 0x0, flags = 0,
--Type <RET> for more, q to quit, c to continue without paging--
            callback = 0x0, defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0x0}}
        usage = {
          0xaaaab1539d78 "git submodule add [<options>] [--]
<repository> [<path>]",
          0x0}
        sb = {alloc = 0, len = 0, buf = 0xaaaab16572f8 <strbuf_slopbuf> ""}
        ret = 1
#14 0x0000aaaab1249c84 in cmd_submodule__helper (argc=3, argv=0xaaaad0010ce0,
    prefix=0x0, repo=0xaaaab1656c98 <the_repo>) at
builtin/submodule--helper.c:3616
        fn = 0xaaaab12490dc <module_add>
        usage = {0xaaaab1539ec0 "git submodule--helper <command>", 0x0}
        options = {{type = OPTION_SUBCOMMAND, short_name = 0,
            long_name = 0xaaaab15384b8 "clone", value =
0xffffd94db430, precision = 0,
            argh = 0x0, help = 0x0, flags = 0, callback = 0x0, defval = 0,
            ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab124403c <module_clone>}, {type =
OPTION_SUBCOMMAND,
            short_name = 0, long_name = 0xaaaab1539b98 "add", value =
0xffffd94db430,
            precision = 0, argh = 0x0, help = 0x0, flags = 0, callback = 0x0,
            defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab12490dc <module_add>}, {type =
OPTION_SUBCOMMAND,
            short_name = 0, long_name = 0xaaaab1538f78 "update",
            value = 0xffffd94db430, precision = 0, argh = 0x0, help =
0x0, flags = 0,
            callback = 0x0, defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab1246e44 <module_update>}, {
            type = OPTION_SUBCOMMAND, short_name = 0,
            long_name = 0xaaaab1537500 "foreach", value = 0xffffd94db430,
            precision = 0, argh = 0x0, help = 0x0, flags = 0, callback = 0x0,
            defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab123fe24 <module_foreach>}, {
            type = OPTION_SUBCOMMAND, short_name = 0,
            long_name = 0xaaaab1539110 "init", value = 0xffffd94db430,
precision = 0,
            argh = 0x0, help = 0x0, flags = 0, callback = 0x0, defval = 0,
            ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab1240418 <module_init>}, {type =
OPTION_SUBCOMMAND,
            short_name = 0, long_name = 0xaaaab15378e8 "status",
            value = 0xffffd94db430, precision = 0, argh = 0x0, help =
0x0, flags = 0,
--Type <RET> for more, q to quit, c to continue without paging--
            callback = 0x0, defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab1240ca8 <module_status>}, {
            type = OPTION_SUBCOMMAND, short_name = 0,
            long_name = 0xaaaab1537e48 "sync", value = 0xffffd94db430,
precision = 0,
            argh = 0x0, help = 0x0, flags = 0, callback = 0x0, defval = 0,
            ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab1242a0c <module_sync>}, {type =
OPTION_SUBCOMMAND,
            short_name = 0, long_name = 0xaaaab1539ee0 "deinit",
            value = 0xffffd94db430, precision = 0, argh = 0x0, help =
0x0, flags = 0,
            callback = 0x0, defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab1243074 <module_deinit>}, {
            type = OPTION_SUBCOMMAND, short_name = 0,
            long_name = 0xaaaab1539ee8 "summary", value = 0xffffd94db430,
            precision = 0, argh = 0x0, help = 0x0, flags = 0, callback = 0x0,
            defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab1242164 <module_summary>}, {
            type = OPTION_SUBCOMMAND, short_name = 0,
            long_name = 0xaaaab1539ef0 "push-check", value = 0xffffd94db430,
            precision = 0, argh = 0x0, help = 0x0, flags = 0, callback = 0x0,
            defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab12476f4 <push_check>}, {type =
OPTION_SUBCOMMAND,
            short_name = 0, long_name = 0xaaaab1539f00 "absorbgitdirs",
            value = 0xffffd94db430, precision = 0, argh = 0x0, help =
0x0, flags = 0,
            callback = 0x0, defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab12479a8 <absorb_git_dirs>}, {
            type = OPTION_SUBCOMMAND, short_name = 0,
            long_name = 0xaaaab1539f10 "set-url", value = 0xffffd94db430,
            precision = 0, argh = 0x0, help = 0x0, flags = 0, callback = 0x0,
            defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab1247b40 <module_set_url>}, {
            type = OPTION_SUBCOMMAND, short_name = 0,
            long_name = 0xaaaab1539f18 "set-branch", value = 0xffffd94db430,
            precision = 0, argh = 0x0, help = 0x0, flags = 0, callback = 0x0,
            defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab1247d74 <module_set_branch>}, {
            type = OPTION_SUBCOMMAND, short_name = 0,
--Type <RET> for more, q to quit, c to continue without paging--
            long_name = 0xaaaab1539f28 "create-branch", value = 0xffffd94db430,
            precision = 0, argh = 0x0, help = 0x0, flags = 0, callback = 0x0,
            defval = 0, ll_callback = 0x0, extra = 0,
            subcommand_fn = 0xaaaab1248038 <module_create_branch>}, {
            type = OPTION_END, short_name = 0, long_name = 0x0, value = 0x0,
            precision = 0, argh = 0x0, help = 0x0, flags = 0, callback = 0x0,
            defval = 0, ll_callback = 0x0, extra = 0, subcommand_fn = 0x0}}
#15 0x0000aaaab1132314 in run_builtin (p=0xaaaab1624be8 <commands+3024>, argc=4,
    argv=0xaaaad0010ce0, repo=0xaaaab1656c98 <the_repo>) at git.c:506
        status = 0
        help = 0
        no_repo = 0
        st = {st_dev = 187650095680628, st_ino = 281474327493248,
          st_mode = 2974518388, st_nlink = 43690, st_uid = 3645750856,
st_gid = 65535,
          st_rdev = 5, __pad1 = 187650097155112, st_size = 281473779032128,
          st_blksize = -1322915768, __pad2 = 43690, st_blocks = 40, st_atim = {
            tv_sec = 281474327493280, tv_nsec = 187650091976020}, st_mtim = {
            tv_sec = 5, tv_nsec = 8}, st_ctim = {tv_sec = 281474327493408,
            tv_nsec = 187650091985032}, __glibc_reserved = {8, 0}}
        prefix = 0x0
        run_setup = 1
        __PRETTY_FUNCTION__ = "run_builtin"
#16 0x0000aaaab11328b0 in handle_builtin (args=0xffffd94dbc50) at git.c:779
        argv_copy = 0xaaaad0010ce0
        ret = 43690
        cmd = 0xaaaad00109b0 "submodule--helper"
        builtin = 0xaaaab1624be8 <commands+3024>
#17 0x0000aaaab1132bd4 in run_argv (args=0xffffd94dbc50) at git.c:862
        done_alias = 0
        expanded_aliases = {items = 0x0, nr = 0, alloc = 0, strdup_strings = 1,
          cmp = 0x0}
#18 0x0000aaaab1133074 in cmd_main (argc=4, argv=0xffffd94dbe50) at git.c:984
        was_alias = 65535
        args = {v = 0xaaaad00109d0, nr = 4, alloc = 24}
        cmd = 0xffffd94dc7ff "submodule--helper"
        done_help = 0
--Type <RET> for more, q to quit, c to continue without paging--
#19 0x0000aaaab125e86c in main (argc=5, argv=0xffffd94dbe48) at common-main.c:9
        result = 0

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-10 10:17 [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally Bello Caleb Olamide
  2026-02-10 15:07 ` Phillip Wood
@ 2026-02-11  9:31 ` Phillip Wood
  2026-02-11 12:05   ` Bello Olamide
  2026-02-11 16:46   ` Junio C Hamano
  1 sibling, 2 replies; 79+ messages in thread
From: Phillip Wood @ 2026-02-11  9:31 UTC (permalink / raw)
  To: Bello Caleb Olamide
  Cc: Junio C Hamano, git, toon, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188, phillip.wood

Thanks for the backtrace which helped me spot the problem though I 
should have spotted this yesterday. The problem is in 
initialize_repository()

>   void initialize_repository(struct repository *repo)
>   {
> +	if (repo->initialized)
> +		BUG("repository initialized already");
> +	repo->initialized = true;
> +
>   	repo->remote_state = remote_state_new();
>   	repo->parsed_objects = parsed_object_pool_new(repo);
>   	ALLOC_ARRAY(repo->index, 1);
>   	index_state_init(repo->index, repo);
>   	repo->check_deprecated_config = true;
> +	repo_config_values_init(repo_config_values(repo));

Here you need to use repo->config_values_private_ instead of using the 
accessor as it is fine to initialize the config values to their defaults 
in any instance, it is only when we read them that we want to assert 
that we're reading from "the_repository".

Thanks

Phillip

>   
>   	/*
>   	 * When a command runs inside a repository, it learns what
> 
> Some of the tests that fail are related to the submodule and a
> couple output is shown below
> 
> ./t7412-submodule-absorbgitdirs.sh  -i -v
> ...
> Initialized empty Git repository in /home/ubuntu/Code/open_source/git/t/trash directory.t7412-submodule-absorbgitdirs/sub1/.git/
> [master (root-commit) 50e526b] first
>   Author: A U Thor <author@example.com>
>   1 file changed, 1 insertion(+)
>   create mode 100644 first.t
> BUG: repository.c:56: trying to read config from wrong repository instance
> Aborted (core dumped)
> not ok 1 - setup a real submodule
> #
> # cwd="$(pwd)" &&
> # git init sub1 &&
> # test_commit -C sub1 first &&
> # git submodule add ./sub1 &&
> # test_tick &&
> # git commit -m superproject
> #
> 1..1
> 
> ./t4027-diff-submodule.sh  -i -v
> ...
> Initialized empty Git repository in /home/ubuntu/Code/open_source/git/t/trash directory.t4027-diff-submodule/sub/.git/
> [master (root-commit) 4431e0b] submodule
>   Author: A U Thor <author@example.com>
>   1 file changed, 1 insertion(+)
>   create mode 100644 world
> BUG: repository.c:56: trying to read config from wrong repository instance
> Aborted (core dumped)
> not ok 1 - setup
> #
> # test_tick &&
> # test_create_repo sub &&
> # (
> # cd sub &&
> # echo hello >world &&
> # git add world &&
> # git commit -m submodule
> # ) &&
> #
> # test_tick &&
> # echo frotz >nitfol &&
> # git add nitfol sub &&
> # git commit -m superproject &&
> #
> # (
> # cd sub &&
> # echo goodbye >world &&
> # git add world &&
> # git commit -m "submodule #2"
> # ) &&
> #
> # git -C sub rev-list HEAD >revs &&
> # set x $(cat revs) &&
> # echo ":160000 160000 $3 $ZERO_OID M sub" >expect &&
> # subtip=$3 subprev=$2
> #
> 1..1
> 
> Thanks
> 


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-11  9:31 ` Phillip Wood
@ 2026-02-11 12:05   ` Bello Olamide
  2026-02-11 16:46   ` Junio C Hamano
  1 sibling, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-02-11 12:05 UTC (permalink / raw)
  To: phillip.wood
  Cc: Junio C Hamano, git, toon, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Wed, 11 Feb 2026 at 10:31, Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Thanks for the backtrace which helped me spot the problem though I
> should have spotted this yesterday. The problem is in
> initialize_repository()
>
> >   void initialize_repository(struct repository *repo)
> >   {
> > +     if (repo->initialized)
> > +             BUG("repository initialized already");
> > +     repo->initialized = true;
> > +
> >       repo->remote_state = remote_state_new();
> >       repo->parsed_objects = parsed_object_pool_new(repo);
> >       ALLOC_ARRAY(repo->index, 1);
> >       index_state_init(repo->index, repo);
> >       repo->check_deprecated_config = true;
> > +     repo_config_values_init(repo_config_values(repo));
>
> Here you need to use repo->config_values_private_ instead of using the
> accessor as it is fine to initialize the config values to their defaults
> in any instance, it is only when we read them that we want to assert
> that we're reading from "the_repository".
>

Okay thank you very much.
I will send an updated version.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-11  9:31 ` Phillip Wood
  2026-02-11 12:05   ` Bello Olamide
@ 2026-02-11 16:46   ` Junio C Hamano
  2026-02-12 10:33     ` Phillip Wood
  1 sibling, 1 reply; 79+ messages in thread
From: Junio C Hamano @ 2026-02-11 16:46 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Bello Caleb Olamide, git, toon, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188, phillip.wood

Phillip Wood <phillip.wood123@gmail.com> writes:

> Thanks for the backtrace which helped me spot the problem though I 
> should have spotted this yesterday. The problem is in 
> initialize_repository()
>
>>   void initialize_repository(struct repository *repo)
>>   {
>> +	if (repo->initialized)
>> +		BUG("repository initialized already");
>> +	repo->initialized = true;
>> +
>>   	repo->remote_state = remote_state_new();
>>   	repo->parsed_objects = parsed_object_pool_new(repo);
>>   	ALLOC_ARRAY(repo->index, 1);
>>   	index_state_init(repo->index, repo);
>>   	repo->check_deprecated_config = true;
>> +	repo_config_values_init(repo_config_values(repo));
>
> Here you need to use repo->config_values_private_ instead of using the 
> accessor as it is fine to initialize the config values to their defaults 
> in any instance, it is only when we read them that we want to assert 
> that we're reading from "the_repository".

Sorry, but ...

At the beginning of repo_config_values() in the patch, there is a
check to ensure that repo->initialized is true and otherwise you get
an error.  But the initialization is already done in the early part
of initialize_repository() as quoted above.  So I do not see what
difference it would make if we rewrote the last line as

	repo_config_values_init(&repo->config_values_private_);

I am confused.


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-11 16:46   ` Junio C Hamano
@ 2026-02-12 10:33     ` Phillip Wood
  2026-02-12 17:13       ` Junio C Hamano
  0 siblings, 1 reply; 79+ messages in thread
From: Phillip Wood @ 2026-02-12 10:33 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Bello Caleb Olamide, git, toon, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188, phillip.wood

On 11/02/2026 16:46, Junio C Hamano wrote:
> Phillip Wood <phillip.wood123@gmail.com> writes:
> 
> At the beginning of repo_config_values() in the patch, there is a
> check to ensure that repo->initialized is true and otherwise you get
> an error.  But the initialization is already done in the early part
> of initialize_repository() as quoted above.  So I do not see what
> difference it would make if we rewrote the last line as

In Bello's patch there is a second assertion in repo_config_values() 
that checks "repo == the_repository" and that one fails. I suggested 
adding it because the config values are still global rather than per 
repository so we should only be reading them from "the_repository".

Thanks

Phillip

> 	repo_config_values_init(&repo->config_values_private_);
> 
> I am confused.
> 


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-12 10:33     ` Phillip Wood
@ 2026-02-12 17:13       ` Junio C Hamano
  0 siblings, 0 replies; 79+ messages in thread
From: Junio C Hamano @ 2026-02-12 17:13 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Bello Caleb Olamide, git, toon, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188, phillip.wood

Phillip Wood <phillip.wood123@gmail.com> writes:

> On 11/02/2026 16:46, Junio C Hamano wrote:
>> Phillip Wood <phillip.wood123@gmail.com> writes:
>> 
>> At the beginning of repo_config_values() in the patch, there is a
>> check to ensure that repo->initialized is true and otherwise you get
>> an error.  But the initialization is already done in the early part
>> of initialize_repository() as quoted above.  So I do not see what
>> difference it would make if we rewrote the last line as
>
> In Bello's patch there is a second assertion in repo_config_values() 
> that checks "repo == the_repository" and that one fails. I suggested 
> adding it because the config values are still global rather than per 
> repository so we should only be reading them from "the_repository".

Ah, OK.  It may indeed be a good safety valve with the current
codebase.  I am not sure what the upgrade path would look like from
there, though.

Thanks.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v7 0/3] store repo specific config values in new `struct repo_config_values`
  2026-01-24 12:21       ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Olamide Caleb Bello
                           ` (4 preceding siblings ...)
  2026-02-03 15:42         ` [Outreachy PATCH v6 " Olamide Caleb Bello
@ 2026-02-16 16:38         ` Olamide Caleb Bello
  2026-02-16 16:38           ` [Outreachy PATCH v7 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
                             ` (3 more replies)
  5 siblings, 4 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-02-16 16:38 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

Hi Git Community,
Over the course of my ongoing internship, which focused on moving repo specific
global variables in environment.h into local scope, I have attempted to move some
variables into the struct repo-settings.
However there has been some design concerns as regards the use of
`prepare_repo_settings()` with respect to when and where to call the
function, and also the change in behaviours when the variable is lazily
loaded as discussed in [1] and [2].

After different deliberations, Phillip Wood proposed creating a new config
struct [3], adding it to the repository struct and passing the repo struct to
`git_default_config()` to store parsed repo specific config values per repo.
This ensures the current behaviours will be retained.

In version v6, the fuzz-commit-graph failed due to the test being run
multiples times in the same process. This makes us get a BUG due to
the repository being reinitialized in the same process.
But now resetting the initialization using memset before rerunning
the test, the failure is not longer experienced.

1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/
2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/
3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com
4. https://github.com/git/git/actions/runs/21633462506/job/62352191148?pr=2166
5. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2303203992

Changes in v7:
==============
- Added the code to reset the initialization of the_repository
  in the fuzz-commit-graph test in Patch 1.
- Changed the call to rep_config_values_init() in
  initialze_repository() by passing it the repo config_values_private_
  struct instead of the accessor function in Patch 1.
- Modified the commit message in Patch 1
- Added extra lines between variable declarations and code

Olamide Caleb Bello (3):
  environment: stop storing `core.attributesFile` globally
  environment: stop using core.sparseCheckout globally
  environment: move "branch.autoSetupMerge" into `struct
    repo_config_values`

 attr.c                       |  7 ++++---
 branch.h                     |  2 --
 builtin/backfill.c           |  3 ++-
 builtin/branch.c             |  3 ++-
 builtin/checkout.c           |  3 ++-
 builtin/clone.c              |  4 +++-
 builtin/grep.c               |  2 +-
 builtin/mv.c                 |  3 ++-
 builtin/push.c               |  3 ++-
 builtin/sparse-checkout.c    | 31 ++++++++++++++++++++-----------
 builtin/submodule--helper.c  |  3 ++-
 builtin/worktree.c           |  3 ++-
 dir.c                        |  4 +++-
 environment.c                | 28 ++++++++++++++++++----------
 environment.h                | 17 +++++++++++++++--
 oss-fuzz/fuzz-commit-graph.c |  1 +
 repository.c                 | 14 ++++++++++++++
 repository.h                 |  7 +++++++
 sparse-index.c               |  7 +++++--
 unpack-trees.c               |  3 ++-
 wt-status.c                  |  4 +++-
 21 files changed, 110 insertions(+), 42 deletions(-)

Range diff versus v6:
=====================
1:  7e3082125d ! 1:  48821a3848 environment: stop storing `core.attributesFile` globally
    @@ Commit message

         The `core.attributeFile` config value is parsed in
         git_default_core_config(), loaded eagerly and stored in the global
    -    variable `git_attributes_file`. Storing this value in a global variable
    -    can lead to it being overwritten by another repository when more than one
    -    Git repository run in the same Git process.
    +    variable `git_attributes_file`. Storing this value in a global
    +    variable can lead to it being overwritten by another repository when
    +    more than one Git repository run in the same Git process.

         Create a new struct `repo_config_values` to hold this value and
         other repository dependent values parsed by `git_default_config()`.
         This will ensure the current behaviour remains the same while also
         enabling the libification of Git.

    -    An accessor function 'repo_config_values()' is created and used to access
    -    the new struct member of the repository struct.
    -    This is to ensure that we detect if the struct repository has been
    -    initialized and also prevent double initialization of the repository.
    -
    -    It is important to note that `git_default_config()` is a wrapper to other
    -    `git_default_*_config()` functions such as `git_default_core_config()`.
    -    Therefore to access and modify this global variable,
    -    the change has to be made `git_default_core_config()`.
    +    An accessor function 'repo_config_values()' to ensure that we do not
    +    access an uninitialized repository, or an instance of a different
    +    repository than the current one.

         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
         Mentored-by: Christian Couder <christian.couder@gmail.com>
    @@ environment.h: extern int assume_unchanged;
      extern int pack_compression_level;
      extern unsigned long pack_size_limit_cfg;

    + ## oss-fuzz/fuzz-commit-graph.c ##
    +@@ oss-fuzz/fuzz-commit-graph.c: int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
    + {
    + 	struct commit_graph *g;
    +
    ++	memset(the_repository, 0, sizeof(*the_repository));
    + 	initialize_repository(the_repository);
    +
    + 	/*
    +
      ## repository.c ##
     @@ repository.c: static void set_default_hash_algo(struct repository *repo)
      	repo_set_hash_algo(repo, algo);
    @@ repository.c: static void set_default_hash_algo(struct repository *repo)

     +struct repo_config_values *repo_config_values(struct repository *repo)
     +{
    ++	if (repo != the_repository)
    ++		BUG("trying to read config from wrong repository instance");
     +	if(!repo->initialized)
     +		BUG("config values from uninitialized repository");
     +	return &repo->config_values_private_;
    @@ repository.c: static void set_default_hash_algo(struct repository *repo)
      	ALLOC_ARRAY(repo->index, 1);
      	index_state_init(repo->index, repo);
      	repo->check_deprecated_config = true;
    -+	repo_config_values_init(repo_config_values(repo));
    ++	repo_config_values_init(&repo->config_values_private_);

      	/*
      	 * When a command runs inside a repository, it learns what
2:  8645b4f595 ! 2:  7d33f1ef0f environment: stop using core.sparseCheckout globally
    @@ builtin/clone.c: static int git_sparse_checkout_init(const char *repo)
      	struct child_process cmd = CHILD_PROCESS_INIT;
      	int result = 0;
     +	struct repo_config_values *cfg = repo_config_values(the_repository);
    ++
      	strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL);

      	/*
    @@ dir.c: enum pattern_match_result path_matches_pattern_list(
      {
     -	if (!core_apply_sparse_checkout)
     +	struct repo_config_values *cfg = repo_config_values(the_repository);
    ++
     +	if (!cfg->apply_sparse_checkout)
      		return 1;
      	if (istate->sparse_checkout_patterns)
    @@ sparse-index.c: static int index_has_unmerged_entries(struct index_state *istate
      {
     -	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
     +	struct repo_config_values *cfg = repo_config_values(the_repository);
    ++
     +	if (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)
      		return 0;

3:  60451b93a5 = 3:  c3ecfa63b9 environment: move "branch.autoSetupMerge" into `struct repo_config_values`


-- 
2.34.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v7 1/3] environment: stop storing `core.attributesFile` globally
  2026-02-16 16:38         ` [Outreachy PATCH v7 " Olamide Caleb Bello
@ 2026-02-16 16:38           ` Olamide Caleb Bello
  2026-02-16 16:38           ` [Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
                             ` (2 subsequent siblings)
  3 siblings, 0 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-02-16 16:38 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The `core.attributeFile` config value is parsed in
git_default_core_config(), loaded eagerly and stored in the global
variable `git_attributes_file`. Storing this value in a global
variable can lead to it being overwritten by another repository when
more than one Git repository run in the same Git process.

Create a new struct `repo_config_values` to hold this value and
other repository dependent values parsed by `git_default_config()`.
This will ensure the current behaviour remains the same while also
enabling the libification of Git.

An accessor function 'repo_config_values()' s created to ensure
that we do not access an uninitialized repository, or an instance
of a different repository than the current one.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 attr.c                       |  7 ++++---
 environment.c                | 12 +++++++++---
 environment.h                | 11 ++++++++++-
 oss-fuzz/fuzz-commit-graph.c |  1 +
 repository.c                 | 14 ++++++++++++++
 repository.h                 |  7 +++++++
 6 files changed, 45 insertions(+), 7 deletions(-)

diff --git a/attr.c b/attr.c
index 4999b7e09d..75369547b3 100644
--- a/attr.c
+++ b/attr.c
@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)
 
 const char *git_attr_global_file(void)
 {
-	if (!git_attributes_file)
-		git_attributes_file = xdg_config_home("attributes");
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+	if (!cfg->attributes_file)
+		cfg->attributes_file = xdg_config_home("attributes");
 
-	return git_attributes_file;
+	return cfg->attributes_file;
 }
 
 int git_attr_system_is_enabled(void)
diff --git a/environment.c b/environment.c
index a770b5921d..4b5c701e80 100644
--- a/environment.c
+++ b/environment.c
@@ -53,7 +53,6 @@ char *git_commit_encoding;
 char *git_log_output_encoding;
 char *apply_default_whitespace;
 char *apply_default_ignorewhitespace;
-char *git_attributes_file;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files = -1;
@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *
 static int git_default_core_config(const char *var, const char *value,
 				   const struct config_context *ctx, void *cb)
 {
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
 		trust_executable_bit = git_config_bool(var, value);
@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.attributesfile")) {
-		FREE_AND_NULL(git_attributes_file);
-		return git_config_pathname(&git_attributes_file, var, value);
+		FREE_AND_NULL(cfg->attributes_file);
+		return git_config_pathname(&cfg->attributes_file, var, value);
 	}
 
 	if (!strcmp(var, "core.bare")) {
@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,
 	/* Add other config variables here and to Documentation/config.adoc. */
 	return 0;
 }
+
+void repo_config_values_init(struct repo_config_values *cfg)
+{
+	cfg->attributes_file = NULL;
+}
diff --git a/environment.h b/environment.h
index 51898c99cd..dfc31b794d 100644
--- a/environment.h
+++ b/environment.h
@@ -84,6 +84,14 @@ extern const char * const local_repo_env[];
 
 struct strvec;
 
+struct repository;
+struct repo_config_values {
+	/* section "core" config values */
+	char *attributes_file;
+};
+
+struct repo_config_values *repo_config_values(struct repository *repo);
+
 /*
  * Wrapper of getenv() that returns a strdup value. This value is kept
  * in argv to be freed later.
@@ -107,6 +115,8 @@ const char *strip_namespace(const char *namespaced_ref);
 int git_default_config(const char *, const char *,
 		       const struct config_context *, void *);
 
+void repo_config_values_init(struct repo_config_values *cfg);
+
 /*
  * TODO: All the below state either explicitly or implicitly relies on
  * `the_repository`. We should eventually get rid of these and make the
@@ -152,7 +162,6 @@ extern int assume_unchanged;
 extern int warn_on_object_refname_ambiguity;
 extern char *apply_default_whitespace;
 extern char *apply_default_ignorewhitespace;
-extern char *git_attributes_file;
 extern int zlib_compression_level;
 extern int pack_compression_level;
 extern unsigned long pack_size_limit_cfg;
diff --git a/oss-fuzz/fuzz-commit-graph.c b/oss-fuzz/fuzz-commit-graph.c
index fb8b8787a4..59bbb849d1 100644
--- a/oss-fuzz/fuzz-commit-graph.c
+++ b/oss-fuzz/fuzz-commit-graph.c
@@ -10,6 +10,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
 {
 	struct commit_graph *g;
 
+	memset(the_repository, 0, sizeof(*the_repository));
 	initialize_repository(the_repository);
 
 	/*
diff --git a/repository.c b/repository.c
index c7e75215ac..4bbbe6b3ba 100644
--- a/repository.c
+++ b/repository.c
@@ -50,13 +50,27 @@ static void set_default_hash_algo(struct repository *repo)
 	repo_set_hash_algo(repo, algo);
 }
 
+struct repo_config_values *repo_config_values(struct repository *repo)
+{
+	if (repo != the_repository)
+		BUG("trying to read config from wrong repository instance");
+	if(!repo->initialized)
+		BUG("config values from uninitialized repository");
+	return &repo->config_values_private_;
+}
+
 void initialize_repository(struct repository *repo)
 {
+	if (repo->initialized)
+		BUG("repository initialized already");
+	repo->initialized = true;
+
 	repo->remote_state = remote_state_new();
 	repo->parsed_objects = parsed_object_pool_new(repo);
 	ALLOC_ARRAY(repo->index, 1);
 	index_state_init(repo->index, repo);
 	repo->check_deprecated_config = true;
+	repo_config_values_init(&repo->config_values_private_);
 
 	/*
 	 * When a command runs inside a repository, it learns what
diff --git a/repository.h b/repository.h
index 6063c4b846..9717e45000 100644
--- a/repository.h
+++ b/repository.h
@@ -3,6 +3,7 @@
 
 #include "strmap.h"
 #include "repo-settings.h"
+#include "environment.h"
 
 struct config_set;
 struct git_hash_algo;
@@ -148,6 +149,9 @@ struct repository {
 	/* Repository's compatibility hash algorithm. */
 	const struct git_hash_algo *compat_hash_algo;
 
+	/* Repository's config values parsed by git_default_config() */
+	struct repo_config_values config_values_private_;
+
 	/* Repository's reference storage format, as serialized on disk. */
 	enum ref_storage_format ref_storage_format;
 
@@ -171,6 +175,9 @@ struct repository {
 
 	/* Should repo_config() check for deprecated settings */
 	bool check_deprecated_config;
+
+	/* Has this repository instance been initialized? */
+	bool initialized;
 };
 
 #ifdef USE_THE_REPOSITORY_VARIABLE
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally
  2026-02-16 16:38         ` [Outreachy PATCH v7 " Olamide Caleb Bello
  2026-02-16 16:38           ` [Outreachy PATCH v7 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
@ 2026-02-16 16:38           ` Olamide Caleb Bello
  2026-02-26 12:57             ` Christian Couder
  2026-02-16 16:38           ` [Outreachy PATCH v7 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
  2026-02-17 20:08           ` [Outreachy PATCH v7 0/3] store repo specific config values in new " Junio C Hamano
  3 siblings, 1 reply; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-02-16 16:38 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The config value `core.sparseCheckout` is parsed in
`git_default_core_config()` and stored globally in
`core_apply_sparse_checkout`. This could cause it to be overwritten
by another repository when different Git repositories run in the same
process.

Move the parsed value into `struct repo_config_values` in the_repository
to retain current behaviours and move towards libifying Git.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
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/backfill.c        |  3 ++-
 builtin/clone.c           |  4 +++-
 builtin/grep.c            |  2 +-
 builtin/mv.c              |  3 ++-
 builtin/sparse-checkout.c | 31 ++++++++++++++++++++-----------
 builtin/worktree.c        |  3 ++-
 dir.c                     |  4 +++-
 environment.c             |  4 ++--
 environment.h             |  2 +-
 sparse-index.c            |  7 +++++--
 unpack-trees.c            |  3 ++-
 wt-status.c               |  4 +++-
 12 files changed, 46 insertions(+), 24 deletions(-)

diff --git a/builtin/backfill.c b/builtin/backfill.c
index e80fc1b694..9b2ca57b6a 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -129,6 +129,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 			 N_("Restrict the missing objects to the current sparse-checkout")),
 		OPT_END(),
 	};
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	show_usage_with_options_if_asked(argc, argv,
 					 builtin_backfill_usage, options);
@@ -139,7 +140,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 	repo_config(repo, git_default_config, NULL);
 
 	if (ctx.sparse < 0)
-		ctx.sparse = core_apply_sparse_checkout;
+		ctx.sparse = cfg->apply_sparse_checkout;
 
 	result = do_backfill(&ctx);
 	backfill_context_clear(&ctx);
diff --git a/builtin/clone.c b/builtin/clone.c
index b19b302b06..9ba08b86ae 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -617,13 +617,15 @@ static int git_sparse_checkout_init(const char *repo)
 {
 	struct child_process cmd = CHILD_PROCESS_INIT;
 	int result = 0;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+
 	strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL);
 
 	/*
 	 * We must apply the setting in the current process
 	 * for the later checkout to use the sparse-checkout file.
 	 */
-	core_apply_sparse_checkout = 1;
+	cfg->apply_sparse_checkout = 1;
 
 	cmd.git_cmd = 1;
 	if (run_command(&cmd)) {
diff --git a/builtin/grep.c b/builtin/grep.c
index 53cccf2d25..207d340548 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,
 	 *	"forget" the sparse-index feature switch. As a result, the index
 	 *	of these submodules are expanded unexpectedly.
 	 *
-	 * 2. "core_apply_sparse_checkout"
+	 * 2. "config_values_private_.apply_sparse_checkout"
 	 *	When running `grep` in the superproject, this setting is
 	 *	populated using the superproject's configs. However, once
 	 *	initialized, this config is globally accessible and is read by
diff --git a/builtin/mv.c b/builtin/mv.c
index d43925097b..2215d34e31 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -238,6 +238,7 @@ int cmd_mv(int argc,
 	struct hashmap moved_dirs = HASHMAP_INIT(pathmap_cmp, NULL);
 	struct strbuf pathbuf = STRBUF_INIT;
 	int ret;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	repo_config(the_repository, git_default_config, NULL);
 
@@ -572,7 +573,7 @@ int cmd_mv(int argc,
 		rename_index_entry_at(the_repository->index, pos, dst);
 
 		if (ignore_sparse &&
-		    core_apply_sparse_checkout &&
+		    cfg->apply_sparse_checkout &&
 		    core_sparse_checkout_cone) {
 			/*
 			 * NEEDSWORK: we are *not* paying attention to
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 15d51e60a8..7f3317841e 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -61,9 +61,10 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
 	struct pattern_list pl;
 	char *sparse_filename;
 	int res;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!cfg->apply_sparse_checkout)
 		die(_("this worktree is not sparse"));
 
 	argc = parse_options(argc, argv, prefix,
@@ -399,12 +400,14 @@ static int set_config(struct repository *repo,
 }
 
 static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+
 	/* If not specified, use previous definition of cone mode */
-	if (*cone_mode == -1 && core_apply_sparse_checkout)
+	if (*cone_mode == -1 && cfg->apply_sparse_checkout)
 		*cone_mode = core_sparse_checkout_cone;
 
 	/* Set cone/non-cone mode appropriately */
-	core_apply_sparse_checkout = 1;
+	cfg->apply_sparse_checkout = 1;
 	if (*cone_mode == 1 || *cone_mode == -1) {
 		core_sparse_checkout_cone = 1;
 		return MODE_CONE_PATTERNS;
@@ -416,9 +419,10 @@ static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
 static int update_modes(struct repository *repo, int *cone_mode, int *sparse_index)
 {
 	int mode, record_mode;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	/* Determine if we need to record the mode; ensure sparse checkout on */
-	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+	record_mode = (*cone_mode != -1) || !cfg->apply_sparse_checkout;
 
 	mode = update_cone_mode(cone_mode);
 	if (record_mode && set_config(repo, mode))
@@ -684,6 +688,7 @@ static int modify_pattern_list(struct repository *repo,
 	int result;
 	int changed_config = 0;
 	struct pattern_list *pl = xcalloc(1, sizeof(*pl));
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	switch (m) {
 	case ADD:
@@ -699,9 +704,9 @@ static int modify_pattern_list(struct repository *repo,
 		break;
 	}
 
-	if (!core_apply_sparse_checkout) {
+	if (!cfg->apply_sparse_checkout) {
 		set_config(repo, MODE_ALL_PATTERNS);
-		core_apply_sparse_checkout = 1;
+		cfg->apply_sparse_checkout = 1;
 		changed_config = 1;
 	}
 
@@ -796,9 +801,10 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
 	};
 	struct strvec patterns = STRVEC_INIT;
 	int ret;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!cfg->apply_sparse_checkout)
 		die(_("no sparse-checkout to add to"));
 
 	repo_read_index(repo);
@@ -905,9 +911,10 @@ static int sparse_checkout_reapply(int argc, const char **argv,
 			 N_("toggle the use of a sparse index")),
 		OPT_END(),
 	};
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!cfg->apply_sparse_checkout)
 		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
 
 	reapply_opts.cone_mode = -1;
@@ -960,6 +967,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
 	size_t worktree_len;
 	int force = 0, dry_run = 0, verbose = 0;
 	int require_force = 1;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	struct option builtin_sparse_checkout_clean_options[] = {
 		OPT__DRY_RUN(&dry_run, N_("dry run")),
@@ -969,7 +977,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
 	};
 
 	setup_work_tree();
-	if (!core_apply_sparse_checkout)
+	if (!cfg->apply_sparse_checkout)
 		die(_("must be in a sparse-checkout to clean directories"));
 	if (!core_sparse_checkout_cone)
 		die(_("must be in a cone-mode sparse-checkout to clean directories"));
@@ -1033,9 +1041,10 @@ static int sparse_checkout_disable(int argc, const char **argv,
 		OPT_END(),
 	};
 	struct pattern_list pl;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	/*
-	 * We do not exit early if !core_apply_sparse_checkout; due to the
+	 * We do not exit early if !repo->config_values.apply_sparse_checkout; due to the
 	 * ability for users to manually muck things up between
 	 *   direct editing of .git/info/sparse-checkout
 	 *   running read-tree -m u HEAD or update-index --skip-worktree
@@ -1061,7 +1070,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
 	hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
 	pl.use_cone_patterns = 0;
-	core_apply_sparse_checkout = 1;
+	cfg->apply_sparse_checkout = 1;
 
 	add_pattern("/*", empty_base, 0, &pl, 0);
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index fbdaf2eb2e..90e56ab495 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -440,6 +440,7 @@ static int add_worktree(const char *path, const char *refname,
 	struct strbuf sb_name = STRBUF_INIT;
 	struct worktree **worktrees, *wt = NULL;
 	struct ref_store *wt_refs;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	worktrees = get_worktrees();
 	check_candidate_path(path, opts->force, worktrees, "add");
@@ -536,7 +537,7 @@ static int add_worktree(const char *path, const char *refname,
 	 * If the current worktree has sparse-checkout enabled, then copy
 	 * the sparse-checkout patterns from the current worktree.
 	 */
-	if (core_apply_sparse_checkout)
+	if (cfg->apply_sparse_checkout)
 		copy_sparse_checkout(sb_repo.buf);
 
 	/*
diff --git a/dir.c b/dir.c
index b00821f294..026d8516a9 100644
--- a/dir.c
+++ b/dir.c
@@ -1551,7 +1551,9 @@ enum pattern_match_result path_matches_pattern_list(
 
 int init_sparse_checkout_patterns(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout)
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+
+	if (!cfg->apply_sparse_checkout)
 		return 1;
 	if (istate->sparse_checkout_patterns)
 		return 0;
diff --git a/environment.c b/environment.c
index 4b5c701e80..390af1ce54 100644
--- a/environment.c
+++ b/environment.c
@@ -74,7 +74,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_apply_sparse_checkout;
 int core_sparse_checkout_cone;
 int sparse_expect_files_outside_of_patterns;
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "core.sparsecheckout")) {
-		core_apply_sparse_checkout = git_config_bool(var, value);
+		cfg->apply_sparse_checkout = git_config_bool(var, value);
 		return 0;
 	}
 
@@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,
 void repo_config_values_init(struct repo_config_values *cfg)
 {
 	cfg->attributes_file = NULL;
+	cfg->apply_sparse_checkout = 0;
 }
diff --git a/environment.h b/environment.h
index dfc31b794d..2e24160322 100644
--- a/environment.h
+++ b/environment.h
@@ -88,6 +88,7 @@ struct repository;
 struct repo_config_values {
 	/* section "core" config values */
 	char *attributes_file;
+	int apply_sparse_checkout;
 };
 
 struct repo_config_values *repo_config_values(struct repository *repo);
@@ -171,7 +172,6 @@ extern int precomposed_unicode;
 extern int protect_hfs;
 extern int protect_ntfs;
 
-extern int core_apply_sparse_checkout;
 extern int core_sparse_checkout_cone;
 extern int sparse_expect_files_outside_of_patterns;
 
diff --git a/sparse-index.c b/sparse-index.c
index 76f90da5f5..386be1d6f1 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -152,7 +152,9 @@ static int index_has_unmerged_entries(struct index_state *istate)
 
 int is_sparse_index_allowed(struct index_state *istate, int flags)
 {
-	if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+
+	if (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)
 		return 0;
 
 	if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
@@ -670,7 +672,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
 
 void clear_skip_worktree_from_present_files(struct index_state *istate)
 {
-	if (!core_apply_sparse_checkout ||
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+	if (!cfg->apply_sparse_checkout ||
 	    sparse_expect_files_outside_of_patterns)
 		return;
 
diff --git a/unpack-trees.c b/unpack-trees.c
index f38c761ab9..998a1e6dc7 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1888,6 +1888,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 	struct pattern_list pl;
 	int free_pattern_list = 0;
 	struct dir_struct dir = DIR_INIT;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	if (o->reset == UNPACK_RESET_INVALID)
 		BUG("o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED");
@@ -1924,7 +1925,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 	if (o->prefix)
 		update_sparsity_for_prefix(o->prefix, o->src_index);
 
-	if (!core_apply_sparse_checkout || !o->update)
+	if (!cfg->apply_sparse_checkout || !o->update)
 		o->skip_sparse_checkout = 1;
 	if (!o->skip_sparse_checkout) {
 		memset(&pl, 0, sizeof(pl));
diff --git a/wt-status.c b/wt-status.c
index e12adb26b9..6609b37cad 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1763,8 +1763,10 @@ static void wt_status_check_sparse_checkout(struct repository *r,
 {
 	int skip_worktree = 0;
 	int i;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
-	if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
+	if (!cfg->apply_sparse_checkout ||
+	    r->index->cache_nr == 0) {
 		/*
 		 * Don't compute percentage of checked out files if we
 		 * aren't in a sparse checkout or would get division by 0.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [Outreachy PATCH v7 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values`
  2026-02-16 16:38         ` [Outreachy PATCH v7 " Olamide Caleb Bello
  2026-02-16 16:38           ` [Outreachy PATCH v7 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
  2026-02-16 16:38           ` [Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-02-16 16:38           ` Olamide Caleb Bello
  2026-02-17 20:08           ` [Outreachy PATCH v7 0/3] store repo specific config values in new " Junio C Hamano
  3 siblings, 0 replies; 79+ messages in thread
From: Olamide Caleb Bello @ 2026-02-16 16:38 UTC (permalink / raw)
  To: git
  Cc: toon, phillip.wood123, gitster, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me, karthik.188,
	Olamide Caleb Bello

The config value `branch.autoSetupMerge` is parsed in
`git_default_branch_config()` and stored in the global variable
`git_branch_track`. This global variable can be overwritten
by another repository when multiple Git repos run in the the same process.

Move this value into `struct repo_config_values` in the_repository to
retain current behaviours and move towards libifying Git.
Since the variable is no longer a global variable, it has been renamed to
`branch_track` in the struct `repo_config_values`.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
---
 branch.h                    |  2 --
 builtin/branch.c            |  3 ++-
 builtin/checkout.c          |  3 ++-
 builtin/push.c              |  3 ++-
 builtin/submodule--helper.c |  3 ++-
 environment.c               | 12 +++++++-----
 environment.h               |  4 ++++
 7 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/branch.h b/branch.h
index ec2f35fda4..3dc6e2a0ff 100644
--- a/branch.h
+++ b/branch.h
@@ -15,8 +15,6 @@ enum branch_track {
 	BRANCH_TRACK_SIMPLE,
 };
 
-extern enum branch_track git_branch_track;
-
 /* Functions for acting on the information about branches. */
 
 /**
diff --git a/builtin/branch.c b/builtin/branch.c
index c577b5d20f..a1a43380d0 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -724,6 +724,7 @@ int cmd_branch(int argc,
 	static struct ref_sorting *sorting;
 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
 	struct ref_format format = REF_FORMAT_INIT;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 	int ret;
 
 	struct option options[] = {
@@ -795,7 +796,7 @@ int cmd_branch(int argc,
 	if (!sorting_options.nr)
 		string_list_append(&sorting_options, "refname");
 
-	track = git_branch_track;
+	track = cfg->branch_track;
 
 	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
 				   0, &head_oid, NULL);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 261699e2f5..ea728e733c 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1588,6 +1588,7 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
 static int checkout_branch(struct checkout_opts *opts,
 			   struct branch_info *new_branch_info)
 {
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 	int noop_switch = (!new_branch_info->name &&
 			   !opts->new_branch &&
 			   !opts->force_detach);
@@ -1631,7 +1632,7 @@ static int checkout_branch(struct checkout_opts *opts,
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
 	} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
-		opts->track = git_branch_track;
+		opts->track = cfg->branch_track;
 
 	if (new_branch_info->name && !new_branch_info->commit)
 		die(_("Cannot switch branch to a non-commit '%s'"),
diff --git a/builtin/push.c b/builtin/push.c
index 5b6cebbb85..7100ffba5d 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -151,6 +151,7 @@ static NORETURN void die_push_simple(struct branch *branch,
 	const char *advice_pushdefault_maybe = "";
 	const char *advice_automergesimple_maybe = "";
 	const char *short_upstream = branch->merge[0]->src;
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	skip_prefix(short_upstream, "refs/heads/", &short_upstream);
 
@@ -162,7 +163,7 @@ static NORETURN void die_push_simple(struct branch *branch,
 		advice_pushdefault_maybe = _("\n"
 				 "To choose either option permanently, "
 				 "see push.default in 'git help config'.\n");
-	if (git_branch_track != BRANCH_TRACK_SIMPLE)
+	if (cfg->branch_track != BRANCH_TRACK_SIMPLE)
 		advice_automergesimple_maybe = _("\n"
 				 "To avoid automatically configuring "
 				 "an upstream branch when its name\n"
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d537ab087a..594cd107b3 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3126,9 +3126,10 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,
 		N_("git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] [-n|--dry-run] <name> <start-oid> <start-name>"),
 		NULL
 	};
+	struct repo_config_values *cfg = repo_config_values(the_repository);
 
 	repo_config(the_repository, git_default_config, NULL);
-	track = git_branch_track;
+	track = cfg->branch_track;
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
 	if (argc != 3)
diff --git a/environment.c b/environment.c
index 390af1ce54..1bc3adb75b 100644
--- a/environment.c
+++ b/environment.c
@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 enum eol core_eol = EOL_UNSET;
 int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
 char *check_roundtrip_encoding;
-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #ifndef OBJECT_CREATION_MODE
@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)
 
 static int git_default_branch_config(const char *var, const char *value)
 {
+	struct repo_config_values *cfg = repo_config_values(the_repository);
+
 	if (!strcmp(var, "branch.autosetupmerge")) {
 		if (value && !strcmp(value, "always")) {
-			git_branch_track = BRANCH_TRACK_ALWAYS;
+			cfg->branch_track = BRANCH_TRACK_ALWAYS;
 			return 0;
 		} else if (value && !strcmp(value, "inherit")) {
-			git_branch_track = BRANCH_TRACK_INHERIT;
+			cfg->branch_track = BRANCH_TRACK_INHERIT;
 			return 0;
 		} else if (value && !strcmp(value, "simple")) {
-			git_branch_track = BRANCH_TRACK_SIMPLE;
+			cfg->branch_track = BRANCH_TRACK_SIMPLE;
 			return 0;
 		}
-		git_branch_track = git_config_bool(var, value);
+		cfg->branch_track = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "branch.autosetuprebase")) {
@@ -761,4 +762,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;
 }
diff --git a/environment.h b/environment.h
index 2e24160322..4bfd798757 100644
--- a/environment.h
+++ b/environment.h
@@ -2,6 +2,7 @@
 #define ENVIRONMENT_H
 
 #include "repo-settings.h"
+#include "branch.h"
 
 /* Double-check local_repo_env below if you add to this list. */
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
@@ -89,6 +90,9 @@ struct repo_config_values {
 	/* section "core" config values */
 	char *attributes_file;
 	int apply_sparse_checkout;
+
+	/* section "branch" config values */
+	enum branch_track branch_track;
 };
 
 struct repo_config_values *repo_config_values(struct repository *repo);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v7 0/3] store repo specific config values in new `struct repo_config_values`
  2026-02-16 16:38         ` [Outreachy PATCH v7 " Olamide Caleb Bello
                             ` (2 preceding siblings ...)
  2026-02-16 16:38           ` [Outreachy PATCH v7 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
@ 2026-02-17 20:08           ` Junio C Hamano
  2026-02-18 11:27             ` Bello Olamide
  2026-02-26 13:03             ` Christian Couder
  3 siblings, 2 replies; 79+ messages in thread
From: Junio C Hamano @ 2026-02-17 20:08 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, toon, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Olamide Caleb Bello <belkid98@gmail.com> writes:

> Changes in v7:
> ==============
> - Added the code to reset the initialization of the_repository
>   in the fuzz-commit-graph test in Patch 1.
> - Changed the call to rep_config_values_init() in
>   initialze_repository() by passing it the repo config_values_private_
>   struct instead of the accessor function in Patch 1.
> - Modified the commit message in Patch 1
> - Added extra lines between variable declarations and code

>      +struct repo_config_values *repo_config_values(struct repository *repo)
>      +{
>     ++	if (repo != the_repository)
>     ++		BUG("trying to read config from wrong repository instance");
>      +	if(!repo->initialized)

$ git clang-format --diff $(git merge-base master HEAD)

would have pointed out the style issue here.

Will amend while queuing but it would be a bit before I can get to
this patch set for real reviews.

Thanks.


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v7 0/3] store repo specific config values in new `struct repo_config_values`
  2026-02-17 20:08           ` [Outreachy PATCH v7 0/3] store repo specific config values in new " Junio C Hamano
@ 2026-02-18 11:27             ` Bello Olamide
  2026-02-26 13:03             ` Christian Couder
  1 sibling, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-02-18 11:27 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, toon, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Tue, 17 Feb 2026 at 21:08, Junio C Hamano <gitster@pobox.com> wrote:
>
> Olamide Caleb Bello <belkid98@gmail.com> writes:
>
> > Changes in v7:
> > ==============
> > - Added the code to reset the initialization of the_repository
> >   in the fuzz-commit-graph test in Patch 1.
> > - Changed the call to rep_config_values_init() in
> >   initialze_repository() by passing it the repo config_values_private_
> >   struct instead of the accessor function in Patch 1.
> > - Modified the commit message in Patch 1
> > - Added extra lines between variable declarations and code
>
> >      +struct repo_config_values *repo_config_values(struct repository *repo)
> >      +{
> >     ++        if (repo != the_repository)
> >     ++                BUG("trying to read config from wrong repository instance");
> >      +        if(!repo->initialized)
>
> $ git clang-format --diff $(git merge-base master HEAD)
>
> would have pointed out the style issue here.
>
> Will amend while queuing but it would be a bit before I can get to
> this patch set for real reviews.
>
> Thanks.
>

Sorry about missing this style issue and thank you for your reviews and guidance
Do I wait for this before continuing to move other repo specific config values
into the struct since I might need to rebase?

Thanks

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally
  2026-02-16 16:38           ` [Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
@ 2026-02-26 12:57             ` Christian Couder
  2026-02-26 15:23               ` Junio C Hamano
  0 siblings, 1 reply; 79+ messages in thread
From: Christian Couder @ 2026-02-26 12:57 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, toon, phillip.wood123, gitster, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Mon, Feb 16, 2026 at 5:39 PM Olamide Caleb Bello <belkid98@gmail.com> wrote:

> @@ -670,7 +672,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
>
>  void clear_skip_worktree_from_present_files(struct index_state *istate)
>  {
> -       if (!core_apply_sparse_checkout ||
> +       struct repo_config_values *cfg = repo_config_values(the_repository);

Nit: it would be better with a blank line here.

> +       if (!cfg->apply_sparse_checkout ||
>             sparse_expect_files_outside_of_patterns)
>                 return;

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v7 0/3] store repo specific config values in new `struct repo_config_values`
  2026-02-17 20:08           ` [Outreachy PATCH v7 0/3] store repo specific config values in new " Junio C Hamano
  2026-02-18 11:27             ` Bello Olamide
@ 2026-02-26 13:03             ` Christian Couder
  2026-02-26 15:19               ` Junio C Hamano
  1 sibling, 1 reply; 79+ messages in thread
From: Christian Couder @ 2026-02-26 13:03 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Olamide Caleb Bello, git, toon, phillip.wood123, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Tue, Feb 17, 2026 at 9:08 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Olamide Caleb Bello <belkid98@gmail.com> writes:
>
> > Changes in v7:
> > ==============
> > - Added the code to reset the initialization of the_repository
> >   in the fuzz-commit-graph test in Patch 1.
> > - Changed the call to rep_config_values_init() in
> >   initialze_repository() by passing it the repo config_values_private_
> >   struct instead of the accessor function in Patch 1.
> > - Modified the commit message in Patch 1
> > - Added extra lines between variable declarations and code
>
> >      +struct repo_config_values *repo_config_values(struct repository *repo)
> >      +{
> >     ++        if (repo != the_repository)
> >     ++                BUG("trying to read config from wrong repository instance");
> >      +        if(!repo->initialized)
>
> $ git clang-format --diff $(git merge-base master HEAD)
>
> would have pointed out the style issue here.
>
> Will amend while queuing but it would be a bit before I can get to
> this patch set for real reviews.

I took another look at the series and found only another nit (a
missing blank line). Otherwise the series looks good to me.

Thanks.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v7 0/3] store repo specific config values in new `struct repo_config_values`
  2026-02-26 13:03             ` Christian Couder
@ 2026-02-26 15:19               ` Junio C Hamano
  0 siblings, 0 replies; 79+ messages in thread
From: Junio C Hamano @ 2026-02-26 15:19 UTC (permalink / raw)
  To: Christian Couder
  Cc: Olamide Caleb Bello, git, toon, phillip.wood123, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Christian Couder <christian.couder@gmail.com> writes:

> I took another look at the series and found only another nit (a
> missing blank line). Otherwise the series looks good to me.
>
> Thanks.

Thanks for reviewing.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally
  2026-02-26 12:57             ` Christian Couder
@ 2026-02-26 15:23               ` Junio C Hamano
  2026-02-26 16:24                 ` Bello Olamide
  0 siblings, 1 reply; 79+ messages in thread
From: Junio C Hamano @ 2026-02-26 15:23 UTC (permalink / raw)
  To: Christian Couder
  Cc: Olamide Caleb Bello, git, toon, phillip.wood123, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

Christian Couder <christian.couder@gmail.com> writes:

> On Mon, Feb 16, 2026 at 5:39 PM Olamide Caleb Bello <belkid98@gmail.com> wrote:
>
>> @@ -670,7 +672,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
>>
>>  void clear_skip_worktree_from_present_files(struct index_state *istate)
>>  {
>> -       if (!core_apply_sparse_checkout ||
>> +       struct repo_config_values *cfg = repo_config_values(the_repository);
>
> Nit: it would be better with a blank line here.
>
>> +       if (!cfg->apply_sparse_checkout ||
>>             sparse_expect_files_outside_of_patterns)
>>                 return;

OK.  Agreed and locally amended.

Let's merge the topic down to 'next'.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally
  2026-02-26 15:23               ` Junio C Hamano
@ 2026-02-26 16:24                 ` Bello Olamide
  0 siblings, 0 replies; 79+ messages in thread
From: Bello Olamide @ 2026-02-26 16:24 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Christian Couder, git, toon, phillip.wood123, usmanakinyemi202,
	kaartic.sivaraam, me, karthik.188

On Thu, 26 Feb 2026 at 16:23, Junio C Hamano <gitster@pobox.com> wrote:
>
> Christian Couder <christian.couder@gmail.com> writes:
>
> > On Mon, Feb 16, 2026 at 5:39 PM Olamide Caleb Bello <belkid98@gmail.com> wrote:
> >
> >> @@ -670,7 +672,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
> >>
> >>  void clear_skip_worktree_from_present_files(struct index_state *istate)
> >>  {
> >> -       if (!core_apply_sparse_checkout ||
> >> +       struct repo_config_values *cfg = repo_config_values(the_repository);
> >
> > Nit: it would be better with a blank line here.
> >
> >> +       if (!cfg->apply_sparse_checkout ||
> >>             sparse_expect_files_outside_of_patterns)
> >>                 return;
>
> OK.  Agreed and locally amended.
>
> Let's merge the topic down to 'next'.

Thank you very much for all your assistance.

^ permalink raw reply	[flat|nested] 79+ messages in thread

end of thread, other threads:[~2026-02-26 16:24 UTC | newest]

Thread overview: 79+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-12 12:59 [Outreachy PATCH RFC 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
2026-01-12 12:59 ` [Outreachy PATCH RFC 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
2026-01-12 14:29   ` Phillip Wood
2026-01-12 15:05     ` Bello Olamide
2026-01-12 12:59 ` [Outreachy PATCH RFC 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
2026-01-12 12:59 ` [Outreachy PATCH RFC 3/3] environment: move "branch.autoSetupMerge" into `struct config_values` Olamide Caleb Bello
2026-01-13 16:43 ` [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct Olamide Caleb Bello
2026-01-13 16:44   ` [Outreachy PATCH v2 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
2026-01-13 19:26     ` Junio C Hamano
2026-01-14  6:59       ` Bello Olamide
2026-01-13 16:44   ` [Outreachy PATCH v2 2/3] environment: environment: stop using core.sparseCheckout globally Olamide Caleb Bello
2026-01-13 19:38     ` Junio C Hamano
2026-01-14  7:16       ` Bello Olamide
2026-01-13 16:44   ` [Outreachy PATCH v2 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
2026-01-13 19:53     ` Junio C Hamano
2026-01-14  7:40       ` Bello Olamide
2026-01-15 22:17   ` [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct Bello Olamide
2026-01-17 20:59   ` [Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values` Olamide Caleb Bello
2026-01-17 20:59     ` [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
2026-01-22 12:13       ` Toon Claes
2026-01-22 15:08         ` Bello Olamide
2026-01-22 14:40       ` Phillip Wood
2026-01-22 15:11         ` Bello Olamide
2026-01-17 20:59     ` [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally Olamide Caleb Bello
2026-01-22 12:13       ` Toon Claes
2026-01-22 15:17         ` Bello Olamide
2026-01-22 14:41       ` Phillip Wood
2026-01-22 15:29         ` Bello Olamide
2026-01-23 10:43           ` Phillip Wood
2026-01-23 13:24             ` Bello Olamide
2026-01-17 20:59     ` [Outreachy PATCH v3 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
2026-01-22 14:41       ` Phillip Wood
2026-01-22 15:29         ` Bello Olamide
2026-01-20 15:19     ` [Outreachy PATCH v3 0/3] store repo specific config values in new " Bello Olamide
2026-01-24 11:55     ` [Outreachy PATCH v4 " Olamide Caleb Bello
2026-01-24 11:55       ` [Outreachy PATCH v4 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
2026-01-24 11:55       ` [Outreachy PATCH v4 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
2026-01-24 11:55       ` [Outreachy PATCH v4 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
2026-01-24 12:21       ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Olamide Caleb Bello
2026-01-24 12:21         ` [Outreachy PATCH v5 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
2026-01-29 18:01           ` Junio C Hamano
2026-01-24 12:21         ` [Outreachy PATCH v5 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
2026-01-29 18:12           ` Junio C Hamano
2026-01-24 12:21         ` [Outreachy PATCH v5 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
2026-01-29 18:37           ` Junio C Hamano
2026-01-30 16:20             ` Junio C Hamano
2026-01-30 20:15               ` Junio C Hamano
2026-01-29  8:29         ` [Outreachy PATCH v5 0/3] store repo specific config values in new " Bello Olamide
2026-02-03 15:42         ` [Outreachy PATCH v6 " Olamide Caleb Bello
2026-02-03 15:42           ` [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
2026-02-04 16:39             ` Phillip Wood
2026-02-09  8:47               ` Bello Olamide
2026-02-07  1:14             ` Junio C Hamano
2026-02-08 11:14               ` Phillip Wood
2026-02-09  8:54                 ` Bello Olamide
2026-02-10  8:40                 ` Bello Olamide
2026-02-03 15:42           ` [Outreachy PATCH v6 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
2026-02-04 16:55             ` Phillip Wood
2026-02-03 15:42           ` [Outreachy PATCH v6 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
2026-02-04 16:57           ` [Outreachy PATCH v6 0/3] store repo specific config values in new " Phillip Wood
2026-02-16 16:38         ` [Outreachy PATCH v7 " Olamide Caleb Bello
2026-02-16 16:38           ` [Outreachy PATCH v7 1/3] environment: stop storing `core.attributesFile` globally Olamide Caleb Bello
2026-02-16 16:38           ` [Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally Olamide Caleb Bello
2026-02-26 12:57             ` Christian Couder
2026-02-26 15:23               ` Junio C Hamano
2026-02-26 16:24                 ` Bello Olamide
2026-02-16 16:38           ` [Outreachy PATCH v7 3/3] environment: move "branch.autoSetupMerge" into `struct repo_config_values` Olamide Caleb Bello
2026-02-17 20:08           ` [Outreachy PATCH v7 0/3] store repo specific config values in new " Junio C Hamano
2026-02-18 11:27             ` Bello Olamide
2026-02-26 13:03             ` Christian Couder
2026-02-26 15:19               ` Junio C Hamano
  -- strict thread matches above, loose matches on Subject: below --
2026-02-10 10:17 [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally Bello Caleb Olamide
2026-02-10 15:07 ` Phillip Wood
2026-02-11  8:05   ` Bello Olamide
2026-02-11  9:31 ` Phillip Wood
2026-02-11 12:05   ` Bello Olamide
2026-02-11 16:46   ` Junio C Hamano
2026-02-12 10:33     ` Phillip Wood
2026-02-12 17:13       ` Junio C Hamano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox