{"thread":{"id":"64782","subject":"[Outreachy PATCH RFC 0/3] store git_default_config() parsed values in new config struct","startedAt":"2026-01-12T12:59:38Z","lastAt":"2026-02-26T16:24:42Z","messageCount":71,"participants":["Olamide Caleb Bello","Phillip Wood","Bello Olamide","Junio C Hamano","Toon Claes","Christian Couder"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"533608","messageId":"cover.1768217572.git.belkid98@gmail.com","threadId":"64782","inReplyTo":null,"subject":"[Outreachy PATCH RFC 0/3] store git_default_config() parsed values in new config struct","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-12T12:59:22Z","receivedAt":"2026-01-12T12:59:38Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"Hi Git Community,\nOver the course of my ongoing internship, which focused on moving global\nvariables in environment.h into local scope, I have attempted to move some\nvariables into the struct repo-settings.\nHowever there have been some design concerns as regards the use of\n`prepare_repo_settings()` with respect to when and where to call the\nfunction, and also the change in behaviours when the variable is lazily\nloaded as discussed in [1] and [2].\n\nAfter different deliberations, Phillip Wood proposed creating a new config\nstruct [3], adding it to the repository struct and passing the repo struct to\n`git_default_config()` to store the parsed config values per repo.\nThis would mean we would not need to remove code from\n`git_default_config()`, and hence the current behaviours will be retained.\n\nI have experimented with this approach for some values and I would\nappreciate feedbacks about this approach before we can move forward\nand use it for more variables related to `git_default_config()`.\n\nFor now, the parsed value is stored in `the_repository` in\n`git_default_*_config()` and further efforts to pass the repository\nparameter to `git_default_config()` as the callback parameter will\nbe looked into later on.\n\n1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/\n2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/\n3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/\n\nOlamide Caleb Bello (3):\n  environment: stop storing `core.attributesFile` globally\n  environment: stop using core.sparseCheckout globally\n  environment: move \"branch.autoSetupMerge\" into `struct config_values`\n\n attr.c                      |  8 +++++---\n builtin/backfill.c          |  3 +--\n builtin/branch.c            |  2 +-\n builtin/checkout.c          |  2 +-\n builtin/clone.c             |  2 +-\n builtin/mv.c                |  2 +-\n builtin/push.c              |  2 +-\n builtin/sparse-checkout.c   | 22 +++++++++++-----------\n builtin/submodule--helper.c |  2 +-\n builtin/worktree.c          |  2 +-\n config.c                    | 10 ++++++++++\n config.h                    | 13 +++++++++++++\n dir.c                       |  2 +-\n environment.c               | 20 ++++++++++----------\n environment.h               |  2 --\n repository.c                |  7 +++++++\n repository.h                |  4 ++++\n sparse-index.c              |  4 ++--\n unpack-trees.c              |  2 +-\n wt-status.c                 |  3 ++-\n 20 files changed, 74 insertions(+), 40 deletions(-)\n\n-- \n2.34.1\n\n"},{"id":"533609","messageId":"abbfe2531158e9bc99ddb903b60a77c26beb0c9c.1768217572.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768217572.git.belkid98@gmail.com","subject":"[Outreachy PATCH RFC 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-12T12:59:23Z","receivedAt":"2026-01-12T12:59:41Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value parsed in git_default_core_config() is loaded eagerly\nand stored in the global variable `git_attributes_file`.\nStoring this value in a global variable can lead to unexpected\nbehaviours when more than one Git repository run in the same Git process.\n\nMove this value into a `struct config_values` which holds all the values\nparsed by `git_default_config()` and can be accessed per\nrepository via `git_default_config()`. This will prevent us from\nmoving any code from git_default_core_config(), ensuring the current\nbehaviour remains the same while also enabling the libification of Git.\n\nIt is important to note that `git_default_config()` is a wrapper to other\n`git_default_*_config()` such as `git_default_core_config()`.\nTherefore to access and modify this global variable,\nthe change has to be made in the function which parses and\nstores the value in the global variable.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n attr.c        | 8 +++++---\n config.c      | 5 +++++\n config.h      | 8 ++++++++\n environment.c | 7 ++++---\n environment.h | 1 -\n repository.c  | 6 ++++++\n repository.h  | 4 ++++\n 7 files changed, 32 insertions(+), 7 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 4999b7e09d..eb7b82707d 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -881,10 +881,12 @@ const char *git_attr_system_file(void)\n \n const char *git_attr_global_file(void)\n {\n-\tif (!git_attributes_file)\n-\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n+\tstruct config_values *cfg = the_repository->cfg_values;\n \n-\treturn git_attributes_file;\n+\tif (!cfg->attributes_file_path)\n+\t\tcfg->attributes_file_path = xdg_config_home(\"attributes\");\n+\n+\treturn cfg->attributes_file_path;\n }\n \n int git_attr_system_is_enabled(void)\ndiff --git a/config.c b/config.c\nindex 7f6d53b473..8b882f64ae 100644\n--- a/config.c\n+++ b/config.c\n@@ -1761,6 +1761,11 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,\n \treturn strcmp(e1->key, e2->key);\n }\n \n+void config_values_clear(struct config_values *cfg)\n+{\n+\tfree(cfg->attributes_file_path);\n+}\n+\n void git_configset_init(struct config_set *set)\n {\n \thashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);\ndiff --git a/config.h b/config.h\nindex ba426a960a..1652d315e2 100644\n--- a/config.h\n+++ b/config.h\n@@ -135,6 +135,13 @@ struct config_context {\n \t/* Config source metadata for key and value. */\n \tconst struct key_value_info *kvi;\n };\n+\n+/* Holds values parsed by git_default_config() */\n+struct config_values {\n+\t/* core config values */\n+\tchar *attributes_file_path;\n+\n+};\n #define CONFIG_CONTEXT_INIT { 0 }\n \n /**\n@@ -187,6 +194,7 @@ int git_config_from_blob_oid(config_fn_t fn, const char *name,\n void git_config_push_parameter(const char *text);\n void git_config_push_env(const char *spec);\n int git_config_from_parameters(config_fn_t fn, void *data);\n+void config_values_clear(struct config_values *cfg);\n \n /*\n  * Read config when the Git directory has not yet been set up. In case\ndiff --git a/environment.c b/environment.c\nindex a770b5921d..d633b0405b 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -53,7 +53,6 @@ char *git_commit_encoding;\n char *git_log_output_encoding;\n char *apply_default_whitespace;\n char *apply_default_ignorewhitespace;\n-char *git_attributes_file;\n int zlib_compression_level = Z_BEST_SPEED;\n int pack_compression_level = Z_DEFAULT_COMPRESSION;\n int fsync_object_files = -1;\n@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n static int git_default_core_config(const char *var, const char *value,\n \t\t\t\t   const struct config_context *ctx, void *cb)\n {\n+\tstruct config_values *cfg = the_repository->cfg_values;\n+\n \t/* This needs a better name */\n \tif (!strcmp(var, \"core.filemode\")) {\n \t\ttrust_executable_bit = git_config_bool(var, value);\n@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.attributesfile\")) {\n-\t\tFREE_AND_NULL(git_attributes_file);\n-\t\treturn git_config_pathname(&git_attributes_file, var, value);\n+\t\tFREE_AND_NULL(cfg->attributes_file_path);\n+\t\treturn git_config_pathname(&cfg->attributes_file_path, var, value);\n \t}\n \n \tif (!strcmp(var, \"core.bare\")) {\ndiff --git a/environment.h b/environment.h\nindex 51898c99cd..3512a7072e 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -152,7 +152,6 @@ extern int assume_unchanged;\n extern int warn_on_object_refname_ambiguity;\n extern char *apply_default_whitespace;\n extern char *apply_default_ignorewhitespace;\n-extern char *git_attributes_file;\n extern int zlib_compression_level;\n extern int pack_compression_level;\n extern unsigned long pack_size_limit_cfg;\ndiff --git a/repository.c b/repository.c\nindex c7e75215ac..3ad944e71c 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -55,6 +55,7 @@ void initialize_repository(struct repository *repo)\n \trepo->remote_state = remote_state_new();\n \trepo->parsed_objects = parsed_object_pool_new(repo);\n \tALLOC_ARRAY(repo->index, 1);\n+\tCALLOC_ARRAY(repo->cfg_values, 1);\n \tindex_state_init(repo->index, repo);\n \trepo->check_deprecated_config = true;\n \n@@ -403,6 +404,11 @@ void repo_clear(struct repository *repo)\n \t\tFREE_AND_NULL(repo->remote_state);\n \t}\n \n+\tif (repo->cfg_values) {\n+\t\tconfig_values_clear(repo->cfg_values);\n+\t\tFREE_AND_NULL(repo->cfg_values);\n+\t}\n+\n \tstrmap_for_each_entry(&repo->submodule_ref_stores, &iter, e)\n \t\tref_store_release(e->value);\n \tstrmap_clear(&repo->submodule_ref_stores, 1);\ndiff --git a/repository.h b/repository.h\nindex 6063c4b846..5fb825f799 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -13,6 +13,7 @@ struct object_database;\n struct submodule_cache;\n struct promisor_remote_config;\n struct remote_state;\n+struct config_values;\n \n enum ref_storage_format {\n \tREF_STORAGE_FORMAT_UNKNOWN,\n@@ -171,6 +172,9 @@ struct repository {\n \n \t/* Should repo_config() check for deprecated settings */\n \tbool check_deprecated_config;\n+\n+\t/* Repository's config values parsed by git_default_config() */\n+\tstruct config_values *cfg_values;\n };\n \n #ifdef USE_THE_REPOSITORY_VARIABLE\n-- \n2.34.1\n\n"},{"id":"533610","messageId":"87b86239ff31cef5ebb6264d630b9072132c490f.1768217572.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768217572.git.belkid98@gmail.com","subject":"[Outreachy PATCH RFC 2/3] environment: stop using core.sparseCheckout globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-12T12:59:24Z","receivedAt":"2026-01-12T12:59:44Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `core.sparseCheckout` is parsed in\n`git_default_core_config()` and stored globally in\n`core_appy_sparse_checkout`. This could cause unintended behaviours\nwhen different Git repositories running in the same process access this\nvariable.\n\nMove the parsed value into `struct config_values` which holds all the\nvalues parsed by `git_default_config()` and can be accessed\nper repo via `git_default_config()`. This would mean we do not need to\nremove code from `git_default_core_config()`, thereby retaining current\nbehaviours.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n builtin/backfill.c        |  3 +--\n builtin/clone.c           |  2 +-\n builtin/mv.c              |  2 +-\n builtin/sparse-checkout.c | 22 +++++++++++-----------\n builtin/worktree.c        |  2 +-\n config.h                  |  1 +\n dir.c                     |  2 +-\n environment.c             |  3 +--\n environment.h             |  1 -\n sparse-index.c            |  4 ++--\n unpack-trees.c            |  2 +-\n wt-status.c               |  3 ++-\n 12 files changed, 23 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/backfill.c b/builtin/backfill.c\nindex e80fc1b694..7745aa6bf9 100644\n--- a/builtin/backfill.c\n+++ b/builtin/backfill.c\n@@ -1,4 +1,3 @@\n-/* We need this macro to access core_apply_sparse_checkout */\n #define USE_THE_REPOSITORY_VARIABLE\n \n #include \"builtin.h\"\n@@ -139,7 +138,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n \trepo_config(repo, git_default_config, NULL);\n \n \tif (ctx.sparse < 0)\n-\t\tctx.sparse = core_apply_sparse_checkout;\n+\t\tctx.sparse = repo->cfg_values->sparse_checkout;\n \n \tresult = do_backfill(&ctx);\n \tbackfill_context_clear(&ctx);\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..568e76a135 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n \t * We must apply the setting in the current process\n \t * for the later checkout to use the sparse-checkout file.\n \t */\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->cfg_values->sparse_checkout = 1;\n \n \tcmd.git_cmd = 1;\n \tif (run_command(&cmd)) {\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex d43925097b..e95c1dfb91 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -572,7 +572,7 @@ int cmd_mv(int argc,\n \t\trename_index_entry_at(the_repository->index, pos, dst);\n \n \t\tif (ignore_sparse &&\n-\t\t    core_apply_sparse_checkout &&\n+\t\t    the_repository->cfg_values->sparse_checkout &&\n \t\t    core_sparse_checkout_cone) {\n \t\t\t/*\n \t\t\t * NEEDSWORK: we are *not* paying attention to\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..0eb81b2188 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,\n \tint res;\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->cfg_values->sparse_checkout)\n \t\tdie(_(\"this worktree is not sparse\"));\n \n \targc = parse_options(argc, argv, prefix,\n@@ -400,11 +400,11 @@ static int set_config(struct repository *repo,\n \n static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n \t/* If not specified, use previous definition of cone mode */\n-\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n+\tif (*cone_mode == -1 && the_repository->cfg_values->sparse_checkout)\n \t\t*cone_mode = core_sparse_checkout_cone;\n \n \t/* Set cone/non-cone mode appropriately */\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->cfg_values->sparse_checkout = 1;\n \tif (*cone_mode == 1 || *cone_mode == -1) {\n \t\tcore_sparse_checkout_cone = 1;\n \t\treturn MODE_CONE_PATTERNS;\n@@ -418,7 +418,7 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind\n \tint mode, record_mode;\n \n \t/* Determine if we need to record the mode; ensure sparse checkout on */\n-\trecord_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n+\trecord_mode = (*cone_mode != -1) || !repo->cfg_values->sparse_checkout;\n \n \tmode = update_cone_mode(cone_mode);\n \tif (record_mode && set_config(repo, mode))\n@@ -699,9 +699,9 @@ static int modify_pattern_list(struct repository *repo,\n \t\tbreak;\n \t}\n \n-\tif (!core_apply_sparse_checkout) {\n+\tif (!repo->cfg_values->sparse_checkout) {\n \t\tset_config(repo, MODE_ALL_PATTERNS);\n-\t\tcore_apply_sparse_checkout = 1;\n+\t\trepo->cfg_values->sparse_checkout = 1;\n \t\tchanged_config = 1;\n \t}\n \n@@ -798,7 +798,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,\n \tint ret;\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!repo->cfg_values->sparse_checkout)\n \t\tdie(_(\"no sparse-checkout to add to\"));\n \n \trepo_read_index(repo);\n@@ -907,7 +907,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!repo->cfg_values->sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n \n \treapply_opts.cone_mode = -1;\n@@ -969,7 +969,7 @@ static int sparse_checkout_clean(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!repo->cfg_values->sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to clean directories\"));\n \tif (!core_sparse_checkout_cone)\n \t\tdie(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n@@ -1035,7 +1035,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \tstruct pattern_list pl;\n \n \t/*\n-\t * We do not exit early if !core_apply_sparse_checkout; due to the\n+\t * We do not exit early if !repo->cfg_values->sparse_checkout; due to the\n \t * ability for users to manually muck things up between\n \t *   direct editing of .git/info/sparse-checkout\n \t *   running read-tree -m u HEAD or update-index --skip-worktree\n@@ -1061,7 +1061,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \thashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);\n \thashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n \tpl.use_cone_patterns = 0;\n-\tcore_apply_sparse_checkout = 1;\n+\trepo->cfg_values->sparse_checkout = 1;\n \n \tadd_pattern(\"/*\", empty_base, 0, &pl, 0);\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex fbdaf2eb2e..921b4a8ca2 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,\n \t * If the current worktree has sparse-checkout enabled, then copy\n \t * the sparse-checkout patterns from the current worktree.\n \t */\n-\tif (core_apply_sparse_checkout)\n+\tif (the_repository->cfg_values->sparse_checkout)\n \t\tcopy_sparse_checkout(sb_repo.buf);\n \n \t/*\ndiff --git a/config.h b/config.h\nindex 1652d315e2..21d65c440e 100644\n--- a/config.h\n+++ b/config.h\n@@ -140,6 +140,7 @@ struct config_context {\n struct config_values {\n \t/* core config values */\n \tchar *attributes_file_path;\n+\tint sparse_checkout;\n \n };\n #define CONFIG_CONTEXT_INIT { 0 }\ndiff --git a/dir.c b/dir.c\nindex b00821f294..d23f057f95 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(\n \n int init_sparse_checkout_patterns(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout)\n+\tif (!istate->repo->cfg_values->sparse_checkout)\n \t\treturn 1;\n \tif (istate->sparse_checkout_patterns)\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex d633b0405b..15b63cdc4a 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -74,7 +74,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #endif\n enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n int grafts_keep_true_parents;\n-int core_apply_sparse_checkout;\n int core_sparse_checkout_cone;\n int sparse_expect_files_outside_of_patterns;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.sparsecheckout\")) {\n-\t\tcore_apply_sparse_checkout = git_config_bool(var, value);\n+\t\tcfg->sparse_checkout = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \ndiff --git a/environment.h b/environment.h\nindex 3512a7072e..60d5933cc2 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -161,7 +161,6 @@ extern int precomposed_unicode;\n extern int protect_hfs;\n extern int protect_ntfs;\n \n-extern int core_apply_sparse_checkout;\n extern int core_sparse_checkout_cone;\n extern int sparse_expect_files_outside_of_patterns;\n \ndiff --git a/sparse-index.c b/sparse-index.c\nindex 76f90da5f5..14f3f07fe9 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -152,7 +152,7 @@ static int index_has_unmerged_entries(struct index_state *istate)\n \n int is_sparse_index_allowed(struct index_state *istate, int flags)\n {\n-\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n+\tif (!istate->repo->cfg_values->sparse_checkout || !core_sparse_checkout_cone)\n \t\treturn 0;\n \n \tif (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n@@ -670,7 +670,7 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n \n void clear_skip_worktree_from_present_files(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout ||\n+\tif (!istate->repo->cfg_values->sparse_checkout ||\n \t    sparse_expect_files_outside_of_patterns)\n \t\treturn;\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex f38c761ab9..ffab104bfb 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tif (o->prefix)\n \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n \n-\tif (!core_apply_sparse_checkout || !o->update)\n+\tif (!repo->cfg_values->sparse_checkout || !o->update)\n \t\to->skip_sparse_checkout = 1;\n \tif (!o->skip_sparse_checkout) {\n \t\tmemset(&pl, 0, sizeof(pl));\ndiff --git a/wt-status.c b/wt-status.c\nindex e12adb26b9..de527bd620 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -35,6 +35,7 @@\n #include \"lockfile.h\"\n #include \"sequencer.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"config.h\"\n \n #define AB_DELAY_WARNING_IN_MS (2 * 1000)\n #define UF_DELAY_WARNING_IN_MS (2 * 1000)\n@@ -1764,7 +1765,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n \tint skip_worktree = 0;\n \tint i;\n \n-\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n+\tif (!r->cfg_values->sparse_checkout || r->index->cache_nr == 0) {\n \t\t/*\n \t\t * Don't compute percentage of checked out files if we\n \t\t * aren't in a sparse checkout or would get division by 0.\n-- \n2.34.1\n\n"},{"id":"533611","messageId":"2ea3bd7816624bc03426ded68ddd1e9c15f19127.1768217572.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768217572.git.belkid98@gmail.com","subject":"[Outreachy PATCH RFC 3/3] environment: move \"branch.autoSetupMerge\" into `struct config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-12T12:59:25Z","receivedAt":"2026-01-12T12:59:47Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `brach.autoSetupMerge` is parsed in\n`git_default_branch_config()` and stored in the global variable\n`git_branch_track`. This global variable can cause unexpected behaviours\nwhen multiple Git repos run in the the same process.\n\nMove this value into `struct config_values` which holds all values\nparsed by `git_default_config()` and can be accessed per\nrepo via `git_default_config()`. This would mean we do not have to remove\ncode from `git_default_branch_config()`, thereby retaining the same\nbehaviour.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n builtin/branch.c            |  2 +-\n builtin/checkout.c          |  2 +-\n builtin/push.c              |  2 +-\n builtin/submodule--helper.c |  2 +-\n config.c                    |  5 +++++\n config.h                    |  4 ++++\n environment.c               | 10 +++++-----\n repository.c                |  1 +\n 8 files changed, 19 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex c577b5d20f..6dde426e90 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -795,7 +795,7 @@ int cmd_branch(int argc,\n \tif (!sorting_options.nr)\n \t\tstring_list_append(&sorting_options, \"refname\");\n \n-\ttrack = git_branch_track;\n+\ttrack = the_repository->cfg_values->git_branch_track;\n \n \thead = refs_resolve_refdup(get_main_ref_store(the_repository), \"HEAD\",\n \t\t\t\t   0, &head_oid, NULL);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 261699e2f5..0b8aa6b45a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1631,7 +1631,7 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n \t} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)\n-\t\topts->track = git_branch_track;\n+\t\topts->track = the_repository->cfg_values->git_branch_track;\n \n \tif (new_branch_info->name && !new_branch_info->commit)\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 5b6cebbb85..14967002ae 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -162,7 +162,7 @@ static NORETURN void die_push_simple(struct branch *branch,\n \t\tadvice_pushdefault_maybe = _(\"\\n\"\n \t\t\t\t \"To choose either option permanently, \"\n \t\t\t\t \"see push.default in 'git help config'.\\n\");\n-\tif (git_branch_track != BRANCH_TRACK_SIMPLE)\n+\tif (the_repository->cfg_values->git_branch_track != BRANCH_TRACK_SIMPLE)\n \t\tadvice_automergesimple_maybe = _(\"\\n\"\n \t\t\t\t \"To avoid automatically configuring \"\n \t\t\t\t \"an upstream branch when its name\\n\"\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d537ab087a..0e2226b3c8 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3128,7 +3128,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,\n \t};\n \n \trepo_config(the_repository, git_default_config, NULL);\n-\ttrack = git_branch_track;\n+\ttrack = the_repository->cfg_values->git_branch_track;\n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \n \tif (argc != 3)\ndiff --git a/config.c b/config.c\nindex 8b882f64ae..c19df32ad6 100644\n--- a/config.c\n+++ b/config.c\n@@ -1766,6 +1766,11 @@ void config_values_clear(struct config_values *cfg)\n \tfree(cfg->attributes_file_path);\n }\n \n+void config_values_init_defaults(struct config_values *cfg)\n+{\n+\tcfg->git_branch_track = BRANCH_TRACK_REMOTE;\n+}\n+\n void git_configset_init(struct config_set *set)\n {\n \thashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);\ndiff --git a/config.h b/config.h\nindex 21d65c440e..62032346e3 100644\n--- a/config.h\n+++ b/config.h\n@@ -5,6 +5,7 @@\n #include \"string-list.h\"\n #include \"repository.h\"\n #include \"parse.h\"\n+#include \"branch.h\"\n \n /**\n  * The config API gives callers a way to access Git configuration files\n@@ -142,6 +143,8 @@ struct config_values {\n \tchar *attributes_file_path;\n \tint sparse_checkout;\n \n+\t/* branch config values */\n+\tenum branch_track git_branch_track;\n };\n #define CONFIG_CONTEXT_INIT { 0 }\n \n@@ -195,6 +198,7 @@ int git_config_from_blob_oid(config_fn_t fn, const char *name,\n void git_config_push_parameter(const char *text);\n void git_config_push_env(const char *spec);\n int git_config_from_parameters(config_fn_t fn, void *data);\n+void config_values_init_defaults(struct config_values *cfg);\n void config_values_clear(struct config_values *cfg);\n \n /*\ndiff --git a/environment.c b/environment.c\nindex 15b63cdc4a..207e63f519 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;\n enum eol core_eol = EOL_UNSET;\n int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;\n char *check_roundtrip_encoding;\n-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n enum rebase_setup_type autorebase = AUTOREBASE_NEVER;\n enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #ifndef OBJECT_CREATION_MODE\n@@ -607,18 +606,19 @@ static int git_default_i18n_config(const char *var, const char *value)\n \n static int git_default_branch_config(const char *var, const char *value)\n {\n+\tstruct config_values *cfg = the_repository->cfg_values;\n \tif (!strcmp(var, \"branch.autosetupmerge\")) {\n \t\tif (value && !strcmp(value, \"always\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_ALWAYS;\n+\t\t\tcfg->git_branch_track = BRANCH_TRACK_ALWAYS;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n+\t\t\tcfg->git_branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"simple\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\tcfg->git_branch_track = BRANCH_TRACK_SIMPLE;\n \t\t\treturn 0;\n \t\t}\n-\t\tgit_branch_track = git_config_bool(var, value);\n+\t\tcfg->git_branch_track = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"branch.autosetuprebase\")) {\ndiff --git a/repository.c b/repository.c\nindex 3ad944e71c..98237eceaa 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -56,6 +56,7 @@ void initialize_repository(struct repository *repo)\n \trepo->parsed_objects = parsed_object_pool_new(repo);\n \tALLOC_ARRAY(repo->index, 1);\n \tCALLOC_ARRAY(repo->cfg_values, 1);\n+\tconfig_values_init_defaults(repo->cfg_values);\n \tindex_state_init(repo->index, repo);\n \trepo->check_deprecated_config = true;\n \n-- \n2.34.1\n\n"},{"id":"533633","messageId":"b0e4b10d-5c2a-4685-9b79-92bf838c90cf@gmail.com","threadId":"64782","inReplyTo":"abbfe2531158e9bc99ddb903b60a77c26beb0c9c.1768217572.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH RFC 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-01-12T14:29:40Z","receivedAt":"2026-01-12T14:29:44Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Olamide\n\nOn 12/01/2026 12:59, Olamide Caleb Bello wrote:\n> The config value parsed in git_default_core_config() is loaded eagerly\n> and stored in the global variable `git_attributes_file`.\n> Storing this value in a global variable can lead to unexpected\n> behaviours when more than one Git repository run in the same Git process.\n> \n> Move this value into a `struct config_values` which holds all the values\n> parsed by `git_default_config()` and can be accessed per\n> repository via `git_default_config()`. This will prevent us from\n> moving any code from git_default_core_config(), ensuring the current\n> behaviour remains the same while also enabling the libification of Git.\n\nThe important thing is that we're not changing when the config is \nparsed, not that we're not removing code from git_default_core_config().\n\nLooking at the changes below, I think it would be simpler to embed \n`struct config_values` in `struct repository` as we do for `struct \nrepo_settings`. That would simplify things as we wouldn't have to mess \nabout allocating an instance on the heap and freeing it in repo_clear(). \nI'd be tempted to call the new struct `repo_config` rather than \n`config_values` which is rather non-specific. I'm also not sure \nconfig.[ch] is the best home for it, maybe it should live in \nenvironment.[ch] for now - we might want to move it to it's own file at \nsome point.\n\nThanks\n\nPhillip\n\n> It is important to note that `git_default_config()` is a wrapper to other\n> `git_default_*_config()` such as `git_default_core_config()`.\n> Therefore to access and modify this global variable,\n> the change has to be made in the function which parses and\n> stores the value in the global variable.\n> \n> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> ---\n>   attr.c        | 8 +++++---\n>   config.c      | 5 +++++\n>   config.h      | 8 ++++++++\n>   environment.c | 7 ++++---\n>   environment.h | 1 -\n>   repository.c  | 6 ++++++\n>   repository.h  | 4 ++++\n>   7 files changed, 32 insertions(+), 7 deletions(-)\n> \n> diff --git a/attr.c b/attr.c\n> index 4999b7e09d..eb7b82707d 100644\n> --- a/attr.c\n> +++ b/attr.c\n> @@ -881,10 +881,12 @@ const char *git_attr_system_file(void)\n>   \n>   const char *git_attr_global_file(void)\n>   {\n> -\tif (!git_attributes_file)\n> -\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n> +\tstruct config_values *cfg = the_repository->cfg_values;\n>   \n> -\treturn git_attributes_file;\n> +\tif (!cfg->attributes_file_path)\n> +\t\tcfg->attributes_file_path = xdg_config_home(\"attributes\");\n> +\n> +\treturn cfg->attributes_file_path;\n>   }\n>   \n>   int git_attr_system_is_enabled(void)\n> diff --git a/config.c b/config.c\n> index 7f6d53b473..8b882f64ae 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -1761,6 +1761,11 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,\n>   \treturn strcmp(e1->key, e2->key);\n>   }\n>   \n> +void config_values_clear(struct config_values *cfg)\n> +{\n> +\tfree(cfg->attributes_file_path);\n> +}\n> +\n>   void git_configset_init(struct config_set *set)\n>   {\n>   \thashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);\n> diff --git a/config.h b/config.h\n> index ba426a960a..1652d315e2 100644\n> --- a/config.h\n> +++ b/config.h\n> @@ -135,6 +135,13 @@ struct config_context {\n>   \t/* Config source metadata for key and value. */\n>   \tconst struct key_value_info *kvi;\n>   };\n> +\n> +/* Holds values parsed by git_default_config() */\n> +struct config_values {\n> +\t/* core config values */\n> +\tchar *attributes_file_path;\n> +\n> +};\n>   #define CONFIG_CONTEXT_INIT { 0 }\n>   \n>   /**\n> @@ -187,6 +194,7 @@ int git_config_from_blob_oid(config_fn_t fn, const char *name,\n>   void git_config_push_parameter(const char *text);\n>   void git_config_push_env(const char *spec);\n>   int git_config_from_parameters(config_fn_t fn, void *data);\n> +void config_values_clear(struct config_values *cfg);\n>   \n>   /*\n>    * Read config when the Git directory has not yet been set up. In case\n> diff --git a/environment.c b/environment.c\n> index a770b5921d..d633b0405b 100644\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -53,7 +53,6 @@ char *git_commit_encoding;\n>   char *git_log_output_encoding;\n>   char *apply_default_whitespace;\n>   char *apply_default_ignorewhitespace;\n> -char *git_attributes_file;\n>   int zlib_compression_level = Z_BEST_SPEED;\n>   int pack_compression_level = Z_DEFAULT_COMPRESSION;\n>   int fsync_object_files = -1;\n> @@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n>   static int git_default_core_config(const char *var, const char *value,\n>   \t\t\t\t   const struct config_context *ctx, void *cb)\n>   {\n> +\tstruct config_values *cfg = the_repository->cfg_values;\n> +\n>   \t/* This needs a better name */\n>   \tif (!strcmp(var, \"core.filemode\")) {\n>   \t\ttrust_executable_bit = git_config_bool(var, value);\n> @@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,\n>   \t}\n>   \n>   \tif (!strcmp(var, \"core.attributesfile\")) {\n> -\t\tFREE_AND_NULL(git_attributes_file);\n> -\t\treturn git_config_pathname(&git_attributes_file, var, value);\n> +\t\tFREE_AND_NULL(cfg->attributes_file_path);\n> +\t\treturn git_config_pathname(&cfg->attributes_file_path, var, value);\n>   \t}\n>   \n>   \tif (!strcmp(var, \"core.bare\")) {\n> diff --git a/environment.h b/environment.h\n> index 51898c99cd..3512a7072e 100644\n> --- a/environment.h\n> +++ b/environment.h\n> @@ -152,7 +152,6 @@ extern int assume_unchanged;\n>   extern int warn_on_object_refname_ambiguity;\n>   extern char *apply_default_whitespace;\n>   extern char *apply_default_ignorewhitespace;\n> -extern char *git_attributes_file;\n>   extern int zlib_compression_level;\n>   extern int pack_compression_level;\n>   extern unsigned long pack_size_limit_cfg;\n> diff --git a/repository.c b/repository.c\n> index c7e75215ac..3ad944e71c 100644\n> --- a/repository.c\n> +++ b/repository.c\n> @@ -55,6 +55,7 @@ void initialize_repository(struct repository *repo)\n>   \trepo->remote_state = remote_state_new();\n>   \trepo->parsed_objects = parsed_object_pool_new(repo);\n>   \tALLOC_ARRAY(repo->index, 1);\n> +\tCALLOC_ARRAY(repo->cfg_values, 1);\n>   \tindex_state_init(repo->index, repo);\n>   \trepo->check_deprecated_config = true;\n>   \n> @@ -403,6 +404,11 @@ void repo_clear(struct repository *repo)\n>   \t\tFREE_AND_NULL(repo->remote_state);\n>   \t}\n>   \n> +\tif (repo->cfg_values) {\n> +\t\tconfig_values_clear(repo->cfg_values);\n> +\t\tFREE_AND_NULL(repo->cfg_values);\n> +\t}\n> +\n>   \tstrmap_for_each_entry(&repo->submodule_ref_stores, &iter, e)\n>   \t\tref_store_release(e->value);\n>   \tstrmap_clear(&repo->submodule_ref_stores, 1);\n> diff --git a/repository.h b/repository.h\n> index 6063c4b846..5fb825f799 100644\n> --- a/repository.h\n> +++ b/repository.h\n> @@ -13,6 +13,7 @@ struct object_database;\n>   struct submodule_cache;\n>   struct promisor_remote_config;\n>   struct remote_state;\n> +struct config_values;\n>   \n>   enum ref_storage_format {\n>   \tREF_STORAGE_FORMAT_UNKNOWN,\n> @@ -171,6 +172,9 @@ struct repository {\n>   \n>   \t/* Should repo_config() check for deprecated settings */\n>   \tbool check_deprecated_config;\n> +\n> +\t/* Repository's config values parsed by git_default_config() */\n> +\tstruct config_values *cfg_values;\n>   };\n>   \n>   #ifdef USE_THE_REPOSITORY_VARIABLE\n\n"},{"id":"533642","messageId":"CAD=f0L-CGTZRTxync-cidJsaruTa7nz4mkcqycATA_C0Oi2rZA@mail.gmail.com","threadId":"64782","inReplyTo":"b0e4b10d-5c2a-4685-9b79-92bf838c90cf@gmail.com","subject":"Re: [Outreachy PATCH RFC 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-12T15:05:44Z","receivedAt":"2026-01-12T15:05:43Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Mon, 12 Jan 2026 at 15:29, Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> Hi Olamide\n\nHellp Phillip\n\n>\n> On 12/01/2026 12:59, Olamide Caleb Bello wrote:\n> > The config value parsed in git_default_core_config() is loaded eagerly\n> > and stored in the global variable `git_attributes_file`.\n> > Storing this value in a global variable can lead to unexpected\n> > behaviours when more than one Git repository run in the same Git process.\n> >\n> > Move this value into a `struct config_values` which holds all the values\n> > parsed by `git_default_config()` and can be accessed per\n> > repository via `git_default_config()`. This will prevent us from\n> > moving any code from git_default_core_config(), ensuring the current\n> > behaviour remains the same while also enabling the libification of Git.\n>\n> The important thing is that we're not changing when the config is\n> parsed, not that we're not removing code from git_default_core_config().\n\nOkay thanks for clarifying\n\n>\n> Looking at the changes below, I think it would be simpler to embed\n> `struct config_values` in `struct repository` as we do for `struct\n> repo_settings`. That would simplify things as we wouldn't have to mess\n> about allocating an instance on the heap and freeing it in repo_clear().\n\nOkay I will try this approach\n\n> I'd be tempted to call the new struct `repo_config` rather than\n> `config_values` which is rather non-specific.\n\nI initially considered `repo_config`, but a struct with the name already exists\n\n> I'm also not sure\n> config.[ch] is the best home for it, maybe it should live in\n> environment.[ch] for now - we might want to move it to it's own file at\n> some point.\n\nOkay this is noted.\nThanks\n"},{"id":"533756","messageId":"cover.1768318762.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768217572.git.belkid98@gmail.com","subject":"[Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-13T16:43:59Z","receivedAt":"2026-01-13T17:25:44Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"Hi Git Community,\nOver the course of my ongoing internship, which focused on moving global\nvariables in environment.h into local scope, I have attempted to move some\nvariables into the struct repo-settings.\nHowever there have been some design concerns as regards the use of\n`prepare_repo_settings()` with respect to when and where to call the\nfunction, and also the change in behaviours when the variable is lazily\nloaded as discussed in [1] and [2].\n\nAfter different deliberations, Phillip Wood proposed creating a new config\nstruct [3], adding it to the repository struct and passing the repo struct to\n`git_default_config()` to store the parsed config values per repo.\nThis ensures the current behaviours will be retained.\n\nI have experimented with this approach for some values and I would\nappreciate feedbacks about this approach before we can move forward\nand use it for more variables related to `git_default_config()`.\n\nFor now, the parsed value is stored in `the_repository` in\n`git_default_*_config()` and further efforts to pass the repository\nparameter to `git_default_config()` as the callback parameter will\nbe looked into later on.\nThe link to the CI tests can be see in [4]\n\n1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/\n2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/\n3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/\n4. https://github.com/git/git/actions/runs/20953059862\n\nChanges in v2:\n==============\n- Renamed new struct to repo_config_values\n- Moved struct and functions declaration and definition to\n  environment.[ch]\n- embedded the new struct in the repository struct thereby removing the\n  need to allocate memory on the heap\n\nOlamide Caleb Bello (3):\n  environment: stop storing `core.attributesFile` globally\n  environment: environment: stop using core.sparseCheckout globally\n  environment: move \"branch.autoSetupMerge\" into `struct\n    repo_config_values`\n\n attr.c                      |  7 ++++---\n branch.h                    |  2 --\n builtin/backfill.c          |  3 +--\n builtin/branch.c            |  2 +-\n builtin/checkout.c          |  2 +-\n builtin/clone.c             |  2 +-\n builtin/grep.c              |  2 +-\n builtin/mv.c                |  2 +-\n builtin/push.c              |  2 +-\n builtin/sparse-checkout.c   | 22 +++++++++++-----------\n builtin/submodule--helper.c |  2 +-\n builtin/worktree.c          |  2 +-\n dir.c                       |  2 +-\n environment.c               | 26 ++++++++++++++++----------\n environment.h               | 15 +++++++++++++--\n repository.c                |  1 +\n repository.h                |  4 ++++\n sparse-index.c              |  6 ++++--\n unpack-trees.c              |  2 +-\n wt-status.c                 |  2 +-\n 20 files changed, 65 insertions(+), 43 deletions(-)\n\n Range diff versus v2\n ====================\n1:  25fc10854e < -:  ---------- environment: stop storing `core.attributesFile` globally\n-:  ---------- > 1:  b6f8deaa40 environment: stop storing `core.attributesFile` globally\n2:  f25afc6f68 ! 2:  1e83c077f2 environment: stop using core.sparseCheckout globally\n    @@ Metadata\n     Author: Olamide Caleb Bello <belkid98@gmail.com>\n     \n      ## Commit message ##\n    -    environment: stop using core.sparseCheckout globally\n    +    environment: environment: stop using core.sparseCheckout globally\n     \n         The config value `core.sparseCheckout` is parsed in\n         `git_default_core_config()` and stored globally in\n    @@ Commit message\n         when different Git repositories running in the same process access this\n         variable.\n     \n    -    Move the parsed value into `struct config_values` which can be accessed\n    -    per repo via `git_default_config()`. This would mean we do not need to\n    -    remove code from `git_default_core_config()`, thereby retaining current\n    -    behaviours.\n    +    Move the parsed value into `struct repo_config_values` which holds all the\n    +    values parsed by `git_default_config()` and can be accessed\n    +    per repo via `git_default_config()`. This retains current\n    +    behaviours while achieving the repository scoped access.\n     \n         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n         Mentored-by: Christian Couder <christian.couder@gmail.com>\n    @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *pr\n      \n      \tif (ctx.sparse < 0)\n     -\t\tctx.sparse = core_apply_sparse_checkout;\n    -+\t\tctx.sparse = repo->cfg_values->sparse_checkout;\n    ++\t\tctx.sparse = repo->config_values.sparse_checkout;\n      \n      \tresult = do_backfill(&ctx);\n      \tbackfill_context_clear(&ctx);\n    @@ builtin/clone.c: static int git_sparse_checkout_init(const char *repo)\n      \t * for the later checkout to use the sparse-checkout file.\n      \t */\n     -\tcore_apply_sparse_checkout = 1;\n    -+\tthe_repository->cfg_values->sparse_checkout = 1;\n    ++\tthe_repository->config_values.sparse_checkout = 1;\n      \n      \tcmd.git_cmd = 1;\n      \tif (run_command(&cmd)) {\n     \n    + ## builtin/grep.c ##\n    +@@ builtin/grep.c: static int grep_submodule(struct grep_opt *opt,\n    + \t *\t\"forget\" the sparse-index feature switch. As a result, the index\n    + \t *\tof these submodules are expanded unexpectedly.\n    + \t *\n    +-\t * 2. \"core_apply_sparse_checkout\"\n    ++\t * 2. \"sparse_checkout\"\n    + \t *\tWhen running `grep` in the superproject, this setting is\n    + \t *\tpopulated using the superproject's configs. However, once\n    + \t *\tinitialized, this config is globally accessible and is read by\n    +\n      ## builtin/mv.c ##\n     @@ builtin/mv.c: int cmd_mv(int argc,\n      \t\trename_index_entry_at(the_repository->index, pos, dst);\n      \n      \t\tif (ignore_sparse &&\n     -\t\t    core_apply_sparse_checkout &&\n    -+\t\t\tthe_repository->cfg_values->sparse_checkout &&\n    ++\t\t    the_repository->config_values.sparse_checkout &&\n      \t\t    core_sparse_checkout_cone) {\n      \t\t\t/*\n      \t\t\t * NEEDSWORK: we are *not* paying attention to\n    @@ builtin/sparse-checkout.c: static int sparse_checkout_list(int argc, const char\n      \n      \tsetup_work_tree();\n     -\tif (!core_apply_sparse_checkout)\n    -+\tif (!the_repository->cfg_values->sparse_checkout)\n    ++\tif (!the_repository->config_values.sparse_checkout)\n      \t\tdie(_(\"this worktree is not sparse\"));\n      \n      \targc = parse_options(argc, argv, prefix,\n    @@ builtin/sparse-checkout.c: static int set_config(struct repository *repo,\n      static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n      \t/* If not specified, use previous definition of cone mode */\n     -\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n    -+\tif (*cone_mode == -1 && the_repository->cfg_values->sparse_checkout)\n    ++\tif (*cone_mode == -1 && the_repository->config_values.sparse_checkout)\n      \t\t*cone_mode = core_sparse_checkout_cone;\n      \n      \t/* Set cone/non-cone mode appropriately */\n     -\tcore_apply_sparse_checkout = 1;\n    -+\tthe_repository->cfg_values->sparse_checkout = 1;\n    ++\tthe_repository->config_values.sparse_checkout = 1;\n      \tif (*cone_mode == 1 || *cone_mode == -1) {\n      \t\tcore_sparse_checkout_cone = 1;\n      \t\treturn MODE_CONE_PATTERNS;\n    @@ builtin/sparse-checkout.c: static int update_modes(struct repository *repo, int\n      \n      \t/* Determine if we need to record the mode; ensure sparse checkout on */\n     -\trecord_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n    -+\trecord_mode = (*cone_mode != -1) || !repo->cfg_values->sparse_checkout;\n    ++\trecord_mode = (*cone_mode != -1) || !repo->config_values.sparse_checkout;\n      \n      \tmode = update_cone_mode(cone_mode);\n      \tif (record_mode && set_config(repo, mode))\n    @@ builtin/sparse-checkout.c: static int modify_pattern_list(struct repository *rep\n      \t}\n      \n     -\tif (!core_apply_sparse_checkout) {\n    -+\tif (!repo->cfg_values->sparse_checkout) {\n    ++\tif (!repo->config_values.sparse_checkout) {\n      \t\tset_config(repo, MODE_ALL_PATTERNS);\n     -\t\tcore_apply_sparse_checkout = 1;\n    -+\t\trepo->cfg_values->sparse_checkout = 1;\n    ++\t\trepo->config_values.sparse_checkout = 1;\n      \t\tchanged_config = 1;\n      \t}\n      \n    @@ builtin/sparse-checkout.c: static int sparse_checkout_add(int argc, const char *\n      \n      \tsetup_work_tree();\n     -\tif (!core_apply_sparse_checkout)\n    -+\tif (!repo->cfg_values->sparse_checkout)\n    ++\tif (!repo->config_values.sparse_checkout)\n      \t\tdie(_(\"no sparse-checkout to add to\"));\n      \n      \trepo_read_index(repo);\n    @@ builtin/sparse-checkout.c: static int sparse_checkout_reapply(int argc, const ch\n      \n      \tsetup_work_tree();\n     -\tif (!core_apply_sparse_checkout)\n    -+\tif (!repo->cfg_values->sparse_checkout)\n    ++\tif (!repo->config_values.sparse_checkout)\n      \t\tdie(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n      \n      \treapply_opts.cone_mode = -1;\n    @@ builtin/sparse-checkout.c: static int sparse_checkout_clean(int argc, const char\n      \n      \tsetup_work_tree();\n     -\tif (!core_apply_sparse_checkout)\n    -+\tif (!repo->cfg_values->sparse_checkout)\n    ++\tif (!repo->config_values.sparse_checkout)\n      \t\tdie(_(\"must be in a sparse-checkout to clean directories\"));\n      \tif (!core_sparse_checkout_cone)\n      \t\tdie(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n    @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const ch\n      \n      \t/*\n     -\t * We do not exit early if !core_apply_sparse_checkout; due to the\n    -+\t * We do not exit early if !repo->cfg_values->sparse_checkout; due to the\n    ++\t * We do not exit early if !repo->config_values.sparse_checkout; due to the\n      \t * ability for users to manually muck things up between\n      \t *   direct editing of .git/info/sparse-checkout\n      \t *   running read-tree -m u HEAD or update-index --skip-worktree\n    @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const ch\n      \thashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n      \tpl.use_cone_patterns = 0;\n     -\tcore_apply_sparse_checkout = 1;\n    -+\trepo->cfg_values->sparse_checkout = 1;\n    ++\trepo->config_values.sparse_checkout = 1;\n      \n      \tadd_pattern(\"/*\", empty_base, 0, &pl, 0);\n      \n    @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam\n      \t * the sparse-checkout patterns from the current worktree.\n      \t */\n     -\tif (core_apply_sparse_checkout)\n    -+\tif (the_repository->cfg_values->sparse_checkout)\n    ++\tif (wt->repo->config_values.sparse_checkout)\n      \t\tcopy_sparse_checkout(sb_repo.buf);\n      \n      \t/*\n     \n    - ## config.h ##\n    -@@ config.h: struct config_context {\n    - struct config_values {\n    - \t/* core config values */\n    - \tchar *attributes_file_path;\n    -+\tint sparse_checkout;\n    - \n    - };\n    - #define CONFIG_CONTEXT_INIT { 0 }\n    -\n      ## dir.c ##\n     @@ dir.c: enum pattern_match_result path_matches_pattern_list(\n      \n      int init_sparse_checkout_patterns(struct index_state *istate)\n      {\n     -\tif (!core_apply_sparse_checkout)\n    -+\tif (!istate->repo->cfg_values->sparse_checkout)\n    ++\tif (!istate->repo->config_values.sparse_checkout)\n      \t\treturn 1;\n      \tif (istate->sparse_checkout_patterns)\n      \t\treturn 0;\n    @@ environment.c: static int git_default_core_config(const char *var, const char *v\n      \n     \n      ## environment.h ##\n    +@@ environment.h: struct strvec;\n    + struct repo_config_values {\n    + \t/* core config values */\n    + \tchar *attributes_file_path;\n    ++\tint sparse_checkout;\n    + };\n    + \n    + /*\n     @@ environment.h: extern int precomposed_unicode;\n      extern int protect_hfs;\n      extern int protect_ntfs;\n    @@ sparse-index.c: static int index_has_unmerged_entries(struct index_state *istate\n      int is_sparse_index_allowed(struct index_state *istate, int flags)\n      {\n     -\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n    -+\tif (!istate->repo->cfg_values->sparse_checkout || !core_sparse_checkout_cone)\n    ++\tstruct repo_config_values *cfg = &istate->repo->config_values;\n    ++\tif (!cfg->sparse_checkout || !core_sparse_checkout_cone)\n      \t\treturn 0;\n      \n      \tif (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n    @@ sparse-index.c: static void clear_skip_worktree_from_present_files_full(struct i\n      void clear_skip_worktree_from_present_files(struct index_state *istate)\n      {\n     -\tif (!core_apply_sparse_checkout ||\n    -+\tif (!istate->repo->cfg_values->sparse_checkout ||\n    ++\tstruct repo_config_values *cfg = &istate->repo->config_values;\n    ++\tif (!cfg->sparse_checkout ||\n      \t    sparse_expect_files_outside_of_patterns)\n      \t\treturn;\n      \n    @@ unpack-trees.c: int unpack_trees(unsigned len, struct tree_desc *t, struct unpac\n      \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n      \n     -\tif (!core_apply_sparse_checkout || !o->update)\n    -+\tif (!repo->cfg_values->sparse_checkout || !o->update)\n    ++\tif (!repo->config_values.sparse_checkout || !o->update)\n      \t\to->skip_sparse_checkout = 1;\n      \tif (!o->skip_sparse_checkout) {\n      \t\tmemset(&pl, 0, sizeof(pl));\n     \n      ## wt-status.c ##\n    -@@\n    - #include \"lockfile.h\"\n    - #include \"sequencer.h\"\n    - #include \"fsmonitor-settings.h\"\n    -+#include \"config.h\"\n    - \n    - #define AB_DELAY_WARNING_IN_MS (2 * 1000)\n    - #define UF_DELAY_WARNING_IN_MS (2 * 1000)\n     @@ wt-status.c: static void wt_status_check_sparse_checkout(struct repository *r,\n      \tint skip_worktree = 0;\n      \tint i;\n      \n     -\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n    -+\tif (!r->cfg_values->sparse_checkout || r->index->cache_nr == 0) {\n    ++\tif (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {\n      \t\t/*\n      \t\t * Don't compute percentage of checked out files if we\n      \t\t * aren't in a sparse checkout or would get division by 0.\n3:  1c86c72f2d < -:  ---------- environment: move \"branch.autoSetupMerge\" into `struct config_values`\n-:  ---------- > 3:  6e54e22ac7 environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`\n\n-- \n2.34.1\n\n"},{"id":"533757","messageId":"b6f8deaa405a59812265160fe1d41ad515a9f388.1768318762.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768318762.git.belkid98@gmail.com","subject":"[Outreachy PATCH v2 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-13T16:44:00Z","receivedAt":"2026-01-13T17:25:48Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value parsed in git_default_core_config() is loaded eagerly\nand stored in the global variable `git_attributes_file`.\nStoring this value in a global variable can lead to unexpected\nbehaviours when more than one Git repository run in the same Git process.\n\nMove this value into a `struct repo_config_values` which holds all the\nvalues parsed by `git_default_config()` and can be accessed per\nrepository via `git_default_config()`. This will ensure the current\nbehaviour remains the same while also enabling the libification of Git.\n\nIt is important to note that `git_default_config()` is a wrapper to other\n`git_default_*_config()` such as `git_default_core_config()`.\nTherefore to access and modify this global variable,\nthe change has to be made in the function which parses and\nstores the value in the global variable.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n attr.c        | 7 ++++---\n environment.c | 7 ++++---\n environment.h | 7 ++++++-\n repository.h  | 4 ++++\n 4 files changed, 18 insertions(+), 7 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 4999b7e09d..fbb9eaffaf 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)\n \n const char *git_attr_global_file(void)\n {\n-\tif (!git_attributes_file)\n-\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\tif (!cfg->attributes_file_path)\n+\t\tcfg->attributes_file_path = xdg_config_home(\"attributes\");\n \n-\treturn git_attributes_file;\n+\treturn cfg->attributes_file_path;\n }\n \n int git_attr_system_is_enabled(void)\ndiff --git a/environment.c b/environment.c\nindex a770b5921d..2789c3514a 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -53,7 +53,6 @@ char *git_commit_encoding;\n char *git_log_output_encoding;\n char *apply_default_whitespace;\n char *apply_default_ignorewhitespace;\n-char *git_attributes_file;\n int zlib_compression_level = Z_BEST_SPEED;\n int pack_compression_level = Z_DEFAULT_COMPRESSION;\n int fsync_object_files = -1;\n@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n static int git_default_core_config(const char *var, const char *value,\n \t\t\t\t   const struct config_context *ctx, void *cb)\n {\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\n \t/* This needs a better name */\n \tif (!strcmp(var, \"core.filemode\")) {\n \t\ttrust_executable_bit = git_config_bool(var, value);\n@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.attributesfile\")) {\n-\t\tFREE_AND_NULL(git_attributes_file);\n-\t\treturn git_config_pathname(&git_attributes_file, var, value);\n+\t\tFREE_AND_NULL(cfg->attributes_file_path);\n+\t\treturn git_config_pathname(&cfg->attributes_file_path, var, value);\n \t}\n \n \tif (!strcmp(var, \"core.bare\")) {\ndiff --git a/environment.h b/environment.h\nindex 51898c99cd..8c7803425e 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -84,6 +84,12 @@ extern const char * const local_repo_env[];\n \n struct strvec;\n \n+/* Config values parsed by git_default_config() */\n+struct repo_config_values {\n+\t/* core config values */\n+\tchar *attributes_file_path;\n+};\n+\n /*\n  * Wrapper of getenv() that returns a strdup value. This value is kept\n  * in argv to be freed later.\n@@ -152,7 +158,6 @@ extern int assume_unchanged;\n extern int warn_on_object_refname_ambiguity;\n extern char *apply_default_whitespace;\n extern char *apply_default_ignorewhitespace;\n-extern char *git_attributes_file;\n extern int zlib_compression_level;\n extern int pack_compression_level;\n extern unsigned long pack_size_limit_cfg;\ndiff --git a/repository.h b/repository.h\nindex 6063c4b846..638a142577 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -3,6 +3,7 @@\n \n #include \"strmap.h\"\n #include \"repo-settings.h\"\n+#include \"environment.h\"\n \n struct config_set;\n struct git_hash_algo;\n@@ -148,6 +149,9 @@ struct repository {\n \t/* Repository's compatibility hash algorithm. */\n \tconst struct git_hash_algo *compat_hash_algo;\n \n+\t/* Repository's config values parsed by git_default_config() */\n+\tstruct repo_config_values config_values;\n+\n \t/* Repository's reference storage format, as serialized on disk. */\n \tenum ref_storage_format ref_storage_format;\n \n-- \n2.34.1\n\n"},{"id":"533758","messageId":"1e83c077f274d73aebe183ae61157593f4a358c2.1768318762.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768318762.git.belkid98@gmail.com","subject":"[Outreachy PATCH v2 2/3] environment: environment: stop using core.sparseCheckout globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-13T16:44:01Z","receivedAt":"2026-01-13T17:25:52Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `core.sparseCheckout` is parsed in\n`git_default_core_config()` and stored globally in\n`core_appy_sparse_checkout`. This could cause unintended behaviours\nwhen different Git repositories running in the same process access this\nvariable.\n\nMove the parsed value into `struct repo_config_values` which holds all the\nvalues parsed by `git_default_config()` and can be accessed\nper repo via `git_default_config()`. This retains current\nbehaviours while achieving the repository scoped access.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n builtin/backfill.c        |  3 +--\n builtin/clone.c           |  2 +-\n builtin/grep.c            |  2 +-\n builtin/mv.c              |  2 +-\n builtin/sparse-checkout.c | 22 +++++++++++-----------\n builtin/worktree.c        |  2 +-\n dir.c                     |  2 +-\n environment.c             |  3 +--\n environment.h             |  2 +-\n sparse-index.c            |  6 ++++--\n unpack-trees.c            |  2 +-\n wt-status.c               |  2 +-\n 12 files changed, 25 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/backfill.c b/builtin/backfill.c\nindex e80fc1b694..90d5312240 100644\n--- a/builtin/backfill.c\n+++ b/builtin/backfill.c\n@@ -1,4 +1,3 @@\n-/* We need this macro to access core_apply_sparse_checkout */\n #define USE_THE_REPOSITORY_VARIABLE\n \n #include \"builtin.h\"\n@@ -139,7 +138,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n \trepo_config(repo, git_default_config, NULL);\n \n \tif (ctx.sparse < 0)\n-\t\tctx.sparse = core_apply_sparse_checkout;\n+\t\tctx.sparse = repo->config_values.sparse_checkout;\n \n \tresult = do_backfill(&ctx);\n \tbackfill_context_clear(&ctx);\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..b6b19e83d1 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n \t * We must apply the setting in the current process\n \t * for the later checkout to use the sparse-checkout file.\n \t */\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->config_values.sparse_checkout = 1;\n \n \tcmd.git_cmd = 1;\n \tif (run_command(&cmd)) {\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 53cccf2d25..525edb5e9c 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,\n \t *\t\"forget\" the sparse-index feature switch. As a result, the index\n \t *\tof these submodules are expanded unexpectedly.\n \t *\n-\t * 2. \"core_apply_sparse_checkout\"\n+\t * 2. \"sparse_checkout\"\n \t *\tWhen running `grep` in the superproject, this setting is\n \t *\tpopulated using the superproject's configs. However, once\n \t *\tinitialized, this config is globally accessible and is read by\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex d43925097b..511620747b 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -572,7 +572,7 @@ int cmd_mv(int argc,\n \t\trename_index_entry_at(the_repository->index, pos, dst);\n \n \t\tif (ignore_sparse &&\n-\t\t    core_apply_sparse_checkout &&\n+\t\t    the_repository->config_values.sparse_checkout &&\n \t\t    core_sparse_checkout_cone) {\n \t\t\t/*\n \t\t\t * NEEDSWORK: we are *not* paying attention to\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..1c2c39b968 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,\n \tint res;\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.sparse_checkout)\n \t\tdie(_(\"this worktree is not sparse\"));\n \n \targc = parse_options(argc, argv, prefix,\n@@ -400,11 +400,11 @@ static int set_config(struct repository *repo,\n \n static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n \t/* If not specified, use previous definition of cone mode */\n-\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n+\tif (*cone_mode == -1 && the_repository->config_values.sparse_checkout)\n \t\t*cone_mode = core_sparse_checkout_cone;\n \n \t/* Set cone/non-cone mode appropriately */\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->config_values.sparse_checkout = 1;\n \tif (*cone_mode == 1 || *cone_mode == -1) {\n \t\tcore_sparse_checkout_cone = 1;\n \t\treturn MODE_CONE_PATTERNS;\n@@ -418,7 +418,7 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind\n \tint mode, record_mode;\n \n \t/* Determine if we need to record the mode; ensure sparse checkout on */\n-\trecord_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n+\trecord_mode = (*cone_mode != -1) || !repo->config_values.sparse_checkout;\n \n \tmode = update_cone_mode(cone_mode);\n \tif (record_mode && set_config(repo, mode))\n@@ -699,9 +699,9 @@ static int modify_pattern_list(struct repository *repo,\n \t\tbreak;\n \t}\n \n-\tif (!core_apply_sparse_checkout) {\n+\tif (!repo->config_values.sparse_checkout) {\n \t\tset_config(repo, MODE_ALL_PATTERNS);\n-\t\tcore_apply_sparse_checkout = 1;\n+\t\trepo->config_values.sparse_checkout = 1;\n \t\tchanged_config = 1;\n \t}\n \n@@ -798,7 +798,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,\n \tint ret;\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!repo->config_values.sparse_checkout)\n \t\tdie(_(\"no sparse-checkout to add to\"));\n \n \trepo_read_index(repo);\n@@ -907,7 +907,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!repo->config_values.sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n \n \treapply_opts.cone_mode = -1;\n@@ -969,7 +969,7 @@ static int sparse_checkout_clean(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!repo->config_values.sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to clean directories\"));\n \tif (!core_sparse_checkout_cone)\n \t\tdie(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n@@ -1035,7 +1035,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \tstruct pattern_list pl;\n \n \t/*\n-\t * We do not exit early if !core_apply_sparse_checkout; due to the\n+\t * We do not exit early if !repo->config_values.sparse_checkout; due to the\n \t * ability for users to manually muck things up between\n \t *   direct editing of .git/info/sparse-checkout\n \t *   running read-tree -m u HEAD or update-index --skip-worktree\n@@ -1061,7 +1061,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \thashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);\n \thashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n \tpl.use_cone_patterns = 0;\n-\tcore_apply_sparse_checkout = 1;\n+\trepo->config_values.sparse_checkout = 1;\n \n \tadd_pattern(\"/*\", empty_base, 0, &pl, 0);\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex fbdaf2eb2e..e401b8253e 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,\n \t * If the current worktree has sparse-checkout enabled, then copy\n \t * the sparse-checkout patterns from the current worktree.\n \t */\n-\tif (core_apply_sparse_checkout)\n+\tif (wt->repo->config_values.sparse_checkout)\n \t\tcopy_sparse_checkout(sb_repo.buf);\n \n \t/*\ndiff --git a/dir.c b/dir.c\nindex b00821f294..56b412a6d2 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(\n \n int init_sparse_checkout_patterns(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout)\n+\tif (!istate->repo->config_values.sparse_checkout)\n \t\treturn 1;\n \tif (istate->sparse_checkout_patterns)\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex 2789c3514a..65919a6c52 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -74,7 +74,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #endif\n enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n int grafts_keep_true_parents;\n-int core_apply_sparse_checkout;\n int core_sparse_checkout_cone;\n int sparse_expect_files_outside_of_patterns;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.sparsecheckout\")) {\n-\t\tcore_apply_sparse_checkout = git_config_bool(var, value);\n+\t\tcfg->sparse_checkout = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \ndiff --git a/environment.h b/environment.h\nindex 8c7803425e..5caf73b4b8 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -88,6 +88,7 @@ struct strvec;\n struct repo_config_values {\n \t/* core config values */\n \tchar *attributes_file_path;\n+\tint sparse_checkout;\n };\n \n /*\n@@ -167,7 +168,6 @@ extern int precomposed_unicode;\n extern int protect_hfs;\n extern int protect_ntfs;\n \n-extern int core_apply_sparse_checkout;\n extern int core_sparse_checkout_cone;\n extern int sparse_expect_files_outside_of_patterns;\n \ndiff --git a/sparse-index.c b/sparse-index.c\nindex 76f90da5f5..6dd8dd679d 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)\n \n int is_sparse_index_allowed(struct index_state *istate, int flags)\n {\n-\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n+\tstruct repo_config_values *cfg = &istate->repo->config_values;\n+\tif (!cfg->sparse_checkout || !core_sparse_checkout_cone)\n \t\treturn 0;\n \n \tif (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n@@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n \n void clear_skip_worktree_from_present_files(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout ||\n+\tstruct repo_config_values *cfg = &istate->repo->config_values;\n+\tif (!cfg->sparse_checkout ||\n \t    sparse_expect_files_outside_of_patterns)\n \t\treturn;\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex f38c761ab9..2bdfa1334c 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tif (o->prefix)\n \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n \n-\tif (!core_apply_sparse_checkout || !o->update)\n+\tif (!repo->config_values.sparse_checkout || !o->update)\n \t\to->skip_sparse_checkout = 1;\n \tif (!o->skip_sparse_checkout) {\n \t\tmemset(&pl, 0, sizeof(pl));\ndiff --git a/wt-status.c b/wt-status.c\nindex e12adb26b9..a2e388606f 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n \tint skip_worktree = 0;\n \tint i;\n \n-\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n+\tif (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {\n \t\t/*\n \t\t * Don't compute percentage of checked out files if we\n \t\t * aren't in a sparse checkout or would get division by 0.\n-- \n2.34.1\n\n"},{"id":"533759","messageId":"6e54e22ac7096f26e774acc4740bcb135496fb69.1768318762.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768318762.git.belkid98@gmail.com","subject":"[Outreachy PATCH v2 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-13T16:44:02Z","receivedAt":"2026-01-13T17:25:56Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `brach.autoSetupMerge` is parsed in\n`git_default_branch_config()` and stored in the global variable\n`git_branch_track`. This global variable can cause unexpected behaviours\nwhen multiple Git repos run in the the same process.\n\nMove this value into `struct repo_config_values` which holds all values\nparsed by `git_default_config()` and can be accessed per\nrepo via `git_default_config()`. This would retain the same\nbehaviours while achieving repository scoped access.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n branch.h                    |  2 --\n builtin/branch.c            |  2 +-\n builtin/checkout.c          |  2 +-\n builtin/push.c              |  2 +-\n builtin/submodule--helper.c |  2 +-\n environment.c               | 16 +++++++++++-----\n environment.h               |  6 ++++++\n repository.c                |  1 +\n 8 files changed, 22 insertions(+), 11 deletions(-)\n\ndiff --git a/branch.h b/branch.h\nindex ec2f35fda4..3dc6e2a0ff 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -15,8 +15,6 @@ enum branch_track {\n \tBRANCH_TRACK_SIMPLE,\n };\n \n-extern enum branch_track git_branch_track;\n-\n /* Functions for acting on the information about branches. */\n \n /**\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex c577b5d20f..d75114b41c 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -795,7 +795,7 @@ int cmd_branch(int argc,\n \tif (!sorting_options.nr)\n \t\tstring_list_append(&sorting_options, \"refname\");\n \n-\ttrack = git_branch_track;\n+\ttrack = the_repository->config_values.git_branch_track;\n \n \thead = refs_resolve_refdup(get_main_ref_store(the_repository), \"HEAD\",\n \t\t\t\t   0, &head_oid, NULL);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 261699e2f5..6c1cb9713c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1631,7 +1631,7 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n \t} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)\n-\t\topts->track = git_branch_track;\n+\t\topts->track = the_repository->config_values.git_branch_track;\n \n \tif (new_branch_info->name && !new_branch_info->commit)\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 5b6cebbb85..68105ecb2d 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -162,7 +162,7 @@ static NORETURN void die_push_simple(struct branch *branch,\n \t\tadvice_pushdefault_maybe = _(\"\\n\"\n \t\t\t\t \"To choose either option permanently, \"\n \t\t\t\t \"see push.default in 'git help config'.\\n\");\n-\tif (git_branch_track != BRANCH_TRACK_SIMPLE)\n+\tif (the_repository->config_values.git_branch_track != BRANCH_TRACK_SIMPLE)\n \t\tadvice_automergesimple_maybe = _(\"\\n\"\n \t\t\t\t \"To avoid automatically configuring \"\n \t\t\t\t \"an upstream branch when its name\\n\"\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d537ab087a..7239722e48 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3128,7 +3128,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,\n \t};\n \n \trepo_config(the_repository, git_default_config, NULL);\n-\ttrack = git_branch_track;\n+\ttrack = the_repository->config_values.git_branch_track;\n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \n \tif (argc != 3)\ndiff --git a/environment.c b/environment.c\nindex 65919a6c52..437d14e1ae 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;\n enum eol core_eol = EOL_UNSET;\n int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;\n char *check_roundtrip_encoding;\n-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n enum rebase_setup_type autorebase = AUTOREBASE_NEVER;\n enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #ifndef OBJECT_CREATION_MODE\n@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)\n \n static int git_default_branch_config(const char *var, const char *value)\n {\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\n \tif (!strcmp(var, \"branch.autosetupmerge\")) {\n \t\tif (value && !strcmp(value, \"always\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_ALWAYS;\n+\t\t\tcfg->git_branch_track = BRANCH_TRACK_ALWAYS;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n+\t\t\tcfg->git_branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"simple\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\tcfg->git_branch_track = BRANCH_TRACK_SIMPLE;\n \t\t\treturn 0;\n \t\t}\n-\t\tgit_branch_track = git_config_bool(var, value);\n+\t\tcfg->git_branch_track = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"branch.autosetuprebase\")) {\n@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,\n \t/* Add other config variables here and to Documentation/config.adoc. */\n \treturn 0;\n }\n+\n+void repo_config_values_init(struct repo_config_values *cfg)\n+{\n+\tcfg->git_branch_track = BRANCH_TRACK_REMOTE;\n+}\ndiff --git a/environment.h b/environment.h\nindex 5caf73b4b8..bfcdffe836 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -2,6 +2,7 @@\n #define ENVIRONMENT_H\n \n #include \"repo-settings.h\"\n+#include \"branch.h\"\n \n /* Double-check local_repo_env below if you add to this list. */\n #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n@@ -89,6 +90,9 @@ struct repo_config_values {\n \t/* core config values */\n \tchar *attributes_file_path;\n \tint sparse_checkout;\n+\n+\t/* branch config values */\n+\tenum branch_track git_branch_track;\n };\n \n /*\n@@ -114,6 +118,8 @@ const char *strip_namespace(const char *namespaced_ref);\n int git_default_config(const char *, const char *,\n \t\t       const struct config_context *, void *);\n \n+void repo_config_values_init(struct repo_config_values *cfg);\n+\n /*\n  * TODO: All the below state either explicitly or implicitly relies on\n  * `the_repository`. We should eventually get rid of these and make the\ndiff --git a/repository.c b/repository.c\nindex c7e75215ac..d308cd78bf 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)\n \tALLOC_ARRAY(repo->index, 1);\n \tindex_state_init(repo->index, repo);\n \trepo->check_deprecated_config = true;\n+\trepo_config_values_init(&repo->config_values);\n \n \t/*\n \t * When a command runs inside a repository, it learns what\n-- \n2.34.1\n\n"},{"id":"533767","messageId":"xmqqzf6hiab2.fsf@gitster.g","threadId":"64782","inReplyTo":"b6f8deaa405a59812265160fe1d41ad515a9f388.1768318762.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v2 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-13T19:26:09Z","receivedAt":"2026-01-13T19:26:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Olamide Caleb Bello <belkid98@gmail.com> writes:\n\n> The config value parsed in git_default_core_config() is loaded eagerly\n> and stored in the global variable `git_attributes_file`.\n> Storing this value in a global variable can lead to unexpected\n> behaviours when more than one Git repository run in the same Git process.\n\nThere are quite a many global singleton variables that are accessed\nby git_default_core_config(), and this patch addresses only one of\nthem.  Are most of these variables per repository, or are there some\nvariables that are inherently just one for a user or for a system?\n\n>  extern int warn_on_object_refname_ambiguity;\n>  extern char *apply_default_whitespace;\n>  extern char *apply_default_ignorewhitespace;\n> -extern char *git_attributes_file;\n>  extern int zlib_compression_level;\n>  extern int pack_compression_level;\n>  extern unsigned long pack_size_limit_cfg;\n"},{"id":"533768","messageId":"xmqqsec9i9qr.fsf@gitster.g","threadId":"64782","inReplyTo":"1e83c077f274d73aebe183ae61157593f4a358c2.1768318762.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v2 2/3] environment: environment: stop using core.sparseCheckout globally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-13T19:38:20Z","receivedAt":"2026-01-13T19:38:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Olamide Caleb Bello <belkid98@gmail.com> writes:\n\n> diff --git a/builtin/backfill.c b/builtin/backfill.c\n> index e80fc1b694..90d5312240 100644\n> --- a/builtin/backfill.c\n> +++ b/builtin/backfill.c\n> @@ -1,4 +1,3 @@\n> -/* We need this macro to access core_apply_sparse_checkout */\n\nWhy this removal?  You'll need to be able to access the_repository\nbecause ...\n\n>  #define USE_THE_REPOSITORY_VARIABLE\n>  \n>  #include \"builtin.h\"\n> @@ -139,7 +138,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n>  \trepo_config(repo, git_default_config, NULL);\n>  \n>  \tif (ctx.sparse < 0)\n> -\t\tctx.sparse = core_apply_sparse_checkout;\n> +\t\tctx.sparse = repo->config_values.sparse_checkout;\n>  \n>  \tresult = do_backfill(&ctx);\n>  \tbackfill_context_clear(&ctx);\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index b19b302b06..b6b19e83d1 100644\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n>  \t * We must apply the setting in the current process\n>  \t * for the later checkout to use the sparse-checkout file.\n>  \t */\n> -\tcore_apply_sparse_checkout = 1;\n> +\tthe_repository->config_values.sparse_checkout = 1;\n\n... you'd need to access this, even if it is now called slightly\ndifferently, no?\n"},{"id":"533769","messageId":"xmqqo6mxi91e.fsf@gitster.g","threadId":"64782","inReplyTo":"6e54e22ac7096f26e774acc4740bcb135496fb69.1768318762.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v2 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-13T19:53:33Z","receivedAt":"2026-01-13T19:53:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Olamide Caleb Bello <belkid98@gmail.com> writes:\n\n> The config value `brach.autoSetupMerge` is parsed in\n> `git_default_branch_config()` and stored in the global variable\n> `git_branch_track`. This global variable can cause unexpected behaviours\n> when multiple Git repos run in the the same process.\n>\n> Move this value into `struct repo_config_values` which holds all values\n> parsed by `git_default_config()` and can be accessed per\n> repo via `git_default_config()`. This would retain the same\n> behaviours while achieving repository scoped access.\n>\n> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> ---\n\nCurrently the code flow is for git_config(git_default_config) to be\ncalled fairly early in the program, updating the singleton globals\nthat are independent from individual repository.  This moves these\nglobal variables to be stored in the config_values structure that is\ntied to the_repository.\n\nThe claim in the cover letter was that this will make it possible\nfor us to later work on more than one repositories at once and each\nrepository can keep its own independent value.  While the updated\ndata structure may make it _possible_, I am not sure if this is a\nsafe approach to get to the final state, without seeing how the\nconfig_values structure in the second \"repo\" structure is populated.\n\nAnd before moving all these globals into config_values, it is not\npossible to safely populate the config_values structure in the\nsecond \"repo\" structure, if git_config(git_default_config) is what\nwe plan to use.  The settings that are still stored in globals will\nthen get overwritten.\n\nThat is why my first question for this round of patches was \"are all\nthese settings inherently per repository?\", because the scheme would\nnot work if there are globals that cannot be moved to config_value\nstructure to be per-repo.\n\n\n> diff --git a/repository.c b/repository.c\n> index c7e75215ac..d308cd78bf 100644\n> --- a/repository.c\n> +++ b/repository.c\n> @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)\n>  \tALLOC_ARRAY(repo->index, 1);\n>  \tindex_state_init(repo->index, repo);\n>  \trepo->check_deprecated_config = true;\n> +\trepo_config_values_init(&repo->config_values);\n\nHaving a call to repo_config_values_init() when initializing an\nin-core repository instance is a reasonable design, and I see this\nstep has an initialization of git_branch_track in that function.\nShouldn't we be doing similar initialization in the same\nconfig_values_init() function for other members of the structure,\nnamely, attributes_file_path and sparse_checkout?\n\nThe function also may be a good place to do an equivalent of calling\ngit_config(git_default_config) there to grab values that are suitable\nfor the given repository, but I didn't think things through.\n\nThanks.\n"},{"id":"533795","messageId":"CAD=f0L8B9npe_VZpWo6i9V4gn1hq6P0cfYbOvRsxFXQBTS4ckA@mail.gmail.com","threadId":"64782","inReplyTo":"xmqqzf6hiab2.fsf@gitster.g","subject":"Re: [Outreachy PATCH v2 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-14T06:59:34Z","receivedAt":"2026-01-14T06:59:38Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Tue, 13 Jan 2026 at 20:26, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Olamide Caleb Bello <belkid98@gmail.com> writes:\n>\n> > The config value parsed in git_default_core_config() is loaded eagerly\n> > and stored in the global variable `git_attributes_file`.\n> > Storing this value in a global variable can lead to unexpected\n> > behaviours when more than one Git repository run in the same Git process.\n>\n> There are quite a many global singleton variables that are accessed\n> by git_default_core_config(), and this patch addresses only one of\n> them.  Are most of these variables per repository, or are there some\n> variables that are inherently just one for a user or for a system?\n\nHello Junio,\nFrom my understanding of the code in `git_default_config()`, some\nconfig are just one for a user such\nas the ones in `git_default_ident_config()`, `git_default_mailmap_config()`.\nHowever a majority of them are repository specific such as the ones in\n`git_default_core_config()`,\n`git_default_branch_config()`, `git_default_push_config()`.\nThese are the ones that the movement to struct repo_config_values targets.\n"},{"id":"533796","messageId":"CAD=f0L9yU=zZq5q-O1tjX3+JKz-CpWbVhA4PCyEq3+QyiTo0DA@mail.gmail.com","threadId":"64782","inReplyTo":"xmqqsec9i9qr.fsf@gitster.g","subject":"Re: [Outreachy PATCH v2 2/3] environment: environment: stop using core.sparseCheckout globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-14T07:16:26Z","receivedAt":"2026-01-14T07:16:27Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Tue, 13 Jan 2026 at 20:38, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Olamide Caleb Bello <belkid98@gmail.com> writes:\n>\n> > diff --git a/builtin/backfill.c b/builtin/backfill.c\n> > index e80fc1b694..90d5312240 100644\n> > --- a/builtin/backfill.c\n> > +++ b/builtin/backfill.c\n> > @@ -1,4 +1,3 @@\n> > -/* We need this macro to access core_apply_sparse_checkout */\n>\n> Why this removal?  You'll need to be able to access the_repository\n> because ...\n>\n> >  #define USE_THE_REPOSITORY_VARIABLE\n> >\n> >  #include \"builtin.h\"\n> > @@ -139,7 +138,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n> >       repo_config(repo, git_default_config, NULL);\n> >\n> >       if (ctx.sparse < 0)\n> > -             ctx.sparse = core_apply_sparse_checkout;\n> > +             ctx.sparse = repo->config_values.sparse_checkout;\n> >\n> >       result = do_backfill(&ctx);\n> >       backfill_context_clear(&ctx);\n> > diff --git a/builtin/clone.c b/builtin/clone.c\n> > index b19b302b06..b6b19e83d1 100644\n> > --- a/builtin/clone.c\n> > +++ b/builtin/clone.c\n> > @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n> >        * We must apply the setting in the current process\n> >        * for the later checkout to use the sparse-checkout file.\n> >        */\n> > -     core_apply_sparse_checkout = 1;\n> > +     the_repository->config_values.sparse_checkout = 1;\n>\n> ... you'd need to access this, even if it is now called slightly\n> differently, no?\n\nOh yes you are right.\nI will return it.\n"},{"id":"533799","messageId":"CAD=f0L-mPX+KECUjXk-WBzEbTP7wCa8sB56GySQT0yh9mfUOWw@mail.gmail.com","threadId":"64782","inReplyTo":"xmqqo6mxi91e.fsf@gitster.g","subject":"Re: [Outreachy PATCH v2 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-14T07:40:52Z","receivedAt":"2026-01-14T07:40:51Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Tue, 13 Jan 2026 at 20:53, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Olamide Caleb Bello <belkid98@gmail.com> writes:\n>\n> > The config value `brach.autoSetupMerge` is parsed in\n> > `git_default_branch_config()` and stored in the global variable\n> > `git_branch_track`. This global variable can cause unexpected behaviours\n> > when multiple Git repos run in the the same process.\n> >\n> > Move this value into `struct repo_config_values` which holds all values\n> > parsed by `git_default_config()` and can be accessed per\n> > repo via `git_default_config()`. This would retain the same\n> > behaviours while achieving repository scoped access.\n> >\n> > Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> > Mentored-by: Christian Couder <christian.couder@gmail.com>\n> > Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n> > Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> > ---\n>\n> Currently the code flow is for git_config(git_default_config) to be\n> called fairly early in the program, updating the singleton globals\n> that are independent from individual repository.  This moves these\n> global variables to be stored in the config_values structure that is\n> tied to the_repository.\n\nThis will move those global variables that are repository dependent into the\nstruct repo_config_values.\nI admit the mistake is from my end. I should have been clear on that in the\ncommit message rather than say '...holds all variables parsed by\ngit_default_config()'.\n\nSorry\n>\n> The claim in the cover letter was that this will make it possible\n> for us to later work on more than one repositories at once and each\n> repository can keep its own independent value.  While the updated\n> data structure may make it _possible_, I am not sure if this is a\n> safe approach to get to the final state, without seeing how the\n> config_values structure in the second \"repo\" structure is populated.\n>\n> And before moving all these globals into config_values, it is not\n> possible to safely populate the config_values structure in the\n> second \"repo\" structure, if git_config(git_default_config) is what\n> we plan to use.  The settings that are still stored in globals will\n> then get overwritten.\n\nThe idea proposed by Phillip is that we pass the repository parameter\nas the call back to `git_default_config()`.\nBut since that will be quite invasive, you proposed we use `the_repository`\nfor now. Then later we can pass the repository parameter as the call back\nand handle the invasiveness by simply checking in git_default_config()\nstruct repository *r =  cb ? cb : the_repository\n\n>\n> That is why my first question for this round of patches was \"are all\n> these settings inherently per repository?\", because the scheme would\n> not work if there are globals that cannot be moved to config_value\n> structure to be per-repo.\n\nThe movement does not target all the global variables, but only those\nthat are dependent on a repository.\n\n>\n>\n> > diff --git a/repository.c b/repository.c\n> > index c7e75215ac..d308cd78bf 100644\n> > --- a/repository.c\n> > +++ b/repository.c\n> > @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)\n> >       ALLOC_ARRAY(repo->index, 1);\n> >       index_state_init(repo->index, repo);\n> >       repo->check_deprecated_config = true;\n> > +     repo_config_values_init(&repo->config_values);\n>\n> Having a call to repo_config_values_init() when initializing an\n> in-core repository instance is a reasonable design, and I see this\n> step has an initialization of git_branch_track in that function.\n> Shouldn't we be doing similar initialization in the same\n> config_values_init() function for other members of the structure,\n> namely, attributes_file_path and sparse_checkout?\n\nI opted to initialize only the git_branch_track because in the original code,\nit is set to a default value BRANCH_TRACK_REMOTE before the call to\ngit_default_config()\nBut attributes_file_path and sparse_checkout were only declared and\nnot initialized\nbefore the call to 'git_default_config'.\n\nI just tried to replicate the current behaviour be initializing\ngit_branch_track and those default\nvalues early before the call to `git_default_config`\n\n>\n> The function also may be a good place to do an equivalent of calling\n> git_config(git_default_config) there to grab values that are suitable\n> for the given repository, but I didn't think things through.\n>\n\nThanks\n"},{"id":"533997","messageId":"CAD=f0L_qrQcp+FbYmSLqwMZt-H99xJGUB=VG_wFvmkHXyPBTAg@mail.gmail.com","threadId":"64782","inReplyTo":"cover.1768318762.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v2 0/3] store git_default_config() parsed values in new config struct","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-15T22:17:17Z","receivedAt":"2026-01-15T22:17:16Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Tue, 13 Jan 2026 at 18:25, Olamide Caleb Bello <belkid98@gmail.com> wrote:\n>\n> Hi Git Community,\n> Over the course of my ongoing internship, which focused on moving global\n> variables in environment.h into local scope, I have attempted to move some\n> variables into the struct repo-settings.\n> However there have been some design concerns as regards the use of\n> `prepare_repo_settings()` with respect to when and where to call the\n> function, and also the change in behaviours when the variable is lazily\n> loaded as discussed in [1] and [2].\n>\n> After different deliberations, Phillip Wood proposed creating a new config\n> struct [3], adding it to the repository struct and passing the repo struct to\n> `git_default_config()` to store the parsed config values per repo.\n> This ensures the current behaviours will be retained.\n>\n> I have experimented with this approach for some values and I would\n> appreciate feedbacks about this approach before we can move forward\n> and use it for more variables related to `git_default_config()`.\n>\n> For now, the parsed value is stored in `the_repository` in\n> `git_default_*_config()` and further efforts to pass the repository\n> parameter to `git_default_config()` as the callback parameter will\n> be looked into later on.\n> The link to the CI tests can be see in [4]\n>\n> 1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/\n> 2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/\n> 3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/\n> 4. https://github.com/git/git/actions/runs/20953059862\n>\n> Changes in v2:\n> ==============\n> - Renamed new struct to repo_config_values\n> - Moved struct and functions declaration and definition to\n>   environment.[ch]\n> - embedded the new struct in the repository struct thereby removing the\n>   need to allocate memory on the heap\n>\n\nHello,\nI would like to know if there are new comments.\nI want to send a new patch version.\n\nThanks\n"},{"id":"534126","messageId":"cover.1768681947.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768318762.git.belkid98@gmail.com","subject":"[Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-17T20:59:25Z","receivedAt":"2026-01-17T21:02:12Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"Hi Git Community,\nOver the course of my ongoing internship, which focused on moving repo specific\nglobal variables in environment.h into local scope, I have attempted to move some\nvariables into the struct repo-settings.\nHowever there has been some design concerns as regards the use of\n`prepare_repo_settings()` with respect to when and where to call the\nfunction, and also the change in behaviours when the variable is lazily\nloaded as discussed in [1] and [2].\n\nAfter different deliberations, Phillip Wood proposed creating a new config\nstruct [3], adding it to the repository struct and passing the repo struct to\n`git_default_config()` to store parsed repo specific config values per repo.\nThis ensures the current behaviours will be retained.\n\nI have experimented with this approach for some values and I would\nappreciate feedbacks about this approach before we can move forward\nand use it for more variables related to `git_default_config()`.\n\nFor now, the parsed value is stored in `the_repository` in\n`git_default_*_config()` and further efforts to pass the repository\nparameter to `git_default_config()` as the callback parameter will\nbe looked into later on.\nThe link to the CI tests can be see in [4]\n\n1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/\n2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/\n3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/\n4. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2266020513\n\nChanges in v3:\n==============\n- Moved declaration and definition of repo_config_values_init into patch 1\n- Returned commit message in line 1 of builtin/backfill.c\n\nOlamide Caleb Bello (3):\n  environment: stop storing `core.attributesFile` globally\n  environment: environment: stop using core.sparseCheckout globally\n  environment: move \"branch.autoSetupMerge\" into `struct\n    repo_config_values`\n\n attr.c                      |  7 ++++---\n branch.h                    |  2 --\n builtin/backfill.c          |  2 +-\n builtin/branch.c            |  2 +-\n builtin/checkout.c          |  2 +-\n builtin/clone.c             |  2 +-\n builtin/grep.c              |  2 +-\n builtin/mv.c                |  2 +-\n builtin/push.c              |  2 +-\n builtin/sparse-checkout.c   | 22 +++++++++++-----------\n builtin/submodule--helper.c |  2 +-\n builtin/worktree.c          |  2 +-\n dir.c                       |  2 +-\n environment.c               | 28 ++++++++++++++++++----------\n environment.h               | 15 +++++++++++++--\n repository.c                |  1 +\n repository.h                |  4 ++++\n sparse-index.c              |  6 ++++--\n unpack-trees.c              |  2 +-\n wt-status.c                 |  2 +-\n 20 files changed, 67 insertions(+), 42 deletions(-)\n\n Range diff versus v2:\n =====================\n\n 1:  b6f8deaa40 ! 1:  1aa41da833 environment: stop storing `core.attributesFile` globally\n    @@ Metadata\n      ## Commit message ##\n         environment: stop storing `core.attributesFile` globally\n\n    -    The config value parsed in git_default_core_config() is loaded eagerly\n    +    The config value is parsed in git_default_core_config(), loaded eagerly\n         and stored in the global variable `git_attributes_file`.\n         Storing this value in a global variable can lead to unexpected\n         behaviours when more than one Git repository run in the same Git process.\n\n    -    Move this value into a `struct repo_config_values` which holds all the\n    -    values parsed by `git_default_config()` and can be accessed per\n    -    repository via `git_default_config()`. This will ensure the current\n    -    behaviour remains the same while also enabling the libification of Git.\n    +    Create a new struct `repo_config_values` to hold this value and\n    +    other repository dependent values parsed by `git_default_config()` and\n    +    can be accessed per repository via `git_default_config()`.\n    +    This will ensure the current behaviour remains the same while also\n    +    enabling the libification of Git.\n\n         It is important to note that `git_default_config()` is a wrapper to other\n         `git_default_*_config()` such as `git_default_core_config()`.\n    @@ environment.c: static int git_default_core_config(const char *var, const char *v\n      \t}\n\n      \tif (!strcmp(var, \"core.bare\")) {\n    +@@ environment.c: int git_default_config(const char *var, const char *value,\n    + \t/* Add other config variables here and to Documentation/config.adoc. */\n    + \treturn 0;\n    + }\n    ++\n    ++void repo_config_values_init(struct repo_config_values *cfg)\n    ++{\n    ++\tcfg->attributes_file_path = NULL;\n    ++}\n\n      ## environment.h ##\n     @@ environment.h: extern const char * const local_repo_env[];\n    @@ environment.h: extern const char * const local_repo_env[];\n      /*\n       * Wrapper of getenv() that returns a strdup value. This value is kept\n       * in argv to be freed later.\n    +@@ environment.h: const char *strip_namespace(const char *namespaced_ref);\n    + int git_default_config(const char *, const char *,\n    + \t\t       const struct config_context *, void *);\n    +\n    ++void repo_config_values_init(struct repo_config_values *cfg);\n    ++\n    + /*\n    +  * TODO: All the below state either explicitly or implicitly relies on\n    +  * `the_repository`. We should eventually get rid of these and make the\n     @@ environment.h: extern int assume_unchanged;\n      extern int warn_on_object_refname_ambiguity;\n      extern char *apply_default_whitespace;\n    @@ environment.h: extern int assume_unchanged;\n      extern int pack_compression_level;\n      extern unsigned long pack_size_limit_cfg;\n\n    + ## repository.c ##\n    +@@ repository.c: void initialize_repository(struct repository *repo)\n    + \tALLOC_ARRAY(repo->index, 1);\n    + \tindex_state_init(repo->index, repo);\n    + \trepo->check_deprecated_config = true;\n    ++\trepo_config_values_init(&repo->config_values);\n    +\n    + \t/*\n    + \t * When a command runs inside a repository, it learns what\n    +\n      ## repository.h ##\n     @@\n\n2:  1e83c077f2 ! 2:  fd95169de4 environment: environment: stop using core.sparseCheckout globally\n    @@ Commit message\n         when different Git repositories running in the same process access this\n         variable.\n\n    -    Move the parsed value into `struct repo_config_values` which holds all the\n    -    values parsed by `git_default_config()` and can be accessed\n    -    per repo via `git_default_config()`. This retains current\n    +    Move the parsed value into `struct repo_config_values` to retains current\n         behaviours while achieving the repository scoped access.\n\n         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n    @@ Commit message\n         Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n\n      ## builtin/backfill.c ##\n    -@@\n    --/* We need this macro to access core_apply_sparse_checkout */\n    - #define USE_THE_REPOSITORY_VARIABLE\n    -\n    - #include \"builtin.h\"\n     @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n      \trepo_config(repo, git_default_config, NULL);\n\n    @@ environment.c: static int git_default_core_config(const char *var, const char *v\n      \t\treturn 0;\n      \t}\n\n    +@@ environment.c: int git_default_config(const char *var, const char *value,\n    + void repo_config_values_init(struct repo_config_values *cfg)\n    + {\n    + \tcfg->attributes_file_path = NULL;\n    ++\tcfg->sparse_checkout = 0;\n    + }\n\n      ## environment.h ##\n     @@ environment.h: struct strvec;\n3:  6e54e22ac7 ! 3:  9a411db9f8 environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`\n    @@ Commit message\n         `git_branch_track`. This global variable can cause unexpected behaviours\n         when multiple Git repos run in the the same process.\n\n    -    Move this value into `struct repo_config_values` which holds all values\n    -    parsed by `git_default_config()` and can be accessed per\n    -    repo via `git_default_config()`. This would retain the same\n    +    Move this value into `struct repo_config_values` to retain current\n         behaviours while achieving repository scoped access.\n\n         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n    @@ environment.c: static int git_default_i18n_config(const char *var, const char *v\n      \t\treturn 0;\n      \t}\n      \tif (!strcmp(var, \"branch.autosetuprebase\")) {\n    -@@ environment.c: int git_default_config(const char *var, const char *value,\n    - \t/* Add other config variables here and to Documentation/config.adoc. */\n    - \treturn 0;\n    - }\n    -+\n    -+void repo_config_values_init(struct repo_config_values *cfg)\n    -+{\n    +@@ environment.c: void repo_config_values_init(struct repo_config_values *cfg)\n    + {\n    + \tcfg->attributes_file_path = NULL;\n    + \tcfg->sparse_checkout = 0;\n     +\tcfg->git_branch_track = BRANCH_TRACK_REMOTE;\n    -+}\n    + }\n\n      ## environment.h ##\n     @@\n    @@ environment.h: struct repo_config_values {\n      };\n\n      /*\n    -@@ environment.h: const char *strip_namespace(const char *namespaced_ref);\n    - int git_default_config(const char *, const char *,\n    - \t\t       const struct config_context *, void *);\n    -\n    -+void repo_config_values_init(struct repo_config_values *cfg);\n    -+\n    - /*\n    -  * TODO: All the below state either explicitly or implicitly relies on\n    -  * `the_repository`. We should eventually get rid of these and make the\n    -\n    - ## repository.c ##\n    -@@ repository.c: void initialize_repository(struct repository *repo)\n    - \tALLOC_ARRAY(repo->index, 1);\n    - \tindex_state_init(repo->index, repo);\n    - \trepo->check_deprecated_config = true;\n    -+\trepo_config_values_init(&repo->config_values);\n    -\n    - \t/*\n    - \t * When a command runs inside a repository, it learns what\n\n\n-- \n2.34.1\n\n"},{"id":"534127","messageId":"1aa41da8334296e4c1063b81fc40ec3b1dcdcb7b.1768681947.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768681947.git.belkid98@gmail.com","subject":"[Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-17T20:59:26Z","receivedAt":"2026-01-17T21:02:15Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value is parsed in git_default_core_config(), loaded eagerly\nand stored in the global variable `git_attributes_file`.\nStoring this value in a global variable can lead to unexpected\nbehaviours when more than one Git repository run in the same Git process.\n\nCreate a new struct `repo_config_values` to hold this value and\nother repository dependent values parsed by `git_default_config()` and\ncan be accessed per repository via `git_default_config()`.\nThis will ensure the current behaviour remains the same while also\nenabling the libification of Git.\n\nIt is important to note that `git_default_config()` is a wrapper to other\n`git_default_*_config()` such as `git_default_core_config()`.\nTherefore to access and modify this global variable,\nthe change has to be made in the function which parses and\nstores the value in the global variable.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n attr.c        |  7 ++++---\n environment.c | 12 +++++++++---\n environment.h |  9 ++++++++-\n repository.c  |  1 +\n repository.h  |  4 ++++\n 5 files changed, 26 insertions(+), 7 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 4999b7e09d..fbb9eaffaf 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)\n \n const char *git_attr_global_file(void)\n {\n-\tif (!git_attributes_file)\n-\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\tif (!cfg->attributes_file_path)\n+\t\tcfg->attributes_file_path = xdg_config_home(\"attributes\");\n \n-\treturn git_attributes_file;\n+\treturn cfg->attributes_file_path;\n }\n \n int git_attr_system_is_enabled(void)\ndiff --git a/environment.c b/environment.c\nindex a770b5921d..283db0a1a0 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -53,7 +53,6 @@ char *git_commit_encoding;\n char *git_log_output_encoding;\n char *apply_default_whitespace;\n char *apply_default_ignorewhitespace;\n-char *git_attributes_file;\n int zlib_compression_level = Z_BEST_SPEED;\n int pack_compression_level = Z_DEFAULT_COMPRESSION;\n int fsync_object_files = -1;\n@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n static int git_default_core_config(const char *var, const char *value,\n \t\t\t\t   const struct config_context *ctx, void *cb)\n {\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\n \t/* This needs a better name */\n \tif (!strcmp(var, \"core.filemode\")) {\n \t\ttrust_executable_bit = git_config_bool(var, value);\n@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.attributesfile\")) {\n-\t\tFREE_AND_NULL(git_attributes_file);\n-\t\treturn git_config_pathname(&git_attributes_file, var, value);\n+\t\tFREE_AND_NULL(cfg->attributes_file_path);\n+\t\treturn git_config_pathname(&cfg->attributes_file_path, var, value);\n \t}\n \n \tif (!strcmp(var, \"core.bare\")) {\n@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,\n \t/* Add other config variables here and to Documentation/config.adoc. */\n \treturn 0;\n }\n+\n+void repo_config_values_init(struct repo_config_values *cfg)\n+{\n+\tcfg->attributes_file_path = NULL;\n+}\ndiff --git a/environment.h b/environment.h\nindex 51898c99cd..aea73ff25b 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -84,6 +84,12 @@ extern const char * const local_repo_env[];\n \n struct strvec;\n \n+/* Config values parsed by git_default_config() */\n+struct repo_config_values {\n+\t/* core config values */\n+\tchar *attributes_file_path;\n+};\n+\n /*\n  * Wrapper of getenv() that returns a strdup value. This value is kept\n  * in argv to be freed later.\n@@ -107,6 +113,8 @@ const char *strip_namespace(const char *namespaced_ref);\n int git_default_config(const char *, const char *,\n \t\t       const struct config_context *, void *);\n \n+void repo_config_values_init(struct repo_config_values *cfg);\n+\n /*\n  * TODO: All the below state either explicitly or implicitly relies on\n  * `the_repository`. We should eventually get rid of these and make the\n@@ -152,7 +160,6 @@ extern int assume_unchanged;\n extern int warn_on_object_refname_ambiguity;\n extern char *apply_default_whitespace;\n extern char *apply_default_ignorewhitespace;\n-extern char *git_attributes_file;\n extern int zlib_compression_level;\n extern int pack_compression_level;\n extern unsigned long pack_size_limit_cfg;\ndiff --git a/repository.c b/repository.c\nindex c7e75215ac..d308cd78bf 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)\n \tALLOC_ARRAY(repo->index, 1);\n \tindex_state_init(repo->index, repo);\n \trepo->check_deprecated_config = true;\n+\trepo_config_values_init(&repo->config_values);\n \n \t/*\n \t * When a command runs inside a repository, it learns what\ndiff --git a/repository.h b/repository.h\nindex 6063c4b846..638a142577 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -3,6 +3,7 @@\n \n #include \"strmap.h\"\n #include \"repo-settings.h\"\n+#include \"environment.h\"\n \n struct config_set;\n struct git_hash_algo;\n@@ -148,6 +149,9 @@ struct repository {\n \t/* Repository's compatibility hash algorithm. */\n \tconst struct git_hash_algo *compat_hash_algo;\n \n+\t/* Repository's config values parsed by git_default_config() */\n+\tstruct repo_config_values config_values;\n+\n \t/* Repository's reference storage format, as serialized on disk. */\n \tenum ref_storage_format ref_storage_format;\n \n-- \n2.34.1\n\n"},{"id":"534128","messageId":"fd95169de42891452b430814476d78c706e4a7e2.1768681947.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768681947.git.belkid98@gmail.com","subject":"[Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-17T20:59:27Z","receivedAt":"2026-01-17T21:02:19Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `core.sparseCheckout` is parsed in\n`git_default_core_config()` and stored globally in\n`core_appy_sparse_checkout`. This could cause unintended behaviours\nwhen different Git repositories running in the same process access this\nvariable.\n\nMove the parsed value into `struct repo_config_values` to retains current\nbehaviours while achieving the repository scoped access.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n builtin/backfill.c        |  2 +-\n builtin/clone.c           |  2 +-\n builtin/grep.c            |  2 +-\n builtin/mv.c              |  2 +-\n builtin/sparse-checkout.c | 22 +++++++++++-----------\n builtin/worktree.c        |  2 +-\n dir.c                     |  2 +-\n environment.c             |  4 ++--\n environment.h             |  2 +-\n sparse-index.c            |  6 ++++--\n unpack-trees.c            |  2 +-\n wt-status.c               |  2 +-\n 12 files changed, 26 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/backfill.c b/builtin/backfill.c\nindex e80fc1b694..5fc8c51ed1 100644\n--- a/builtin/backfill.c\n+++ b/builtin/backfill.c\n@@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n \trepo_config(repo, git_default_config, NULL);\n \n \tif (ctx.sparse < 0)\n-\t\tctx.sparse = core_apply_sparse_checkout;\n+\t\tctx.sparse = repo->config_values.sparse_checkout;\n \n \tresult = do_backfill(&ctx);\n \tbackfill_context_clear(&ctx);\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..b6b19e83d1 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n \t * We must apply the setting in the current process\n \t * for the later checkout to use the sparse-checkout file.\n \t */\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->config_values.sparse_checkout = 1;\n \n \tcmd.git_cmd = 1;\n \tif (run_command(&cmd)) {\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 53cccf2d25..525edb5e9c 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,\n \t *\t\"forget\" the sparse-index feature switch. As a result, the index\n \t *\tof these submodules are expanded unexpectedly.\n \t *\n-\t * 2. \"core_apply_sparse_checkout\"\n+\t * 2. \"sparse_checkout\"\n \t *\tWhen running `grep` in the superproject, this setting is\n \t *\tpopulated using the superproject's configs. However, once\n \t *\tinitialized, this config is globally accessible and is read by\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex d43925097b..511620747b 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -572,7 +572,7 @@ int cmd_mv(int argc,\n \t\trename_index_entry_at(the_repository->index, pos, dst);\n \n \t\tif (ignore_sparse &&\n-\t\t    core_apply_sparse_checkout &&\n+\t\t    the_repository->config_values.sparse_checkout &&\n \t\t    core_sparse_checkout_cone) {\n \t\t\t/*\n \t\t\t * NEEDSWORK: we are *not* paying attention to\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..1c2c39b968 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,\n \tint res;\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.sparse_checkout)\n \t\tdie(_(\"this worktree is not sparse\"));\n \n \targc = parse_options(argc, argv, prefix,\n@@ -400,11 +400,11 @@ static int set_config(struct repository *repo,\n \n static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n \t/* If not specified, use previous definition of cone mode */\n-\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n+\tif (*cone_mode == -1 && the_repository->config_values.sparse_checkout)\n \t\t*cone_mode = core_sparse_checkout_cone;\n \n \t/* Set cone/non-cone mode appropriately */\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->config_values.sparse_checkout = 1;\n \tif (*cone_mode == 1 || *cone_mode == -1) {\n \t\tcore_sparse_checkout_cone = 1;\n \t\treturn MODE_CONE_PATTERNS;\n@@ -418,7 +418,7 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind\n \tint mode, record_mode;\n \n \t/* Determine if we need to record the mode; ensure sparse checkout on */\n-\trecord_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n+\trecord_mode = (*cone_mode != -1) || !repo->config_values.sparse_checkout;\n \n \tmode = update_cone_mode(cone_mode);\n \tif (record_mode && set_config(repo, mode))\n@@ -699,9 +699,9 @@ static int modify_pattern_list(struct repository *repo,\n \t\tbreak;\n \t}\n \n-\tif (!core_apply_sparse_checkout) {\n+\tif (!repo->config_values.sparse_checkout) {\n \t\tset_config(repo, MODE_ALL_PATTERNS);\n-\t\tcore_apply_sparse_checkout = 1;\n+\t\trepo->config_values.sparse_checkout = 1;\n \t\tchanged_config = 1;\n \t}\n \n@@ -798,7 +798,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,\n \tint ret;\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!repo->config_values.sparse_checkout)\n \t\tdie(_(\"no sparse-checkout to add to\"));\n \n \trepo_read_index(repo);\n@@ -907,7 +907,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!repo->config_values.sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n \n \treapply_opts.cone_mode = -1;\n@@ -969,7 +969,7 @@ static int sparse_checkout_clean(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!repo->config_values.sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to clean directories\"));\n \tif (!core_sparse_checkout_cone)\n \t\tdie(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n@@ -1035,7 +1035,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \tstruct pattern_list pl;\n \n \t/*\n-\t * We do not exit early if !core_apply_sparse_checkout; due to the\n+\t * We do not exit early if !repo->config_values.sparse_checkout; due to the\n \t * ability for users to manually muck things up between\n \t *   direct editing of .git/info/sparse-checkout\n \t *   running read-tree -m u HEAD or update-index --skip-worktree\n@@ -1061,7 +1061,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \thashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);\n \thashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n \tpl.use_cone_patterns = 0;\n-\tcore_apply_sparse_checkout = 1;\n+\trepo->config_values.sparse_checkout = 1;\n \n \tadd_pattern(\"/*\", empty_base, 0, &pl, 0);\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex fbdaf2eb2e..e401b8253e 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,\n \t * If the current worktree has sparse-checkout enabled, then copy\n \t * the sparse-checkout patterns from the current worktree.\n \t */\n-\tif (core_apply_sparse_checkout)\n+\tif (wt->repo->config_values.sparse_checkout)\n \t\tcopy_sparse_checkout(sb_repo.buf);\n \n \t/*\ndiff --git a/dir.c b/dir.c\nindex b00821f294..56b412a6d2 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(\n \n int init_sparse_checkout_patterns(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout)\n+\tif (!istate->repo->config_values.sparse_checkout)\n \t\treturn 1;\n \tif (istate->sparse_checkout_patterns)\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex 283db0a1a0..6633542750 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -74,7 +74,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #endif\n enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n int grafts_keep_true_parents;\n-int core_apply_sparse_checkout;\n int core_sparse_checkout_cone;\n int sparse_expect_files_outside_of_patterns;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.sparsecheckout\")) {\n-\t\tcore_apply_sparse_checkout = git_config_bool(var, value);\n+\t\tcfg->sparse_checkout = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \n@@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,\n void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file_path = NULL;\n+\tcfg->sparse_checkout = 0;\n }\ndiff --git a/environment.h b/environment.h\nindex aea73ff25b..3b5ff7094a 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -88,6 +88,7 @@ struct strvec;\n struct repo_config_values {\n \t/* core config values */\n \tchar *attributes_file_path;\n+\tint sparse_checkout;\n };\n \n /*\n@@ -169,7 +170,6 @@ extern int precomposed_unicode;\n extern int protect_hfs;\n extern int protect_ntfs;\n \n-extern int core_apply_sparse_checkout;\n extern int core_sparse_checkout_cone;\n extern int sparse_expect_files_outside_of_patterns;\n \ndiff --git a/sparse-index.c b/sparse-index.c\nindex 76f90da5f5..6dd8dd679d 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)\n \n int is_sparse_index_allowed(struct index_state *istate, int flags)\n {\n-\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n+\tstruct repo_config_values *cfg = &istate->repo->config_values;\n+\tif (!cfg->sparse_checkout || !core_sparse_checkout_cone)\n \t\treturn 0;\n \n \tif (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n@@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n \n void clear_skip_worktree_from_present_files(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout ||\n+\tstruct repo_config_values *cfg = &istate->repo->config_values;\n+\tif (!cfg->sparse_checkout ||\n \t    sparse_expect_files_outside_of_patterns)\n \t\treturn;\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex f38c761ab9..2bdfa1334c 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tif (o->prefix)\n \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n \n-\tif (!core_apply_sparse_checkout || !o->update)\n+\tif (!repo->config_values.sparse_checkout || !o->update)\n \t\to->skip_sparse_checkout = 1;\n \tif (!o->skip_sparse_checkout) {\n \t\tmemset(&pl, 0, sizeof(pl));\ndiff --git a/wt-status.c b/wt-status.c\nindex e12adb26b9..a2e388606f 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n \tint skip_worktree = 0;\n \tint i;\n \n-\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n+\tif (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {\n \t\t/*\n \t\t * Don't compute percentage of checked out files if we\n \t\t * aren't in a sparse checkout or would get division by 0.\n-- \n2.34.1\n\n"},{"id":"534129","messageId":"9a411db9f8f9df37c7a5f100d97c8d5ff5f6ba97.1768681947.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768681947.git.belkid98@gmail.com","subject":"[Outreachy PATCH v3 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-17T20:59:28Z","receivedAt":"2026-01-17T21:02:23Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `brach.autoSetupMerge` is parsed in\n`git_default_branch_config()` and stored in the global variable\n`git_branch_track`. This global variable can cause unexpected behaviours\nwhen multiple Git repos run in the the same process.\n\nMove this value into `struct repo_config_values` to retain current\nbehaviours while achieving repository scoped access.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n branch.h                    |  2 --\n builtin/branch.c            |  2 +-\n builtin/checkout.c          |  2 +-\n builtin/push.c              |  2 +-\n builtin/submodule--helper.c |  2 +-\n environment.c               | 12 +++++++-----\n environment.h               |  4 ++++\n 7 files changed, 15 insertions(+), 11 deletions(-)\n\ndiff --git a/branch.h b/branch.h\nindex ec2f35fda4..3dc6e2a0ff 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -15,8 +15,6 @@ enum branch_track {\n \tBRANCH_TRACK_SIMPLE,\n };\n \n-extern enum branch_track git_branch_track;\n-\n /* Functions for acting on the information about branches. */\n \n /**\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex c577b5d20f..d75114b41c 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -795,7 +795,7 @@ int cmd_branch(int argc,\n \tif (!sorting_options.nr)\n \t\tstring_list_append(&sorting_options, \"refname\");\n \n-\ttrack = git_branch_track;\n+\ttrack = the_repository->config_values.git_branch_track;\n \n \thead = refs_resolve_refdup(get_main_ref_store(the_repository), \"HEAD\",\n \t\t\t\t   0, &head_oid, NULL);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 261699e2f5..6c1cb9713c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1631,7 +1631,7 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n \t} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)\n-\t\topts->track = git_branch_track;\n+\t\topts->track = the_repository->config_values.git_branch_track;\n \n \tif (new_branch_info->name && !new_branch_info->commit)\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 5b6cebbb85..68105ecb2d 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -162,7 +162,7 @@ static NORETURN void die_push_simple(struct branch *branch,\n \t\tadvice_pushdefault_maybe = _(\"\\n\"\n \t\t\t\t \"To choose either option permanently, \"\n \t\t\t\t \"see push.default in 'git help config'.\\n\");\n-\tif (git_branch_track != BRANCH_TRACK_SIMPLE)\n+\tif (the_repository->config_values.git_branch_track != BRANCH_TRACK_SIMPLE)\n \t\tadvice_automergesimple_maybe = _(\"\\n\"\n \t\t\t\t \"To avoid automatically configuring \"\n \t\t\t\t \"an upstream branch when its name\\n\"\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d537ab087a..7239722e48 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3128,7 +3128,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,\n \t};\n \n \trepo_config(the_repository, git_default_config, NULL);\n-\ttrack = git_branch_track;\n+\ttrack = the_repository->config_values.git_branch_track;\n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \n \tif (argc != 3)\ndiff --git a/environment.c b/environment.c\nindex 6633542750..2092a5c3c6 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;\n enum eol core_eol = EOL_UNSET;\n int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;\n char *check_roundtrip_encoding;\n-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n enum rebase_setup_type autorebase = AUTOREBASE_NEVER;\n enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #ifndef OBJECT_CREATION_MODE\n@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)\n \n static int git_default_branch_config(const char *var, const char *value)\n {\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\n \tif (!strcmp(var, \"branch.autosetupmerge\")) {\n \t\tif (value && !strcmp(value, \"always\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_ALWAYS;\n+\t\t\tcfg->git_branch_track = BRANCH_TRACK_ALWAYS;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n+\t\t\tcfg->git_branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"simple\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\tcfg->git_branch_track = BRANCH_TRACK_SIMPLE;\n \t\t\treturn 0;\n \t\t}\n-\t\tgit_branch_track = git_config_bool(var, value);\n+\t\tcfg->git_branch_track = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"branch.autosetuprebase\")) {\n@@ -761,4 +762,5 @@ void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file_path = NULL;\n \tcfg->sparse_checkout = 0;\n+\tcfg->git_branch_track = BRANCH_TRACK_REMOTE;\n }\ndiff --git a/environment.h b/environment.h\nindex 3b5ff7094a..bfcdffe836 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -2,6 +2,7 @@\n #define ENVIRONMENT_H\n \n #include \"repo-settings.h\"\n+#include \"branch.h\"\n \n /* Double-check local_repo_env below if you add to this list. */\n #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n@@ -89,6 +90,9 @@ struct repo_config_values {\n \t/* core config values */\n \tchar *attributes_file_path;\n \tint sparse_checkout;\n+\n+\t/* branch config values */\n+\tenum branch_track git_branch_track;\n };\n \n /*\n-- \n2.34.1\n\n"},{"id":"534246","messageId":"CAD=f0L8fs5Jxs7o-KkbXx3s3eR9jh6uN=Sok-z2S=fMX1yFMtQ@mail.gmail.com","threadId":"64782","inReplyTo":"cover.1768681947.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v3 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-20T15:19:05Z","receivedAt":"2026-01-20T15:19:05Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Sat, 17 Jan 2026 at 22:02, Olamide Caleb Bello <belkid98@gmail.com> wrote:\n>\n> Hi Git Community,\n> Over the course of my ongoing internship, which focused on moving repo specific\n> global variables in environment.h into local scope, I have attempted to move some\n> variables into the struct repo-settings.\n> However there has been some design concerns as regards the use of\n> `prepare_repo_settings()` with respect to when and where to call the\n> function, and also the change in behaviours when the variable is lazily\n> loaded as discussed in [1] and [2].\n>\n> After different deliberations, Phillip Wood proposed creating a new config\n> struct [3], adding it to the repository struct and passing the repo struct to\n> `git_default_config()` to store parsed repo specific config values per repo.\n> This ensures the current behaviours will be retained.\n>\n> I have experimented with this approach for some values and I would\n> appreciate feedbacks about this approach before we can move forward\n> and use it for more variables related to `git_default_config()`.\n>\n> For now, the parsed value is stored in `the_repository` in\n> `git_default_*_config()` and further efforts to pass the repository\n> parameter to `git_default_config()` as the callback parameter will\n> be looked into later on.\n> The link to the CI tests can be see in [4]\n>\n> 1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/\n> 2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/\n> 3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/\n> 4. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2266020513\n>\n> Changes in v3:\n> ==============\n> - Moved declaration and definition of repo_config_values_init into patch 1\n> - Returned commit message in line 1 of builtin/backfill.c\n>\n> Olamide Caleb Bello (3):\n>   environment: stop storing `core.attributesFile` globally\n>   environment: environment: stop using core.sparseCheckout globally\n>   environment: move \"branch.autoSetupMerge\" into `struct\n>     repo_config_values`\n>\n>  attr.c                      |  7 ++++---\n>  branch.h                    |  2 --\n>  builtin/backfill.c          |  2 +-\n>  builtin/branch.c            |  2 +-\n>  builtin/checkout.c          |  2 +-\n>  builtin/clone.c             |  2 +-\n>  builtin/grep.c              |  2 +-\n>  builtin/mv.c                |  2 +-\n>  builtin/push.c              |  2 +-\n>  builtin/sparse-checkout.c   | 22 +++++++++++-----------\n>  builtin/submodule--helper.c |  2 +-\n>  builtin/worktree.c          |  2 +-\n>  dir.c                       |  2 +-\n>  environment.c               | 28 ++++++++++++++++++----------\n>  environment.h               | 15 +++++++++++++--\n>  repository.c                |  1 +\n>  repository.h                |  4 ++++\n>  sparse-index.c              |  6 ++++--\n>  unpack-trees.c              |  2 +-\n>  wt-status.c                 |  2 +-\n>  20 files changed, 67 insertions(+), 42 deletions(-)\n>\n>  Range diff versus v2:\n>  =====================\n>\n>  1:  b6f8deaa40 ! 1:  1aa41da833 environment: stop storing `core.attributesFile` globally\n>     @@ Metadata\n>       ## Commit message ##\n>          environment: stop storing `core.attributesFile` globally\n>\n>     -    The config value parsed in git_default_core_config() is loaded eagerly\n>     +    The config value is parsed in git_default_core_config(), loaded eagerly\n>          and stored in the global variable `git_attributes_file`.\n>          Storing this value in a global variable can lead to unexpected\n>          behaviours when more than one Git repository run in the same Git process.\n>\n>     -    Move this value into a `struct repo_config_values` which holds all the\n>     -    values parsed by `git_default_config()` and can be accessed per\n>     -    repository via `git_default_config()`. This will ensure the current\n>     -    behaviour remains the same while also enabling the libification of Git.\n>     +    Create a new struct `repo_config_values` to hold this value and\n>     +    other repository dependent values parsed by `git_default_config()` and\n>     +    can be accessed per repository via `git_default_config()`.\n>     +    This will ensure the current behaviour remains the same while also\n>     +    enabling the libification of Git.\n>\n>          It is important to note that `git_default_config()` is a wrapper to other\n>          `git_default_*_config()` such as `git_default_core_config()`.\n>     @@ environment.c: static int git_default_core_config(const char *var, const char *v\n>         }\n>\n>         if (!strcmp(var, \"core.bare\")) {\n>     +@@ environment.c: int git_default_config(const char *var, const char *value,\n>     +   /* Add other config variables here and to Documentation/config.adoc. */\n>     +   return 0;\n>     + }\n>     ++\n>     ++void repo_config_values_init(struct repo_config_values *cfg)\n>     ++{\n>     ++  cfg->attributes_file_path = NULL;\n>     ++}\n>\n>       ## environment.h ##\n>      @@ environment.h: extern const char * const local_repo_env[];\n>     @@ environment.h: extern const char * const local_repo_env[];\n>       /*\n>        * Wrapper of getenv() that returns a strdup value. This value is kept\n>        * in argv to be freed later.\n>     +@@ environment.h: const char *strip_namespace(const char *namespaced_ref);\n>     + int git_default_config(const char *, const char *,\n>     +                  const struct config_context *, void *);\n>     +\n>     ++void repo_config_values_init(struct repo_config_values *cfg);\n>     ++\n>     + /*\n>     +  * TODO: All the below state either explicitly or implicitly relies on\n>     +  * `the_repository`. We should eventually get rid of these and make the\n>      @@ environment.h: extern int assume_unchanged;\n>       extern int warn_on_object_refname_ambiguity;\n>       extern char *apply_default_whitespace;\n>     @@ environment.h: extern int assume_unchanged;\n>       extern int pack_compression_level;\n>       extern unsigned long pack_size_limit_cfg;\n>\n>     + ## repository.c ##\n>     +@@ repository.c: void initialize_repository(struct repository *repo)\n>     +   ALLOC_ARRAY(repo->index, 1);\n>     +   index_state_init(repo->index, repo);\n>     +   repo->check_deprecated_config = true;\n>     ++  repo_config_values_init(&repo->config_values);\n>     +\n>     +   /*\n>     +    * When a command runs inside a repository, it learns what\n>     +\n>       ## repository.h ##\n>      @@\n>\n> 2:  1e83c077f2 ! 2:  fd95169de4 environment: environment: stop using core.sparseCheckout globally\n>     @@ Commit message\n>          when different Git repositories running in the same process access this\n>          variable.\n>\n>     -    Move the parsed value into `struct repo_config_values` which holds all the\n>     -    values parsed by `git_default_config()` and can be accessed\n>     -    per repo via `git_default_config()`. This retains current\n>     +    Move the parsed value into `struct repo_config_values` to retains current\n>          behaviours while achieving the repository scoped access.\n>\n>          Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n>     @@ Commit message\n>          Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n>\n>       ## builtin/backfill.c ##\n>     -@@\n>     --/* We need this macro to access core_apply_sparse_checkout */\n>     - #define USE_THE_REPOSITORY_VARIABLE\n>     -\n>     - #include \"builtin.h\"\n>      @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n>         repo_config(repo, git_default_config, NULL);\n>\n>     @@ environment.c: static int git_default_core_config(const char *var, const char *v\n>                 return 0;\n>         }\n>\n>     +@@ environment.c: int git_default_config(const char *var, const char *value,\n>     + void repo_config_values_init(struct repo_config_values *cfg)\n>     + {\n>     +   cfg->attributes_file_path = NULL;\n>     ++  cfg->sparse_checkout = 0;\n>     + }\n>\n>       ## environment.h ##\n>      @@ environment.h: struct strvec;\n> 3:  6e54e22ac7 ! 3:  9a411db9f8 environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`\n>     @@ Commit message\n>          `git_branch_track`. This global variable can cause unexpected behaviours\n>          when multiple Git repos run in the the same process.\n>\n>     -    Move this value into `struct repo_config_values` which holds all values\n>     -    parsed by `git_default_config()` and can be accessed per\n>     -    repo via `git_default_config()`. This would retain the same\n>     +    Move this value into `struct repo_config_values` to retain current\n>          behaviours while achieving repository scoped access.\n>\n>          Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n>     @@ environment.c: static int git_default_i18n_config(const char *var, const char *v\n>                 return 0;\n>         }\n>         if (!strcmp(var, \"branch.autosetuprebase\")) {\n>     -@@ environment.c: int git_default_config(const char *var, const char *value,\n>     -   /* Add other config variables here and to Documentation/config.adoc. */\n>     -   return 0;\n>     - }\n>     -+\n>     -+void repo_config_values_init(struct repo_config_values *cfg)\n>     -+{\n>     +@@ environment.c: void repo_config_values_init(struct repo_config_values *cfg)\n>     + {\n>     +   cfg->attributes_file_path = NULL;\n>     +   cfg->sparse_checkout = 0;\n>      +  cfg->git_branch_track = BRANCH_TRACK_REMOTE;\n>     -+}\n>     + }\n>\n>       ## environment.h ##\n>      @@\n>     @@ environment.h: struct repo_config_values {\n>       };\n>\n>       /*\n>     -@@ environment.h: const char *strip_namespace(const char *namespaced_ref);\n>     - int git_default_config(const char *, const char *,\n>     -                  const struct config_context *, void *);\n>     -\n>     -+void repo_config_values_init(struct repo_config_values *cfg);\n>     -+\n>     - /*\n>     -  * TODO: All the below state either explicitly or implicitly relies on\n>     -  * `the_repository`. We should eventually get rid of these and make the\n>     -\n>     - ## repository.c ##\n>     -@@ repository.c: void initialize_repository(struct repository *repo)\n>     -   ALLOC_ARRAY(repo->index, 1);\n>     -   index_state_init(repo->index, repo);\n>     -   repo->check_deprecated_config = true;\n>     -+  repo_config_values_init(&repo->config_values);\n>     -\n>     -   /*\n>     -    * When a command runs inside a repository, it learns what\n>\n>\n\nHello\nReplying to this to get reviews and feedback.\n\nThanks\n"},{"id":"534446","messageId":"871pjhkfq7.fsf@iotcl.com","threadId":"64782","inReplyTo":"1aa41da8334296e4c1063b81fc40ec3b1dcdcb7b.1768681947.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2026-01-22T12:13:20Z","receivedAt":"2026-01-22T12:13:39Z","isPatch":true,"sender":{"key":"toon@iotcl.com","avatar":"https://avatars.githubusercontent.com/u/121621?v=4"},"body":"Olamide Caleb Bello <belkid98@gmail.com> writes:\n\n> The config value is parsed in git_default_core_config(), loaded\n\nI assume you mean 'core.attributesFile' because it's in the title. But\npersonnally I don't mind seeing the name repeated in the body to make it\nmore clear.\n\n> eagerly and stored in the global variable `git_attributes_file`.\n> Storing this value in a global variable can lead to unexpected\n> behaviours when more than one Git repository run in the same Git process.\n>\n> Create a new struct `repo_config_values` to hold this value and\n> other repository dependent values parsed by `git_default_config()` and\n> can be accessed per repository via `git_default_config()`.\n\nI'd suggest to split off the part after the second 'and' into a new\nsentence.\n\n> This will ensure the current behaviour remains the same while also\n> enabling the libification of Git.\n\nHow is this true? Was that value already accessible through\n`git_default_config()`? \n\n> It is important to note that `git_default_config()` is a wrapper to other\n> `git_default_*_config()` such as `git_default_core_config()`.\n\nI'd suggest to insert 'functions' before 'such'.\n\n> Therefore to access and modify this global variable,\n> the change has to be made in the function which parses and\n> stores the value in the global variable.\n\nThis doesn't clarify much for me. Do you mean 'git_attr_global_file()'\nand 'git_default_core_config()'?\n\n>\n> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> ---\n>  attr.c        |  7 ++++---\n>  environment.c | 12 +++++++++---\n>  environment.h |  9 ++++++++-\n>  repository.c  |  1 +\n>  repository.h  |  4 ++++\n>  5 files changed, 26 insertions(+), 7 deletions(-)\n>\n> diff --git a/attr.c b/attr.c\n> index 4999b7e09d..fbb9eaffaf 100644\n> --- a/attr.c\n> +++ b/attr.c\n> @@ -881,10 +881,11 @@ const char *git_attr_system_file(void)\n>  \n>  const char *git_attr_global_file(void)\n>  {\n> -\tif (!git_attributes_file)\n> -\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n> +\tstruct repo_config_values *cfg = &the_repository->config_values;\n\nIs 'cfg' guaranteed to be != NULL?\n\n> +\tif (!cfg->attributes_file_path)\n> +\t\tcfg->attributes_file_path = xdg_config_home(\"attributes\");\n>  \n> -\treturn git_attributes_file;\n> +\treturn cfg->attributes_file_path;\n>  }\n>  \n>  int git_attr_system_is_enabled(void)\n> diff --git a/environment.c b/environment.c\n> index a770b5921d..283db0a1a0 100644\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -53,7 +53,6 @@ char *git_commit_encoding;\n>  char *git_log_output_encoding;\n>  char *apply_default_whitespace;\n>  char *apply_default_ignorewhitespace;\n> -char *git_attributes_file;\n>  int zlib_compression_level = Z_BEST_SPEED;\n>  int pack_compression_level = Z_DEFAULT_COMPRESSION;\n>  int fsync_object_files = -1;\n> @@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n>  static int git_default_core_config(const char *var, const char *value,\n>  \t\t\t\t   const struct config_context *ctx, void *cb)\n>  {\n> +\tstruct repo_config_values *cfg = &the_repository->config_values;\n> +\n>  \t/* This needs a better name */\n>  \tif (!strcmp(var, \"core.filemode\")) {\n>  \t\ttrust_executable_bit = git_config_bool(var, value);\n> @@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,\n>  \t}\n>  \n>  \tif (!strcmp(var, \"core.attributesfile\")) {\n> -\t\tFREE_AND_NULL(git_attributes_file);\n> -\t\treturn git_config_pathname(&git_attributes_file, var, value);\n> +\t\tFREE_AND_NULL(cfg->attributes_file_path);\n> +\t\treturn git_config_pathname(&cfg->attributes_file_path, var, value);\n>  \t}\n>  \n>  \tif (!strcmp(var, \"core.bare\")) {\n> @@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,\n>  \t/* Add other config variables here and to Documentation/config.adoc. */\n>  \treturn 0;\n>  }\n> +\n> +void repo_config_values_init(struct repo_config_values *cfg)\n> +{\n> +\tcfg->attributes_file_path = NULL;\n> +}\n\nI assume the reason for adding this function becomes clear in a later\ncommit?\n\n> diff --git a/environment.h b/environment.h\n> index 51898c99cd..aea73ff25b 100644\n> --- a/environment.h\n> +++ b/environment.h\n> @@ -84,6 +84,12 @@ extern const char * const local_repo_env[];\n>  \n>  struct strvec;\n>  \n> +/* Config values parsed by git_default_config() */\n\nMentioning here they get filled from git_default_config() doesn't feel\nreally correct? Although I'm sure what comment would fit better, maybe\njust drop the comment above the struct. I see you have a similar comment\nin 'struct repository', where it *does* make sense.\n\n> +struct repo_config_values {\n> +\t/* core config values */\n\nI prefer emphasizing it's the \"section 'core'\" or something like that.\n\n> +\tchar *attributes_file_path;\n\nWould it be overkill to append: /* `core.attributesFile` */? This can\nhelp when grepping through the codebase to find where some settings are\nbeing parsed into. What do you think?\n\n> +};\n> +\n>  /*\n>   * Wrapper of getenv() that returns a strdup value. This value is kept\n>   * in argv to be freed later.\n> @@ -107,6 +113,8 @@ const char *strip_namespace(const char *namespaced_ref);\n>  int git_default_config(const char *, const char *,\n>  \t\t       const struct config_context *, void *);\n>  \n> +void repo_config_values_init(struct repo_config_values *cfg);\n> +\n>  /*\n>   * TODO: All the below state either explicitly or implicitly relies on\n>   * `the_repository`. We should eventually get rid of these and make the\n> @@ -152,7 +160,6 @@ extern int assume_unchanged;\n>  extern int warn_on_object_refname_ambiguity;\n>  extern char *apply_default_whitespace;\n>  extern char *apply_default_ignorewhitespace;\n> -extern char *git_attributes_file;\n>  extern int zlib_compression_level;\n>  extern int pack_compression_level;\n>  extern unsigned long pack_size_limit_cfg;\n> diff --git a/repository.c b/repository.c\n> index c7e75215ac..d308cd78bf 100644\n> --- a/repository.c\n> +++ b/repository.c\n> @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)\n>  \tALLOC_ARRAY(repo->index, 1);\n>  \tindex_state_init(repo->index, repo);\n>  \trepo->check_deprecated_config = true;\n> +\trepo_config_values_init(&repo->config_values);\n>  \n>  \t/*\n>  \t * When a command runs inside a repository, it learns what\n> diff --git a/repository.h b/repository.h\n> index 6063c4b846..638a142577 100644\n> --- a/repository.h\n> +++ b/repository.h\n> @@ -3,6 +3,7 @@\n>  \n>  #include \"strmap.h\"\n>  #include \"repo-settings.h\"\n> +#include \"environment.h\"\n>  \n>  struct config_set;\n>  struct git_hash_algo;\n> @@ -148,6 +149,9 @@ struct repository {\n>  \t/* Repository's compatibility hash algorithm. */\n>  \tconst struct git_hash_algo *compat_hash_algo;\n>  \n> +\t/* Repository's config values parsed by git_default_config() */\n> +\tstruct repo_config_values config_values;\n> +\n>  \t/* Repository's reference storage format, as serialized on disk. */\n>  \tenum ref_storage_format ref_storage_format;\n>  \n> -- \n> 2.34.1\n>\n>\n\n-- \nCheers,\nToon\n"},{"id":"534447","messageId":"87zf65j152.fsf@iotcl.com","threadId":"64782","inReplyTo":"fd95169de42891452b430814476d78c706e4a7e2.1768681947.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2026-01-22T12:13:45Z","receivedAt":"2026-01-22T12:13:58Z","isPatch":true,"sender":{"key":"toon@iotcl.com","avatar":"https://avatars.githubusercontent.com/u/121621?v=4"},"body":"Olamide Caleb Bello <belkid98@gmail.com> writes:\n\nIt seems you have 'environment:' twice in the title?\n\n> The config value `core.sparseCheckout` is parsed in\n> `git_default_core_config()` and stored globally in\n> `core_appy_sparse_checkout`. This could cause unintended behaviours\n\ns/core_appy_sparse_checkout/core_apply_sparse_checkout/ ?\n\n> when different Git repositories running in the same process access this\n> variable.\n>\n> Move the parsed value into `struct repo_config_values` to retains current\n> behaviours while achieving the repository scoped access.\n>\n> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> ---\n>  builtin/backfill.c        |  2 +-\n>  builtin/clone.c           |  2 +-\n>  builtin/grep.c            |  2 +-\n>  builtin/mv.c              |  2 +-\n>  builtin/sparse-checkout.c | 22 +++++++++++-----------\n>  builtin/worktree.c        |  2 +-\n>  dir.c                     |  2 +-\n>  environment.c             |  4 ++--\n>  environment.h             |  2 +-\n>  sparse-index.c            |  6 ++++--\n>  unpack-trees.c            |  2 +-\n>  wt-status.c               |  2 +-\n>  12 files changed, 26 insertions(+), 24 deletions(-)\n>\n> diff --git a/builtin/backfill.c b/builtin/backfill.c\n> index e80fc1b694..5fc8c51ed1 100644\n> --- a/builtin/backfill.c\n> +++ b/builtin/backfill.c\n> @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n>  \trepo_config(repo, git_default_config, NULL);\n>  \n>  \tif (ctx.sparse < 0)\n> -\t\tctx.sparse = core_apply_sparse_checkout;\n> +\t\tctx.sparse = repo->config_values.sparse_checkout;\n>  \n>  \tresult = do_backfill(&ctx);\n>  \tbackfill_context_clear(&ctx);\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index b19b302b06..b6b19e83d1 100644\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n>  \t * We must apply the setting in the current process\n>  \t * for the later checkout to use the sparse-checkout file.\n>  \t */\n> -\tcore_apply_sparse_checkout = 1;\n> +\tthe_repository->config_values.sparse_checkout = 1;\n>  \n>  \tcmd.git_cmd = 1;\n>  \tif (run_command(&cmd)) {\n> diff --git a/builtin/grep.c b/builtin/grep.c\n> index 53cccf2d25..525edb5e9c 100644\n> --- a/builtin/grep.c\n> +++ b/builtin/grep.c\n> @@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,\n>  \t *\t\"forget\" the sparse-index feature switch. As a result, the index\n>  \t *\tof these submodules are expanded unexpectedly.\n>  \t *\n> -\t * 2. \"core_apply_sparse_checkout\"\n> +\t * 2. \"sparse_checkout\"\n>  \t *\tWhen running `grep` in the superproject, this setting is\n>  \t *\tpopulated using the superproject's configs. However, once\n>  \t *\tinitialized, this config is globally accessible and is read by\n> diff --git a/builtin/mv.c b/builtin/mv.c\n> index d43925097b..511620747b 100644\n> --- a/builtin/mv.c\n> +++ b/builtin/mv.c\n> @@ -572,7 +572,7 @@ int cmd_mv(int argc,\n>  \t\trename_index_entry_at(the_repository->index, pos, dst);\n>  \n>  \t\tif (ignore_sparse &&\n> -\t\t    core_apply_sparse_checkout &&\n> +\t\t    the_repository->config_values.sparse_checkout &&\n>  \t\t    core_sparse_checkout_cone) {\n>  \t\t\t/*\n>  \t\t\t * NEEDSWORK: we are *not* paying attention to\n> diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\n> index 15d51e60a8..1c2c39b968 100644\n> --- a/builtin/sparse-checkout.c\n> +++ b/builtin/sparse-checkout.c\n> @@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,\n>  \tint res;\n>  \n>  \tsetup_work_tree();\n> -\tif (!core_apply_sparse_checkout)\n> +\tif (!the_repository->config_values.sparse_checkout)\n>  \t\tdie(_(\"this worktree is not sparse\"));\n>  \n>  \targc = parse_options(argc, argv, prefix,\n> @@ -400,11 +400,11 @@ static int set_config(struct repository *repo,\n>  \n>  static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n>  \t/* If not specified, use previous definition of cone mode */\n> -\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n> +\tif (*cone_mode == -1 && the_repository->config_values.sparse_checkout)\n>  \t\t*cone_mode = core_sparse_checkout_cone;\n>  \n>  \t/* Set cone/non-cone mode appropriately */\n> -\tcore_apply_sparse_checkout = 1;\n> +\tthe_repository->config_values.sparse_checkout = 1;\n>  \tif (*cone_mode == 1 || *cone_mode == -1) {\n>  \t\tcore_sparse_checkout_cone = 1;\n>  \t\treturn MODE_CONE_PATTERNS;\n> @@ -418,7 +418,7 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind\n>  \tint mode, record_mode;\n>  \n>  \t/* Determine if we need to record the mode; ensure sparse checkout on */\n> -\trecord_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n> +\trecord_mode = (*cone_mode != -1) || !repo->config_values.sparse_checkout;\n>  \n>  \tmode = update_cone_mode(cone_mode);\n>  \tif (record_mode && set_config(repo, mode))\n> @@ -699,9 +699,9 @@ static int modify_pattern_list(struct repository *repo,\n>  \t\tbreak;\n>  \t}\n>  \n> -\tif (!core_apply_sparse_checkout) {\n> +\tif (!repo->config_values.sparse_checkout) {\n>  \t\tset_config(repo, MODE_ALL_PATTERNS);\n> -\t\tcore_apply_sparse_checkout = 1;\n> +\t\trepo->config_values.sparse_checkout = 1;\n>  \t\tchanged_config = 1;\n>  \t}\n>  \n> @@ -798,7 +798,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,\n>  \tint ret;\n>  \n>  \tsetup_work_tree();\n> -\tif (!core_apply_sparse_checkout)\n> +\tif (!repo->config_values.sparse_checkout)\n>  \t\tdie(_(\"no sparse-checkout to add to\"));\n>  \n>  \trepo_read_index(repo);\n> @@ -907,7 +907,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,\n>  \t};\n>  \n>  \tsetup_work_tree();\n> -\tif (!core_apply_sparse_checkout)\n> +\tif (!repo->config_values.sparse_checkout)\n>  \t\tdie(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n>  \n>  \treapply_opts.cone_mode = -1;\n> @@ -969,7 +969,7 @@ static int sparse_checkout_clean(int argc, const char **argv,\n>  \t};\n>  \n>  \tsetup_work_tree();\n> -\tif (!core_apply_sparse_checkout)\n> +\tif (!repo->config_values.sparse_checkout)\n>  \t\tdie(_(\"must be in a sparse-checkout to clean directories\"));\n>  \tif (!core_sparse_checkout_cone)\n>  \t\tdie(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n> @@ -1035,7 +1035,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n>  \tstruct pattern_list pl;\n>  \n>  \t/*\n> -\t * We do not exit early if !core_apply_sparse_checkout; due to the\n> +\t * We do not exit early if !repo->config_values.sparse_checkout; due to the\n>  \t * ability for users to manually muck things up between\n>  \t *   direct editing of .git/info/sparse-checkout\n>  \t *   running read-tree -m u HEAD or update-index --skip-worktree\n> @@ -1061,7 +1061,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n>  \thashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);\n>  \thashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n>  \tpl.use_cone_patterns = 0;\n> -\tcore_apply_sparse_checkout = 1;\n> +\trepo->config_values.sparse_checkout = 1;\n>  \n>  \tadd_pattern(\"/*\", empty_base, 0, &pl, 0);\n>  \n> diff --git a/builtin/worktree.c b/builtin/worktree.c\n> index fbdaf2eb2e..e401b8253e 100644\n> --- a/builtin/worktree.c\n> +++ b/builtin/worktree.c\n> @@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,\n>  \t * If the current worktree has sparse-checkout enabled, then copy\n>  \t * the sparse-checkout patterns from the current worktree.\n>  \t */\n> -\tif (core_apply_sparse_checkout)\n> +\tif (wt->repo->config_values.sparse_checkout)\n>  \t\tcopy_sparse_checkout(sb_repo.buf);\n>  \n>  \t/*\n> diff --git a/dir.c b/dir.c\n> index b00821f294..56b412a6d2 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(\n>  \n>  int init_sparse_checkout_patterns(struct index_state *istate)\n>  {\n> -\tif (!core_apply_sparse_checkout)\n> +\tif (!istate->repo->config_values.sparse_checkout)\n>  \t\treturn 1;\n>  \tif (istate->sparse_checkout_patterns)\n>  \t\treturn 0;\n> diff --git a/environment.c b/environment.c\n> index 283db0a1a0..6633542750 100644\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -74,7 +74,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n>  #endif\n>  enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n>  int grafts_keep_true_parents;\n> -int core_apply_sparse_checkout;\n>  int core_sparse_checkout_cone;\n>  int sparse_expect_files_outside_of_patterns;\n>  int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n> @@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,\n>  \t}\n>  \n>  \tif (!strcmp(var, \"core.sparsecheckout\")) {\n> -\t\tcore_apply_sparse_checkout = git_config_bool(var, value);\n> +\t\tcfg->sparse_checkout = git_config_bool(var, value);\n>  \t\treturn 0;\n>  \t}\n>  \n> @@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,\n>  void repo_config_values_init(struct repo_config_values *cfg)\n>  {\n>  \tcfg->attributes_file_path = NULL;\n> +\tcfg->sparse_checkout = 0;\n>  }\n> diff --git a/environment.h b/environment.h\n> index aea73ff25b..3b5ff7094a 100644\n> --- a/environment.h\n> +++ b/environment.h\n> @@ -88,6 +88,7 @@ struct strvec;\n>  struct repo_config_values {\n>  \t/* core config values */\n>  \tchar *attributes_file_path;\n> +\tint sparse_checkout;\n>  };\n>  \n>  /*\n> @@ -169,7 +170,6 @@ extern int precomposed_unicode;\n>  extern int protect_hfs;\n>  extern int protect_ntfs;\n>  \n> -extern int core_apply_sparse_checkout;\n\nIn the field you're adding to 'struct repo_config_values' you have\ndropped the 'core_' prefix, what the reason for that? If I understand it\ncorrectly also settings from other sections might end up in that struct,\nso wouldn't it be better to keep the prefix?\n\n>  extern int core_sparse_checkout_cone;\n>  extern int sparse_expect_files_outside_of_patterns;\n>  \n> diff --git a/sparse-index.c b/sparse-index.c\n> index 76f90da5f5..6dd8dd679d 100644\n> --- a/sparse-index.c\n> +++ b/sparse-index.c\n> @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)\n>  \n>  int is_sparse_index_allowed(struct index_state *istate, int flags)\n>  {\n> -\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n> +\tstruct repo_config_values *cfg = &istate->repo->config_values;\n> +\tif (!cfg->sparse_checkout || !core_sparse_checkout_cone)\n>  \t\treturn 0;\n>  \n>  \tif (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n> @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n>  \n>  void clear_skip_worktree_from_present_files(struct index_state *istate)\n>  {\n> -\tif (!core_apply_sparse_checkout ||\n> +\tstruct repo_config_values *cfg = &istate->repo->config_values;\n> +\tif (!cfg->sparse_checkout ||\n>  \t    sparse_expect_files_outside_of_patterns)\n>  \t\treturn;\n>  \n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index f38c761ab9..2bdfa1334c 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n>  \tif (o->prefix)\n>  \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n>  \n> -\tif (!core_apply_sparse_checkout || !o->update)\n> +\tif (!repo->config_values.sparse_checkout || !o->update)\n>  \t\to->skip_sparse_checkout = 1;\n>  \tif (!o->skip_sparse_checkout) {\n>  \t\tmemset(&pl, 0, sizeof(pl));\n> diff --git a/wt-status.c b/wt-status.c\n> index e12adb26b9..a2e388606f 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n>  \tint skip_worktree = 0;\n>  \tint i;\n>  \n> -\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n> +\tif (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {\n>  \t\t/*\n>  \t\t * Don't compute percentage of checked out files if we\n>  \t\t * aren't in a sparse checkout or would get division by 0.\n> -- \n> 2.34.1\n>\n>\n\n-- \nCheers,\nToon\n"},{"id":"534450","messageId":"60dfb907-c2b8-4fd4-b975-742f7ec18721@gmail.com","threadId":"64782","inReplyTo":"1aa41da8334296e4c1063b81fc40ec3b1dcdcb7b.1768681947.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-01-22T14:40:00Z","receivedAt":"2026-01-22T14:40:04Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Olamide\n\nOn 17/01/2026 20:59, Olamide Caleb Bello wrote:\n> The config value is parsed in git_default_core_config(), loaded eagerly\n> and stored in the global variable `git_attributes_file`.\n> Storing this value in a global variable can lead to unexpected\n> behaviours when more than one Git repository run in the same Git process.\n\nIt would maybe be helpful to explain what the unexpected behavior is and \nhow it is caused.\n\n> diff --git a/environment.h b/environment.h\n> index 51898c99cd..aea73ff25b 100644\n> --- a/environment.h\n> +++ b/environment.h\n> @@ -84,6 +84,12 @@ extern const char * const local_repo_env[];\n>   \n>   struct strvec;\n>   \n> +/* Config values parsed by git_default_config() */\n> +struct repo_config_values {\n> +\t/* core config values */\n> +\tchar *attributes_file_path;\n\nThe variable we're converting is called \"attributes_file\", do we really \nneed to add a \"_path\" suffix?\n\nApart from that everything here looks good\n\nThanks\n\nPhillip\n\n> +};\n> +\n>   /*\n>    * Wrapper of getenv() that returns a strdup value. This value is kept\n>    * in argv to be freed later.\n> @@ -107,6 +113,8 @@ const char *strip_namespace(const char *namespaced_ref);\n>   int git_default_config(const char *, const char *,\n>   \t\t       const struct config_context *, void *);\n>   \n> +void repo_config_values_init(struct repo_config_values *cfg);\n> +\n>   /*\n>    * TODO: All the below state either explicitly or implicitly relies on\n>    * `the_repository`. We should eventually get rid of these and make the\n> @@ -152,7 +160,6 @@ extern int assume_unchanged;\n>   extern int warn_on_object_refname_ambiguity;\n>   extern char *apply_default_whitespace;\n>   extern char *apply_default_ignorewhitespace;\n> -extern char *git_attributes_file;\n>   extern int zlib_compression_level;\n>   extern int pack_compression_level;\n>   extern unsigned long pack_size_limit_cfg;\n> diff --git a/repository.c b/repository.c\n> index c7e75215ac..d308cd78bf 100644\n> --- a/repository.c\n> +++ b/repository.c\n> @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)\n>   \tALLOC_ARRAY(repo->index, 1);\n>   \tindex_state_init(repo->index, repo);\n>   \trepo->check_deprecated_config = true;\n> +\trepo_config_values_init(&repo->config_values);\n>   \n>   \t/*\n>   \t * When a command runs inside a repository, it learns what\n> diff --git a/repository.h b/repository.h\n> index 6063c4b846..638a142577 100644\n> --- a/repository.h\n> +++ b/repository.h\n> @@ -3,6 +3,7 @@\n>   \n>   #include \"strmap.h\"\n>   #include \"repo-settings.h\"\n> +#include \"environment.h\"\n>   \n>   struct config_set;\n>   struct git_hash_algo;\n> @@ -148,6 +149,9 @@ struct repository {\n>   \t/* Repository's compatibility hash algorithm. */\n>   \tconst struct git_hash_algo *compat_hash_algo;\n>   \n> +\t/* Repository's config values parsed by git_default_config() */\n> +\tstruct repo_config_values config_values;\n> +\n>   \t/* Repository's reference storage format, as serialized on disk. */\n>   \tenum ref_storage_format ref_storage_format;\n>   \n\n"},{"id":"534451","messageId":"18b5d932-8a5a-4f33-a803-ef6f0c7d2750@gmail.com","threadId":"64782","inReplyTo":"fd95169de42891452b430814476d78c706e4a7e2.1768681947.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-01-22T14:41:02Z","receivedAt":"2026-01-22T14:41:05Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Olamide\n\nOn 17/01/2026 20:59, Olamide Caleb Bello wrote:\n> The config value `core.sparseCheckout` is parsed in\n> `git_default_core_config()` and stored globally in\n> `core_appy_sparse_checkout`. This could cause unintended behaviours\n> when different Git repositories running in the same process access this\n> variable.\n> \n> Move the parsed value into `struct repo_config_values` to retains current\n> behaviours while achieving the repository scoped access.\n\nIt doesn't achieve repository scoped access though because we only ever \npopulate the values in \"the_repository\", all other instances of \"struct \nrepository\" are initialized by config_values_init() but not the config \nsettings.\n\n> diff --git a/builtin/backfill.c b/builtin/backfill.c\n> index e80fc1b694..5fc8c51ed1 100644\n> --- a/builtin/backfill.c\n> +++ b/builtin/backfill.c\n> @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n>   \trepo_config(repo, git_default_config, NULL);\n>   \n>   \tif (ctx.sparse < 0)\n> -\t\tctx.sparse = core_apply_sparse_checkout;\n> +\t\tctx.sparse = repo->config_values.sparse_checkout;\n\nUsing \"repo\" rather than \"the_repository\" here is dangerous because only \n\"the_repository\" contains the parsed config. This applies throughout \nthis patch.\n\n>   \n>   \tresult = do_backfill(&ctx);\n>   \tbackfill_context_clear(&ctx);\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index b19b302b06..b6b19e83d1 100644\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n>   \t * We must apply the setting in the current process\n>   \t * for the later checkout to use the sparse-checkout file.\n>   \t */\n> -\tcore_apply_sparse_checkout = 1;\n> +\tthe_repository->config_values.sparse_checkout = 1;\n>   \n>   \tcmd.git_cmd = 1;\n>   \tif (run_command(&cmd)) {\n> diff --git a/builtin/grep.c b/builtin/grep.c\n> index 53cccf2d25..525edb5e9c 100644\n> --- a/builtin/grep.c\n> +++ b/builtin/grep.c\n> @@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,\n>   \t *\t\"forget\" the sparse-index feature switch. As a result, the index\n>   \t *\tof these submodules are expanded unexpectedly.\n>   \t *\n> -\t * 2. \"core_apply_sparse_checkout\"\n> +\t * 2. \"sparse_checkout\"\n\nThat should be something like config_values.sparse_checkout to make it \nclear that \"sparse_checkout\" is the name of a member of a struct, not \nthe name of a variable.\n\n> diff --git a/environment.h b/environment.h\n> index aea73ff25b..3b5ff7094a 100644\n> --- a/environment.h\n> +++ b/environment.h\n> @@ -88,6 +88,7 @@ struct strvec;\n>   struct repo_config_values {\n>   \t/* core config values */\n>   \tchar *attributes_file_path;\n> +\tint sparse_checkout;\n\nThere are several other sparse checkout variables like \ncore_sparse_checkout_cone that we'll need to convert in the future so \n\"apply_sparse_checkout\" or \"sparse_checkout_apply\" would be better names.\n\nThanks\n\nPhillip\n\n\n>   };\n>   \n>   /*\n> @@ -169,7 +170,6 @@ extern int precomposed_unicode;\n>   extern int protect_hfs;\n>   extern int protect_ntfs;\n>   \n> -extern int core_apply_sparse_checkout;\n>   extern int core_sparse_checkout_cone;\n>   extern int sparse_expect_files_outside_of_patterns;\n>   \n> diff --git a/sparse-index.c b/sparse-index.c\n> index 76f90da5f5..6dd8dd679d 100644\n> --- a/sparse-index.c\n> +++ b/sparse-index.c\n> @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)\n>   \n>   int is_sparse_index_allowed(struct index_state *istate, int flags)\n>   {\n> -\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n> +\tstruct repo_config_values *cfg = &istate->repo->config_values;\n> +\tif (!cfg->sparse_checkout || !core_sparse_checkout_cone)\n>   \t\treturn 0;\n>   \n>   \tif (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n> @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n>   \n>   void clear_skip_worktree_from_present_files(struct index_state *istate)\n>   {\n> -\tif (!core_apply_sparse_checkout ||\n> +\tstruct repo_config_values *cfg = &istate->repo->config_values;\n> +\tif (!cfg->sparse_checkout ||\n>   \t    sparse_expect_files_outside_of_patterns)\n>   \t\treturn;\n>   \n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index f38c761ab9..2bdfa1334c 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n>   \tif (o->prefix)\n>   \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n>   \n> -\tif (!core_apply_sparse_checkout || !o->update)\n> +\tif (!repo->config_values.sparse_checkout || !o->update)\n>   \t\to->skip_sparse_checkout = 1;\n>   \tif (!o->skip_sparse_checkout) {\n>   \t\tmemset(&pl, 0, sizeof(pl));\n> diff --git a/wt-status.c b/wt-status.c\n> index e12adb26b9..a2e388606f 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n>   \tint skip_worktree = 0;\n>   \tint i;\n>   \n> -\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n> +\tif (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {\n>   \t\t/*\n>   \t\t * Don't compute percentage of checked out files if we\n>   \t\t * aren't in a sparse checkout or would get division by 0.\n\n"},{"id":"534452","messageId":"56ac85bc-8e12-4cb9-ba22-db021c473110@gmail.com","threadId":"64782","inReplyTo":"9a411db9f8f9df37c7a5f100d97c8d5ff5f6ba97.1768681947.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v3 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-01-22T14:41:22Z","receivedAt":"2026-01-22T14:41:26Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Olamide\n\nOn 17/01/2026 20:59, Olamide Caleb Bello wrote:\n> The config value `brach.autoSetupMerge` is parsed in\n> `git_default_branch_config()` and stored in the global variable\n> `git_branch_track`. This global variable can cause unexpected behaviours\n> when multiple Git repos run in the the same process.\n> \n> Move this value into `struct repo_config_values` to retain current\n> behaviours while achieving repository scoped access.\n\nSame comment as the previous patch about repository scoped access.\n\n> diff --git a/environment.h b/environment.h\n> index 3b5ff7094a..bfcdffe836 100644\n> --- a/environment.h\n> +++ b/environment.h\n> @@ -2,6 +2,7 @@\n>   #define ENVIRONMENT_H\n>   \n>   #include \"repo-settings.h\"\n> +#include \"branch.h\"\n>   \n>   /* Double-check local_repo_env below if you add to this list. */\n>   #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n> @@ -89,6 +90,9 @@ struct repo_config_values {\n>   \t/* core config values */\n>   \tchar *attributes_file_path;\n>   \tint sparse_checkout;\n> +\n> +\t/* branch config values */\n> +\tenum branch_track git_branch_track;\n\nWe could probably drop the \"git_\" prefix now that it is not a global \nvariable.\n\nThanks\n\nPhillip\n\n"},{"id":"534456","messageId":"CAD=f0L9nYtuiUEDVC9UcKSCThqQspR8TDzoegAte3jBepxdE_A@mail.gmail.com","threadId":"64782","inReplyTo":"871pjhkfq7.fsf@iotcl.com","subject":"Re: [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-22T15:08:18Z","receivedAt":"2026-01-22T15:08:18Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Thu, 22 Jan 2026 at 13:13, Toon Claes <toon@iotcl.com> wrote:\n>\n> Olamide Caleb Bello <belkid98@gmail.com> writes:\n>\n> > The config value is parsed in git_default_core_config(), loaded\n>\n> I assume you mean 'core.attributesFile' because it's in the title. But\n> personnally I don't mind seeing the name repeated in the body to make it\n> more clear.\nHello Toon,\n\nOkay thank you for your review.\nI will take note of this.\n\n>\n> > eagerly and stored in the global variable `git_attributes_file`.\n> > Storing this value in a global variable can lead to unexpected\n> > behaviours when more than one Git repository run in the same Git process.\n> >\n> > Create a new struct `repo_config_values` to hold this value and\n> > other repository dependent values parsed by `git_default_config()` and\n> > can be accessed per repository via `git_default_config()`.\n>\n> I'd suggest to split off the part after the second 'and' into a new\n> sentence.\n\nOkay\n\n>\n> > This will ensure the current behaviour remains the same while also\n> > enabling the libification of Git.\n>\n> How is this true? Was that value already accessible through\n> `git_default_config()`?\n>\n> > It is important to note that `git_default_config()` is a wrapper to other\n> > `git_default_*_config()` such as `git_default_core_config()`.\n>\n> I'd suggest to insert 'functions' before 'such'.\n\nAlright, noted.\n\n>\n> > Therefore to access and modify this global variable,\n> > the change has to be made in the function which parses and\n> > stores the value in the global variable.\n>\n> This doesn't clarify much for me. Do you mean 'git_attr_global_file()'\n> and 'git_default_core_config()'?\n\nOkay I meant git_default_core_config().\nI will modify it.\n\n>\n> >\n> > Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> > Mentored-by: Christian Couder <christian.couder@gmail.com>\n> > Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n> > Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> > ---\n> >  attr.c        |  7 ++++---\n> >  environment.c | 12 +++++++++---\n> >  environment.h |  9 ++++++++-\n> >  repository.c  |  1 +\n> >  repository.h  |  4 ++++\n> >  5 files changed, 26 insertions(+), 7 deletions(-)\n> >\n> > diff --git a/attr.c b/attr.c\n> > index 4999b7e09d..fbb9eaffaf 100644\n> > --- a/attr.c\n> > +++ b/attr.c\n> > @@ -881,10 +881,11 @@ const char *git_attr_system_file(void)\n> >\n> >  const char *git_attr_global_file(void)\n> >  {\n> > -     if (!git_attributes_file)\n> > -             git_attributes_file = xdg_config_home(\"attributes\");\n> > +     struct repo_config_values *cfg = &the_repository->config_values;\n>\n> Is 'cfg' guaranteed to be != NULL?\n>\n> > +     if (!cfg->attributes_file_path)\n> > +             cfg->attributes_file_path = xdg_config_home(\"attributes\");\n> >\n> > -     return git_attributes_file;\n> > +     return cfg->attributes_file_path;\n> >  }\n> >\n> >  int git_attr_system_is_enabled(void)\n> > diff --git a/environment.c b/environment.c\n> > index a770b5921d..283db0a1a0 100644\n> > --- a/environment.c\n> > +++ b/environment.c\n> > @@ -53,7 +53,6 @@ char *git_commit_encoding;\n> >  char *git_log_output_encoding;\n> >  char *apply_default_whitespace;\n> >  char *apply_default_ignorewhitespace;\n> > -char *git_attributes_file;\n> >  int zlib_compression_level = Z_BEST_SPEED;\n> >  int pack_compression_level = Z_DEFAULT_COMPRESSION;\n> >  int fsync_object_files = -1;\n> > @@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n> >  static int git_default_core_config(const char *var, const char *value,\n> >                                  const struct config_context *ctx, void *cb)\n> >  {\n> > +     struct repo_config_values *cfg = &the_repository->config_values;\n> > +\n> >       /* This needs a better name */\n> >       if (!strcmp(var, \"core.filemode\")) {\n> >               trust_executable_bit = git_config_bool(var, value);\n> > @@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,\n> >       }\n> >\n> >       if (!strcmp(var, \"core.attributesfile\")) {\n> > -             FREE_AND_NULL(git_attributes_file);\n> > -             return git_config_pathname(&git_attributes_file, var, value);\n> > +             FREE_AND_NULL(cfg->attributes_file_path);\n> > +             return git_config_pathname(&cfg->attributes_file_path, var, value);\n> >       }\n> >\n> >       if (!strcmp(var, \"core.bare\")) {\n> > @@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,\n> >       /* Add other config variables here and to Documentation/config.adoc. */\n> >       return 0;\n> >  }\n> > +\n> > +void repo_config_values_init(struct repo_config_values *cfg)\n> > +{\n> > +     cfg->attributes_file_path = NULL;\n> > +}\n>\n> I assume the reason for adding this function becomes clear in a later\n> commit?\n\nYes.\n\n>\n> > diff --git a/environment.h b/environment.h\n> > index 51898c99cd..aea73ff25b 100644\n> > --- a/environment.h\n> > +++ b/environment.h\n> > @@ -84,6 +84,12 @@ extern const char * const local_repo_env[];\n> >\n> >  struct strvec;\n> >\n> > +/* Config values parsed by git_default_config() */\n>\n> Mentioning here they get filled from git_default_config() doesn't feel\n> really correct? Although I'm sure what comment would fit better, maybe\n> just drop the comment above the struct. I see you have a similar comment\n> in 'struct repository', where it *does* make sense.\n\nOkay thank you\n\n>\n> > +struct repo_config_values {\n> > +     /* core config values */\n>\n> I prefer emphasizing it's the \"section 'core'\" or something like that.\n\nNoted\n\n>\n> > +     char *attributes_file_path;\n>\n> Would it be overkill to append: /* `core.attributesFile` */? This can\n> help when grepping through the codebase to find where some settings are\n> being parsed into. What do you think?\n\nAlright\n\n>\n> > +};\n> > +\n> >  /*\n> >   * Wrapper of getenv() that returns a strdup value. This value is kept\n> >   * in argv to be freed later.\n> > @@ -107,6 +113,8 @@ const char *strip_namespace(const char *namespaced_ref);\n> >  int git_default_config(const char *, const char *,\n> >                      const struct config_context *, void *);\n> >\n> > +void repo_config_values_init(struct repo_config_values *cfg);\n> > +\n> >  /*\n> >   * TODO: All the below state either explicitly or implicitly relies on\n> >   * `the_repository`. We should eventually get rid of these and make the\n> > @@ -152,7 +160,6 @@ extern int assume_unchanged;\n> >  extern int warn_on_object_refname_ambiguity;\n> >  extern char *apply_default_whitespace;\n> >  extern char *apply_default_ignorewhitespace;\n> > -extern char *git_attributes_file;\n> >  extern int zlib_compression_level;\n> >  extern int pack_compression_level;\n> >  extern unsigned long pack_size_limit_cfg;\n> > diff --git a/repository.c b/repository.c\n> > index c7e75215ac..d308cd78bf 100644\n> > --- a/repository.c\n> > +++ b/repository.c\n> > @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)\n> >       ALLOC_ARRAY(repo->index, 1);\n> >       index_state_init(repo->index, repo);\n> >       repo->check_deprecated_config = true;\n> > +     repo_config_values_init(&repo->config_values);\n> >\n> >       /*\n> >        * When a command runs inside a repository, it learns what\n> > diff --git a/repository.h b/repository.h\n> > index 6063c4b846..638a142577 100644\n> > --- a/repository.h\n> > +++ b/repository.h\n> > @@ -3,6 +3,7 @@\n> >\n> >  #include \"strmap.h\"\n> >  #include \"repo-settings.h\"\n> > +#include \"environment.h\"\n> >\n> >  struct config_set;\n> >  struct git_hash_algo;\n> > @@ -148,6 +149,9 @@ struct repository {\n> >       /* Repository's compatibility hash algorithm. */\n> >       const struct git_hash_algo *compat_hash_algo;\n> >\n> > +     /* Repository's config values parsed by git_default_config() */\n> > +     struct repo_config_values config_values;\n> > +\n> >       /* Repository's reference storage format, as serialized on disk. */\n> >       enum ref_storage_format ref_storage_format;\n> >\n> > --\n> > 2.34.1\n> >\n> >\n>\n> --\n> Cheers,\n> Toon\n"},{"id":"534458","messageId":"CAD=f0L9BAjFeURkngMM6kzK+6_8TmLHy9zvFKXcz3ZYPvDG=_g@mail.gmail.com","threadId":"64782","inReplyTo":"60dfb907-c2b8-4fd4-b975-742f7ec18721@gmail.com","subject":"Re: [Outreachy PATCH v3 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-22T15:11:26Z","receivedAt":"2026-01-22T15:11:26Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Thu, 22 Jan 2026 at 15:40, Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> Hi Olamide\n\nHello Phillip\n\n>\n> On 17/01/2026 20:59, Olamide Caleb Bello wrote:\n> > The config value is parsed in git_default_core_config(), loaded eagerly\n> > and stored in the global variable `git_attributes_file`.\n> > Storing this value in a global variable can lead to unexpected\n> > behaviours when more than one Git repository run in the same Git process.\n>\n> It would maybe be helpful to explain what the unexpected behavior is and\n> how it is caused.\n\nOkay thank you.\n\n>\n> > diff --git a/environment.h b/environment.h\n> > index 51898c99cd..aea73ff25b 100644\n> > --- a/environment.h\n> > +++ b/environment.h\n> > @@ -84,6 +84,12 @@ extern const char * const local_repo_env[];\n> >\n> >   struct strvec;\n> >\n> > +/* Config values parsed by git_default_config() */\n> > +struct repo_config_values {\n> > +     /* core config values */\n> > +     char *attributes_file_path;\n>\n> The variable we're converting is called \"attributes_file\", do we really\n> need to add a \"_path\" suffix?\n\nOkay I will remove it\n\n>\n> Apart from that everything here looks good\n>\n> Thanks\n>\n> Phillip\n\nThank you\n"},{"id":"534459","messageId":"CAD=f0L97wxzgEdo3nDmFVKTdqxMCVRL2aV6kGgEM_Jh=J9cbHg@mail.gmail.com","threadId":"64782","inReplyTo":"87zf65j152.fsf@iotcl.com","subject":"Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-22T15:17:44Z","receivedAt":"2026-01-22T15:17:44Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Thu, 22 Jan 2026 at 13:13, Toon Claes <toon@iotcl.com> wrote:\n>\n> Olamide Caleb Bello <belkid98@gmail.com> writes:\n>\n> It seems you have 'environment:' twice in the title?\n\nArh! Thank you for spotting it\n\n>\n> > The config value `core.sparseCheckout` is parsed in\n> > `git_default_core_config()` and stored globally in\n> > `core_appy_sparse_checkout`. This could cause unintended behaviours\n>\n> s/core_appy_sparse_checkout/core_apply_sparse_checkout/ ?\n\nThank you.\n\n>\n> > when different Git repositories running in the same process access this\n> > variable.\n> >\n> > Move the parsed value into `struct repo_config_values` to retains current\n> > behaviours while achieving the repository scoped access.\n> >\n> > Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> > Mentored-by: Christian Couder <christian.couder@gmail.com>\n> > Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n> > Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> > ---\n> >  builtin/backfill.c        |  2 +-\n> >  builtin/clone.c           |  2 +-\n> >  builtin/grep.c            |  2 +-\n> >  builtin/mv.c              |  2 +-\n> >  builtin/sparse-checkout.c | 22 +++++++++++-----------\n> >  builtin/worktree.c        |  2 +-\n> >  dir.c                     |  2 +-\n> >  environment.c             |  4 ++--\n> >  environment.h             |  2 +-\n> >  sparse-index.c            |  6 ++++--\n> >  unpack-trees.c            |  2 +-\n> >  wt-status.c               |  2 +-\n> >  12 files changed, 26 insertions(+), 24 deletions(-)\n> >\n> > diff --git a/builtin/backfill.c b/builtin/backfill.c\n> > index e80fc1b694..5fc8c51ed1 100644\n> > --- a/builtin/backfill.c\n> > +++ b/builtin/backfill.c\n> > @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n> >       repo_config(repo, git_default_config, NULL);\n> >\n> >       if (ctx.sparse < 0)\n> > -             ctx.sparse = core_apply_sparse_checkout;\n> > +             ctx.sparse = repo->config_values.sparse_checkout;\n> >\n> >       result = do_backfill(&ctx);\n> >       backfill_context_clear(&ctx);\n> > diff --git a/builtin/clone.c b/builtin/clone.c\n> > index b19b302b06..b6b19e83d1 100644\n> > --- a/builtin/clone.c\n> > +++ b/builtin/clone.c\n> > @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n> >        * We must apply the setting in the current process\n> >        * for the later checkout to use the sparse-checkout file.\n> >        */\n> > -     core_apply_sparse_checkout = 1;\n> > +     the_repository->config_values.sparse_checkout = 1;\n> >\n> >       cmd.git_cmd = 1;\n> >       if (run_command(&cmd)) {\n> > diff --git a/builtin/grep.c b/builtin/grep.c\n> > index 53cccf2d25..525edb5e9c 100644\n> > --- a/builtin/grep.c\n> > +++ b/builtin/grep.c\n> > @@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,\n> >        *      \"forget\" the sparse-index feature switch. As a result, the index\n> >        *      of these submodules are expanded unexpectedly.\n> >        *\n> > -      * 2. \"core_apply_sparse_checkout\"\n> > +      * 2. \"sparse_checkout\"\n> >        *      When running `grep` in the superproject, this setting is\n> >        *      populated using the superproject's configs. However, once\n> >        *      initialized, this config is globally accessible and is read by\n> > diff --git a/builtin/mv.c b/builtin/mv.c\n> > index d43925097b..511620747b 100644\n> > --- a/builtin/mv.c\n> > +++ b/builtin/mv.c\n> > @@ -572,7 +572,7 @@ int cmd_mv(int argc,\n> >               rename_index_entry_at(the_repository->index, pos, dst);\n> >\n> >               if (ignore_sparse &&\n> > -                 core_apply_sparse_checkout &&\n> > +                 the_repository->config_values.sparse_checkout &&\n> >                   core_sparse_checkout_cone) {\n> >                       /*\n> >                        * NEEDSWORK: we are *not* paying attention to\n> > diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\n> > index 15d51e60a8..1c2c39b968 100644\n> > --- a/builtin/sparse-checkout.c\n> > +++ b/builtin/sparse-checkout.c\n> > @@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,\n> >       int res;\n> >\n> >       setup_work_tree();\n> > -     if (!core_apply_sparse_checkout)\n> > +     if (!the_repository->config_values.sparse_checkout)\n> >               die(_(\"this worktree is not sparse\"));\n> >\n> >       argc = parse_options(argc, argv, prefix,\n> > @@ -400,11 +400,11 @@ static int set_config(struct repository *repo,\n> >\n> >  static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n> >       /* If not specified, use previous definition of cone mode */\n> > -     if (*cone_mode == -1 && core_apply_sparse_checkout)\n> > +     if (*cone_mode == -1 && the_repository->config_values.sparse_checkout)\n> >               *cone_mode = core_sparse_checkout_cone;\n> >\n> >       /* Set cone/non-cone mode appropriately */\n> > -     core_apply_sparse_checkout = 1;\n> > +     the_repository->config_values.sparse_checkout = 1;\n> >       if (*cone_mode == 1 || *cone_mode == -1) {\n> >               core_sparse_checkout_cone = 1;\n> >               return MODE_CONE_PATTERNS;\n> > @@ -418,7 +418,7 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind\n> >       int mode, record_mode;\n> >\n> >       /* Determine if we need to record the mode; ensure sparse checkout on */\n> > -     record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n> > +     record_mode = (*cone_mode != -1) || !repo->config_values.sparse_checkout;\n> >\n> >       mode = update_cone_mode(cone_mode);\n> >       if (record_mode && set_config(repo, mode))\n> > @@ -699,9 +699,9 @@ static int modify_pattern_list(struct repository *repo,\n> >               break;\n> >       }\n> >\n> > -     if (!core_apply_sparse_checkout) {\n> > +     if (!repo->config_values.sparse_checkout) {\n> >               set_config(repo, MODE_ALL_PATTERNS);\n> > -             core_apply_sparse_checkout = 1;\n> > +             repo->config_values.sparse_checkout = 1;\n> >               changed_config = 1;\n> >       }\n> >\n> > @@ -798,7 +798,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,\n> >       int ret;\n> >\n> >       setup_work_tree();\n> > -     if (!core_apply_sparse_checkout)\n> > +     if (!repo->config_values.sparse_checkout)\n> >               die(_(\"no sparse-checkout to add to\"));\n> >\n> >       repo_read_index(repo);\n> > @@ -907,7 +907,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,\n> >       };\n> >\n> >       setup_work_tree();\n> > -     if (!core_apply_sparse_checkout)\n> > +     if (!repo->config_values.sparse_checkout)\n> >               die(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n> >\n> >       reapply_opts.cone_mode = -1;\n> > @@ -969,7 +969,7 @@ static int sparse_checkout_clean(int argc, const char **argv,\n> >       };\n> >\n> >       setup_work_tree();\n> > -     if (!core_apply_sparse_checkout)\n> > +     if (!repo->config_values.sparse_checkout)\n> >               die(_(\"must be in a sparse-checkout to clean directories\"));\n> >       if (!core_sparse_checkout_cone)\n> >               die(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n> > @@ -1035,7 +1035,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n> >       struct pattern_list pl;\n> >\n> >       /*\n> > -      * We do not exit early if !core_apply_sparse_checkout; due to the\n> > +      * We do not exit early if !repo->config_values.sparse_checkout; due to the\n> >        * ability for users to manually muck things up between\n> >        *   direct editing of .git/info/sparse-checkout\n> >        *   running read-tree -m u HEAD or update-index --skip-worktree\n> > @@ -1061,7 +1061,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n> >       hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);\n> >       hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n> >       pl.use_cone_patterns = 0;\n> > -     core_apply_sparse_checkout = 1;\n> > +     repo->config_values.sparse_checkout = 1;\n> >\n> >       add_pattern(\"/*\", empty_base, 0, &pl, 0);\n> >\n> > diff --git a/builtin/worktree.c b/builtin/worktree.c\n> > index fbdaf2eb2e..e401b8253e 100644\n> > --- a/builtin/worktree.c\n> > +++ b/builtin/worktree.c\n> > @@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,\n> >        * If the current worktree has sparse-checkout enabled, then copy\n> >        * the sparse-checkout patterns from the current worktree.\n> >        */\n> > -     if (core_apply_sparse_checkout)\n> > +     if (wt->repo->config_values.sparse_checkout)\n> >               copy_sparse_checkout(sb_repo.buf);\n> >\n> >       /*\n> > diff --git a/dir.c b/dir.c\n> > index b00821f294..56b412a6d2 100644\n> > --- a/dir.c\n> > +++ b/dir.c\n> > @@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(\n> >\n> >  int init_sparse_checkout_patterns(struct index_state *istate)\n> >  {\n> > -     if (!core_apply_sparse_checkout)\n> > +     if (!istate->repo->config_values.sparse_checkout)\n> >               return 1;\n> >       if (istate->sparse_checkout_patterns)\n> >               return 0;\n> > diff --git a/environment.c b/environment.c\n> > index 283db0a1a0..6633542750 100644\n> > --- a/environment.c\n> > +++ b/environment.c\n> > @@ -74,7 +74,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n> >  #endif\n> >  enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n> >  int grafts_keep_true_parents;\n> > -int core_apply_sparse_checkout;\n> >  int core_sparse_checkout_cone;\n> >  int sparse_expect_files_outside_of_patterns;\n> >  int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n> > @@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,\n> >       }\n> >\n> >       if (!strcmp(var, \"core.sparsecheckout\")) {\n> > -             core_apply_sparse_checkout = git_config_bool(var, value);\n> > +             cfg->sparse_checkout = git_config_bool(var, value);\n> >               return 0;\n> >       }\n> >\n> > @@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,\n> >  void repo_config_values_init(struct repo_config_values *cfg)\n> >  {\n> >       cfg->attributes_file_path = NULL;\n> > +     cfg->sparse_checkout = 0;\n> >  }\n> > diff --git a/environment.h b/environment.h\n> > index aea73ff25b..3b5ff7094a 100644\n> > --- a/environment.h\n> > +++ b/environment.h\n> > @@ -88,6 +88,7 @@ struct strvec;\n> >  struct repo_config_values {\n> >       /* core config values */\n> >       char *attributes_file_path;\n> > +     int sparse_checkout;\n> >  };\n> >\n> >  /*\n> > @@ -169,7 +170,6 @@ extern int precomposed_unicode;\n> >  extern int protect_hfs;\n> >  extern int protect_ntfs;\n> >\n> > -extern int core_apply_sparse_checkout;\n>\n> In the field you're adding to 'struct repo_config_values' you have\n> dropped the 'core_' prefix, what the reason for that? If I understand it\n> correctly also settings from other sections might end up in that struct,\n> so wouldn't it be better to keep the prefix?\n\nOkay, noted\n\n>\n> >  extern int core_sparse_checkout_cone;\n> >  extern int sparse_expect_files_outside_of_patterns;\n> >\n> > diff --git a/sparse-index.c b/sparse-index.c\n> > index 76f90da5f5..6dd8dd679d 100644\n> > --- a/sparse-index.c\n> > +++ b/sparse-index.c\n> > @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)\n> >\n> >  int is_sparse_index_allowed(struct index_state *istate, int flags)\n> >  {\n> > -     if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n> > +     struct repo_config_values *cfg = &istate->repo->config_values;\n> > +     if (!cfg->sparse_checkout || !core_sparse_checkout_cone)\n> >               return 0;\n> >\n> >       if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n> > @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n> >\n> >  void clear_skip_worktree_from_present_files(struct index_state *istate)\n> >  {\n> > -     if (!core_apply_sparse_checkout ||\n> > +     struct repo_config_values *cfg = &istate->repo->config_values;\n> > +     if (!cfg->sparse_checkout ||\n> >           sparse_expect_files_outside_of_patterns)\n> >               return;\n> >\n> > diff --git a/unpack-trees.c b/unpack-trees.c\n> > index f38c761ab9..2bdfa1334c 100644\n> > --- a/unpack-trees.c\n> > +++ b/unpack-trees.c\n> > @@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n> >       if (o->prefix)\n> >               update_sparsity_for_prefix(o->prefix, o->src_index);\n> >\n> > -     if (!core_apply_sparse_checkout || !o->update)\n> > +     if (!repo->config_values.sparse_checkout || !o->update)\n> >               o->skip_sparse_checkout = 1;\n> >       if (!o->skip_sparse_checkout) {\n> >               memset(&pl, 0, sizeof(pl));\n> > diff --git a/wt-status.c b/wt-status.c\n> > index e12adb26b9..a2e388606f 100644\n> > --- a/wt-status.c\n> > +++ b/wt-status.c\n> > @@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n> >       int skip_worktree = 0;\n> >       int i;\n> >\n> > -     if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n> > +     if (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {\n> >               /*\n> >                * Don't compute percentage of checked out files if we\n> >                * aren't in a sparse checkout or would get division by 0.\n> > --\n> > 2.34.1\n> >\n> >\n>\n> --\n> Cheers,\n> Toon\n"},{"id":"534461","messageId":"CAD=f0L9JhJq95kV7oUsaN5FqmUAH2qeSTLPLYXKAHUtNiHK_WA@mail.gmail.com","threadId":"64782","inReplyTo":"18b5d932-8a5a-4f33-a803-ef6f0c7d2750@gmail.com","subject":"Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-22T15:29:01Z","receivedAt":"2026-01-22T15:29:01Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Thu, 22 Jan 2026 at 15:41, Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> Hi Olamide\n>\n> On 17/01/2026 20:59, Olamide Caleb Bello wrote:\n> > The config value `core.sparseCheckout` is parsed in\n> > `git_default_core_config()` and stored globally in\n> > `core_appy_sparse_checkout`. This could cause unintended behaviours\n> > when different Git repositories running in the same process access this\n> > variable.\n> >\n> > Move the parsed value into `struct repo_config_values` to retains current\n> > behaviours while achieving the repository scoped access.\n>\n> It doesn't achieve repository scoped access though because we only ever\n> populate the values in \"the_repository\", all other instances of \"struct\n> repository\" are initialized by config_values_init() but not the config\n> settings.\n\nOkay I understand.\nThank you for clarifying.\n\n>\n> > diff --git a/builtin/backfill.c b/builtin/backfill.c\n> > index e80fc1b694..5fc8c51ed1 100644\n> > --- a/builtin/backfill.c\n> > +++ b/builtin/backfill.c\n> > @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n> >       repo_config(repo, git_default_config, NULL);\n> >\n> >       if (ctx.sparse < 0)\n> > -             ctx.sparse = core_apply_sparse_checkout;\n> > +             ctx.sparse = repo->config_values.sparse_checkout;\n>\n> Using \"repo\" rather than \"the_repository\" here is dangerous because only\n> \"the_repository\" contains the parsed config. This applies throughout\n> this patch.\n\nOkay noted...\nSorry but I have a question.\nI observed that the address of \"repo\" is passed to builtin/backfill.c,\nis gotten from git.c:handle_builtin\nwhich passed run_builtin \"the_repository\" as a parameter.\n\nWon't the address of \"repo\" and \"the_repository be the same\"?\n\n>\n> >\n> >       result = do_backfill(&ctx);\n> >       backfill_context_clear(&ctx);\n> > diff --git a/builtin/clone.c b/builtin/clone.c\n> > index b19b302b06..b6b19e83d1 100644\n> > --- a/builtin/clone.c\n> > +++ b/builtin/clone.c\n> > @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n> >        * We must apply the setting in the current process\n> >        * for the later checkout to use the sparse-checkout file.\n> >        */\n> > -     core_apply_sparse_checkout = 1;\n> > +     the_repository->config_values.sparse_checkout = 1;\n> >\n> >       cmd.git_cmd = 1;\n> >       if (run_command(&cmd)) {\n> > diff --git a/builtin/grep.c b/builtin/grep.c\n> > index 53cccf2d25..525edb5e9c 100644\n> > --- a/builtin/grep.c\n> > +++ b/builtin/grep.c\n> > @@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,\n> >        *      \"forget\" the sparse-index feature switch. As a result, the index\n> >        *      of these submodules are expanded unexpectedly.\n> >        *\n> > -      * 2. \"core_apply_sparse_checkout\"\n> > +      * 2. \"sparse_checkout\"\n>\n> That should be something like config_values.sparse_checkout to make it\n> clear that \"sparse_checkout\" is the name of a member of a struct, not\n> the name of a variable.\n\nOkay noted\n\n>\n> > diff --git a/environment.h b/environment.h\n> > index aea73ff25b..3b5ff7094a 100644\n> > --- a/environment.h\n> > +++ b/environment.h\n> > @@ -88,6 +88,7 @@ struct strvec;\n> >   struct repo_config_values {\n> >       /* core config values */\n> >       char *attributes_file_path;\n> > +     int sparse_checkout;\n>\n> There are several other sparse checkout variables like\n> core_sparse_checkout_cone that we'll need to convert in the future so\n> \"apply_sparse_checkout\" or \"sparse_checkout_apply\" would be better names.\n\nOkay noted.\n\n>\n> Thanks\n>\n> Phillip\n>\n>\n> >   };\n> >\n> >   /*\n> > @@ -169,7 +170,6 @@ extern int precomposed_unicode;\n> >   extern int protect_hfs;\n> >   extern int protect_ntfs;\n> >\n> > -extern int core_apply_sparse_checkout;\n> >   extern int core_sparse_checkout_cone;\n> >   extern int sparse_expect_files_outside_of_patterns;\n> >\n> > diff --git a/sparse-index.c b/sparse-index.c\n> > index 76f90da5f5..6dd8dd679d 100644\n> > --- a/sparse-index.c\n> > +++ b/sparse-index.c\n> > @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)\n> >\n> >   int is_sparse_index_allowed(struct index_state *istate, int flags)\n> >   {\n> > -     if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n> > +     struct repo_config_values *cfg = &istate->repo->config_values;\n> > +     if (!cfg->sparse_checkout || !core_sparse_checkout_cone)\n> >               return 0;\n> >\n> >       if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n> > @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n> >\n> >   void clear_skip_worktree_from_present_files(struct index_state *istate)\n> >   {\n> > -     if (!core_apply_sparse_checkout ||\n> > +     struct repo_config_values *cfg = &istate->repo->config_values;\n> > +     if (!cfg->sparse_checkout ||\n> >           sparse_expect_files_outside_of_patterns)\n> >               return;\n> >\n> > diff --git a/unpack-trees.c b/unpack-trees.c\n> > index f38c761ab9..2bdfa1334c 100644\n> > --- a/unpack-trees.c\n> > +++ b/unpack-trees.c\n> > @@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n> >       if (o->prefix)\n> >               update_sparsity_for_prefix(o->prefix, o->src_index);\n> >\n> > -     if (!core_apply_sparse_checkout || !o->update)\n> > +     if (!repo->config_values.sparse_checkout || !o->update)\n> >               o->skip_sparse_checkout = 1;\n> >       if (!o->skip_sparse_checkout) {\n> >               memset(&pl, 0, sizeof(pl));\n> > diff --git a/wt-status.c b/wt-status.c\n> > index e12adb26b9..a2e388606f 100644\n> > --- a/wt-status.c\n> > +++ b/wt-status.c\n> > @@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n> >       int skip_worktree = 0;\n> >       int i;\n> >\n> > -     if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n> > +     if (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {\n> >               /*\n> >                * Don't compute percentage of checked out files if we\n> >                * aren't in a sparse checkout or would get division by 0.\n>\n"},{"id":"534462","messageId":"CAD=f0L_VP3jN-_B9-iyEd6WPURHFBvOUJgy4HPUv0vSp29P+Qw@mail.gmail.com","threadId":"64782","inReplyTo":"56ac85bc-8e12-4cb9-ba22-db021c473110@gmail.com","subject":"Re: [Outreachy PATCH v3 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-22T15:29:43Z","receivedAt":"2026-01-22T15:29:47Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Thu, 22 Jan 2026 at 15:41, Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> Hi Olamide\n>\n> On 17/01/2026 20:59, Olamide Caleb Bello wrote:\n> > The config value `brach.autoSetupMerge` is parsed in\n> > `git_default_branch_config()` and stored in the global variable\n> > `git_branch_track`. This global variable can cause unexpected behaviours\n> > when multiple Git repos run in the the same process.\n> >\n> > Move this value into `struct repo_config_values` to retain current\n> > behaviours while achieving repository scoped access.\n>\n> Same comment as the previous patch about repository scoped access.\n\nOkay thank you\n\n>\n> > diff --git a/environment.h b/environment.h\n> > index 3b5ff7094a..bfcdffe836 100644\n> > --- a/environment.h\n> > +++ b/environment.h\n> > @@ -2,6 +2,7 @@\n> >   #define ENVIRONMENT_H\n> >\n> >   #include \"repo-settings.h\"\n> > +#include \"branch.h\"\n> >\n> >   /* Double-check local_repo_env below if you add to this list. */\n> >   #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n> > @@ -89,6 +90,9 @@ struct repo_config_values {\n> >       /* core config values */\n> >       char *attributes_file_path;\n> >       int sparse_checkout;\n> > +\n> > +     /* branch config values */\n> > +     enum branch_track git_branch_track;\n>\n> We could probably drop the \"git_\" prefix now that it is not a global\n> variable.\n\nAlright thank you for the review.\n>\n> Thanks\n>\n> Phillip\n>\n"},{"id":"534536","messageId":"4f19e70f-8ab5-4322-ac71-76bc925b324a@gmail.com","threadId":"64782","inReplyTo":"CAD=f0L9JhJq95kV7oUsaN5FqmUAH2qeSTLPLYXKAHUtNiHK_WA@mail.gmail.com","subject":"Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-01-23T10:43:19Z","receivedAt":"2026-01-23T10:43:24Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 22/01/2026 15:29, Bello Olamide wrote:\n> On Thu, 22 Jan 2026 at 15:41, Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>\n>>> diff --git a/builtin/backfill.c b/builtin/backfill.c\n>>> index e80fc1b694..5fc8c51ed1 100644\n>>> --- a/builtin/backfill.c\n>>> +++ b/builtin/backfill.c\n>>> @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n>>>        repo_config(repo, git_default_config, NULL);\n>>>\n>>>        if (ctx.sparse < 0)\n>>> -             ctx.sparse = core_apply_sparse_checkout;\n>>> +             ctx.sparse = repo->config_values.sparse_checkout;\n>>\n>> Using \"repo\" rather than \"the_repository\" here is dangerous because only\n>> \"the_repository\" contains the parsed config. This applies throughout\n>> this patch.\n> \n> Okay noted...\n> Sorry but I have a question.\n\nYou don't need to be sorry for having a question - it shows you have \nbeen thinking about the feedback you have received which is very good.\n\n> I observed that the address of \"repo\" is passed to builtin/backfill.c,\n> is gotten from git.c:handle_builtin\n> which passed run_builtin \"the_repository\" as a parameter.\n> \n> Won't the address of \"repo\" and \"the_repository be the same\"?\n\nYes, but I think it is safer to explicitly say \"the_repository\" so that\nif any of the functions you convert here are ever passed another \nrepository instance the code will keep working as expected. It also \ndocuments that the config value is only stored in \"the_repository\". Once \nwe make these config values per-repository then we can use the \nrepository instance passed to the function.\n\nThanks\n\nPhillip\n\n>>\n>>>\n>>>        result = do_backfill(&ctx);\n>>>        backfill_context_clear(&ctx);\n>>> diff --git a/builtin/clone.c b/builtin/clone.c\n>>> index b19b302b06..b6b19e83d1 100644\n>>> --- a/builtin/clone.c\n>>> +++ b/builtin/clone.c\n>>> @@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n>>>         * We must apply the setting in the current process\n>>>         * for the later checkout to use the sparse-checkout file.\n>>>         */\n>>> -     core_apply_sparse_checkout = 1;\n>>> +     the_repository->config_values.sparse_checkout = 1;\n>>>\n>>>        cmd.git_cmd = 1;\n>>>        if (run_command(&cmd)) {\n>>> diff --git a/builtin/grep.c b/builtin/grep.c\n>>> index 53cccf2d25..525edb5e9c 100644\n>>> --- a/builtin/grep.c\n>>> +++ b/builtin/grep.c\n>>> @@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,\n>>>         *      \"forget\" the sparse-index feature switch. As a result, the index\n>>>         *      of these submodules are expanded unexpectedly.\n>>>         *\n>>> -      * 2. \"core_apply_sparse_checkout\"\n>>> +      * 2. \"sparse_checkout\"\n>>\n>> That should be something like config_values.sparse_checkout to make it\n>> clear that \"sparse_checkout\" is the name of a member of a struct, not\n>> the name of a variable.\n> \n> Okay noted\n> \n>>\n>>> diff --git a/environment.h b/environment.h\n>>> index aea73ff25b..3b5ff7094a 100644\n>>> --- a/environment.h\n>>> +++ b/environment.h\n>>> @@ -88,6 +88,7 @@ struct strvec;\n>>>    struct repo_config_values {\n>>>        /* core config values */\n>>>        char *attributes_file_path;\n>>> +     int sparse_checkout;\n>>\n>> There are several other sparse checkout variables like\n>> core_sparse_checkout_cone that we'll need to convert in the future so\n>> \"apply_sparse_checkout\" or \"sparse_checkout_apply\" would be better names.\n> \n> Okay noted.\n> \n>>\n>> Thanks\n>>\n>> Phillip\n>>\n>>\n>>>    };\n>>>\n>>>    /*\n>>> @@ -169,7 +170,6 @@ extern int precomposed_unicode;\n>>>    extern int protect_hfs;\n>>>    extern int protect_ntfs;\n>>>\n>>> -extern int core_apply_sparse_checkout;\n>>>    extern int core_sparse_checkout_cone;\n>>>    extern int sparse_expect_files_outside_of_patterns;\n>>>\n>>> diff --git a/sparse-index.c b/sparse-index.c\n>>> index 76f90da5f5..6dd8dd679d 100644\n>>> --- a/sparse-index.c\n>>> +++ b/sparse-index.c\n>>> @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)\n>>>\n>>>    int is_sparse_index_allowed(struct index_state *istate, int flags)\n>>>    {\n>>> -     if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n>>> +     struct repo_config_values *cfg = &istate->repo->config_values;\n>>> +     if (!cfg->sparse_checkout || !core_sparse_checkout_cone)\n>>>                return 0;\n>>>\n>>>        if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n>>> @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n>>>\n>>>    void clear_skip_worktree_from_present_files(struct index_state *istate)\n>>>    {\n>>> -     if (!core_apply_sparse_checkout ||\n>>> +     struct repo_config_values *cfg = &istate->repo->config_values;\n>>> +     if (!cfg->sparse_checkout ||\n>>>            sparse_expect_files_outside_of_patterns)\n>>>                return;\n>>>\n>>> diff --git a/unpack-trees.c b/unpack-trees.c\n>>> index f38c761ab9..2bdfa1334c 100644\n>>> --- a/unpack-trees.c\n>>> +++ b/unpack-trees.c\n>>> @@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n>>>        if (o->prefix)\n>>>                update_sparsity_for_prefix(o->prefix, o->src_index);\n>>>\n>>> -     if (!core_apply_sparse_checkout || !o->update)\n>>> +     if (!repo->config_values.sparse_checkout || !o->update)\n>>>                o->skip_sparse_checkout = 1;\n>>>        if (!o->skip_sparse_checkout) {\n>>>                memset(&pl, 0, sizeof(pl));\n>>> diff --git a/wt-status.c b/wt-status.c\n>>> index e12adb26b9..a2e388606f 100644\n>>> --- a/wt-status.c\n>>> +++ b/wt-status.c\n>>> @@ -1764,7 +1764,7 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n>>>        int skip_worktree = 0;\n>>>        int i;\n>>>\n>>> -     if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n>>> +     if (!r->config_values.sparse_checkout || r->index->cache_nr == 0) {\n>>>                /*\n>>>                 * Don't compute percentage of checked out files if we\n>>>                 * aren't in a sparse checkout or would get division by 0.\n>>\n\n"},{"id":"534540","messageId":"CAD=f0L_YMvWdzG=qNN1xSCFsu7RdnM+7TRNUKbH1_SJirHUOsA@mail.gmail.com","threadId":"64782","inReplyTo":"4f19e70f-8ab5-4322-ac71-76bc925b324a@gmail.com","subject":"Re: [Outreachy PATCH v3 2/3] environment: environment: stop using core.sparseCheckout globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-23T13:24:09Z","receivedAt":"2026-01-23T13:24:10Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Fri, 23 Jan 2026 at 11:43, Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 22/01/2026 15:29, Bello Olamide wrote:\n> > On Thu, 22 Jan 2026 at 15:41, Phillip Wood <phillip.wood123@gmail.com> wrote:\n> >>\n> >>> diff --git a/builtin/backfill.c b/builtin/backfill.c\n> >>> index e80fc1b694..5fc8c51ed1 100644\n> >>> --- a/builtin/backfill.c\n> >>> +++ b/builtin/backfill.c\n> >>> @@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n> >>>        repo_config(repo, git_default_config, NULL);\n> >>>\n> >>>        if (ctx.sparse < 0)\n> >>> -             ctx.sparse = core_apply_sparse_checkout;\n> >>> +             ctx.sparse = repo->config_values.sparse_checkout;\n> >>\n> >> Using \"repo\" rather than \"the_repository\" here is dangerous because only\n> >> \"the_repository\" contains the parsed config. This applies throughout\n> >> this patch.\n> >\n> > Okay noted...\n> > Sorry but I have a question.\n>\n> You don't need to be sorry for having a question - it shows you have\n> been thinking about the feedback you have received which is very good.\n\nThank you\n\n>\n> > I observed that the address of \"repo\" is passed to builtin/backfill.c,\n> > is gotten from git.c:handle_builtin\n> > which passed run_builtin \"the_repository\" as a parameter.\n> >\n> > Won't the address of \"repo\" and \"the_repository be the same\"?\n>\n> Yes, but I think it is safer to explicitly say \"the_repository\" so that\n> if any of the functions you convert here are ever passed another\n> repository instance the code will keep working as expected. It also\n> documents that the config value is only stored in \"the_repository\". Once\n> we make these config values per-repository then we can use the\n> repository instance passed to the function.\n\nOkay thank you for clarifying.\n"},{"id":"534596","messageId":"cover.1769252118.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1768681947.git.belkid98@gmail.com","subject":"[Outreachy PATCH v4 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-24T11:55:03Z","receivedAt":"2026-01-24T11:55:28Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"Hi Git Community,\nOver the course of my ongoing internship, which focused on moving repo specific\nglobal variables in environment.h into local scope, I have attempted to move some\nvariables into the struct repo-settings.\nHowever there has been some design concerns as regards the use of\n`prepare_repo_settings()` with respect to when and where to call the\nfunction, and also the change in behaviours when the variable is lazily\nloaded as discussed in [1] and [2].\n\nAfter different deliberations, Phillip Wood proposed creating a new config\nstruct [3], adding it to the repository struct and passing the repo struct to\n`git_default_config()` to store parsed repo specific config values per repo.\nThis ensures the current behaviours will be retained.\n\nI have experimented with this approach for some values and I would\nappreciate feedbacks about this approach before we can move forward\nand use it for more variables related to `git_default_config()`.\n\nFor now, the parsed value is stored in `the_repository` in\n`git_default_*_config()` and further efforts to pass the repository\nparameter to `git_default_config()` as the callback parameter will\nbe looked into later on.\nThe link to the CI tests can be see in [4]\n\n1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/\n2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/\n3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/\n4. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2266020513\n\nChanges in v4:\n==============\n- Changed variable name in the struct to attributes_file\n- Modified commit messages in all patches to explain how the variable\n  being global can be overwritten when the process has multiple\n  repositories\n- Changed the variable name in patch 2 to apply_sparse_checkout\n- Changes the variable name in patch 3 to branch_track\n\n\nOlamide Caleb Bello (3):\n  environment: stop storing `core.attributesFile` globally\n  environment: stop using core.sparseCheckout globally\n  environment: move \"branch.autoSetupMerge\" into `struct\n    repo_config_values`\n\n attr.c                      |  7 ++++---\n branch.h                    |  2 --\n builtin/backfill.c          |  2 +-\n builtin/branch.c            |  2 +-\n builtin/checkout.c          |  2 +-\n builtin/clone.c             |  2 +-\n builtin/grep.c              |  2 +-\n builtin/mv.c                |  2 +-\n builtin/push.c              |  2 +-\n builtin/sparse-checkout.c   | 23 ++++++++++++-----------\n builtin/submodule--helper.c |  2 +-\n builtin/worktree.c          |  2 +-\n dir.c                       |  2 +-\n environment.c               | 28 ++++++++++++++++++----------\n environment.h               | 14 ++++++++++++--\n repository.c                |  1 +\n repository.h                |  4 ++++\n sparse-index.c              |  6 ++++--\n unpack-trees.c              |  2 +-\n wt-status.c                 |  3 ++-\n 20 files changed, 68 insertions(+), 42 deletions(-)\n\n Range diff versus v3:\n =====================\n1:  1aa41da833 ! 1:  d28850bcdb environment: stop storing `core.attributesFile` globally\n    @@ Metadata\n      ## Commit message ##\n         environment: stop storing `core.attributesFile` globally\n     \n    -    The config value is parsed in git_default_core_config(), loaded eagerly\n    -    and stored in the global variable `git_attributes_file`.\n    -    Storing this value in a global variable can lead to unexpected\n    -    behaviours when more than one Git repository run in the same Git process.\n    +    The `core.attributeFile` config value is parsed in\n    +    git_default_core_config(), loaded eagerly and stored in the global\n    +    variable `git_attributes_file`. Storing this value in a global variable\n    +    can lead to it being overwritten by another repository when more than one\n    +    Git repository run in the same Git process.\n     \n         Create a new struct `repo_config_values` to hold this value and\n    -    other repository dependent values parsed by `git_default_config()` and\n    -    can be accessed per repository via `git_default_config()`.\n    +    other repository dependent values parsed by `git_default_config()`.\n    +    For now the value can be accessed via the_repository in\n    +    `git_default_config()`.\n         This will ensure the current behaviour remains the same while also\n         enabling the libification of Git.\n     \n         It is important to note that `git_default_config()` is a wrapper to other\n    -    `git_default_*_config()` such as `git_default_core_config()`.\n    +    `git_default_*_config()` functions such as `git_default_core_config()`.\n         Therefore to access and modify this global variable,\n    -    the change has to be made in the function which parses and\n    -    stores the value in the global variable.\n    +    the change has to be made `git_default_core_config()`.\n     \n         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n         Mentored-by: Christian Couder <christian.couder@gmail.com>\n    @@ attr.c: const char *git_attr_system_file(void)\n     -\tif (!git_attributes_file)\n     -\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n     +\tstruct repo_config_values *cfg = &the_repository->config_values;\n    -+\tif (!cfg->attributes_file_path)\n    -+\t\tcfg->attributes_file_path = xdg_config_home(\"attributes\");\n    ++\tif (!cfg->attributes_file)\n    ++\t\tcfg->attributes_file = xdg_config_home(\"attributes\");\n      \n     -\treturn git_attributes_file;\n    -+\treturn cfg->attributes_file_path;\n    ++\treturn cfg->attributes_file;\n      }\n      \n      int git_attr_system_is_enabled(void)\n    @@ environment.c: static int git_default_core_config(const char *var, const char *v\n      \tif (!strcmp(var, \"core.attributesfile\")) {\n     -\t\tFREE_AND_NULL(git_attributes_file);\n     -\t\treturn git_config_pathname(&git_attributes_file, var, value);\n    -+\t\tFREE_AND_NULL(cfg->attributes_file_path);\n    -+\t\treturn git_config_pathname(&cfg->attributes_file_path, var, value);\n    ++\t\tFREE_AND_NULL(cfg->attributes_file);\n    ++\t\treturn git_config_pathname(&cfg->attributes_file, var, value);\n      \t}\n      \n      \tif (!strcmp(var, \"core.bare\")) {\n    @@ environment.c: int git_default_config(const char *var, const char *value,\n     +\n     +void repo_config_values_init(struct repo_config_values *cfg)\n     +{\n    -+\tcfg->attributes_file_path = NULL;\n    ++\tcfg->attributes_file = NULL;\n     +}\n     \n      ## environment.h ##\n    @@ environment.h: extern const char * const local_repo_env[];\n      \n      struct strvec;\n      \n    -+/* Config values parsed by git_default_config() */\n     +struct repo_config_values {\n    -+\t/* core config values */\n    -+\tchar *attributes_file_path;\n    ++\t/* section \"core\" config values */\n    ++\tchar *attributes_file;\n     +};\n     +\n      /*\n2:  fd95169de4 ! 2:  e44ce7be3d environment: environment: stop using core.sparseCheckout globally\n    @@ Metadata\n     Author: Olamide Caleb Bello <belkid98@gmail.com>\n     \n      ## Commit message ##\n    -    environment: environment: stop using core.sparseCheckout globally\n    +    environment: stop using core.sparseCheckout globally\n     \n         The config value `core.sparseCheckout` is parsed in\n         `git_default_core_config()` and stored globally in\n    -    `core_appy_sparse_checkout`. This could cause unintended behaviours\n    -    when different Git repositories running in the same process access this\n    -    variable.\n    +    `core_apply_sparse_checkout`. This could cause it to be overwritten\n    +    by another repository when different Git repositories run in the same\n    +    process.\n     \n    -    Move the parsed value into `struct repo_config_values` to retains current\n    -    behaviours while achieving the repository scoped access.\n    +    Move the parsed value into `struct repo_config_values` in the_repository\n    +    to retain current behaviours and move towards libifying Git.\n     \n         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n         Mentored-by: Christian Couder <christian.couder@gmail.com>\n    @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *pr\n      \n      \tif (ctx.sparse < 0)\n     -\t\tctx.sparse = core_apply_sparse_checkout;\n    -+\t\tctx.sparse = repo->config_values.sparse_checkout;\n    ++\t\tctx.sparse = the_repository->config_values.apply_sparse_checkout;\n      \n      \tresult = do_backfill(&ctx);\n      \tbackfill_context_clear(&ctx);\n    @@ builtin/clone.c: static int git_sparse_checkout_init(const char *repo)\n      \t * for the later checkout to use the sparse-checkout file.\n      \t */\n     -\tcore_apply_sparse_checkout = 1;\n    -+\tthe_repository->config_values.sparse_checkout = 1;\n    ++\tthe_repository->config_values.apply_sparse_checkout = 1;\n      \n      \tcmd.git_cmd = 1;\n      \tif (run_command(&cmd)) {\n    @@ builtin/grep.c: static int grep_submodule(struct grep_opt *opt,\n      \t *\tof these submodules are expanded unexpectedly.\n      \t *\n     -\t * 2. \"core_apply_sparse_checkout\"\n    -+\t * 2. \"sparse_checkout\"\n    ++\t * 2. \"config_values.apply_sparse_checkout\"\n      \t *\tWhen running `grep` in the superproject, this setting is\n      \t *\tpopulated using the superproject's configs. However, once\n      \t *\tinitialized, this config is globally accessible and is read by\n    @@ builtin/mv.c: int cmd_mv(int argc,\n      \n      \t\tif (ignore_sparse &&\n     -\t\t    core_apply_sparse_checkout &&\n    -+\t\t    the_repository->config_values.sparse_checkout &&\n    ++\t\t    the_repository->config_values.apply_sparse_checkout &&\n      \t\t    core_sparse_checkout_cone) {\n      \t\t\t/*\n      \t\t\t * NEEDSWORK: we are *not* paying attention to\n    @@ builtin/sparse-checkout.c: static int sparse_checkout_list(int argc, const char\n      \n      \tsetup_work_tree();\n     -\tif (!core_apply_sparse_checkout)\n    -+\tif (!the_repository->config_values.sparse_checkout)\n    ++\tif (!the_repository->config_values.apply_sparse_checkout)\n      \t\tdie(_(\"this worktree is not sparse\"));\n      \n      \targc = parse_options(argc, argv, prefix,\n    @@ builtin/sparse-checkout.c: static int set_config(struct repository *repo,\n      static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n      \t/* If not specified, use previous definition of cone mode */\n     -\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n    -+\tif (*cone_mode == -1 && the_repository->config_values.sparse_checkout)\n    ++\tif (*cone_mode == -1 && the_repository->config_values.apply_sparse_checkout)\n      \t\t*cone_mode = core_sparse_checkout_cone;\n      \n      \t/* Set cone/non-cone mode appropriately */\n     -\tcore_apply_sparse_checkout = 1;\n    -+\tthe_repository->config_values.sparse_checkout = 1;\n    ++\tthe_repository->config_values.apply_sparse_checkout = 1;\n      \tif (*cone_mode == 1 || *cone_mode == -1) {\n      \t\tcore_sparse_checkout_cone = 1;\n\n\n-- \n2.34.1\n\n"},{"id":"534597","messageId":"d28850bcdb5677ad0c81cf4bfa51ae1c056aabd3.1769252118.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1769252118.git.belkid98@gmail.com","subject":"[Outreachy PATCH v4 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-24T11:55:04Z","receivedAt":"2026-01-24T11:55:31Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The `core.attributeFile` config value is parsed in\ngit_default_core_config(), loaded eagerly and stored in the global\nvariable `git_attributes_file`. Storing this value in a global variable\ncan lead to it being overwritten by another repository when more than one\nGit repository run in the same Git process.\n\nCreate a new struct `repo_config_values` to hold this value and\nother repository dependent values parsed by `git_default_config()`.\nFor now the value can be accessed via the_repository in\n`git_default_config()`.\nThis will ensure the current behaviour remains the same while also\nenabling the libification of Git.\n\nIt is important to note that `git_default_config()` is a wrapper to other\n`git_default_*_config()` functions such as `git_default_core_config()`.\nTherefore to access and modify this global variable,\nthe change has to be made `git_default_core_config()`.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n attr.c        |  7 ++++---\n environment.c | 12 +++++++++---\n environment.h |  8 +++++++-\n repository.c  |  1 +\n repository.h  |  4 ++++\n 5 files changed, 25 insertions(+), 7 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 4999b7e09d..b8b70e6dce 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)\n \n const char *git_attr_global_file(void)\n {\n-\tif (!git_attributes_file)\n-\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\tif (!cfg->attributes_file)\n+\t\tcfg->attributes_file = xdg_config_home(\"attributes\");\n \n-\treturn git_attributes_file;\n+\treturn cfg->attributes_file;\n }\n \n int git_attr_system_is_enabled(void)\ndiff --git a/environment.c b/environment.c\nindex a770b5921d..72735d9e4b 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -53,7 +53,6 @@ char *git_commit_encoding;\n char *git_log_output_encoding;\n char *apply_default_whitespace;\n char *apply_default_ignorewhitespace;\n-char *git_attributes_file;\n int zlib_compression_level = Z_BEST_SPEED;\n int pack_compression_level = Z_DEFAULT_COMPRESSION;\n int fsync_object_files = -1;\n@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n static int git_default_core_config(const char *var, const char *value,\n \t\t\t\t   const struct config_context *ctx, void *cb)\n {\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\n \t/* This needs a better name */\n \tif (!strcmp(var, \"core.filemode\")) {\n \t\ttrust_executable_bit = git_config_bool(var, value);\n@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.attributesfile\")) {\n-\t\tFREE_AND_NULL(git_attributes_file);\n-\t\treturn git_config_pathname(&git_attributes_file, var, value);\n+\t\tFREE_AND_NULL(cfg->attributes_file);\n+\t\treturn git_config_pathname(&cfg->attributes_file, var, value);\n \t}\n \n \tif (!strcmp(var, \"core.bare\")) {\n@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,\n \t/* Add other config variables here and to Documentation/config.adoc. */\n \treturn 0;\n }\n+\n+void repo_config_values_init(struct repo_config_values *cfg)\n+{\n+\tcfg->attributes_file = NULL;\n+}\ndiff --git a/environment.h b/environment.h\nindex 51898c99cd..0c0dcc6847 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -84,6 +84,11 @@ extern const char * const local_repo_env[];\n \n struct strvec;\n \n+struct repo_config_values {\n+\t/* section \"core\" config values */\n+\tchar *attributes_file;\n+};\n+\n /*\n  * Wrapper of getenv() that returns a strdup value. This value is kept\n  * in argv to be freed later.\n@@ -107,6 +112,8 @@ const char *strip_namespace(const char *namespaced_ref);\n int git_default_config(const char *, const char *,\n \t\t       const struct config_context *, void *);\n \n+void repo_config_values_init(struct repo_config_values *cfg);\n+\n /*\n  * TODO: All the below state either explicitly or implicitly relies on\n  * `the_repository`. We should eventually get rid of these and make the\n@@ -152,7 +159,6 @@ extern int assume_unchanged;\n extern int warn_on_object_refname_ambiguity;\n extern char *apply_default_whitespace;\n extern char *apply_default_ignorewhitespace;\n-extern char *git_attributes_file;\n extern int zlib_compression_level;\n extern int pack_compression_level;\n extern unsigned long pack_size_limit_cfg;\ndiff --git a/repository.c b/repository.c\nindex c7e75215ac..d308cd78bf 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)\n \tALLOC_ARRAY(repo->index, 1);\n \tindex_state_init(repo->index, repo);\n \trepo->check_deprecated_config = true;\n+\trepo_config_values_init(&repo->config_values);\n \n \t/*\n \t * When a command runs inside a repository, it learns what\ndiff --git a/repository.h b/repository.h\nindex 6063c4b846..638a142577 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -3,6 +3,7 @@\n \n #include \"strmap.h\"\n #include \"repo-settings.h\"\n+#include \"environment.h\"\n \n struct config_set;\n struct git_hash_algo;\n@@ -148,6 +149,9 @@ struct repository {\n \t/* Repository's compatibility hash algorithm. */\n \tconst struct git_hash_algo *compat_hash_algo;\n \n+\t/* Repository's config values parsed by git_default_config() */\n+\tstruct repo_config_values config_values;\n+\n \t/* Repository's reference storage format, as serialized on disk. */\n \tenum ref_storage_format ref_storage_format;\n \n-- \n2.34.1\n\n"},{"id":"534598","messageId":"e44ce7be3d9f49917586cd0b1f6480cafc0047cd.1769252118.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1769252118.git.belkid98@gmail.com","subject":"[Outreachy PATCH v4 2/3] environment: stop using core.sparseCheckout globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-24T11:55:05Z","receivedAt":"2026-01-24T11:55:35Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `core.sparseCheckout` is parsed in\n`git_default_core_config()` and stored globally in\n`core_apply_sparse_checkout`. This could cause it to be overwritten\nby another repository when different Git repositories run in the same\nprocess.\n\nMove the parsed value into `struct repo_config_values` in the_repository\nto retain current behaviours and move towards libifying Git.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n builtin/backfill.c        |  2 +-\n builtin/clone.c           |  2 +-\n builtin/grep.c            |  2 +-\n builtin/mv.c              |  2 +-\n builtin/sparse-checkout.c | 23 ++++++++++++-----------\n builtin/worktree.c        |  2 +-\n dir.c                     |  2 +-\n environment.c             |  4 ++--\n environment.h             |  2 +-\n sparse-index.c            |  6 ++++--\n unpack-trees.c            |  2 +-\n wt-status.c               |  3 ++-\n 12 files changed, 28 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/backfill.c b/builtin/backfill.c\nindex e80fc1b694..d8cc13aaa4 100644\n--- a/builtin/backfill.c\n+++ b/builtin/backfill.c\n@@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n \trepo_config(repo, git_default_config, NULL);\n \n \tif (ctx.sparse < 0)\n-\t\tctx.sparse = core_apply_sparse_checkout;\n+\t\tctx.sparse = the_repository->config_values.apply_sparse_checkout;\n \n \tresult = do_backfill(&ctx);\n \tbackfill_context_clear(&ctx);\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..ee0736b634 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n \t * We must apply the setting in the current process\n \t * for the later checkout to use the sparse-checkout file.\n \t */\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->config_values.apply_sparse_checkout = 1;\n \n \tcmd.git_cmd = 1;\n \tif (run_command(&cmd)) {\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 53cccf2d25..4b5a96703a 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,\n \t *\t\"forget\" the sparse-index feature switch. As a result, the index\n \t *\tof these submodules are expanded unexpectedly.\n \t *\n-\t * 2. \"core_apply_sparse_checkout\"\n+\t * 2. \"config_values.apply_sparse_checkout\"\n \t *\tWhen running `grep` in the superproject, this setting is\n \t *\tpopulated using the superproject's configs. However, once\n \t *\tinitialized, this config is globally accessible and is read by\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex d43925097b..29801f5fe7 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -572,7 +572,7 @@ int cmd_mv(int argc,\n \t\trename_index_entry_at(the_repository->index, pos, dst);\n \n \t\tif (ignore_sparse &&\n-\t\t    core_apply_sparse_checkout &&\n+\t\t    the_repository->config_values.apply_sparse_checkout &&\n \t\t    core_sparse_checkout_cone) {\n \t\t\t/*\n \t\t\t * NEEDSWORK: we are *not* paying attention to\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..c57793ebdc 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,\n \tint res;\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.apply_sparse_checkout)\n \t\tdie(_(\"this worktree is not sparse\"));\n \n \targc = parse_options(argc, argv, prefix,\n@@ -400,11 +400,11 @@ static int set_config(struct repository *repo,\n \n static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n \t/* If not specified, use previous definition of cone mode */\n-\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n+\tif (*cone_mode == -1 && the_repository->config_values.apply_sparse_checkout)\n \t\t*cone_mode = core_sparse_checkout_cone;\n \n \t/* Set cone/non-cone mode appropriately */\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->config_values.apply_sparse_checkout = 1;\n \tif (*cone_mode == 1 || *cone_mode == -1) {\n \t\tcore_sparse_checkout_cone = 1;\n \t\treturn MODE_CONE_PATTERNS;\n@@ -418,7 +418,8 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind\n \tint mode, record_mode;\n \n \t/* Determine if we need to record the mode; ensure sparse checkout on */\n-\trecord_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n+\trecord_mode = (*cone_mode != -1) ||\n+\t              !the_repository->config_values.apply_sparse_checkout;\n \n \tmode = update_cone_mode(cone_mode);\n \tif (record_mode && set_config(repo, mode))\n@@ -699,9 +700,9 @@ static int modify_pattern_list(struct repository *repo,\n \t\tbreak;\n \t}\n \n-\tif (!core_apply_sparse_checkout) {\n+\tif (!the_repository->config_values.apply_sparse_checkout) {\n \t\tset_config(repo, MODE_ALL_PATTERNS);\n-\t\tcore_apply_sparse_checkout = 1;\n+\t\tthe_repository->config_values.apply_sparse_checkout = 1;\n \t\tchanged_config = 1;\n \t}\n \n@@ -798,7 +799,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,\n \tint ret;\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.apply_sparse_checkout)\n \t\tdie(_(\"no sparse-checkout to add to\"));\n \n \trepo_read_index(repo);\n@@ -907,7 +908,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.apply_sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n \n \treapply_opts.cone_mode = -1;\n@@ -969,7 +970,7 @@ static int sparse_checkout_clean(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.apply_sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to clean directories\"));\n \tif (!core_sparse_checkout_cone)\n \t\tdie(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n@@ -1035,7 +1036,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \tstruct pattern_list pl;\n \n \t/*\n-\t * We do not exit early if !core_apply_sparse_checkout; due to the\n+\t * We do not exit early if !repo->config_values.apply_sparse_checkout; due to the\n \t * ability for users to manually muck things up between\n \t *   direct editing of .git/info/sparse-checkout\n \t *   running read-tree -m u HEAD or update-index --skip-worktree\n@@ -1061,7 +1062,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \thashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);\n \thashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n \tpl.use_cone_patterns = 0;\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->config_values.apply_sparse_checkout = 1;\n \n \tadd_pattern(\"/*\", empty_base, 0, &pl, 0);\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex fbdaf2eb2e..4b0c3e09a9 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,\n \t * If the current worktree has sparse-checkout enabled, then copy\n \t * the sparse-checkout patterns from the current worktree.\n \t */\n-\tif (core_apply_sparse_checkout)\n+\tif (the_repository->config_values.apply_sparse_checkout)\n \t\tcopy_sparse_checkout(sb_repo.buf);\n \n \t/*\ndiff --git a/dir.c b/dir.c\nindex b00821f294..bc59d8cd6a 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(\n \n int init_sparse_checkout_patterns(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.apply_sparse_checkout)\n \t\treturn 1;\n \tif (istate->sparse_checkout_patterns)\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex 72735d9e4b..269cac6d6e 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -74,7 +74,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #endif\n enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n int grafts_keep_true_parents;\n-int core_apply_sparse_checkout;\n int core_sparse_checkout_cone;\n int sparse_expect_files_outside_of_patterns;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.sparsecheckout\")) {\n-\t\tcore_apply_sparse_checkout = git_config_bool(var, value);\n+\t\tcfg->apply_sparse_checkout = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \n@@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,\n void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file = NULL;\n+\tcfg->apply_sparse_checkout = 0;\n }\ndiff --git a/environment.h b/environment.h\nindex 0c0dcc6847..ddce69c6ba 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -87,6 +87,7 @@ struct strvec;\n struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n+\tint apply_sparse_checkout;\n };\n \n /*\n@@ -168,7 +169,6 @@ extern int precomposed_unicode;\n extern int protect_hfs;\n extern int protect_ntfs;\n \n-extern int core_apply_sparse_checkout;\n extern int core_sparse_checkout_cone;\n extern int sparse_expect_files_outside_of_patterns;\n \ndiff --git a/sparse-index.c b/sparse-index.c\nindex 76f90da5f5..f00c601986 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)\n \n int is_sparse_index_allowed(struct index_state *istate, int flags)\n {\n-\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\tif (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)\n \t\treturn 0;\n \n \tif (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n@@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n \n void clear_skip_worktree_from_present_files(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout ||\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\tif (!cfg->apply_sparse_checkout ||\n \t    sparse_expect_files_outside_of_patterns)\n \t\treturn;\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex f38c761ab9..a2f4d568da 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tif (o->prefix)\n \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n \n-\tif (!core_apply_sparse_checkout || !o->update)\n+\tif (!the_repository->config_values.apply_sparse_checkout || !o->update)\n \t\to->skip_sparse_checkout = 1;\n \tif (!o->skip_sparse_checkout) {\n \t\tmemset(&pl, 0, sizeof(pl));\ndiff --git a/wt-status.c b/wt-status.c\nindex e12adb26b9..ed93e219dc 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1764,7 +1764,8 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n \tint skip_worktree = 0;\n \tint i;\n \n-\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n+\tif (!the_repository->config_values.apply_sparse_checkout ||\n+\t    r->index->cache_nr == 0) {\n \t\t/*\n \t\t * Don't compute percentage of checked out files if we\n \t\t * aren't in a sparse checkout or would get division by 0.\n-- \n2.34.1\n\n"},{"id":"534599","messageId":"c74973d6e97745451ef54616a3d35d0e2a67cf8a.1769252118.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1769252118.git.belkid98@gmail.com","subject":"[Outreachy PATCH v4 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-24T11:55:06Z","receivedAt":"2026-01-24T11:55:39Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `brach.autoSetupMerge` is parsed in\n`git_default_branch_config()` and stored in the global variable\n`git_branch_track`. This global variable can be overwritten\nby another repository when multiple Git repos run in the the same process.\n\nMove this value into `struct repo_config_values` in the_repository to\nretain current behaviours and move towards libifying Git.\nSince the variable is no longer a global variable, it has been renamed to\n`branch_track` in the struct `repo_config_values`.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n branch.h                    |  2 --\n builtin/branch.c            |  2 +-\n builtin/checkout.c          |  2 +-\n builtin/push.c              |  2 +-\n builtin/submodule--helper.c |  2 +-\n environment.c               | 12 +++++++-----\n environment.h               |  4 ++++\n 7 files changed, 15 insertions(+), 11 deletions(-)\n\ndiff --git a/branch.h b/branch.h\nindex ec2f35fda4..3dc6e2a0ff 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -15,8 +15,6 @@ enum branch_track {\n \tBRANCH_TRACK_SIMPLE,\n };\n \n-extern enum branch_track git_branch_track;\n-\n /* Functions for acting on the information about branches. */\n \n /**\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex c577b5d20f..7d27951a7e 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -795,7 +795,7 @@ int cmd_branch(int argc,\n \tif (!sorting_options.nr)\n \t\tstring_list_append(&sorting_options, \"refname\");\n \n-\ttrack = git_branch_track;\n+\ttrack = the_repository->config_values.branch_track;\n \n \thead = refs_resolve_refdup(get_main_ref_store(the_repository), \"HEAD\",\n \t\t\t\t   0, &head_oid, NULL);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 261699e2f5..ba6fea9aee 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1631,7 +1631,7 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n \t} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)\n-\t\topts->track = git_branch_track;\n+\t\topts->track = the_repository->config_values.branch_track;\n \n \tif (new_branch_info->name && !new_branch_info->commit)\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 5b6cebbb85..7be20a1035 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -162,7 +162,7 @@ static NORETURN void die_push_simple(struct branch *branch,\n \t\tadvice_pushdefault_maybe = _(\"\\n\"\n \t\t\t\t \"To choose either option permanently, \"\n \t\t\t\t \"see push.default in 'git help config'.\\n\");\n-\tif (git_branch_track != BRANCH_TRACK_SIMPLE)\n+\tif (the_repository->config_values.branch_track != BRANCH_TRACK_SIMPLE)\n \t\tadvice_automergesimple_maybe = _(\"\\n\"\n \t\t\t\t \"To avoid automatically configuring \"\n \t\t\t\t \"an upstream branch when its name\\n\"\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d537ab087a..f2b6f027d7 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3128,7 +3128,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,\n \t};\n \n \trepo_config(the_repository, git_default_config, NULL);\n-\ttrack = git_branch_track;\n+\ttrack = the_repository->config_values.branch_track;\n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \n \tif (argc != 3)\ndiff --git a/environment.c b/environment.c\nindex 269cac6d6e..de8721657e 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;\n enum eol core_eol = EOL_UNSET;\n int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;\n char *check_roundtrip_encoding;\n-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n enum rebase_setup_type autorebase = AUTOREBASE_NEVER;\n enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #ifndef OBJECT_CREATION_MODE\n@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)\n \n static int git_default_branch_config(const char *var, const char *value)\n {\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\n \tif (!strcmp(var, \"branch.autosetupmerge\")) {\n \t\tif (value && !strcmp(value, \"always\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_ALWAYS;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_ALWAYS;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"simple\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_SIMPLE;\n \t\t\treturn 0;\n \t\t}\n-\t\tgit_branch_track = git_config_bool(var, value);\n+\t\tcfg->branch_track = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"branch.autosetuprebase\")) {\n@@ -761,4 +762,5 @@ void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file = NULL;\n \tcfg->apply_sparse_checkout = 0;\n+\tcfg->branch_track = BRANCH_TRACK_REMOTE;\n }\ndiff --git a/environment.h b/environment.h\nindex ddce69c6ba..27161d56ab 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -2,6 +2,7 @@\n #define ENVIRONMENT_H\n \n #include \"repo-settings.h\"\n+#include \"branch.h\"\n \n /* Double-check local_repo_env below if you add to this list. */\n #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n@@ -88,6 +89,9 @@ struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n \tint apply_sparse_checkout;\n+\n+\t/* section \"branch\" config values */\n+\tenum branch_track branch_track;\n };\n \n /*\n-- \n2.34.1\n\n"},{"id":"534601","messageId":"cover.1769256839.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1769252118.git.belkid98@gmail.com","subject":"[Outreachy PATCH v5 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-24T12:21:10Z","receivedAt":"2026-01-24T12:21:29Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"Hi Git Community,\nOver the course of my ongoing internship, which focused on moving repo specific\nglobal variables in environment.h into local scope, I have attempted to move some\nvariables into the struct repo-settings.\nHowever there has been some design concerns as regards the use of\n`prepare_repo_settings()` with respect to when and where to call the\nfunction, and also the change in behaviours when the variable is lazily\nloaded as discussed in [1] and [2].\n\nAfter different deliberations, Phillip Wood proposed creating a new config\nstruct [3], adding it to the repository struct and passing the repo struct to\n`git_default_config()` to store parsed repo specific config values per repo.\nThis ensures the current behaviours will be retained.\n\nI have experimented with this approach for some values and I would\nappreciate feedbacks about this approach before we can move forward\nand use it for more variables related to `git_default_config()`.\n\nFor now, the parsed value is stored in `the_repository` in\n`git_default_*_config()` and further efforts to pass the repository\nparameter to `git_default_config()` as the callback parameter will\nbe looked into later on.\nThe link to the CI tests can be see in [4]\n\n1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/\n2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/\n3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/\n4. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2266020513\n\nChanges in v5:\n==============\n- Corrected mistake in commit message of patch 3 which spelt branch\n  wrongly in `branch.autoSetupmerge`\n\n\nOlamide Caleb Bello (3):\n  environment: stop storing `core.attributesFile` globally\n  environment: stop using core.sparseCheckout globally\n  environment: move \"branch.autoSetupMerge\" into `struct\n    repo_config_values`\n\n attr.c                      |  7 ++++---\n branch.h                    |  2 --\n builtin/backfill.c          |  2 +-\n builtin/branch.c            |  2 +-\n builtin/checkout.c          |  2 +-\n builtin/clone.c             |  2 +-\n builtin/grep.c              |  2 +-\n builtin/mv.c                |  2 +-\n builtin/push.c              |  2 +-\n builtin/sparse-checkout.c   | 23 ++++++++++++-----------\n builtin/submodule--helper.c |  2 +-\n builtin/worktree.c          |  2 +-\n dir.c                       |  2 +-\n environment.c               | 28 ++++++++++++++++++----------\n environment.h               | 14 ++++++++++++--\n repository.c                |  1 +\n repository.h                |  4 ++++\n sparse-index.c              |  6 ++++--\n unpack-trees.c              |  2 +-\n wt-status.c                 |  3 ++-\n 20 files changed, 68 insertions(+), 42 deletions(-)\n\nRange diff versus v4:\n====================\n1:  d28850bcdb = 1:  d28850bcdb environment: stop storing `core.attributesFile` globally\n2:  5e56e1cc41 = 2:  5e56e1cc41 environment: stop using core.sparseCheckout globally\n3:  aed3183321 ! 3:  e7f37bac87 environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`\n    @@ Metadata\n      ## Commit message ##\n         environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`\n\n    -    The config value `brach.autoSetupMerge` is parsed in\n    +    The config value `branch.autoSetupMerge` is parsed in\n         `git_default_branch_config()` and stored in the global variable\n         `git_branch_track`. This global variable can be overwritten\n         by another repository when multiple Git repos run in the the same process.\n\n-- \n2.34.1\n\n"},{"id":"534602","messageId":"d28850bcdb5677ad0c81cf4bfa51ae1c056aabd3.1769256839.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1769256839.git.belkid98@gmail.com","subject":"[Outreachy PATCH v5 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-24T12:21:11Z","receivedAt":"2026-01-24T12:21:32Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The `core.attributeFile` config value is parsed in\ngit_default_core_config(), loaded eagerly and stored in the global\nvariable `git_attributes_file`. Storing this value in a global variable\ncan lead to it being overwritten by another repository when more than one\nGit repository run in the same Git process.\n\nCreate a new struct `repo_config_values` to hold this value and\nother repository dependent values parsed by `git_default_config()`.\nFor now the value can be accessed via the_repository in\n`git_default_config()`.\nThis will ensure the current behaviour remains the same while also\nenabling the libification of Git.\n\nIt is important to note that `git_default_config()` is a wrapper to other\n`git_default_*_config()` functions such as `git_default_core_config()`.\nTherefore to access and modify this global variable,\nthe change has to be made `git_default_core_config()`.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n attr.c        |  7 ++++---\n environment.c | 12 +++++++++---\n environment.h |  8 +++++++-\n repository.c  |  1 +\n repository.h  |  4 ++++\n 5 files changed, 25 insertions(+), 7 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 4999b7e09d..b8b70e6dce 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)\n \n const char *git_attr_global_file(void)\n {\n-\tif (!git_attributes_file)\n-\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\tif (!cfg->attributes_file)\n+\t\tcfg->attributes_file = xdg_config_home(\"attributes\");\n \n-\treturn git_attributes_file;\n+\treturn cfg->attributes_file;\n }\n \n int git_attr_system_is_enabled(void)\ndiff --git a/environment.c b/environment.c\nindex a770b5921d..72735d9e4b 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -53,7 +53,6 @@ char *git_commit_encoding;\n char *git_log_output_encoding;\n char *apply_default_whitespace;\n char *apply_default_ignorewhitespace;\n-char *git_attributes_file;\n int zlib_compression_level = Z_BEST_SPEED;\n int pack_compression_level = Z_DEFAULT_COMPRESSION;\n int fsync_object_files = -1;\n@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n static int git_default_core_config(const char *var, const char *value,\n \t\t\t\t   const struct config_context *ctx, void *cb)\n {\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\n \t/* This needs a better name */\n \tif (!strcmp(var, \"core.filemode\")) {\n \t\ttrust_executable_bit = git_config_bool(var, value);\n@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.attributesfile\")) {\n-\t\tFREE_AND_NULL(git_attributes_file);\n-\t\treturn git_config_pathname(&git_attributes_file, var, value);\n+\t\tFREE_AND_NULL(cfg->attributes_file);\n+\t\treturn git_config_pathname(&cfg->attributes_file, var, value);\n \t}\n \n \tif (!strcmp(var, \"core.bare\")) {\n@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,\n \t/* Add other config variables here and to Documentation/config.adoc. */\n \treturn 0;\n }\n+\n+void repo_config_values_init(struct repo_config_values *cfg)\n+{\n+\tcfg->attributes_file = NULL;\n+}\ndiff --git a/environment.h b/environment.h\nindex 51898c99cd..0c0dcc6847 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -84,6 +84,11 @@ extern const char * const local_repo_env[];\n \n struct strvec;\n \n+struct repo_config_values {\n+\t/* section \"core\" config values */\n+\tchar *attributes_file;\n+};\n+\n /*\n  * Wrapper of getenv() that returns a strdup value. This value is kept\n  * in argv to be freed later.\n@@ -107,6 +112,8 @@ const char *strip_namespace(const char *namespaced_ref);\n int git_default_config(const char *, const char *,\n \t\t       const struct config_context *, void *);\n \n+void repo_config_values_init(struct repo_config_values *cfg);\n+\n /*\n  * TODO: All the below state either explicitly or implicitly relies on\n  * `the_repository`. We should eventually get rid of these and make the\n@@ -152,7 +159,6 @@ extern int assume_unchanged;\n extern int warn_on_object_refname_ambiguity;\n extern char *apply_default_whitespace;\n extern char *apply_default_ignorewhitespace;\n-extern char *git_attributes_file;\n extern int zlib_compression_level;\n extern int pack_compression_level;\n extern unsigned long pack_size_limit_cfg;\ndiff --git a/repository.c b/repository.c\nindex c7e75215ac..d308cd78bf 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)\n \tALLOC_ARRAY(repo->index, 1);\n \tindex_state_init(repo->index, repo);\n \trepo->check_deprecated_config = true;\n+\trepo_config_values_init(&repo->config_values);\n \n \t/*\n \t * When a command runs inside a repository, it learns what\ndiff --git a/repository.h b/repository.h\nindex 6063c4b846..638a142577 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -3,6 +3,7 @@\n \n #include \"strmap.h\"\n #include \"repo-settings.h\"\n+#include \"environment.h\"\n \n struct config_set;\n struct git_hash_algo;\n@@ -148,6 +149,9 @@ struct repository {\n \t/* Repository's compatibility hash algorithm. */\n \tconst struct git_hash_algo *compat_hash_algo;\n \n+\t/* Repository's config values parsed by git_default_config() */\n+\tstruct repo_config_values config_values;\n+\n \t/* Repository's reference storage format, as serialized on disk. */\n \tenum ref_storage_format ref_storage_format;\n \n-- \n2.34.1\n\n"},{"id":"534603","messageId":"5e56e1cc4172cfff9e917a068184e102aa70bf1d.1769256839.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1769256839.git.belkid98@gmail.com","subject":"[Outreachy PATCH v5 2/3] environment: stop using core.sparseCheckout globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-24T12:21:12Z","receivedAt":"2026-01-24T12:21:36Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `core.sparseCheckout` is parsed in\n`git_default_core_config()` and stored globally in\n`core_apply_sparse_checkout`. This could cause it to be overwritten\nby another repository when different Git repositories run in the same\nprocess.\n\nMove the parsed value into `struct repo_config_values` in the_repository\nto retain current behaviours and move towards libifying Git.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n builtin/backfill.c        |  2 +-\n builtin/clone.c           |  2 +-\n builtin/grep.c            |  2 +-\n builtin/mv.c              |  2 +-\n builtin/sparse-checkout.c | 23 ++++++++++++-----------\n builtin/worktree.c        |  2 +-\n dir.c                     |  2 +-\n environment.c             |  4 ++--\n environment.h             |  2 +-\n sparse-index.c            |  6 ++++--\n unpack-trees.c            |  2 +-\n wt-status.c               |  3 ++-\n 12 files changed, 28 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/backfill.c b/builtin/backfill.c\nindex e80fc1b694..d8cc13aaa4 100644\n--- a/builtin/backfill.c\n+++ b/builtin/backfill.c\n@@ -139,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n \trepo_config(repo, git_default_config, NULL);\n \n \tif (ctx.sparse < 0)\n-\t\tctx.sparse = core_apply_sparse_checkout;\n+\t\tctx.sparse = the_repository->config_values.apply_sparse_checkout;\n \n \tresult = do_backfill(&ctx);\n \tbackfill_context_clear(&ctx);\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..ee0736b634 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -623,7 +623,7 @@ static int git_sparse_checkout_init(const char *repo)\n \t * We must apply the setting in the current process\n \t * for the later checkout to use the sparse-checkout file.\n \t */\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->config_values.apply_sparse_checkout = 1;\n \n \tcmd.git_cmd = 1;\n \tif (run_command(&cmd)) {\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 53cccf2d25..4b5a96703a 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,\n \t *\t\"forget\" the sparse-index feature switch. As a result, the index\n \t *\tof these submodules are expanded unexpectedly.\n \t *\n-\t * 2. \"core_apply_sparse_checkout\"\n+\t * 2. \"config_values.apply_sparse_checkout\"\n \t *\tWhen running `grep` in the superproject, this setting is\n \t *\tpopulated using the superproject's configs. However, once\n \t *\tinitialized, this config is globally accessible and is read by\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex d43925097b..29801f5fe7 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -572,7 +572,7 @@ int cmd_mv(int argc,\n \t\trename_index_entry_at(the_repository->index, pos, dst);\n \n \t\tif (ignore_sparse &&\n-\t\t    core_apply_sparse_checkout &&\n+\t\t    the_repository->config_values.apply_sparse_checkout &&\n \t\t    core_sparse_checkout_cone) {\n \t\t\t/*\n \t\t\t * NEEDSWORK: we are *not* paying attention to\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..cfd107db5e 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,\n \tint res;\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.apply_sparse_checkout)\n \t\tdie(_(\"this worktree is not sparse\"));\n \n \targc = parse_options(argc, argv, prefix,\n@@ -400,11 +400,11 @@ static int set_config(struct repository *repo,\n \n static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n \t/* If not specified, use previous definition of cone mode */\n-\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n+\tif (*cone_mode == -1 && the_repository->config_values.apply_sparse_checkout)\n \t\t*cone_mode = core_sparse_checkout_cone;\n \n \t/* Set cone/non-cone mode appropriately */\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->config_values.apply_sparse_checkout = 1;\n \tif (*cone_mode == 1 || *cone_mode == -1) {\n \t\tcore_sparse_checkout_cone = 1;\n \t\treturn MODE_CONE_PATTERNS;\n@@ -418,7 +418,8 @@ static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind\n \tint mode, record_mode;\n \n \t/* Determine if we need to record the mode; ensure sparse checkout on */\n-\trecord_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n+\trecord_mode = (*cone_mode != -1) ||\n+\t\t      !the_repository->config_values.apply_sparse_checkout;\n \n \tmode = update_cone_mode(cone_mode);\n \tif (record_mode && set_config(repo, mode))\n@@ -699,9 +700,9 @@ static int modify_pattern_list(struct repository *repo,\n \t\tbreak;\n \t}\n \n-\tif (!core_apply_sparse_checkout) {\n+\tif (!the_repository->config_values.apply_sparse_checkout) {\n \t\tset_config(repo, MODE_ALL_PATTERNS);\n-\t\tcore_apply_sparse_checkout = 1;\n+\t\tthe_repository->config_values.apply_sparse_checkout = 1;\n \t\tchanged_config = 1;\n \t}\n \n@@ -798,7 +799,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,\n \tint ret;\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.apply_sparse_checkout)\n \t\tdie(_(\"no sparse-checkout to add to\"));\n \n \trepo_read_index(repo);\n@@ -907,7 +908,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.apply_sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n \n \treapply_opts.cone_mode = -1;\n@@ -969,7 +970,7 @@ static int sparse_checkout_clean(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.apply_sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to clean directories\"));\n \tif (!core_sparse_checkout_cone)\n \t\tdie(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n@@ -1035,7 +1036,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \tstruct pattern_list pl;\n \n \t/*\n-\t * We do not exit early if !core_apply_sparse_checkout; due to the\n+\t * We do not exit early if !repo->config_values.apply_sparse_checkout; due to the\n \t * ability for users to manually muck things up between\n \t *   direct editing of .git/info/sparse-checkout\n \t *   running read-tree -m u HEAD or update-index --skip-worktree\n@@ -1061,7 +1062,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \thashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);\n \thashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n \tpl.use_cone_patterns = 0;\n-\tcore_apply_sparse_checkout = 1;\n+\tthe_repository->config_values.apply_sparse_checkout = 1;\n \n \tadd_pattern(\"/*\", empty_base, 0, &pl, 0);\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex fbdaf2eb2e..4b0c3e09a9 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -536,7 +536,7 @@ static int add_worktree(const char *path, const char *refname,\n \t * If the current worktree has sparse-checkout enabled, then copy\n \t * the sparse-checkout patterns from the current worktree.\n \t */\n-\tif (core_apply_sparse_checkout)\n+\tif (the_repository->config_values.apply_sparse_checkout)\n \t\tcopy_sparse_checkout(sb_repo.buf);\n \n \t/*\ndiff --git a/dir.c b/dir.c\nindex b00821f294..bc59d8cd6a 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1551,7 +1551,7 @@ enum pattern_match_result path_matches_pattern_list(\n \n int init_sparse_checkout_patterns(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout)\n+\tif (!the_repository->config_values.apply_sparse_checkout)\n \t\treturn 1;\n \tif (istate->sparse_checkout_patterns)\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex 72735d9e4b..269cac6d6e 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -74,7 +74,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #endif\n enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n int grafts_keep_true_parents;\n-int core_apply_sparse_checkout;\n int core_sparse_checkout_cone;\n int sparse_expect_files_outside_of_patterns;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.sparsecheckout\")) {\n-\t\tcore_apply_sparse_checkout = git_config_bool(var, value);\n+\t\tcfg->apply_sparse_checkout = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \n@@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,\n void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file = NULL;\n+\tcfg->apply_sparse_checkout = 0;\n }\ndiff --git a/environment.h b/environment.h\nindex 0c0dcc6847..ddce69c6ba 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -87,6 +87,7 @@ struct strvec;\n struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n+\tint apply_sparse_checkout;\n };\n \n /*\n@@ -168,7 +169,6 @@ extern int precomposed_unicode;\n extern int protect_hfs;\n extern int protect_ntfs;\n \n-extern int core_apply_sparse_checkout;\n extern int core_sparse_checkout_cone;\n extern int sparse_expect_files_outside_of_patterns;\n \ndiff --git a/sparse-index.c b/sparse-index.c\nindex 76f90da5f5..f00c601986 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)\n \n int is_sparse_index_allowed(struct index_state *istate, int flags)\n {\n-\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\tif (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)\n \t\treturn 0;\n \n \tif (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n@@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n \n void clear_skip_worktree_from_present_files(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout ||\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\tif (!cfg->apply_sparse_checkout ||\n \t    sparse_expect_files_outside_of_patterns)\n \t\treturn;\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex f38c761ab9..a2f4d568da 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1924,7 +1924,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tif (o->prefix)\n \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n \n-\tif (!core_apply_sparse_checkout || !o->update)\n+\tif (!the_repository->config_values.apply_sparse_checkout || !o->update)\n \t\to->skip_sparse_checkout = 1;\n \tif (!o->skip_sparse_checkout) {\n \t\tmemset(&pl, 0, sizeof(pl));\ndiff --git a/wt-status.c b/wt-status.c\nindex e12adb26b9..4c66b47052 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1764,7 +1764,8 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n \tint skip_worktree = 0;\n \tint i;\n \n-\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n+\tif (!the_repository->config_values.apply_sparse_checkout ||\n+\t    r->index->cache_nr == 0) {\n \t\t/*\n \t\t * Don't compute percentage of checked out files if we\n \t\t * aren't in a sparse checkout or would get division by 0.\n-- \n2.34.1\n\n"},{"id":"534604","messageId":"e7f37bac87aac74ca13f85ae7e393a38d2079b9c.1769256839.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1769256839.git.belkid98@gmail.com","subject":"[Outreachy PATCH v5 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-24T12:21:13Z","receivedAt":"2026-01-24T12:21:39Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `branch.autoSetupMerge` is parsed in\n`git_default_branch_config()` and stored in the global variable\n`git_branch_track`. This global variable can be overwritten\nby another repository when multiple Git repos run in the the same process.\n\nMove this value into `struct repo_config_values` in the_repository to\nretain current behaviours and move towards libifying Git.\nSince the variable is no longer a global variable, it has been renamed to\n`branch_track` in the struct `repo_config_values`.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n branch.h                    |  2 --\n builtin/branch.c            |  2 +-\n builtin/checkout.c          |  2 +-\n builtin/push.c              |  2 +-\n builtin/submodule--helper.c |  2 +-\n environment.c               | 12 +++++++-----\n environment.h               |  4 ++++\n 7 files changed, 15 insertions(+), 11 deletions(-)\n\ndiff --git a/branch.h b/branch.h\nindex ec2f35fda4..3dc6e2a0ff 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -15,8 +15,6 @@ enum branch_track {\n \tBRANCH_TRACK_SIMPLE,\n };\n \n-extern enum branch_track git_branch_track;\n-\n /* Functions for acting on the information about branches. */\n \n /**\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex c577b5d20f..7d27951a7e 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -795,7 +795,7 @@ int cmd_branch(int argc,\n \tif (!sorting_options.nr)\n \t\tstring_list_append(&sorting_options, \"refname\");\n \n-\ttrack = git_branch_track;\n+\ttrack = the_repository->config_values.branch_track;\n \n \thead = refs_resolve_refdup(get_main_ref_store(the_repository), \"HEAD\",\n \t\t\t\t   0, &head_oid, NULL);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 261699e2f5..ba6fea9aee 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1631,7 +1631,7 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n \t} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)\n-\t\topts->track = git_branch_track;\n+\t\topts->track = the_repository->config_values.branch_track;\n \n \tif (new_branch_info->name && !new_branch_info->commit)\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 5b6cebbb85..7be20a1035 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -162,7 +162,7 @@ static NORETURN void die_push_simple(struct branch *branch,\n \t\tadvice_pushdefault_maybe = _(\"\\n\"\n \t\t\t\t \"To choose either option permanently, \"\n \t\t\t\t \"see push.default in 'git help config'.\\n\");\n-\tif (git_branch_track != BRANCH_TRACK_SIMPLE)\n+\tif (the_repository->config_values.branch_track != BRANCH_TRACK_SIMPLE)\n \t\tadvice_automergesimple_maybe = _(\"\\n\"\n \t\t\t\t \"To avoid automatically configuring \"\n \t\t\t\t \"an upstream branch when its name\\n\"\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d537ab087a..f2b6f027d7 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3128,7 +3128,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,\n \t};\n \n \trepo_config(the_repository, git_default_config, NULL);\n-\ttrack = git_branch_track;\n+\ttrack = the_repository->config_values.branch_track;\n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \n \tif (argc != 3)\ndiff --git a/environment.c b/environment.c\nindex 269cac6d6e..de8721657e 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;\n enum eol core_eol = EOL_UNSET;\n int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;\n char *check_roundtrip_encoding;\n-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n enum rebase_setup_type autorebase = AUTOREBASE_NEVER;\n enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #ifndef OBJECT_CREATION_MODE\n@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)\n \n static int git_default_branch_config(const char *var, const char *value)\n {\n+\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\n \tif (!strcmp(var, \"branch.autosetupmerge\")) {\n \t\tif (value && !strcmp(value, \"always\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_ALWAYS;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_ALWAYS;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"simple\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_SIMPLE;\n \t\t\treturn 0;\n \t\t}\n-\t\tgit_branch_track = git_config_bool(var, value);\n+\t\tcfg->branch_track = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"branch.autosetuprebase\")) {\n@@ -761,4 +762,5 @@ void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file = NULL;\n \tcfg->apply_sparse_checkout = 0;\n+\tcfg->branch_track = BRANCH_TRACK_REMOTE;\n }\ndiff --git a/environment.h b/environment.h\nindex ddce69c6ba..27161d56ab 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -2,6 +2,7 @@\n #define ENVIRONMENT_H\n \n #include \"repo-settings.h\"\n+#include \"branch.h\"\n \n /* Double-check local_repo_env below if you add to this list. */\n #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n@@ -88,6 +89,9 @@ struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n \tint apply_sparse_checkout;\n+\n+\t/* section \"branch\" config values */\n+\tenum branch_track branch_track;\n };\n \n /*\n-- \n2.34.1\n\n"},{"id":"534799","messageId":"CAD=f0L_5E+FsnxKOCnFt_Rot9FJ8aP3hzeLTHKEqWD312QJ9kw@mail.gmail.com","threadId":"64782","inReplyTo":"cover.1769256839.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v5 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-01-29T08:29:52Z","receivedAt":"2026-01-29T08:29:53Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Sat, 24 Jan 2026 at 13:21, Olamide Caleb Bello <belkid98@gmail.com> wrote:\n>\n> Hi Git Community,\n> Over the course of my ongoing internship, which focused on moving repo specific\n> global variables in environment.h into local scope, I have attempted to move some\n> variables into the struct repo-settings.\n> However there has been some design concerns as regards the use of\n> `prepare_repo_settings()` with respect to when and where to call the\n> function, and also the change in behaviours when the variable is lazily\n> loaded as discussed in [1] and [2].\n>\n> After different deliberations, Phillip Wood proposed creating a new config\n> struct [3], adding it to the repository struct and passing the repo struct to\n> `git_default_config()` to store parsed repo specific config values per repo.\n> This ensures the current behaviours will be retained.\n>\n> I have experimented with this approach for some values and I would\n> appreciate feedbacks about this approach before we can move forward\n> and use it for more variables related to `git_default_config()`.\n>\n> For now, the parsed value is stored in `the_repository` in\n> `git_default_*_config()` and further efforts to pass the repository\n> parameter to `git_default_config()` as the callback parameter will\n> be looked into later on.\n> The link to the CI tests can be see in [4]\n>\n> 1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/\n> 2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/\n> 3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com/\n> 4. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2266020513\n>\n> Changes in v5:\n> ==============\n> - Corrected mistake in commit message of patch 3 which spelt branch\n>   wrongly in `branch.autoSetupmerge`\n\nHello,\nJust a gentle nudge for a review\nThanks\n"},{"id":"534823","messageId":"xmqqv7gkb8mj.fsf@gitster.g","threadId":"64782","inReplyTo":"d28850bcdb5677ad0c81cf4bfa51ae1c056aabd3.1769256839.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v5 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-29T18:01:56Z","receivedAt":"2026-01-29T18:01:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Olamide Caleb Bello <belkid98@gmail.com> writes:\n\n>  const char *git_attr_global_file(void)\n>  {\n> -\tif (!git_attributes_file)\n> -\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n> +\tstruct repo_config_values *cfg = &the_repository->config_values;\n\nHere, the_repository, being defined in repository.c as the address\nof a singleton \"the_repo\" instance, cannot be NULL even outside a\nrepository, so taking the address of its config_values member is\nalways safe.  OK.\n\n> +\tif (!cfg->attributes_file)\n> +\t\tcfg->attributes_file = xdg_config_home(\"attributes\");\n>  \n> -\treturn git_attributes_file;\n> +\treturn cfg->attributes_file;\n>  }\n\n> diff --git a/repository.h b/repository.h\n> index 6063c4b846..638a142577 100644\n> --- a/repository.h\n> +++ b/repository.h\n> @@ -3,6 +3,7 @@\n>  \n>  #include \"strmap.h\"\n>  #include \"repo-settings.h\"\n> +#include \"environment.h\"\n>  \n>  struct config_set;\n>  struct git_hash_algo;\n> @@ -148,6 +149,9 @@ struct repository {\n>  \t/* Repository's compatibility hash algorithm. */\n>  \tconst struct git_hash_algo *compat_hash_algo;\n>  \n> +\t/* Repository's config values parsed by git_default_config() */\n> +\tstruct repo_config_values config_values;\n> +\n>  \t/* Repository's reference storage format, as serialized on disk. */\n>  \tenum ref_storage_format ref_storage_format;\n\nAnd because this new config_values member is directly embedded in\nthe repository structure, and \"the_repo\" instance is a global in\nBSS, its members are initialized exactly the same way as the\nglobal variables like git_attributes_file were initialized.  Good.\n\n\n> diff --git a/environment.c b/environment.c\n> index a770b5921d..72735d9e4b 100644\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -53,7 +53,6 @@ char *git_commit_encoding;\n>  char *git_log_output_encoding;\n>  char *apply_default_whitespace;\n>  char *apply_default_ignorewhitespace;\n> -char *git_attributes_file;\n\nAnd we lose this global, that used to be zero-initialized for being\nin BSS.\n\n> @@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n>  static int git_default_core_config(const char *var, const char *value,\n>  \t\t\t\t   const struct config_context *ctx, void *cb)\n>  {\n> +\tstruct repo_config_values *cfg = &the_repository->config_values;\n> +\n>  \t/* This needs a better name */\n>  \tif (!strcmp(var, \"core.filemode\")) {\n>  \t\ttrust_executable_bit = git_config_bool(var, value);\n> @@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,\n>  \t}\n>  \n>  \tif (!strcmp(var, \"core.attributesfile\")) {\n> -\t\tFREE_AND_NULL(git_attributes_file);\n> -\t\treturn git_config_pathname(&git_attributes_file, var, value);\n> +\t\tFREE_AND_NULL(cfg->attributes_file);\n> +\t\treturn git_config_pathname(&cfg->attributes_file, var, value);\n>  \t}\n>  \n>  \tif (!strcmp(var, \"core.bare\")) {\n> @@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,\n>  \t/* Add other config variables here and to Documentation/config.adoc. */\n>  \treturn 0;\n>  }\n\nAnd instead of assigning to the global git_attributes_file, we\nassign to the config_values.attributes_file member via the global\n\"the_repository\".  No functional changes.  OK.\n\n> +void repo_config_values_init(struct repo_config_values *cfg)\n> +{\n> +\tcfg->attributes_file = NULL;\n> +}\n\nThis is not strictly needed, as git_attributes_file is left to be\nzero-initialized for being in BSS; its replacement, i.e.,\nthe_repo.config_values.attributes_file, will be zero-initialized the\nsame way.\n\nBut other members we may want add later to the struct may need a\nplace to initialize them.  Or we can do a static initialization for\nthe_repo in repository.c then we do not have to have this function\nand we do not have to call it.  Either would work fine, as long as\neverybody calls initialize_repository() function, which is the only\ncaller of this helper.\n\n> diff --git a/environment.h b/environment.h\n> index 51898c99cd..0c0dcc6847 100644\n> --- a/environment.h\n> +++ b/environment.h\n> @@ -84,6 +84,11 @@ extern const char * const local_repo_env[];\n>  \n>  struct strvec;\n>  \n> +struct repo_config_values {\n> +\t/* section \"core\" config values */\n> +\tchar *attributes_file;\n> +};\n\nOK.\n\n> diff --git a/repository.c b/repository.c\n> index c7e75215ac..d308cd78bf 100644\n> --- a/repository.c\n> +++ b/repository.c\n> @@ -57,6 +57,7 @@ void initialize_repository(struct repository *repo)\n>  \tALLOC_ARRAY(repo->index, 1);\n>  \tindex_state_init(repo->index, repo);\n>  \trepo->check_deprecated_config = true;\n> +\trepo_config_values_init(&repo->config_values);\n>  \n>  \t/*\n>  \t * When a command runs inside a repository, it learns what\n\nContinuing the discussion on repo_config_values_init(), currently,\ninitialize_repository() is called by init_git(), which is called\nfrom \"main()\", so it should be fairly safe to assume that all in Git\ncodebase will call repo_config_values_init().\n\nBut those who replace \"main()\" for their own libified use of Git\ncode may not call init_git() hence initialize_repository() hence\nyour repo_config_values_init().  In that sense, this is less safe\nthan the other alternative.\n"},{"id":"534824","messageId":"xmqqqzr8b85c.fsf@gitster.g","threadId":"64782","inReplyTo":"5e56e1cc4172cfff9e917a068184e102aa70bf1d.1769256839.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v5 2/3] environment: stop using core.sparseCheckout globally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-29T18:12:15Z","receivedAt":"2026-01-29T18:12:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Olamide Caleb Bello <belkid98@gmail.com> writes:\n\n> The config value `core.sparseCheckout` is parsed in\n> `git_default_core_config()` and stored globally in\n> `core_apply_sparse_checkout`. This could cause it to be overwritten\n> by another repository when different Git repositories run in the same\n> process.\n>\n> Move the parsed value into `struct repo_config_values` in the_repository\n> to retain current behaviours and move towards libifying Git.\n>\n> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> ---\n\nA lot of changes, almost all of which are mechanical\n\n    s/core_apply_sparse_checkout/the_repository->config_values.apply_sparse_checkout/ \n\nchanges, which is very straight-forward.\n\nThe same comment applies.  Do we want programs to require calling\nrepo_config_values_init(), or should we rely on program load-time\ninitialization, like all these globals that are moved to the\nrepo_config_values struct used to do?\n\nI do not have a good answer to this question, but so far, these two\nglobal variables that were both zero initialized for being in BSS do\nnot *need* initialization byh an explicit runtime assignment that\nrepo_config_values_init() allows us to do.\n"},{"id":"534825","messageId":"xmqqms1wb6zg.fsf@gitster.g","threadId":"64782","inReplyTo":"e7f37bac87aac74ca13f85ae7e393a38d2079b9c.1769256839.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v5 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-29T18:37:23Z","receivedAt":"2026-01-29T18:37:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Olamide Caleb Bello <belkid98@gmail.com> writes:\n\n> -enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n\nUnlike the other two, this global variable was not zero-initialized,\nso we can do something like this to initialize it statically in the\nnew world order.  That way, we do not need to worry about the\nrepo_config_values_init() helper function, which (1) we can easily\nforget to adjust, and (2) third-party may not be able to call.\n\n\n\ndiff --git i/repository.c w/repository.c\nindex d308cd78bf..b540e2ba01 100644\n--- i/repository.c\n+++ w/repository.c\n@@ -25,7 +25,11 @@\n extern struct repository *the_repository;\n \n /* The main repository */\n-static struct repository the_repo;\n+static struct repository the_repo = {\n+\t.config_values = {\n+\t\t.branch_track = BRANCH_TRACK_REMOTE,\n+\t},\n+};\n struct repository *the_repository = &the_repo;\n \n /*\n\n\n\n\n\n\n\n>  enum rebase_setup_type autorebase = AUTOREBASE_NEVER;\n>  enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n>  #ifndef OBJECT_CREATION_MODE\n> @@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)\n>  \n>  static int git_default_branch_config(const char *var, const char *value)\n>  {\n> +\tstruct repo_config_values *cfg = &the_repository->config_values;\n> +\n>  \tif (!strcmp(var, \"branch.autosetupmerge\")) {\n>  \t\tif (value && !strcmp(value, \"always\")) {\n> -\t\t\tgit_branch_track = BRANCH_TRACK_ALWAYS;\n> +\t\t\tcfg->branch_track = BRANCH_TRACK_ALWAYS;\n>  \t\t\treturn 0;\n>  \t\t} else if (value && !strcmp(value, \"inherit\")) {\n> -\t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n> +\t\t\tcfg->branch_track = BRANCH_TRACK_INHERIT;\n>  \t\t\treturn 0;\n>  \t\t} else if (value && !strcmp(value, \"simple\")) {\n> -\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n> +\t\t\tcfg->branch_track = BRANCH_TRACK_SIMPLE;\n>  \t\t\treturn 0;\n>  \t\t}\n> -\t\tgit_branch_track = git_config_bool(var, value);\n> +\t\tcfg->branch_track = git_config_bool(var, value);\n>  \t\treturn 0;\n>  \t}\n>  \tif (!strcmp(var, \"branch.autosetuprebase\")) {\n> @@ -761,4 +762,5 @@ void repo_config_values_init(struct repo_config_values *cfg)\n>  {\n>  \tcfg->attributes_file = NULL;\n>  \tcfg->apply_sparse_checkout = 0;\n> +\tcfg->branch_track = BRANCH_TRACK_REMOTE;\n>  }\n> diff --git a/environment.h b/environment.h\n> index ddce69c6ba..27161d56ab 100644\n> --- a/environment.h\n> +++ b/environment.h\n> @@ -2,6 +2,7 @@\n>  #define ENVIRONMENT_H\n>  \n>  #include \"repo-settings.h\"\n> +#include \"branch.h\"\n>  \n>  /* Double-check local_repo_env below if you add to this list. */\n>  #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n> @@ -88,6 +89,9 @@ struct repo_config_values {\n>  \t/* section \"core\" config values */\n>  \tchar *attributes_file;\n>  \tint apply_sparse_checkout;\n> +\n> +\t/* section \"branch\" config values */\n> +\tenum branch_track branch_track;\n>  };\n>  \n>  /*\n"},{"id":"534887","messageId":"xmqqikcj842o.fsf@gitster.g","threadId":"64782","inReplyTo":"xmqqms1wb6zg.fsf@gitster.g","subject":"Re: [Outreachy PATCH v5 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-30T16:20:47Z","receivedAt":"2026-01-30T16:20:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Olamide Caleb Bello <belkid98@gmail.com> writes:\n>\n>> -enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n>\n> Unlike the other two, this global variable was not zero-initialized,\n> so we can do something like this to initialize it statically in the\n> new world order.  That way, we do not need to worry about the\n> repo_config_values_init() helper function, which (1) we can easily\n> forget to adjust, and (2) third-party may not be able to call.\n>\n> diff --git i/repository.c w/repository.c\n> index d308cd78bf..b540e2ba01 100644\n> --- i/repository.c\n> +++ w/repository.c\n> @@ -25,7 +25,11 @@\n>  extern struct repository *the_repository;\n>  \n>  /* The main repository */\n> -static struct repository the_repo;\n> +static struct repository the_repo = {\n> +\t.config_values = {\n> +\t\t.branch_track = BRANCH_TRACK_REMOTE,\n> +\t},\n> +};\n\nAfter having slept over this one, I think a static initialization,\nwhich mimicks what we have been doing with the global variables very\nclosely, is not what we want in the longer term.\n\nThe aim of these patches is to prepare for a new world order in\nwhich we can add new Git subcommands that operates in two or more\nrepositories at the same time, and have these repositories use their\nown versions of these \"global\" variables independently.  While the\nstatic initialization has these advantages:\n\n * Once the code is written, no new command can forget to initialize\n   them; initialization happens at the program load time.\n\n * No programming error can wipe what has already been read from the\n   configuration by making a second repo_config_values_init() call\n   by mistake.\n\nneither of these advantages apply to second and subsequent\nrepositories.  We'd need to somehow initialize an instance of a\nrepository that is not \"the_repo\".\n\nAnd for that, repo_config_values_init() is needed, even though it\nadds the downsides that are opposite of the above two advantages of\nnot having to have an \"initialization\" step.\n\nSo, we need to take it as a given that repo_config_values_init()\nneeds to exist.  Under that condition, I wonder if we can somehow\nhave a cheap way to assert the following two things:\n\n * Before a repository instance is used, repo_config_values_init()\n   has been called on it, as using an instance without initializing\n   is a no-no.\n\n * repo_config_values_init() is never called twice on a repository\n   instance, as the second call will wipe what the first call and\n   subsequent reading of the configuration files have done.\n\nThanks.\n"},{"id":"534904","messageId":"xmqq5x8i7t7j.fsf@gitster.g","threadId":"64782","inReplyTo":"xmqqikcj842o.fsf@gitster.g","subject":"Re: [Outreachy PATCH v5 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-30T20:15:28Z","receivedAt":"2026-01-30T20:15:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> So, we need to take it as a given that repo_config_values_init()\n> needs to exist.  Under that condition, I wonder if we can somehow\n> have a cheap way to assert the following two things:\n>\n>  * Before a repository instance is used, repo_config_values_init()\n>    has been called on it, as using an instance without initializing\n>    is a no-no.\n>\n>  * repo_config_values_init() is never called twice on a repository\n>    instance, as the second call will wipe what the first call and\n>    subsequent reading of the configuration files have done.\n\nSomething like this squashed into your [1/3] would give us these two\nassertions, but I am not sure if it is a good idea or if I am overly\nparanoid.\n\nThe main ideas are:\n\n * \"struct repository\" now knows if it has been initialized via its\n   \"bool initialized\" member.\n\n * \"initialize_repository()\" detects double initialization of the\n   repository struct itself.\n\n * \"repo_config_values\" member in \"struct repository\" has been\n   renamed to make it clear it is \"private\", and there is an\n   accessor function of the same name.  It barfs if you ask the\n   address of repo_config_values in a repository instance that\n   hasn't been initialized.\n\n * Any code outside what implement the above are supposed to call\n   repo_config_values() on the repository they are working in, to\n   request the address of the repo_config_values instance to use.\n\nIt didn't barf when I ran all the tests, which means there isn't\nanybody who calls initialize_repository() twice in the current code.\n\nI am not sure if this is being overly paranoid, or exercising a\nreasonable caution, but anyway,...\n\n\n attr.c        |  3 ++-\n environment.c |  2 +-\n environment.h |  3 +++\n repository.c  | 13 ++++++++++++-\n repository.h  |  5 ++++-\n 5 files changed, 22 insertions(+), 4 deletions(-)\n\ndiff --git c/attr.c w/attr.c\nindex b8b70e6dce..cd39e6d2bf 100644\n--- c/attr.c\n+++ w/attr.c\n@@ -881,7 +881,8 @@ const char *git_attr_system_file(void)\n \n const char *git_attr_global_file(void)\n {\n-\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\n \tif (!cfg->attributes_file)\n \t\tcfg->attributes_file = xdg_config_home(\"attributes\");\n \ndiff --git c/environment.c w/environment.c\nindex c876589b05..208a52ce11 100644\n--- c/environment.c\n+++ w/environment.c\n@@ -302,7 +302,7 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n int git_default_core_config(const char *var, const char *value,\n \t\t\t    const struct config_context *ctx, void *cb)\n {\n-\tstruct repo_config_values *cfg = &the_repository->config_values;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \t/* This needs a better name */\n \tif (!strcmp(var, \"core.filemode\")) {\ndiff --git c/environment.h w/environment.h\nindex 2b861a61de..254fec6b7c 100644\n--- c/environment.h\n+++ w/environment.h\n@@ -84,11 +84,14 @@ extern const char * const local_repo_env[];\n \n struct strvec;\n \n+struct repository;\n struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n };\n \n+struct repo_config_values *repo_config_values(struct repository *);\n+\n /*\n  * Wrapper of getenv() that returns a strdup value. This value is kept\n  * in argv to be freed later.\ndiff --git c/repository.c w/repository.c\nindex d308cd78bf..4cb487b5b2 100644\n--- c/repository.c\n+++ w/repository.c\n@@ -50,14 +50,25 @@ static void set_default_hash_algo(struct repository *repo)\n \trepo_set_hash_algo(repo, algo);\n }\n \n+struct repo_config_values *repo_config_values(struct repository *repo)\n+{\n+\tif (!repo->initialized)\n+\t\tBUG(\"config values from uninitialied repository?\");\n+\treturn &repo->config_values_private_;\n+}\n+\n void initialize_repository(struct repository *repo)\n {\n+\tif (repo->initialized)\n+\t\tBUG(\"repository initialized already!\");\n+\trepo->initialized = true;\n+\n \trepo->remote_state = remote_state_new();\n \trepo->parsed_objects = parsed_object_pool_new(repo);\n \tALLOC_ARRAY(repo->index, 1);\n \tindex_state_init(repo->index, repo);\n \trepo->check_deprecated_config = true;\n-\trepo_config_values_init(&repo->config_values);\n+\trepo_config_values_init(repo_config_values(repo));\n \n \t/*\n \t * When a command runs inside a repository, it learns what\ndiff --git c/repository.h w/repository.h\nindex 638a142577..9717e45000 100644\n--- c/repository.h\n+++ w/repository.h\n@@ -150,7 +150,7 @@ struct repository {\n \tconst struct git_hash_algo *compat_hash_algo;\n \n \t/* Repository's config values parsed by git_default_config() */\n-\tstruct repo_config_values config_values;\n+\tstruct repo_config_values config_values_private_;\n \n \t/* Repository's reference storage format, as serialized on disk. */\n \tenum ref_storage_format ref_storage_format;\n@@ -175,6 +175,9 @@ struct repository {\n \n \t/* Should repo_config() check for deprecated settings */\n \tbool check_deprecated_config;\n+\n+\t/* Has this repository instance been initialized? */\n+\tbool initialized;\n };\n \n #ifdef USE_THE_REPOSITORY_VARIABLE\n"},{"id":"535066","messageId":"cover.1770127568.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1769256839.git.belkid98@gmail.com","subject":"[Outreachy PATCH v6 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-03T15:42:08Z","receivedAt":"2026-02-03T15:42:32Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"Hi Git Community,\nOver the course of my ongoing internship, which focused on moving repo specific\nglobal variables in environment.h into local scope, I have attempted to move some\nvariables into the struct repo-settings.\nHowever there has been some design concerns as regards the use of\n`prepare_repo_settings()` with respect to when and where to call the\nfunction, and also the change in behaviours when the variable is lazily\nloaded as discussed in [1] and [2].\n\nAfter different deliberations, Phillip Wood proposed creating a new config\nstruct [3], adding it to the repository struct and passing the repo struct to\n`git_default_config()` to store parsed repo specific config values per repo.\nThis ensures the current behaviours will be retained.\n\nI have experimented with this approach for some values and I would\nappreciate feedbacks about this approach before we can move forward\nand use it for more variables related to `git_default_config()`.\n\nFor now, the parsed value is stored in `the_repository` in\n`git_default_*_config()` and further efforts to pass the repository\nparameter to `git_default_config()` as the callback parameter will\nbe looked into later on.\nThe link to the CI tests can be see in [4] and [5]\nin the CI, some BUGs were gotten as a result of double initialization\n\n1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/\n2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/\n3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com\n4. https://github.com/git/git/actions/runs/21633462506/job/62352191148?pr=2166\n5. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2303203992\n\nChanges in v6:\n==============\n- Added a new accessor function 'repo_config_values()' which ensures we detect\n  if a repository has been initialized already\n- Changed the member name to 'config_values_private_' to indicate this member\n  is private\n- Modified commit message of patch 1 to reflect these changes\n\nOlamide Caleb Bello (3):\n  environment: stop storing `core.attributesFile` globally\n  environment: stop using core.sparseCheckout globally\n  environment: move \"branch.autoSetupMerge\" into `struct\n    repo_config_values`\n\n attr.c                      |  7 ++++---\n branch.h                    |  2 --\n builtin/backfill.c          |  3 ++-\n builtin/branch.c            |  3 ++-\n builtin/checkout.c          |  3 ++-\n builtin/clone.c             |  3 ++-\n builtin/grep.c              |  2 +-\n builtin/mv.c                |  3 ++-\n builtin/push.c              |  3 ++-\n builtin/sparse-checkout.c   | 31 ++++++++++++++++++++-----------\n builtin/submodule--helper.c |  3 ++-\n builtin/worktree.c          |  3 ++-\n dir.c                       |  3 ++-\n environment.c               | 28 ++++++++++++++++++----------\n environment.h               | 17 +++++++++++++++--\n repository.c                | 12 ++++++++++++\n repository.h                |  7 +++++++\n sparse-index.c              |  6 ++++--\n unpack-trees.c              |  3 ++-\n wt-status.c                 |  4 +++-\n 20 files changed, 104 insertions(+), 42 deletions(-)\n\n Range diff versus v5:\n =====================\n\n 1:  d28850bcdb ! 1:  7e3082125d environment: stop storing `core.attributesFile` globally\n    @@ Commit message\n\n         Create a new struct `repo_config_values` to hold this value and\n         other repository dependent values parsed by `git_default_config()`.\n    -    For now the value can be accessed via the_repository in\n    -    `git_default_config()`.\n         This will ensure the current behaviour remains the same while also\n         enabling the libification of Git.\n\n    +    An accessor function 'repo_config_values()' is created and used to access\n    +    the new struct member of the repository struct.\n    +    This is to ensure that we detect if the struct repository has been\n    +    initialized and also prevent double initialization of the repository.\n    +\n         It is important to note that `git_default_config()` is a wrapper to other\n         `git_default_*_config()` functions such as `git_default_core_config()`.\n         Therefore to access and modify this global variable,\n    @@ Commit message\n         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n         Mentored-by: Christian Couder <christian.couder@gmail.com>\n         Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n    +    Helped-by: Junio C Hamano <gitster@pobox.com>\n         Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n\n      ## attr.c ##\n    @@ attr.c: const char *git_attr_system_file(void)\n      {\n     -\tif (!git_attributes_file)\n     -\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n    -+\tstruct repo_config_values *cfg = &the_repository->config_values;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n     +\tif (!cfg->attributes_file)\n     +\t\tcfg->attributes_file = xdg_config_home(\"attributes\");\n\n    @@ environment.c: static enum fsync_component parse_fsync_components(const char *va\n      static int git_default_core_config(const char *var, const char *value,\n      \t\t\t\t   const struct config_context *ctx, void *cb)\n      {\n    -+\tstruct repo_config_values *cfg = &the_repository->config_values;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n     +\n      \t/* This needs a better name */\n      \tif (!strcmp(var, \"core.filemode\")) {\n    @@ environment.h: extern const char * const local_repo_env[];\n\n      struct strvec;\n\n    ++struct repository;\n     +struct repo_config_values {\n     +\t/* section \"core\" config values */\n     +\tchar *attributes_file;\n     +};\n    ++\n    ++struct repo_config_values *repo_config_values(struct repository *repo);\n     +\n      /*\n       * Wrapper of getenv() that returns a strdup value. This value is kept\n    @@ environment.h: extern int assume_unchanged;\n      extern unsigned long pack_size_limit_cfg;\n\n      ## repository.c ##\n    -@@ repository.c: void initialize_repository(struct repository *repo)\n    +@@ repository.c: static void set_default_hash_algo(struct repository *repo)\n    + \trepo_set_hash_algo(repo, algo);\n    + }\n    +\n    ++struct repo_config_values *repo_config_values(struct repository *repo)\n    ++{\n    ++\tif(!repo->initialized)\n    ++\t\tBUG(\"config values from uninitialized repository\");\n    ++\treturn &repo->config_values_private_;\n    ++}\n    ++\n    + void initialize_repository(struct repository *repo)\n    + {\n    ++\tif (repo->initialized)\n    ++\t\tBUG(\"repository initialized already\");\n    ++\trepo->initialized = true;\n    ++\n    + \trepo->remote_state = remote_state_new();\n    + \trepo->parsed_objects = parsed_object_pool_new(repo);\n      \tALLOC_ARRAY(repo->index, 1);\n      \tindex_state_init(repo->index, repo);\n      \trepo->check_deprecated_config = true;\n    -+\trepo_config_values_init(&repo->config_values);\n    ++\trepo_config_values_init(repo_config_values(repo));\n\n      \t/*\n      \t * When a command runs inside a repository, it learns what\n    @@ repository.h: struct repository {\n      \tconst struct git_hash_algo *compat_hash_algo;\n\n     +\t/* Repository's config values parsed by git_default_config() */\n    -+\tstruct repo_config_values config_values;\n    ++\tstruct repo_config_values config_values_private_;\n     +\n      \t/* Repository's reference storage format, as serialized on disk. */\n      \tenum ref_storage_format ref_storage_format;\n\n    +@@ repository.h: struct repository {\n    +\n    + \t/* Should repo_config() check for deprecated settings */\n    + \tbool check_deprecated_config;\n    ++\n    ++\t/* Has this repository instance been initialized? */\n    ++\tbool initialized;\n    + };\n    +\n    + #ifdef USE_THE_REPOSITORY_VARIABLE\n2:  5e56e1cc41 ! 2:  8645b4f595 environment: stop using core.sparseCheckout globally\n    @@ Commit message\n         Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n\n      ## builtin/backfill.c ##\n    +@@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n    + \t\t\t N_(\"Restrict the missing objects to the current sparse-checkout\")),\n    + \t\tOPT_END(),\n    + \t};\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    +\n    + \tshow_usage_with_options_if_asked(argc, argv,\n    + \t\t\t\t\t builtin_backfill_usage, options);\n     @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n      \trepo_config(repo, git_default_config, NULL);\n\n      \tif (ctx.sparse < 0)\n     -\t\tctx.sparse = core_apply_sparse_checkout;\n    -+\t\tctx.sparse = the_repository->config_values.apply_sparse_checkout;\n    ++\t\tctx.sparse = cfg->apply_sparse_checkout;\n\n      \tresult = do_backfill(&ctx);\n      \tbackfill_context_clear(&ctx);\n\n      ## builtin/clone.c ##\n     @@ builtin/clone.c: static int git_sparse_checkout_init(const char *repo)\n    + {\n    + \tstruct child_process cmd = CHILD_PROCESS_INIT;\n    + \tint result = 0;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    + \tstrvec_pushl(&cmd.args, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n    +\n    + \t/*\n      \t * We must apply the setting in the current process\n      \t * for the later checkout to use the sparse-checkout file.\n      \t */\n     -\tcore_apply_sparse_checkout = 1;\n    -+\tthe_repository->config_values.apply_sparse_checkout = 1;\n    ++\tcfg->apply_sparse_checkout = 1;\n\n      \tcmd.git_cmd = 1;\n      \tif (run_command(&cmd)) {\n    @@ builtin/grep.c: static int grep_submodule(struct grep_opt *opt,\n      \t *\tof these submodules are expanded unexpectedly.\n      \t *\n     -\t * 2. \"core_apply_sparse_checkout\"\n    -+\t * 2. \"config_values.apply_sparse_checkout\"\n    ++\t * 2. \"config_values_private_.apply_sparse_checkout\"\n      \t *\tWhen running `grep` in the superproject, this setting is\n      \t *\tpopulated using the superproject's configs. However, once\n      \t *\tinitialized, this config is globally accessible and is read by\n\n      ## builtin/mv.c ##\n    +@@ builtin/mv.c: int cmd_mv(int argc,\n    + \tstruct hashmap moved_dirs = HASHMAP_INIT(pathmap_cmp, NULL);\n    + \tstruct strbuf pathbuf = STRBUF_INIT;\n    + \tint ret;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    +\n    + \trepo_config(the_repository, git_default_config, NULL);\n    +\n     @@ builtin/mv.c: int cmd_mv(int argc,\n      \t\trename_index_entry_at(the_repository->index, pos, dst);\n\n      \t\tif (ignore_sparse &&\n     -\t\t    core_apply_sparse_checkout &&\n    -+\t\t    the_repository->config_values.apply_sparse_checkout &&\n    ++\t\t    cfg->apply_sparse_checkout &&\n      \t\t    core_sparse_checkout_cone) {\n      \t\t\t/*\n      \t\t\t * NEEDSWORK: we are *not* paying attention to\n\n      ## builtin/sparse-checkout.c ##\n     @@ builtin/sparse-checkout.c: static int sparse_checkout_list(int argc, const char **argv, const char *prefix,\n    + \tstruct pattern_list pl;\n    + \tchar *sparse_filename;\n      \tint res;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n\n      \tsetup_work_tree();\n     -\tif (!core_apply_sparse_checkout)\n    -+\tif (!the_repository->config_values.apply_sparse_checkout)\n    ++\tif (!cfg->apply_sparse_checkout)\n      \t\tdie(_(\"this worktree is not sparse\"));\n\n      \targc = parse_options(argc, argv, prefix,\n     @@ builtin/sparse-checkout.c: static int set_config(struct repository *repo,\n    + }\n\n      static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    ++\n      \t/* If not specified, use previous definition of cone mode */\n     -\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n    -+\tif (*cone_mode == -1 && the_repository->config_values.apply_sparse_checkout)\n    ++\tif (*cone_mode == -1 && cfg->apply_sparse_checkout)\n      \t\t*cone_mode = core_sparse_checkout_cone;\n\n      \t/* Set cone/non-cone mode appropriately */\n     -\tcore_apply_sparse_checkout = 1;\n    -+\tthe_repository->config_values.apply_sparse_checkout = 1;\n    ++\tcfg->apply_sparse_checkout = 1;\n      \tif (*cone_mode == 1 || *cone_mode == -1) {\n      \t\tcore_sparse_checkout_cone = 1;\n      \t\treturn MODE_CONE_PATTERNS;\n    -@@ builtin/sparse-checkout.c: static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind\n    +@@ builtin/sparse-checkout.c: static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n    + static int update_modes(struct repository *repo, int *cone_mode, int *sparse_index)\n    + {\n      \tint mode, record_mode;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n\n      \t/* Determine if we need to record the mode; ensure sparse checkout on */\n     -\trecord_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n    -+\trecord_mode = (*cone_mode != -1) ||\n    -+\t\t      !the_repository->config_values.apply_sparse_checkout;\n    ++\trecord_mode = (*cone_mode != -1) || !cfg->apply_sparse_checkout;\n\n      \tmode = update_cone_mode(cone_mode);\n      \tif (record_mode && set_config(repo, mode))\n    +@@ builtin/sparse-checkout.c: static int modify_pattern_list(struct repository *repo,\n    + \tint result;\n    + \tint changed_config = 0;\n    + \tstruct pattern_list *pl = xcalloc(1, sizeof(*pl));\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    +\n    + \tswitch (m) {\n    + \tcase ADD:\n     @@ builtin/sparse-checkout.c: static int modify_pattern_list(struct repository *repo,\n      \t\tbreak;\n      \t}\n\n     -\tif (!core_apply_sparse_checkout) {\n    -+\tif (!the_repository->config_values.apply_sparse_checkout) {\n    ++\tif (!cfg->apply_sparse_checkout) {\n      \t\tset_config(repo, MODE_ALL_PATTERNS);\n     -\t\tcore_apply_sparse_checkout = 1;\n    -+\t\tthe_repository->config_values.apply_sparse_checkout = 1;\n    ++\t\tcfg->apply_sparse_checkout = 1;\n      \t\tchanged_config = 1;\n      \t}\n\n     @@ builtin/sparse-checkout.c: static int sparse_checkout_add(int argc, const char **argv, const char *prefix,\n    + \t};\n    + \tstruct strvec patterns = STRVEC_INIT;\n      \tint ret;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n\n      \tsetup_work_tree();\n     -\tif (!core_apply_sparse_checkout)\n    -+\tif (!the_repository->config_values.apply_sparse_checkout)\n    ++\tif (!cfg->apply_sparse_checkout)\n      \t\tdie(_(\"no sparse-checkout to add to\"));\n\n      \trepo_read_index(repo);\n     @@ builtin/sparse-checkout.c: static int sparse_checkout_reapply(int argc, const char **argv,\n    + \t\t\t N_(\"toggle the use of a sparse index\")),\n    + \t\tOPT_END(),\n      \t};\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n\n      \tsetup_work_tree();\n     -\tif (!core_apply_sparse_checkout)\n    -+\tif (!the_repository->config_values.apply_sparse_checkout)\n    ++\tif (!cfg->apply_sparse_checkout)\n      \t\tdie(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n\n      \treapply_opts.cone_mode = -1;\n    +@@ builtin/sparse-checkout.c: static int sparse_checkout_clean(int argc, const char **argv,\n    + \tsize_t worktree_len;\n    + \tint force = 0, dry_run = 0, verbose = 0;\n    + \tint require_force = 1;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    +\n    + \tstruct option builtin_sparse_checkout_clean_options[] = {\n    + \t\tOPT__DRY_RUN(&dry_run, N_(\"dry run\")),\n     @@ builtin/sparse-checkout.c: static int sparse_checkout_clean(int argc, const char **argv,\n      \t};\n\n      \tsetup_work_tree();\n     -\tif (!core_apply_sparse_checkout)\n    -+\tif (!the_repository->config_values.apply_sparse_checkout)\n    ++\tif (!cfg->apply_sparse_checkout)\n      \t\tdie(_(\"must be in a sparse-checkout to clean directories\"));\n      \tif (!core_sparse_checkout_cone)\n      \t\tdie(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n     @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const char **argv,\n    + \t\tOPT_END(),\n    + \t};\n      \tstruct pattern_list pl;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n\n      \t/*\n     -\t * We do not exit early if !core_apply_sparse_checkout; due to the\n    @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const ch\n      \thashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n      \tpl.use_cone_patterns = 0;\n     -\tcore_apply_sparse_checkout = 1;\n    -+\tthe_repository->config_values.apply_sparse_checkout = 1;\n    ++\tcfg->apply_sparse_checkout = 1;\n\n      \tadd_pattern(\"/*\", empty_base, 0, &pl, 0);\n\n\n      ## builtin/worktree.c ##\n    +@@ builtin/worktree.c: static int add_worktree(const char *path, const char *refname,\n    + \tstruct strbuf sb_name = STRBUF_INIT;\n    + \tstruct worktree **worktrees, *wt = NULL;\n    + \tstruct ref_store *wt_refs;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    +\n    + \tworktrees = get_worktrees();\n    + \tcheck_candidate_path(path, opts->force, worktrees, \"add\");\n     @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refname,\n      \t * If the current worktree has sparse-checkout enabled, then copy\n      \t * the sparse-checkout patterns from the current worktree.\n      \t */\n     -\tif (core_apply_sparse_checkout)\n    -+\tif (the_repository->config_values.apply_sparse_checkout)\n    ++\tif (cfg->apply_sparse_checkout)\n      \t\tcopy_sparse_checkout(sb_repo.buf);\n\n      \t/*\n    @@ dir.c: enum pattern_match_result path_matches_pattern_list(\n      int init_sparse_checkout_patterns(struct index_state *istate)\n      {\n     -\tif (!core_apply_sparse_checkout)\n    -+\tif (!the_repository->config_values.apply_sparse_checkout)\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    ++\tif (!cfg->apply_sparse_checkout)\n      \t\treturn 1;\n      \tif (istate->sparse_checkout_patterns)\n      \t\treturn 0;\n    @@ environment.c: int git_default_config(const char *var, const char *value,\n      }\n\n      ## environment.h ##\n    -@@ environment.h: struct strvec;\n    +@@ environment.h: struct repository;\n      struct repo_config_values {\n      \t/* section \"core\" config values */\n      \tchar *attributes_file;\n     +\tint apply_sparse_checkout;\n      };\n\n    - /*\n    + struct repo_config_values *repo_config_values(struct repository *repo);\n     @@ environment.h: extern int precomposed_unicode;\n      extern int protect_hfs;\n      extern int protect_ntfs;\n    @@ sparse-index.c: static int index_has_unmerged_entries(struct index_state *istate\n      int is_sparse_index_allowed(struct index_state *istate, int flags)\n      {\n     -\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n    -+\tstruct repo_config_values *cfg = &the_repository->config_values;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n     +\tif (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)\n      \t\treturn 0;\n\n    @@ sparse-index.c: static void clear_skip_worktree_from_present_files_full(struct i\n      void clear_skip_worktree_from_present_files(struct index_state *istate)\n      {\n     -\tif (!core_apply_sparse_checkout ||\n    -+\tstruct repo_config_values *cfg = &the_repository->config_values;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n     +\tif (!cfg->apply_sparse_checkout ||\n      \t    sparse_expect_files_outside_of_patterns)\n      \t\treturn;\n\n\n      ## unpack-trees.c ##\n    +@@ unpack-trees.c: int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n    + \tstruct pattern_list pl;\n    + \tint free_pattern_list = 0;\n    + \tstruct dir_struct dir = DIR_INIT;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    +\n    + \tif (o->reset == UNPACK_RESET_INVALID)\n    + \t\tBUG(\"o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED\");\n     @@ unpack-trees.c: int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n      \tif (o->prefix)\n      \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n\n     -\tif (!core_apply_sparse_checkout || !o->update)\n    -+\tif (!the_repository->config_values.apply_sparse_checkout || !o->update)\n    ++\tif (!cfg->apply_sparse_checkout || !o->update)\n      \t\to->skip_sparse_checkout = 1;\n      \tif (!o->skip_sparse_checkout) {\n      \t\tmemset(&pl, 0, sizeof(pl));\n\n      ## wt-status.c ##\n     @@ wt-status.c: static void wt_status_check_sparse_checkout(struct repository *r,\n    + {\n      \tint skip_worktree = 0;\n      \tint i;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n\n     -\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n    -+\tif (!the_repository->config_values.apply_sparse_checkout ||\n    ++\tif (!cfg->apply_sparse_checkout ||\n     +\t    r->index->cache_nr == 0) {\n      \t\t/*\n      \t\t * Don't compute percentage of checked out files if we\n3:  e7f37bac87 ! 3:  60451b93a5 environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`\n    @@ branch.h: enum branch_track {\n      /**\n\n      ## builtin/branch.c ##\n    +@@ builtin/branch.c: int cmd_branch(int argc,\n    + \tstatic struct ref_sorting *sorting;\n    + \tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n    + \tstruct ref_format format = REF_FORMAT_INIT;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    + \tint ret;\n    +\n    + \tstruct option options[] = {\n     @@ builtin/branch.c: int cmd_branch(int argc,\n      \tif (!sorting_options.nr)\n      \t\tstring_list_append(&sorting_options, \"refname\");\n\n     -\ttrack = git_branch_track;\n    -+\ttrack = the_repository->config_values.branch_track;\n    ++\ttrack = cfg->branch_track;\n\n      \thead = refs_resolve_refdup(get_main_ref_store(the_repository), \"HEAD\",\n      \t\t\t\t   0, &head_oid, NULL);\n\n      ## builtin/checkout.c ##\n    +@@ builtin/checkout.c: static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,\n    + static int checkout_branch(struct checkout_opts *opts,\n    + \t\t\t   struct branch_info *new_branch_info)\n    + {\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    + \tint noop_switch = (!new_branch_info->name &&\n    + \t\t\t   !opts->new_branch &&\n    + \t\t\t   !opts->force_detach);\n     @@ builtin/checkout.c: static int checkout_branch(struct checkout_opts *opts,\n      \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n      \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n      \t} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)\n     -\t\topts->track = git_branch_track;\n    -+\t\topts->track = the_repository->config_values.branch_track;\n    ++\t\topts->track = cfg->branch_track;\n\n      \tif (new_branch_info->name && !new_branch_info->commit)\n      \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\n\n      ## builtin/push.c ##\n    +@@ builtin/push.c: static NORETURN void die_push_simple(struct branch *branch,\n    + \tconst char *advice_pushdefault_maybe = \"\";\n    + \tconst char *advice_automergesimple_maybe = \"\";\n    + \tconst char *short_upstream = branch->merge[0]->src;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    +\n    + \tskip_prefix(short_upstream, \"refs/heads/\", &short_upstream);\n    +\n     @@ builtin/push.c: static NORETURN void die_push_simple(struct branch *branch,\n      \t\tadvice_pushdefault_maybe = _(\"\\n\"\n      \t\t\t\t \"To choose either option permanently, \"\n      \t\t\t\t \"see push.default in 'git help config'.\\n\");\n     -\tif (git_branch_track != BRANCH_TRACK_SIMPLE)\n    -+\tif (the_repository->config_values.branch_track != BRANCH_TRACK_SIMPLE)\n    ++\tif (cfg->branch_track != BRANCH_TRACK_SIMPLE)\n      \t\tadvice_automergesimple_maybe = _(\"\\n\"\n      \t\t\t\t \"To avoid automatically configuring \"\n      \t\t\t\t \"an upstream branch when its name\\n\"\n\n      ## builtin/submodule--helper.c ##\n     @@ builtin/submodule--helper.c: static int module_create_branch(int argc, const char **argv, const char *prefix,\n    + \t\tN_(\"git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] [-n|--dry-run] <name> <start-oid> <start-name>\"),\n    + \t\tNULL\n      \t};\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n\n      \trepo_config(the_repository, git_default_config, NULL);\n     -\ttrack = git_branch_track;\n    -+\ttrack = the_repository->config_values.branch_track;\n    ++\ttrack = cfg->branch_track;\n      \targc = parse_options(argc, argv, prefix, options, usage, 0);\n\n      \tif (argc != 3)\n    @@ environment.c: static int git_default_i18n_config(const char *var, const char *v\n\n      static int git_default_branch_config(const char *var, const char *value)\n      {\n    -+\tstruct repo_config_values *cfg = &the_repository->config_values;\n    ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n     +\n      \tif (!strcmp(var, \"branch.autosetupmerge\")) {\n      \t\tif (value && !strcmp(value, \"always\")) {\n    @@ environment.h: struct repo_config_values {\n     +\tenum branch_track branch_track;\n      };\n\n    - /*\n    + struct repo_config_values *repo_config_values(struct repository *repo);\n\n\n-- \n2.34.1\n\n"},{"id":"535067","messageId":"7e3082125df08d3e5fb2195d73698c4c28c6645e.1770127568.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1770127568.git.belkid98@gmail.com","subject":"[Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-03T15:42:09Z","receivedAt":"2026-02-03T15:42:37Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The `core.attributeFile` config value is parsed in\ngit_default_core_config(), loaded eagerly and stored in the global\nvariable `git_attributes_file`. Storing this value in a global variable\ncan lead to it being overwritten by another repository when more than one\nGit repository run in the same Git process.\n\nCreate a new struct `repo_config_values` to hold this value and\nother repository dependent values parsed by `git_default_config()`.\nThis will ensure the current behaviour remains the same while also\nenabling the libification of Git.\n\nAn accessor function 'repo_config_values()' is created and used to access\nthe new struct member of the repository struct.\nThis is to ensure that we detect if the struct repository has been\ninitialized and also prevent double initialization of the repository.\n\nIt is important to note that `git_default_config()` is a wrapper to other\n`git_default_*_config()` functions such as `git_default_core_config()`.\nTherefore to access and modify this global variable,\nthe change has to be made `git_default_core_config()`.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n attr.c        |  7 ++++---\n environment.c | 12 +++++++++---\n environment.h | 11 ++++++++++-\n repository.c  | 12 ++++++++++++\n repository.h  |  7 +++++++\n 5 files changed, 42 insertions(+), 7 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 4999b7e09d..75369547b3 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)\n \n const char *git_attr_global_file(void)\n {\n-\tif (!git_attributes_file)\n-\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\tif (!cfg->attributes_file)\n+\t\tcfg->attributes_file = xdg_config_home(\"attributes\");\n \n-\treturn git_attributes_file;\n+\treturn cfg->attributes_file;\n }\n \n int git_attr_system_is_enabled(void)\ndiff --git a/environment.c b/environment.c\nindex a770b5921d..4b5c701e80 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -53,7 +53,6 @@ char *git_commit_encoding;\n char *git_log_output_encoding;\n char *apply_default_whitespace;\n char *apply_default_ignorewhitespace;\n-char *git_attributes_file;\n int zlib_compression_level = Z_BEST_SPEED;\n int pack_compression_level = Z_DEFAULT_COMPRESSION;\n int fsync_object_files = -1;\n@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n static int git_default_core_config(const char *var, const char *value,\n \t\t\t\t   const struct config_context *ctx, void *cb)\n {\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\n \t/* This needs a better name */\n \tif (!strcmp(var, \"core.filemode\")) {\n \t\ttrust_executable_bit = git_config_bool(var, value);\n@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.attributesfile\")) {\n-\t\tFREE_AND_NULL(git_attributes_file);\n-\t\treturn git_config_pathname(&git_attributes_file, var, value);\n+\t\tFREE_AND_NULL(cfg->attributes_file);\n+\t\treturn git_config_pathname(&cfg->attributes_file, var, value);\n \t}\n \n \tif (!strcmp(var, \"core.bare\")) {\n@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,\n \t/* Add other config variables here and to Documentation/config.adoc. */\n \treturn 0;\n }\n+\n+void repo_config_values_init(struct repo_config_values *cfg)\n+{\n+\tcfg->attributes_file = NULL;\n+}\ndiff --git a/environment.h b/environment.h\nindex 51898c99cd..dfc31b794d 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -84,6 +84,14 @@ extern const char * const local_repo_env[];\n \n struct strvec;\n \n+struct repository;\n+struct repo_config_values {\n+\t/* section \"core\" config values */\n+\tchar *attributes_file;\n+};\n+\n+struct repo_config_values *repo_config_values(struct repository *repo);\n+\n /*\n  * Wrapper of getenv() that returns a strdup value. This value is kept\n  * in argv to be freed later.\n@@ -107,6 +115,8 @@ const char *strip_namespace(const char *namespaced_ref);\n int git_default_config(const char *, const char *,\n \t\t       const struct config_context *, void *);\n \n+void repo_config_values_init(struct repo_config_values *cfg);\n+\n /*\n  * TODO: All the below state either explicitly or implicitly relies on\n  * `the_repository`. We should eventually get rid of these and make the\n@@ -152,7 +162,6 @@ extern int assume_unchanged;\n extern int warn_on_object_refname_ambiguity;\n extern char *apply_default_whitespace;\n extern char *apply_default_ignorewhitespace;\n-extern char *git_attributes_file;\n extern int zlib_compression_level;\n extern int pack_compression_level;\n extern unsigned long pack_size_limit_cfg;\ndiff --git a/repository.c b/repository.c\nindex c7e75215ac..a9b727540f 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -50,13 +50,25 @@ static void set_default_hash_algo(struct repository *repo)\n \trepo_set_hash_algo(repo, algo);\n }\n \n+struct repo_config_values *repo_config_values(struct repository *repo)\n+{\n+\tif(!repo->initialized)\n+\t\tBUG(\"config values from uninitialized repository\");\n+\treturn &repo->config_values_private_;\n+}\n+\n void initialize_repository(struct repository *repo)\n {\n+\tif (repo->initialized)\n+\t\tBUG(\"repository initialized already\");\n+\trepo->initialized = true;\n+\n \trepo->remote_state = remote_state_new();\n \trepo->parsed_objects = parsed_object_pool_new(repo);\n \tALLOC_ARRAY(repo->index, 1);\n \tindex_state_init(repo->index, repo);\n \trepo->check_deprecated_config = true;\n+\trepo_config_values_init(repo_config_values(repo));\n \n \t/*\n \t * When a command runs inside a repository, it learns what\ndiff --git a/repository.h b/repository.h\nindex 6063c4b846..9717e45000 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -3,6 +3,7 @@\n \n #include \"strmap.h\"\n #include \"repo-settings.h\"\n+#include \"environment.h\"\n \n struct config_set;\n struct git_hash_algo;\n@@ -148,6 +149,9 @@ struct repository {\n \t/* Repository's compatibility hash algorithm. */\n \tconst struct git_hash_algo *compat_hash_algo;\n \n+\t/* Repository's config values parsed by git_default_config() */\n+\tstruct repo_config_values config_values_private_;\n+\n \t/* Repository's reference storage format, as serialized on disk. */\n \tenum ref_storage_format ref_storage_format;\n \n@@ -171,6 +175,9 @@ struct repository {\n \n \t/* Should repo_config() check for deprecated settings */\n \tbool check_deprecated_config;\n+\n+\t/* Has this repository instance been initialized? */\n+\tbool initialized;\n };\n \n #ifdef USE_THE_REPOSITORY_VARIABLE\n-- \n2.34.1\n\n"},{"id":"535068","messageId":"8645b4f5952a98ed32180466fc40a184c61081b5.1770127568.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1770127568.git.belkid98@gmail.com","subject":"[Outreachy PATCH v6 2/3] environment: stop using core.sparseCheckout globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-03T15:42:10Z","receivedAt":"2026-02-03T15:42:40Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `core.sparseCheckout` is parsed in\n`git_default_core_config()` and stored globally in\n`core_apply_sparse_checkout`. This could cause it to be overwritten\nby another repository when different Git repositories run in the same\nprocess.\n\nMove the parsed value into `struct repo_config_values` in the_repository\nto retain current behaviours and move towards libifying Git.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n builtin/backfill.c        |  3 ++-\n builtin/clone.c           |  3 ++-\n builtin/grep.c            |  2 +-\n builtin/mv.c              |  3 ++-\n builtin/sparse-checkout.c | 31 ++++++++++++++++++++-----------\n builtin/worktree.c        |  3 ++-\n dir.c                     |  3 ++-\n environment.c             |  4 ++--\n environment.h             |  2 +-\n sparse-index.c            |  6 ++++--\n unpack-trees.c            |  3 ++-\n wt-status.c               |  4 +++-\n 12 files changed, 43 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/backfill.c b/builtin/backfill.c\nindex e80fc1b694..9b2ca57b6a 100644\n--- a/builtin/backfill.c\n+++ b/builtin/backfill.c\n@@ -129,6 +129,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n \t\t\t N_(\"Restrict the missing objects to the current sparse-checkout\")),\n \t\tOPT_END(),\n \t};\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tshow_usage_with_options_if_asked(argc, argv,\n \t\t\t\t\t builtin_backfill_usage, options);\n@@ -139,7 +140,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n \trepo_config(repo, git_default_config, NULL);\n \n \tif (ctx.sparse < 0)\n-\t\tctx.sparse = core_apply_sparse_checkout;\n+\t\tctx.sparse = cfg->apply_sparse_checkout;\n \n \tresult = do_backfill(&ctx);\n \tbackfill_context_clear(&ctx);\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..0005fd7118 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -617,13 +617,14 @@ static int git_sparse_checkout_init(const char *repo)\n {\n \tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint result = 0;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \tstrvec_pushl(&cmd.args, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n \n \t/*\n \t * We must apply the setting in the current process\n \t * for the later checkout to use the sparse-checkout file.\n \t */\n-\tcore_apply_sparse_checkout = 1;\n+\tcfg->apply_sparse_checkout = 1;\n \n \tcmd.git_cmd = 1;\n \tif (run_command(&cmd)) {\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 53cccf2d25..207d340548 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,\n \t *\t\"forget\" the sparse-index feature switch. As a result, the index\n \t *\tof these submodules are expanded unexpectedly.\n \t *\n-\t * 2. \"core_apply_sparse_checkout\"\n+\t * 2. \"config_values_private_.apply_sparse_checkout\"\n \t *\tWhen running `grep` in the superproject, this setting is\n \t *\tpopulated using the superproject's configs. However, once\n \t *\tinitialized, this config is globally accessible and is read by\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex d43925097b..2215d34e31 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -238,6 +238,7 @@ int cmd_mv(int argc,\n \tstruct hashmap moved_dirs = HASHMAP_INIT(pathmap_cmp, NULL);\n \tstruct strbuf pathbuf = STRBUF_INIT;\n \tint ret;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \trepo_config(the_repository, git_default_config, NULL);\n \n@@ -572,7 +573,7 @@ int cmd_mv(int argc,\n \t\trename_index_entry_at(the_repository->index, pos, dst);\n \n \t\tif (ignore_sparse &&\n-\t\t    core_apply_sparse_checkout &&\n+\t\t    cfg->apply_sparse_checkout &&\n \t\t    core_sparse_checkout_cone) {\n \t\t\t/*\n \t\t\t * NEEDSWORK: we are *not* paying attention to\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..7f3317841e 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -61,9 +61,10 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,\n \tstruct pattern_list pl;\n \tchar *sparse_filename;\n \tint res;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!cfg->apply_sparse_checkout)\n \t\tdie(_(\"this worktree is not sparse\"));\n \n \targc = parse_options(argc, argv, prefix,\n@@ -399,12 +400,14 @@ static int set_config(struct repository *repo,\n }\n \n static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\n \t/* If not specified, use previous definition of cone mode */\n-\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n+\tif (*cone_mode == -1 && cfg->apply_sparse_checkout)\n \t\t*cone_mode = core_sparse_checkout_cone;\n \n \t/* Set cone/non-cone mode appropriately */\n-\tcore_apply_sparse_checkout = 1;\n+\tcfg->apply_sparse_checkout = 1;\n \tif (*cone_mode == 1 || *cone_mode == -1) {\n \t\tcore_sparse_checkout_cone = 1;\n \t\treturn MODE_CONE_PATTERNS;\n@@ -416,9 +419,10 @@ static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n static int update_modes(struct repository *repo, int *cone_mode, int *sparse_index)\n {\n \tint mode, record_mode;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \t/* Determine if we need to record the mode; ensure sparse checkout on */\n-\trecord_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n+\trecord_mode = (*cone_mode != -1) || !cfg->apply_sparse_checkout;\n \n \tmode = update_cone_mode(cone_mode);\n \tif (record_mode && set_config(repo, mode))\n@@ -684,6 +688,7 @@ static int modify_pattern_list(struct repository *repo,\n \tint result;\n \tint changed_config = 0;\n \tstruct pattern_list *pl = xcalloc(1, sizeof(*pl));\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tswitch (m) {\n \tcase ADD:\n@@ -699,9 +704,9 @@ static int modify_pattern_list(struct repository *repo,\n \t\tbreak;\n \t}\n \n-\tif (!core_apply_sparse_checkout) {\n+\tif (!cfg->apply_sparse_checkout) {\n \t\tset_config(repo, MODE_ALL_PATTERNS);\n-\t\tcore_apply_sparse_checkout = 1;\n+\t\tcfg->apply_sparse_checkout = 1;\n \t\tchanged_config = 1;\n \t}\n \n@@ -796,9 +801,10 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,\n \t};\n \tstruct strvec patterns = STRVEC_INIT;\n \tint ret;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!cfg->apply_sparse_checkout)\n \t\tdie(_(\"no sparse-checkout to add to\"));\n \n \trepo_read_index(repo);\n@@ -905,9 +911,10 @@ static int sparse_checkout_reapply(int argc, const char **argv,\n \t\t\t N_(\"toggle the use of a sparse index\")),\n \t\tOPT_END(),\n \t};\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!cfg->apply_sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n \n \treapply_opts.cone_mode = -1;\n@@ -960,6 +967,7 @@ static int sparse_checkout_clean(int argc, const char **argv,\n \tsize_t worktree_len;\n \tint force = 0, dry_run = 0, verbose = 0;\n \tint require_force = 1;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tstruct option builtin_sparse_checkout_clean_options[] = {\n \t\tOPT__DRY_RUN(&dry_run, N_(\"dry run\")),\n@@ -969,7 +977,7 @@ static int sparse_checkout_clean(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!cfg->apply_sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to clean directories\"));\n \tif (!core_sparse_checkout_cone)\n \t\tdie(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n@@ -1033,9 +1041,10 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \t\tOPT_END(),\n \t};\n \tstruct pattern_list pl;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \t/*\n-\t * We do not exit early if !core_apply_sparse_checkout; due to the\n+\t * We do not exit early if !repo->config_values.apply_sparse_checkout; due to the\n \t * ability for users to manually muck things up between\n \t *   direct editing of .git/info/sparse-checkout\n \t *   running read-tree -m u HEAD or update-index --skip-worktree\n@@ -1061,7 +1070,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \thashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);\n \thashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n \tpl.use_cone_patterns = 0;\n-\tcore_apply_sparse_checkout = 1;\n+\tcfg->apply_sparse_checkout = 1;\n \n \tadd_pattern(\"/*\", empty_base, 0, &pl, 0);\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex fbdaf2eb2e..90e56ab495 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -440,6 +440,7 @@ static int add_worktree(const char *path, const char *refname,\n \tstruct strbuf sb_name = STRBUF_INIT;\n \tstruct worktree **worktrees, *wt = NULL;\n \tstruct ref_store *wt_refs;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tworktrees = get_worktrees();\n \tcheck_candidate_path(path, opts->force, worktrees, \"add\");\n@@ -536,7 +537,7 @@ static int add_worktree(const char *path, const char *refname,\n \t * If the current worktree has sparse-checkout enabled, then copy\n \t * the sparse-checkout patterns from the current worktree.\n \t */\n-\tif (core_apply_sparse_checkout)\n+\tif (cfg->apply_sparse_checkout)\n \t\tcopy_sparse_checkout(sb_repo.buf);\n \n \t/*\ndiff --git a/dir.c b/dir.c\nindex b00821f294..0e05b3a383 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1551,7 +1551,8 @@ enum pattern_match_result path_matches_pattern_list(\n \n int init_sparse_checkout_patterns(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout)\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\tif (!cfg->apply_sparse_checkout)\n \t\treturn 1;\n \tif (istate->sparse_checkout_patterns)\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex 4b5c701e80..390af1ce54 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -74,7 +74,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #endif\n enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n int grafts_keep_true_parents;\n-int core_apply_sparse_checkout;\n int core_sparse_checkout_cone;\n int sparse_expect_files_outside_of_patterns;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.sparsecheckout\")) {\n-\t\tcore_apply_sparse_checkout = git_config_bool(var, value);\n+\t\tcfg->apply_sparse_checkout = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \n@@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,\n void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file = NULL;\n+\tcfg->apply_sparse_checkout = 0;\n }\ndiff --git a/environment.h b/environment.h\nindex dfc31b794d..2e24160322 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -88,6 +88,7 @@ struct repository;\n struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n+\tint apply_sparse_checkout;\n };\n \n struct repo_config_values *repo_config_values(struct repository *repo);\n@@ -171,7 +172,6 @@ extern int precomposed_unicode;\n extern int protect_hfs;\n extern int protect_ntfs;\n \n-extern int core_apply_sparse_checkout;\n extern int core_sparse_checkout_cone;\n extern int sparse_expect_files_outside_of_patterns;\n \ndiff --git a/sparse-index.c b/sparse-index.c\nindex 76f90da5f5..cb4c99dcae 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)\n \n int is_sparse_index_allowed(struct index_state *istate, int flags)\n {\n-\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\tif (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)\n \t\treturn 0;\n \n \tif (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n@@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n \n void clear_skip_worktree_from_present_files(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout ||\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\tif (!cfg->apply_sparse_checkout ||\n \t    sparse_expect_files_outside_of_patterns)\n \t\treturn;\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex f38c761ab9..998a1e6dc7 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1888,6 +1888,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tstruct pattern_list pl;\n \tint free_pattern_list = 0;\n \tstruct dir_struct dir = DIR_INIT;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tif (o->reset == UNPACK_RESET_INVALID)\n \t\tBUG(\"o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED\");\n@@ -1924,7 +1925,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tif (o->prefix)\n \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n \n-\tif (!core_apply_sparse_checkout || !o->update)\n+\tif (!cfg->apply_sparse_checkout || !o->update)\n \t\to->skip_sparse_checkout = 1;\n \tif (!o->skip_sparse_checkout) {\n \t\tmemset(&pl, 0, sizeof(pl));\ndiff --git a/wt-status.c b/wt-status.c\nindex e12adb26b9..6609b37cad 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1763,8 +1763,10 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n {\n \tint skip_worktree = 0;\n \tint i;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n-\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n+\tif (!cfg->apply_sparse_checkout ||\n+\t    r->index->cache_nr == 0) {\n \t\t/*\n \t\t * Don't compute percentage of checked out files if we\n \t\t * aren't in a sparse checkout or would get division by 0.\n-- \n2.34.1\n\n"},{"id":"535069","messageId":"60451b93a57faba9b6c782c7e290f7db7e8411ad.1770127568.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1770127568.git.belkid98@gmail.com","subject":"[Outreachy PATCH v6 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-03T15:42:11Z","receivedAt":"2026-02-03T15:42:44Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `branch.autoSetupMerge` is parsed in\n`git_default_branch_config()` and stored in the global variable\n`git_branch_track`. This global variable can be overwritten\nby another repository when multiple Git repos run in the the same process.\n\nMove this value into `struct repo_config_values` in the_repository to\nretain current behaviours and move towards libifying Git.\nSince the variable is no longer a global variable, it has been renamed to\n`branch_track` in the struct `repo_config_values`.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n branch.h                    |  2 --\n builtin/branch.c            |  3 ++-\n builtin/checkout.c          |  3 ++-\n builtin/push.c              |  3 ++-\n builtin/submodule--helper.c |  3 ++-\n environment.c               | 12 +++++++-----\n environment.h               |  4 ++++\n 7 files changed, 19 insertions(+), 11 deletions(-)\n\ndiff --git a/branch.h b/branch.h\nindex ec2f35fda4..3dc6e2a0ff 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -15,8 +15,6 @@ enum branch_track {\n \tBRANCH_TRACK_SIMPLE,\n };\n \n-extern enum branch_track git_branch_track;\n-\n /* Functions for acting on the information about branches. */\n \n /**\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex c577b5d20f..a1a43380d0 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -724,6 +724,7 @@ int cmd_branch(int argc,\n \tstatic struct ref_sorting *sorting;\n \tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n \tstruct ref_format format = REF_FORMAT_INIT;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \tint ret;\n \n \tstruct option options[] = {\n@@ -795,7 +796,7 @@ int cmd_branch(int argc,\n \tif (!sorting_options.nr)\n \t\tstring_list_append(&sorting_options, \"refname\");\n \n-\ttrack = git_branch_track;\n+\ttrack = cfg->branch_track;\n \n \thead = refs_resolve_refdup(get_main_ref_store(the_repository), \"HEAD\",\n \t\t\t\t   0, &head_oid, NULL);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 261699e2f5..ea728e733c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1588,6 +1588,7 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,\n static int checkout_branch(struct checkout_opts *opts,\n \t\t\t   struct branch_info *new_branch_info)\n {\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \tint noop_switch = (!new_branch_info->name &&\n \t\t\t   !opts->new_branch &&\n \t\t\t   !opts->force_detach);\n@@ -1631,7 +1632,7 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n \t} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)\n-\t\topts->track = git_branch_track;\n+\t\topts->track = cfg->branch_track;\n \n \tif (new_branch_info->name && !new_branch_info->commit)\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 5b6cebbb85..7100ffba5d 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -151,6 +151,7 @@ static NORETURN void die_push_simple(struct branch *branch,\n \tconst char *advice_pushdefault_maybe = \"\";\n \tconst char *advice_automergesimple_maybe = \"\";\n \tconst char *short_upstream = branch->merge[0]->src;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tskip_prefix(short_upstream, \"refs/heads/\", &short_upstream);\n \n@@ -162,7 +163,7 @@ static NORETURN void die_push_simple(struct branch *branch,\n \t\tadvice_pushdefault_maybe = _(\"\\n\"\n \t\t\t\t \"To choose either option permanently, \"\n \t\t\t\t \"see push.default in 'git help config'.\\n\");\n-\tif (git_branch_track != BRANCH_TRACK_SIMPLE)\n+\tif (cfg->branch_track != BRANCH_TRACK_SIMPLE)\n \t\tadvice_automergesimple_maybe = _(\"\\n\"\n \t\t\t\t \"To avoid automatically configuring \"\n \t\t\t\t \"an upstream branch when its name\\n\"\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d537ab087a..594cd107b3 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3126,9 +3126,10 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,\n \t\tN_(\"git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] [-n|--dry-run] <name> <start-oid> <start-name>\"),\n \t\tNULL\n \t};\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \trepo_config(the_repository, git_default_config, NULL);\n-\ttrack = git_branch_track;\n+\ttrack = cfg->branch_track;\n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \n \tif (argc != 3)\ndiff --git a/environment.c b/environment.c\nindex 390af1ce54..1bc3adb75b 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;\n enum eol core_eol = EOL_UNSET;\n int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;\n char *check_roundtrip_encoding;\n-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n enum rebase_setup_type autorebase = AUTOREBASE_NEVER;\n enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #ifndef OBJECT_CREATION_MODE\n@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)\n \n static int git_default_branch_config(const char *var, const char *value)\n {\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\n \tif (!strcmp(var, \"branch.autosetupmerge\")) {\n \t\tif (value && !strcmp(value, \"always\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_ALWAYS;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_ALWAYS;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"simple\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_SIMPLE;\n \t\t\treturn 0;\n \t\t}\n-\t\tgit_branch_track = git_config_bool(var, value);\n+\t\tcfg->branch_track = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"branch.autosetuprebase\")) {\n@@ -761,4 +762,5 @@ void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file = NULL;\n \tcfg->apply_sparse_checkout = 0;\n+\tcfg->branch_track = BRANCH_TRACK_REMOTE;\n }\ndiff --git a/environment.h b/environment.h\nindex 2e24160322..4bfd798757 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -2,6 +2,7 @@\n #define ENVIRONMENT_H\n \n #include \"repo-settings.h\"\n+#include \"branch.h\"\n \n /* Double-check local_repo_env below if you add to this list. */\n #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n@@ -89,6 +90,9 @@ struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n \tint apply_sparse_checkout;\n+\n+\t/* section \"branch\" config values */\n+\tenum branch_track branch_track;\n };\n \n struct repo_config_values *repo_config_values(struct repository *repo);\n-- \n2.34.1\n\n"},{"id":"535166","messageId":"c95a7730-7b14-4be0-a4e4-861b2f5430ea@gmail.com","threadId":"64782","inReplyTo":"7e3082125df08d3e5fb2195d73698c4c28c6645e.1770127568.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-02-04T16:39:09Z","receivedAt":"2026-02-04T16:39:12Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 03/02/2026 15:42, Olamide Caleb Bello wrote:\n> The `core.attributeFile` config value is parsed in\n> git_default_core_config(), loaded eagerly and stored in the global\n> variable `git_attributes_file`. Storing this value in a global variable\n> can lead to it being overwritten by another repository when more than one\n> Git repository run in the same Git process.\n> \n> Create a new struct `repo_config_values` to hold this value and\n> other repository dependent values parsed by `git_default_config()`.\n> This will ensure the current behaviour remains the same while also\n> enabling the libification of Git.\n> \n> An accessor function 'repo_config_values()' is created and used to access\n> the new struct member of the repository struct.\n> This is to ensure that we detect if the struct repository has been\n> initialized and also prevent double initialization of the repository.\n\nSounds sensible. This paragraph could be reflowed.\n\n> It is important to note that `git_default_config()` is a wrapper to other\n> `git_default_*_config()` functions such as `git_default_core_config()`.\n> Therefore to access and modify this global variable,\n> the change has to be made `git_default_core_config()`.\n\nI'm not sure what this paragraph is saying with regard to the changes in \nthis patch.\n\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,\n>   \t/* Add other config variables here and to Documentation/config.adoc. */\n>   \treturn 0;\n>   }\n> +\n> +void repo_config_values_init(struct repo_config_values *cfg)\n> +{\n> +\tcfg->attributes_file = NULL;\n> +}\n\nShould we be free()ing cfg->attributes_file when the repository instance \nis free()d? At the moment we're using \"the_repository\" which points to a \nstatic instance so it does not make any practical difference but once we \nstart storing the config per-repository instance we will need to free \nthe config when the repository instance is free()d.\n\n> diff --git a/repository.c b/repository.c\n> index c7e75215ac..a9b727540f 100644\n> --- a/repository.c\n> +++ b/repository.c\n> @@ -50,13 +50,25 @@ static void set_default_hash_algo(struct repository *repo)\n>   \trepo_set_hash_algo(repo, algo);\n>   }\n>   \n> +struct repo_config_values *repo_config_values(struct repository *repo)\n> +{\n> +\tif(!repo->initialized)\n> +\t\tBUG(\"config values from uninitialized repository\");\n\nThis check and the one in initialize_repository() below assume that the \nrepository instance is zeroed out when it is created, that's a \nreasonable requirement but we should probably document it as our other \ndata structures tend not to require that they're zeroed out before they \nare initialized. For example\n\n\tstruct strbuf buf;\n\tstrbuf_init(&buf, 0);\n\nis perfectly fine as strbuf_init() does not assume the instance passed \nto it has been zeroed out.\n\nAs we only support retrieving values from \"the_repository\" at the moment \nwe should perhaps add\n\n\tif (repo != the_repository)\n\t\tBUG(\"trying to read config from wrong repository instance\");\n\nEverything else looks fine to me\n\nThanks\n\nPhillip\n\n> +\treturn &repo->config_values_private_;\n> +}\n> +\n>   void initialize_repository(struct repository *repo)\n>   {\n> +\tif (repo->initialized)\n> +\t\tBUG(\"repository initialized already\");\n> +\trepo->initialized = true;\n> +\n>   \trepo->remote_state = remote_state_new();\n>   \trepo->parsed_objects = parsed_object_pool_new(repo);\n>   \tALLOC_ARRAY(repo->index, 1);\n>   \tindex_state_init(repo->index, repo);\n>   \trepo->check_deprecated_config = true;\n> +\trepo_config_values_init(repo_config_values(repo));\n>   \n>   \t/*\n>   \t * When a command runs inside a repository, it learns what\n> diff --git a/repository.h b/repository.h\n> index 6063c4b846..9717e45000 100644\n> --- a/repository.h\n> +++ b/repository.h\n> @@ -3,6 +3,7 @@\n>   \n>   #include \"strmap.h\"\n>   #include \"repo-settings.h\"\n> +#include \"environment.h\"\n>   \n>   struct config_set;\n>   struct git_hash_algo;\n> @@ -148,6 +149,9 @@ struct repository {\n>   \t/* Repository's compatibility hash algorithm. */\n>   \tconst struct git_hash_algo *compat_hash_algo;\n>   \n> +\t/* Repository's config values parsed by git_default_config() */\n> +\tstruct repo_config_values config_values_private_;\n> +\n>   \t/* Repository's reference storage format, as serialized on disk. */\n>   \tenum ref_storage_format ref_storage_format;\n>   \n> @@ -171,6 +175,9 @@ struct repository {\n>   \n>   \t/* Should repo_config() check for deprecated settings */\n>   \tbool check_deprecated_config;\n> +\n> +\t/* Has this repository instance been initialized? */\n> +\tbool initialized;\n>   };\n>   \n>   #ifdef USE_THE_REPOSITORY_VARIABLE\n\n"},{"id":"535173","messageId":"534b70cc-e1e9-432c-932c-73215d9a4632@gmail.com","threadId":"64782","inReplyTo":"8645b4f5952a98ed32180466fc40a184c61081b5.1770127568.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v6 2/3] environment: stop using core.sparseCheckout globally","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-02-04T16:55:51Z","receivedAt":"2026-02-04T16:55:54Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 03/02/2026 15:42, Olamide Caleb Bello wrote:\n> The config value `core.sparseCheckout` is parsed in\n> `git_default_core_config()` and stored globally in\n> `core_apply_sparse_checkout`. This could cause it to be overwritten\n> by another repository when different Git repositories run in the same\n> process.\n> \n> Move the parsed value into `struct repo_config_values` in the_repository\n> to retain current behaviours and move towards libifying Git.\n\nThis looks good, I've left a small style comment below\n\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index b19b302b06..0005fd7118 100644\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -617,13 +617,14 @@ static int git_sparse_checkout_init(const char *repo)\n>   {\n>   \tstruct child_process cmd = CHILD_PROCESS_INIT;\n>   \tint result = 0;\n> +\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n\nWe normally have a blank line between the varibale declarations and \nstatements. As you're adding a new declaration here it would be good to \nadd a blank line as well. The same goes for dir.c and sparse-index.c below.\n\nThanks\n\nPhillip\n\n>   \tstrvec_pushl(&cmd.args, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n\n > diff --git a/dir.c b/dir.c\n > index b00821f294..0e05b3a383 100644\n > --- a/dir.c\n > +++ b/dir.c\n > @@ -1551,7 +1551,8 @@ enum pattern_match_result \npath_matches_pattern_list(\n >\n >  int init_sparse_checkout_patterns(struct index_state *istate)\n >  {\n > -\tif (!core_apply_sparse_checkout)\n > +\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n > +\tif (!cfg->apply_sparse_checkout)\n >  \t\treturn 1;\n >  \tif (istate->sparse_checkout_patterns)\n >  \t\treturn 0;\n\n> diff --git a/sparse-index.c b/sparse-index.c\n> index 76f90da5f5..cb4c99dcae 100644\n> --- a/sparse-index.c\n> +++ b/sparse-index.c\n> @@ -152,7 +152,8 @@ static int index_has_unmerged_entries(struct index_state *istate)\n>   \n>   int is_sparse_index_allowed(struct index_state *istate, int flags)\n>   {\n> -\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n> +\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n> +\tif (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)\n>   \t\treturn 0;\n>   \n>   \tif (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n> @@ -670,7 +671,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n>   \n>   void clear_skip_worktree_from_present_files(struct index_state *istate)\n>   {\n> -\tif (!core_apply_sparse_checkout ||\n> +\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n> +\tif (!cfg->apply_sparse_checkout ||\n>   \t    sparse_expect_files_outside_of_patterns)\n>   \t\treturn;\n>   \n"},{"id":"535174","messageId":"71b88eca-54b4-49f3-8802-69ff479f1f9c@gmail.com","threadId":"64782","inReplyTo":"cover.1770127568.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v6 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-02-04T16:57:42Z","receivedAt":"2026-02-04T16:57:46Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Olamide\n\nThis version looks pretty good. I've left a couple of suggestions, but \nthe changes since v4 look good.\n\nThanks\n\nPhillip\n\nOn 03/02/2026 15:42, Olamide Caleb Bello wrote:\n> Hi Git Community,\n> Over the course of my ongoing internship, which focused on moving repo specific\n> global variables in environment.h into local scope, I have attempted to move some\n> variables into the struct repo-settings.\n> However there has been some design concerns as regards the use of\n> `prepare_repo_settings()` with respect to when and where to call the\n> function, and also the change in behaviours when the variable is lazily\n> loaded as discussed in [1] and [2].\n> \n> After different deliberations, Phillip Wood proposed creating a new config\n> struct [3], adding it to the repository struct and passing the repo struct to\n> `git_default_config()` to store parsed repo specific config values per repo.\n> This ensures the current behaviours will be retained.\n> \n> I have experimented with this approach for some values and I would\n> appreciate feedbacks about this approach before we can move forward\n> and use it for more variables related to `git_default_config()`.\n> \n> For now, the parsed value is stored in `the_repository` in\n> `git_default_*_config()` and further efforts to pass the repository\n> parameter to `git_default_config()` as the callback parameter will\n> be looked into later on.\n> The link to the CI tests can be see in [4] and [5]\n> in the CI, some BUGs were gotten as a result of double initialization\n> \n> 1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/\n> 2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/\n> 3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com\n> 4. https://github.com/git/git/actions/runs/21633462506/job/62352191148?pr=2166\n> 5. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2303203992\n> \n> Changes in v6:\n> ==============\n> - Added a new accessor function 'repo_config_values()' which ensures we detect\n>    if a repository has been initialized already\n> - Changed the member name to 'config_values_private_' to indicate this member\n>    is private\n> - Modified commit message of patch 1 to reflect these changes\n> \n> Olamide Caleb Bello (3):\n>    environment: stop storing `core.attributesFile` globally\n>    environment: stop using core.sparseCheckout globally\n>    environment: move \"branch.autoSetupMerge\" into `struct\n>      repo_config_values`\n> \n>   attr.c                      |  7 ++++---\n>   branch.h                    |  2 --\n>   builtin/backfill.c          |  3 ++-\n>   builtin/branch.c            |  3 ++-\n>   builtin/checkout.c          |  3 ++-\n>   builtin/clone.c             |  3 ++-\n>   builtin/grep.c              |  2 +-\n>   builtin/mv.c                |  3 ++-\n>   builtin/push.c              |  3 ++-\n>   builtin/sparse-checkout.c   | 31 ++++++++++++++++++++-----------\n>   builtin/submodule--helper.c |  3 ++-\n>   builtin/worktree.c          |  3 ++-\n>   dir.c                       |  3 ++-\n>   environment.c               | 28 ++++++++++++++++++----------\n>   environment.h               | 17 +++++++++++++++--\n>   repository.c                | 12 ++++++++++++\n>   repository.h                |  7 +++++++\n>   sparse-index.c              |  6 ++++--\n>   unpack-trees.c              |  3 ++-\n>   wt-status.c                 |  4 +++-\n>   20 files changed, 104 insertions(+), 42 deletions(-)\n> \n>   Range diff versus v5:\n>   =====================\n> \n>   1:  d28850bcdb ! 1:  7e3082125d environment: stop storing `core.attributesFile` globally\n>      @@ Commit message\n> \n>           Create a new struct `repo_config_values` to hold this value and\n>           other repository dependent values parsed by `git_default_config()`.\n>      -    For now the value can be accessed via the_repository in\n>      -    `git_default_config()`.\n>           This will ensure the current behaviour remains the same while also\n>           enabling the libification of Git.\n> \n>      +    An accessor function 'repo_config_values()' is created and used to access\n>      +    the new struct member of the repository struct.\n>      +    This is to ensure that we detect if the struct repository has been\n>      +    initialized and also prevent double initialization of the repository.\n>      +\n>           It is important to note that `git_default_config()` is a wrapper to other\n>           `git_default_*_config()` functions such as `git_default_core_config()`.\n>           Therefore to access and modify this global variable,\n>      @@ Commit message\n>           Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n>           Mentored-by: Christian Couder <christian.couder@gmail.com>\n>           Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n>      +    Helped-by: Junio C Hamano <gitster@pobox.com>\n>           Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> \n>        ## attr.c ##\n>      @@ attr.c: const char *git_attr_system_file(void)\n>        {\n>       -\tif (!git_attributes_file)\n>       -\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n>      -+\tstruct repo_config_values *cfg = &the_repository->config_values;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>       +\tif (!cfg->attributes_file)\n>       +\t\tcfg->attributes_file = xdg_config_home(\"attributes\");\n> \n>      @@ environment.c: static enum fsync_component parse_fsync_components(const char *va\n>        static int git_default_core_config(const char *var, const char *value,\n>        \t\t\t\t   const struct config_context *ctx, void *cb)\n>        {\n>      -+\tstruct repo_config_values *cfg = &the_repository->config_values;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>       +\n>        \t/* This needs a better name */\n>        \tif (!strcmp(var, \"core.filemode\")) {\n>      @@ environment.h: extern const char * const local_repo_env[];\n> \n>        struct strvec;\n> \n>      ++struct repository;\n>       +struct repo_config_values {\n>       +\t/* section \"core\" config values */\n>       +\tchar *attributes_file;\n>       +};\n>      ++\n>      ++struct repo_config_values *repo_config_values(struct repository *repo);\n>       +\n>        /*\n>         * Wrapper of getenv() that returns a strdup value. This value is kept\n>      @@ environment.h: extern int assume_unchanged;\n>        extern unsigned long pack_size_limit_cfg;\n> \n>        ## repository.c ##\n>      -@@ repository.c: void initialize_repository(struct repository *repo)\n>      +@@ repository.c: static void set_default_hash_algo(struct repository *repo)\n>      + \trepo_set_hash_algo(repo, algo);\n>      + }\n>      +\n>      ++struct repo_config_values *repo_config_values(struct repository *repo)\n>      ++{\n>      ++\tif(!repo->initialized)\n>      ++\t\tBUG(\"config values from uninitialized repository\");\n>      ++\treturn &repo->config_values_private_;\n>      ++}\n>      ++\n>      + void initialize_repository(struct repository *repo)\n>      + {\n>      ++\tif (repo->initialized)\n>      ++\t\tBUG(\"repository initialized already\");\n>      ++\trepo->initialized = true;\n>      ++\n>      + \trepo->remote_state = remote_state_new();\n>      + \trepo->parsed_objects = parsed_object_pool_new(repo);\n>        \tALLOC_ARRAY(repo->index, 1);\n>        \tindex_state_init(repo->index, repo);\n>        \trepo->check_deprecated_config = true;\n>      -+\trepo_config_values_init(&repo->config_values);\n>      ++\trepo_config_values_init(repo_config_values(repo));\n> \n>        \t/*\n>        \t * When a command runs inside a repository, it learns what\n>      @@ repository.h: struct repository {\n>        \tconst struct git_hash_algo *compat_hash_algo;\n> \n>       +\t/* Repository's config values parsed by git_default_config() */\n>      -+\tstruct repo_config_values config_values;\n>      ++\tstruct repo_config_values config_values_private_;\n>       +\n>        \t/* Repository's reference storage format, as serialized on disk. */\n>        \tenum ref_storage_format ref_storage_format;\n> \n>      +@@ repository.h: struct repository {\n>      +\n>      + \t/* Should repo_config() check for deprecated settings */\n>      + \tbool check_deprecated_config;\n>      ++\n>      ++\t/* Has this repository instance been initialized? */\n>      ++\tbool initialized;\n>      + };\n>      +\n>      + #ifdef USE_THE_REPOSITORY_VARIABLE\n> 2:  5e56e1cc41 ! 2:  8645b4f595 environment: stop using core.sparseCheckout globally\n>      @@ Commit message\n>           Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> \n>        ## builtin/backfill.c ##\n>      +@@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n>      + \t\t\t N_(\"Restrict the missing objects to the current sparse-checkout\")),\n>      + \t\tOPT_END(),\n>      + \t};\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      +\n>      + \tshow_usage_with_options_if_asked(argc, argv,\n>      + \t\t\t\t\t builtin_backfill_usage, options);\n>       @@ builtin/backfill.c: int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n>        \trepo_config(repo, git_default_config, NULL);\n> \n>        \tif (ctx.sparse < 0)\n>       -\t\tctx.sparse = core_apply_sparse_checkout;\n>      -+\t\tctx.sparse = the_repository->config_values.apply_sparse_checkout;\n>      ++\t\tctx.sparse = cfg->apply_sparse_checkout;\n> \n>        \tresult = do_backfill(&ctx);\n>        \tbackfill_context_clear(&ctx);\n> \n>        ## builtin/clone.c ##\n>       @@ builtin/clone.c: static int git_sparse_checkout_init(const char *repo)\n>      + {\n>      + \tstruct child_process cmd = CHILD_PROCESS_INIT;\n>      + \tint result = 0;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      + \tstrvec_pushl(&cmd.args, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n>      +\n>      + \t/*\n>        \t * We must apply the setting in the current process\n>        \t * for the later checkout to use the sparse-checkout file.\n>        \t */\n>       -\tcore_apply_sparse_checkout = 1;\n>      -+\tthe_repository->config_values.apply_sparse_checkout = 1;\n>      ++\tcfg->apply_sparse_checkout = 1;\n> \n>        \tcmd.git_cmd = 1;\n>        \tif (run_command(&cmd)) {\n>      @@ builtin/grep.c: static int grep_submodule(struct grep_opt *opt,\n>        \t *\tof these submodules are expanded unexpectedly.\n>        \t *\n>       -\t * 2. \"core_apply_sparse_checkout\"\n>      -+\t * 2. \"config_values.apply_sparse_checkout\"\n>      ++\t * 2. \"config_values_private_.apply_sparse_checkout\"\n>        \t *\tWhen running `grep` in the superproject, this setting is\n>        \t *\tpopulated using the superproject's configs. However, once\n>        \t *\tinitialized, this config is globally accessible and is read by\n> \n>        ## builtin/mv.c ##\n>      +@@ builtin/mv.c: int cmd_mv(int argc,\n>      + \tstruct hashmap moved_dirs = HASHMAP_INIT(pathmap_cmp, NULL);\n>      + \tstruct strbuf pathbuf = STRBUF_INIT;\n>      + \tint ret;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      +\n>      + \trepo_config(the_repository, git_default_config, NULL);\n>      +\n>       @@ builtin/mv.c: int cmd_mv(int argc,\n>        \t\trename_index_entry_at(the_repository->index, pos, dst);\n> \n>        \t\tif (ignore_sparse &&\n>       -\t\t    core_apply_sparse_checkout &&\n>      -+\t\t    the_repository->config_values.apply_sparse_checkout &&\n>      ++\t\t    cfg->apply_sparse_checkout &&\n>        \t\t    core_sparse_checkout_cone) {\n>        \t\t\t/*\n>        \t\t\t * NEEDSWORK: we are *not* paying attention to\n> \n>        ## builtin/sparse-checkout.c ##\n>       @@ builtin/sparse-checkout.c: static int sparse_checkout_list(int argc, const char **argv, const char *prefix,\n>      + \tstruct pattern_list pl;\n>      + \tchar *sparse_filename;\n>        \tint res;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n> \n>        \tsetup_work_tree();\n>       -\tif (!core_apply_sparse_checkout)\n>      -+\tif (!the_repository->config_values.apply_sparse_checkout)\n>      ++\tif (!cfg->apply_sparse_checkout)\n>        \t\tdie(_(\"this worktree is not sparse\"));\n> \n>        \targc = parse_options(argc, argv, prefix,\n>       @@ builtin/sparse-checkout.c: static int set_config(struct repository *repo,\n>      + }\n> \n>        static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      ++\n>        \t/* If not specified, use previous definition of cone mode */\n>       -\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n>      -+\tif (*cone_mode == -1 && the_repository->config_values.apply_sparse_checkout)\n>      ++\tif (*cone_mode == -1 && cfg->apply_sparse_checkout)\n>        \t\t*cone_mode = core_sparse_checkout_cone;\n> \n>        \t/* Set cone/non-cone mode appropriately */\n>       -\tcore_apply_sparse_checkout = 1;\n>      -+\tthe_repository->config_values.apply_sparse_checkout = 1;\n>      ++\tcfg->apply_sparse_checkout = 1;\n>        \tif (*cone_mode == 1 || *cone_mode == -1) {\n>        \t\tcore_sparse_checkout_cone = 1;\n>        \t\treturn MODE_CONE_PATTERNS;\n>      -@@ builtin/sparse-checkout.c: static int update_modes(struct repository *repo, int *cone_mode, int *sparse_ind\n>      +@@ builtin/sparse-checkout.c: static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n>      + static int update_modes(struct repository *repo, int *cone_mode, int *sparse_index)\n>      + {\n>        \tint mode, record_mode;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n> \n>        \t/* Determine if we need to record the mode; ensure sparse checkout on */\n>       -\trecord_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n>      -+\trecord_mode = (*cone_mode != -1) ||\n>      -+\t\t      !the_repository->config_values.apply_sparse_checkout;\n>      ++\trecord_mode = (*cone_mode != -1) || !cfg->apply_sparse_checkout;\n> \n>        \tmode = update_cone_mode(cone_mode);\n>        \tif (record_mode && set_config(repo, mode))\n>      +@@ builtin/sparse-checkout.c: static int modify_pattern_list(struct repository *repo,\n>      + \tint result;\n>      + \tint changed_config = 0;\n>      + \tstruct pattern_list *pl = xcalloc(1, sizeof(*pl));\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      +\n>      + \tswitch (m) {\n>      + \tcase ADD:\n>       @@ builtin/sparse-checkout.c: static int modify_pattern_list(struct repository *repo,\n>        \t\tbreak;\n>        \t}\n> \n>       -\tif (!core_apply_sparse_checkout) {\n>      -+\tif (!the_repository->config_values.apply_sparse_checkout) {\n>      ++\tif (!cfg->apply_sparse_checkout) {\n>        \t\tset_config(repo, MODE_ALL_PATTERNS);\n>       -\t\tcore_apply_sparse_checkout = 1;\n>      -+\t\tthe_repository->config_values.apply_sparse_checkout = 1;\n>      ++\t\tcfg->apply_sparse_checkout = 1;\n>        \t\tchanged_config = 1;\n>        \t}\n> \n>       @@ builtin/sparse-checkout.c: static int sparse_checkout_add(int argc, const char **argv, const char *prefix,\n>      + \t};\n>      + \tstruct strvec patterns = STRVEC_INIT;\n>        \tint ret;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n> \n>        \tsetup_work_tree();\n>       -\tif (!core_apply_sparse_checkout)\n>      -+\tif (!the_repository->config_values.apply_sparse_checkout)\n>      ++\tif (!cfg->apply_sparse_checkout)\n>        \t\tdie(_(\"no sparse-checkout to add to\"));\n> \n>        \trepo_read_index(repo);\n>       @@ builtin/sparse-checkout.c: static int sparse_checkout_reapply(int argc, const char **argv,\n>      + \t\t\t N_(\"toggle the use of a sparse index\")),\n>      + \t\tOPT_END(),\n>        \t};\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n> \n>        \tsetup_work_tree();\n>       -\tif (!core_apply_sparse_checkout)\n>      -+\tif (!the_repository->config_values.apply_sparse_checkout)\n>      ++\tif (!cfg->apply_sparse_checkout)\n>        \t\tdie(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n> \n>        \treapply_opts.cone_mode = -1;\n>      +@@ builtin/sparse-checkout.c: static int sparse_checkout_clean(int argc, const char **argv,\n>      + \tsize_t worktree_len;\n>      + \tint force = 0, dry_run = 0, verbose = 0;\n>      + \tint require_force = 1;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      +\n>      + \tstruct option builtin_sparse_checkout_clean_options[] = {\n>      + \t\tOPT__DRY_RUN(&dry_run, N_(\"dry run\")),\n>       @@ builtin/sparse-checkout.c: static int sparse_checkout_clean(int argc, const char **argv,\n>        \t};\n> \n>        \tsetup_work_tree();\n>       -\tif (!core_apply_sparse_checkout)\n>      -+\tif (!the_repository->config_values.apply_sparse_checkout)\n>      ++\tif (!cfg->apply_sparse_checkout)\n>        \t\tdie(_(\"must be in a sparse-checkout to clean directories\"));\n>        \tif (!core_sparse_checkout_cone)\n>        \t\tdie(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n>       @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const char **argv,\n>      + \t\tOPT_END(),\n>      + \t};\n>        \tstruct pattern_list pl;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n> \n>        \t/*\n>       -\t * We do not exit early if !core_apply_sparse_checkout; due to the\n>      @@ builtin/sparse-checkout.c: static int sparse_checkout_disable(int argc, const ch\n>        \thashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n>        \tpl.use_cone_patterns = 0;\n>       -\tcore_apply_sparse_checkout = 1;\n>      -+\tthe_repository->config_values.apply_sparse_checkout = 1;\n>      ++\tcfg->apply_sparse_checkout = 1;\n> \n>        \tadd_pattern(\"/*\", empty_base, 0, &pl, 0);\n> \n> \n>        ## builtin/worktree.c ##\n>      +@@ builtin/worktree.c: static int add_worktree(const char *path, const char *refname,\n>      + \tstruct strbuf sb_name = STRBUF_INIT;\n>      + \tstruct worktree **worktrees, *wt = NULL;\n>      + \tstruct ref_store *wt_refs;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      +\n>      + \tworktrees = get_worktrees();\n>      + \tcheck_candidate_path(path, opts->force, worktrees, \"add\");\n>       @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refname,\n>        \t * If the current worktree has sparse-checkout enabled, then copy\n>        \t * the sparse-checkout patterns from the current worktree.\n>        \t */\n>       -\tif (core_apply_sparse_checkout)\n>      -+\tif (the_repository->config_values.apply_sparse_checkout)\n>      ++\tif (cfg->apply_sparse_checkout)\n>        \t\tcopy_sparse_checkout(sb_repo.buf);\n> \n>        \t/*\n>      @@ dir.c: enum pattern_match_result path_matches_pattern_list(\n>        int init_sparse_checkout_patterns(struct index_state *istate)\n>        {\n>       -\tif (!core_apply_sparse_checkout)\n>      -+\tif (!the_repository->config_values.apply_sparse_checkout)\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      ++\tif (!cfg->apply_sparse_checkout)\n>        \t\treturn 1;\n>        \tif (istate->sparse_checkout_patterns)\n>        \t\treturn 0;\n>      @@ environment.c: int git_default_config(const char *var, const char *value,\n>        }\n> \n>        ## environment.h ##\n>      -@@ environment.h: struct strvec;\n>      +@@ environment.h: struct repository;\n>        struct repo_config_values {\n>        \t/* section \"core\" config values */\n>        \tchar *attributes_file;\n>       +\tint apply_sparse_checkout;\n>        };\n> \n>      - /*\n>      + struct repo_config_values *repo_config_values(struct repository *repo);\n>       @@ environment.h: extern int precomposed_unicode;\n>        extern int protect_hfs;\n>        extern int protect_ntfs;\n>      @@ sparse-index.c: static int index_has_unmerged_entries(struct index_state *istate\n>        int is_sparse_index_allowed(struct index_state *istate, int flags)\n>        {\n>       -\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n>      -+\tstruct repo_config_values *cfg = &the_repository->config_values;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>       +\tif (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)\n>        \t\treturn 0;\n> \n>      @@ sparse-index.c: static void clear_skip_worktree_from_present_files_full(struct i\n>        void clear_skip_worktree_from_present_files(struct index_state *istate)\n>        {\n>       -\tif (!core_apply_sparse_checkout ||\n>      -+\tstruct repo_config_values *cfg = &the_repository->config_values;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>       +\tif (!cfg->apply_sparse_checkout ||\n>        \t    sparse_expect_files_outside_of_patterns)\n>        \t\treturn;\n> \n> \n>        ## unpack-trees.c ##\n>      +@@ unpack-trees.c: int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n>      + \tstruct pattern_list pl;\n>      + \tint free_pattern_list = 0;\n>      + \tstruct dir_struct dir = DIR_INIT;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      +\n>      + \tif (o->reset == UNPACK_RESET_INVALID)\n>      + \t\tBUG(\"o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED\");\n>       @@ unpack-trees.c: int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n>        \tif (o->prefix)\n>        \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n> \n>       -\tif (!core_apply_sparse_checkout || !o->update)\n>      -+\tif (!the_repository->config_values.apply_sparse_checkout || !o->update)\n>      ++\tif (!cfg->apply_sparse_checkout || !o->update)\n>        \t\to->skip_sparse_checkout = 1;\n>        \tif (!o->skip_sparse_checkout) {\n>        \t\tmemset(&pl, 0, sizeof(pl));\n> \n>        ## wt-status.c ##\n>       @@ wt-status.c: static void wt_status_check_sparse_checkout(struct repository *r,\n>      + {\n>        \tint skip_worktree = 0;\n>        \tint i;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n> \n>       -\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n>      -+\tif (!the_repository->config_values.apply_sparse_checkout ||\n>      ++\tif (!cfg->apply_sparse_checkout ||\n>       +\t    r->index->cache_nr == 0) {\n>        \t\t/*\n>        \t\t * Don't compute percentage of checked out files if we\n> 3:  e7f37bac87 ! 3:  60451b93a5 environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`\n>      @@ branch.h: enum branch_track {\n>        /**\n> \n>        ## builtin/branch.c ##\n>      +@@ builtin/branch.c: int cmd_branch(int argc,\n>      + \tstatic struct ref_sorting *sorting;\n>      + \tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n>      + \tstruct ref_format format = REF_FORMAT_INIT;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      + \tint ret;\n>      +\n>      + \tstruct option options[] = {\n>       @@ builtin/branch.c: int cmd_branch(int argc,\n>        \tif (!sorting_options.nr)\n>        \t\tstring_list_append(&sorting_options, \"refname\");\n> \n>       -\ttrack = git_branch_track;\n>      -+\ttrack = the_repository->config_values.branch_track;\n>      ++\ttrack = cfg->branch_track;\n> \n>        \thead = refs_resolve_refdup(get_main_ref_store(the_repository), \"HEAD\",\n>        \t\t\t\t   0, &head_oid, NULL);\n> \n>        ## builtin/checkout.c ##\n>      +@@ builtin/checkout.c: static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,\n>      + static int checkout_branch(struct checkout_opts *opts,\n>      + \t\t\t   struct branch_info *new_branch_info)\n>      + {\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      + \tint noop_switch = (!new_branch_info->name &&\n>      + \t\t\t   !opts->new_branch &&\n>      + \t\t\t   !opts->force_detach);\n>       @@ builtin/checkout.c: static int checkout_branch(struct checkout_opts *opts,\n>        \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n>        \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n>        \t} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)\n>       -\t\topts->track = git_branch_track;\n>      -+\t\topts->track = the_repository->config_values.branch_track;\n>      ++\t\topts->track = cfg->branch_track;\n> \n>        \tif (new_branch_info->name && !new_branch_info->commit)\n>        \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\n> \n>        ## builtin/push.c ##\n>      +@@ builtin/push.c: static NORETURN void die_push_simple(struct branch *branch,\n>      + \tconst char *advice_pushdefault_maybe = \"\";\n>      + \tconst char *advice_automergesimple_maybe = \"\";\n>      + \tconst char *short_upstream = branch->merge[0]->src;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>      +\n>      + \tskip_prefix(short_upstream, \"refs/heads/\", &short_upstream);\n>      +\n>       @@ builtin/push.c: static NORETURN void die_push_simple(struct branch *branch,\n>        \t\tadvice_pushdefault_maybe = _(\"\\n\"\n>        \t\t\t\t \"To choose either option permanently, \"\n>        \t\t\t\t \"see push.default in 'git help config'.\\n\");\n>       -\tif (git_branch_track != BRANCH_TRACK_SIMPLE)\n>      -+\tif (the_repository->config_values.branch_track != BRANCH_TRACK_SIMPLE)\n>      ++\tif (cfg->branch_track != BRANCH_TRACK_SIMPLE)\n>        \t\tadvice_automergesimple_maybe = _(\"\\n\"\n>        \t\t\t\t \"To avoid automatically configuring \"\n>        \t\t\t\t \"an upstream branch when its name\\n\"\n> \n>        ## builtin/submodule--helper.c ##\n>       @@ builtin/submodule--helper.c: static int module_create_branch(int argc, const char **argv, const char *prefix,\n>      + \t\tN_(\"git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] [-n|--dry-run] <name> <start-oid> <start-name>\"),\n>      + \t\tNULL\n>        \t};\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n> \n>        \trepo_config(the_repository, git_default_config, NULL);\n>       -\ttrack = git_branch_track;\n>      -+\ttrack = the_repository->config_values.branch_track;\n>      ++\ttrack = cfg->branch_track;\n>        \targc = parse_options(argc, argv, prefix, options, usage, 0);\n> \n>        \tif (argc != 3)\n>      @@ environment.c: static int git_default_i18n_config(const char *var, const char *v\n> \n>        static int git_default_branch_config(const char *var, const char *value)\n>        {\n>      -+\tstruct repo_config_values *cfg = &the_repository->config_values;\n>      ++\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n>       +\n>        \tif (!strcmp(var, \"branch.autosetupmerge\")) {\n>        \t\tif (value && !strcmp(value, \"always\")) {\n>      @@ environment.h: struct repo_config_values {\n>       +\tenum branch_track branch_track;\n>        };\n> \n>      - /*\n>      + struct repo_config_values *repo_config_values(struct repository *repo);\n> \n> \n\n"},{"id":"535400","messageId":"xmqq7bspcq3c.fsf@gitster.g","threadId":"64782","inReplyTo":"7e3082125df08d3e5fb2195d73698c4c28c6645e.1770127568.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-07T01:14:15Z","receivedAt":"2026-02-07T01:14:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Olamide Caleb Bello <belkid98@gmail.com> writes:\n\n> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> ---\n>  attr.c        |  7 ++++---\n>  environment.c | 12 +++++++++---\n>  environment.h | 11 ++++++++++-\n>  repository.c  | 12 ++++++++++++\n>  repository.h  |  7 +++++++\n>  5 files changed, 42 insertions(+), 7 deletions(-)\n\nI bisected our recent CI failures that break fuzz smoke test down to\nthis change.\n\n$ make -j32 \\\n            NO_CURL=NoThanks \\\n            CC=clang \\\n            FUZZ_CXX=clang++ \\\n            CFLAGS=\"-fsanitize=fuzzer-no-link,address\" \\\n            LIB_FUZZING_ENGINE=\"-fsanitize=fuzzer,address\" \\\n            fuzz-all >/dev/null &&\n$ oss-fuzz/fuzz-commit-graph -verbosity=0 -runs=1\n\nINFO: Running with entropic power schedule (0xFF, 100).\nINFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes\nINFO: A corpus is not provided, starting from an empty corpus\nBUG: repository.c:63: repository initialized already\n==2050473== ERROR: libFuzzer: deadly signal\n    #0 0x56169aaf4065 in __sanitizer_print_stack_trace (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x32a065) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n    #1 0x56169aa4835c in fuzzer::PrintStackTrace() (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x27e35c) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n    #2 0x56169aa2d2d7 in fuzzer::Fuzzer::CrashCallback() (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x2632d7) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n    #3 0x7fb4c6f59def  (/lib/x86_64-linux-gnu/libc.so.6+0x3fdef) (BuildId: 61e1dea1f540b3b4b4d8ec76716e409cec096ece)\n    #4 0x7fb4c6fae95b in __pthread_kill_implementation nptl/pthread_kill.c:43:17\n    #5 0x7fb4c6f59cc1 in raise signal/../sysdeps/posix/raise.c:26:13\n    #6 0x7fb4c6f424ab in abort stdlib/abort.c:73:3\n    #7 0x56169ae91316 in BUG_vfl usage.c\n    #8 0x56169ae8f527 in BUG_fl (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x6c5527) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n    #9 0x56169ad79d07 in initialize_repository (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x5afd07) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n    #10 0x56169ab2c4e2 in LLVMFuzzerTestOneInput (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x3624e2) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n    #11 0x56169aa2e9da in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x2649da) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n    #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)\n    #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)\n    #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)\n    #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)\n    #16 0x56169aa48ec6 in main (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x27eec6) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n    #17 0x7fb4c6f43ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16\n    #18 0x7fb4c6f43d64 in __libc_start_main csu/../csu/libc-start.c:360:3\n    #19 0x56169aa10ec0 in _start (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x246ec0) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n\nNOTE: libFuzzer has rudimentary signal handlers.\n"},{"id":"535472","messageId":"88570b6c-2ff3-416f-b63f-2fb7c80c5af8@gmail.com","threadId":"64782","inReplyTo":"xmqq7bspcq3c.fsf@gitster.g","subject":"Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-02-08T11:14:37Z","receivedAt":"2026-02-08T11:14:45Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"\n\nOn 07/02/2026 01:14, Junio C Hamano wrote:\n> Olamide Caleb Bello <belkid98@gmail.com> writes:\n> \n>> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n>> Mentored-by: Christian Couder <christian.couder@gmail.com>\n>> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n>> Helped-by: Junio C Hamano <gitster@pobox.com>\n>> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n>> ---\n>>   attr.c        |  7 ++++---\n>>   environment.c | 12 +++++++++---\n>>   environment.h | 11 ++++++++++-\n>>   repository.c  | 12 ++++++++++++\n>>   repository.h  |  7 +++++++\n>>   5 files changed, 42 insertions(+), 7 deletions(-)\n> \n> I bisected our recent CI failures that break fuzz smoke test down to\n> this change.\n\nThe documentation for the LibFuzzer [1] notes\n\n     * The fuzzing engine will execute the fuzz target many times with\n       different inputs in the same process.\n\nand the first thing that the callback in oss-fuzz/fuzz-commit-graph.c \ndoes is\n\n\tinitialize_repository(the_repository);\n\nso I think the problem is that the assumption that a process will only \ninitialize \"the_repository\" once is incompatible with the way LibFuzzer \nworks. Maybe we should add\n\n\tmemset(the_repository, 0, sizeof(*the_repository));\n\nbefore the call in initialize_repository()?\n\nThanks\n\nPhillip\n\n[1] https://llvm.org/docs/LibFuzzer.html\n\n> $ make -j32 \\\n>              NO_CURL=NoThanks \\\n>              CC=clang \\\n>              FUZZ_CXX=clang++ \\\n>              CFLAGS=\"-fsanitize=fuzzer-no-link,address\" \\\n>              LIB_FUZZING_ENGINE=\"-fsanitize=fuzzer,address\" \\\n>              fuzz-all >/dev/null &&\n> $ oss-fuzz/fuzz-commit-graph -verbosity=0 -runs=1\n> \n> INFO: Running with entropic power schedule (0xFF, 100).\n> INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes\n> INFO: A corpus is not provided, starting from an empty corpus\n> BUG: repository.c:63: repository initialized already\n> ==2050473== ERROR: libFuzzer: deadly signal\n>      #0 0x56169aaf4065 in __sanitizer_print_stack_trace (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x32a065) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n>      #1 0x56169aa4835c in fuzzer::PrintStackTrace() (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x27e35c) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n>      #2 0x56169aa2d2d7 in fuzzer::Fuzzer::CrashCallback() (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x2632d7) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n>      #3 0x7fb4c6f59def  (/lib/x86_64-linux-gnu/libc.so.6+0x3fdef) (BuildId: 61e1dea1f540b3b4b4d8ec76716e409cec096ece)\n>      #4 0x7fb4c6fae95b in __pthread_kill_implementation nptl/pthread_kill.c:43:17\n>      #5 0x7fb4c6f59cc1 in raise signal/../sysdeps/posix/raise.c:26:13\n>      #6 0x7fb4c6f424ab in abort stdlib/abort.c:73:3\n>      #7 0x56169ae91316 in BUG_vfl usage.c\n>      #8 0x56169ae8f527 in BUG_fl (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x6c5527) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n>      #9 0x56169ad79d07 in initialize_repository (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x5afd07) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n>      #10 0x56169ab2c4e2 in LLVMFuzzerTestOneInput (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x3624e2) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n>      #11 0x56169aa2e9da in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x2649da) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n>      #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)\n>      #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)\n>      #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)\n>      #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)\n>      #16 0x56169aa48ec6 in main (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x27eec6) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n>      #17 0x7fb4c6f43ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16\n>      #18 0x7fb4c6f43d64 in __libc_start_main csu/../csu/libc-start.c:360:3\n>      #19 0x56169aa10ec0 in _start (/home/gitster/git.git/oss-fuzz/fuzz-commit-graph+0x246ec0) (BuildId: ec362419d512b5bd707ae18eef56a6a12a18fc92)\n> \n> NOTE: libFuzzer has rudimentary signal handlers.\n\n"},{"id":"535504","messageId":"CAD=f0L9h-_pqak2tjjpASmt91mx2RPha2ZW7bsvmBNdhEv=VAw@mail.gmail.com","threadId":"64782","inReplyTo":"c95a7730-7b14-4be0-a4e4-861b2f5430ea@gmail.com","subject":"Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-09T08:47:05Z","receivedAt":"2026-02-09T08:47:05Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Wed, 4 Feb 2026 at 17:39, Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 03/02/2026 15:42, Olamide Caleb Bello wrote:\n> > The `core.attributeFile` config value is parsed in\n> > git_default_core_config(), loaded eagerly and stored in the global\n> > variable `git_attributes_file`. Storing this value in a global variable\n> > can lead to it being overwritten by another repository when more than one\n> > Git repository run in the same Git process.\n> >\n> > Create a new struct `repo_config_values` to hold this value and\n> > other repository dependent values parsed by `git_default_config()`.\n> > This will ensure the current behaviour remains the same while also\n> > enabling the libification of Git.\n> >\n> > An accessor function 'repo_config_values()' is created and used to access\n> > the new struct member of the repository struct.\n> > This is to ensure that we detect if the struct repository has been\n> > initialized and also prevent double initialization of the repository.\n>\n> Sounds sensible. This paragraph could be reflowed.\n\nOkay\n\n>\n> > It is important to note that `git_default_config()` is a wrapper to other\n> > `git_default_*_config()` functions such as `git_default_core_config()`.\n> > Therefore to access and modify this global variable,\n> > the change has to be made `git_default_core_config()`.\n>\n> I'm not sure what this paragraph is saying with regard to the changes in\n> this patch.\n\nOkay I will fix it.\n\n>\n> > --- a/environment.c\n> > +++ b/environment.c\n> > @@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,\n> >       /* Add other config variables here and to Documentation/config.adoc. */\n> >       return 0;\n> >   }\n> > +\n> > +void repo_config_values_init(struct repo_config_values *cfg)\n> > +{\n> > +     cfg->attributes_file = NULL;\n> > +}\n>\n> Should we be free()ing cfg->attributes_file when the repository instance\n> is free()d? At the moment we're using \"the_repository\" which points to a\n> static instance so it does not make any practical difference but once we\n> start storing the config per-repository instance we will need to free\n> the config when the repository instance is free()d.\n\nOkay I will keep this in mind.\n\n>\n> > diff --git a/repository.c b/repository.c\n> > index c7e75215ac..a9b727540f 100644\n> > --- a/repository.c\n> > +++ b/repository.c\n> > @@ -50,13 +50,25 @@ static void set_default_hash_algo(struct repository *repo)\n> >       repo_set_hash_algo(repo, algo);\n> >   }\n> >\n> > +struct repo_config_values *repo_config_values(struct repository *repo)\n> > +{\n> > +     if(!repo->initialized)\n> > +             BUG(\"config values from uninitialized repository\");\n>\n> This check and the one in initialize_repository() below assume that the\n> repository instance is zeroed out when it is created, that's a\n> reasonable requirement but we should probably document it as our other\n> data structures tend not to require that they're zeroed out before they\n> are initialized. For example\n>\n>         struct strbuf buf;\n>         strbuf_init(&buf, 0);\n>\n> is perfectly fine as strbuf_init() does not assume the instance passed\n> to it has been zeroed out.\n>\n> As we only support retrieving values from \"the_repository\" at the moment\n> we should perhaps add\n>\n>         if (repo != the_repository)\n>                 BUG(\"trying to read config from wrong repository instance\");\n\nOkay noted\n\n>\n> Everything else looks fine to me\n>\n\nThanks\n"},{"id":"535505","messageId":"CAD=f0L9ZeFGo=fD0oOS4rjprBjJTGZiGL8EodsjMj9DFk0hO2w@mail.gmail.com","threadId":"64782","inReplyTo":"88570b6c-2ff3-416f-b63f-2fb7c80c5af8@gmail.com","subject":"Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-09T08:54:43Z","receivedAt":"2026-02-09T08:54:43Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Sun, 8 Feb 2026 at 12:14, Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n>\n>\n> On 07/02/2026 01:14, Junio C Hamano wrote:\n> > Olamide Caleb Bello <belkid98@gmail.com> writes:\n> >\n> >> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> >> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> >> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n> >> Helped-by: Junio C Hamano <gitster@pobox.com>\n> >> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> >> ---\n> >>   attr.c        |  7 ++++---\n> >>   environment.c | 12 +++++++++---\n> >>   environment.h | 11 ++++++++++-\n> >>   repository.c  | 12 ++++++++++++\n> >>   repository.h  |  7 +++++++\n> >>   5 files changed, 42 insertions(+), 7 deletions(-)\n> >\n> > I bisected our recent CI failures that break fuzz smoke test down to\n> > this change.\n>\n> The documentation for the LibFuzzer [1] notes\n>\n>      * The fuzzing engine will execute the fuzz target many times with\n>        different inputs in the same process.\n>\n> and the first thing that the callback in oss-fuzz/fuzz-commit-graph.c\n> does is\n>\n>         initialize_repository(the_repository);\n>\n> so I think the problem is that the assumption that a process will only\n> initialize \"the_repository\" once is incompatible with the way LibFuzzer\n> works. Maybe we should add\n>\n>         memset(the_repository, 0, sizeof(*the_repository));\n>\n> before the call in initialize_repository()?\n>\n> Thanks\n>\n> Phillip\n>\n> [1] https://llvm.org/docs/LibFuzzer.html\n>\nThank you Phillip, I will try this.\nI noticed something like this.\n"},{"id":"535658","messageId":"CAD=f0L8jeLE_Zy-5aW+DZrM6JNgbwRcgWCdW1f7SJ5uG_2R-yA@mail.gmail.com","threadId":"64782","inReplyTo":"88570b6c-2ff3-416f-b63f-2fb7c80c5af8@gmail.com","subject":"Re: [Outreachy PATCH v6 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-10T08:40:16Z","receivedAt":"2026-02-10T08:40:16Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Sun, 8 Feb 2026 at 12:14, Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n>\n>\n> On 07/02/2026 01:14, Junio C Hamano wrote:\n> > Olamide Caleb Bello <belkid98@gmail.com> writes:\n> >\n> >> Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n> >> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> >> Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\n> >> Helped-by: Junio C Hamano <gitster@pobox.com>\n> >> Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n> >> ---\n> >>   attr.c        |  7 ++++---\n> >>   environment.c | 12 +++++++++---\n> >>   environment.h | 11 ++++++++++-\n> >>   repository.c  | 12 ++++++++++++\n> >>   repository.h  |  7 +++++++\n> >>   5 files changed, 42 insertions(+), 7 deletions(-)\n> >\n> > I bisected our recent CI failures that break fuzz smoke test down to\n> > this change.\n>\n> The documentation for the LibFuzzer [1] notes\n>\n>      * The fuzzing engine will execute the fuzz target many times with\n>        different inputs in the same process.\n>\n> and the first thing that the callback in oss-fuzz/fuzz-commit-graph.c\n> does is\n>\n>         initialize_repository(the_repository);\n>\n> so I think the problem is that the assumption that a process will only\n> initialize \"the_repository\" once is incompatible with the way LibFuzzer\n> works. Maybe we should add\n>\n>         memset(the_repository, 0, sizeof(*the_repository));\n>\n> before the call in initialize_repository()?\n>\n> Thanks\n>\n> Phillip\n>\n> [1] https://llvm.org/docs/LibFuzzer.html\n>\n\nHello Phillip,\nthank you for your reviews and assistance so far.\n\nSo I moved the code\n memset(the_repository, 0, sizeof(*the_repository))\ninto the fuzz-commit-graph.c Fuzzer test before the call to\ninitialize_repository().\nThis made the fuzzer smoke tests pass.\n\nBut the line below,\n`if (repo != the_repository)\n    BUG(\"trying to read config from wrong repository instance\")`,\nin the repo_config_values() accessor function\nto make sure we are reading the config_values only for the_repository ,\nmakes many tests fail.\n\nI believe this is because repo_init() calls initialize_repository() and\nrepo_init() is also called in repo_submodule_init() when creating a\nsubrepo from a super project, and this subrepo passed to repo_init() is\nnot the_repository.\n"},{"id":"536128","messageId":"cover.1771258573.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1769256839.git.belkid98@gmail.com","subject":"[Outreachy PATCH v7 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-16T16:38:24Z","receivedAt":"2026-02-16T16:38:53Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"Hi Git Community,\nOver the course of my ongoing internship, which focused on moving repo specific\nglobal variables in environment.h into local scope, I have attempted to move some\nvariables into the struct repo-settings.\nHowever there has been some design concerns as regards the use of\n`prepare_repo_settings()` with respect to when and where to call the\nfunction, and also the change in behaviours when the variable is lazily\nloaded as discussed in [1] and [2].\n\nAfter different deliberations, Phillip Wood proposed creating a new config\nstruct [3], adding it to the repository struct and passing the repo struct to\n`git_default_config()` to store parsed repo specific config values per repo.\nThis ensures the current behaviours will be retained.\n\nIn version v6, the fuzz-commit-graph failed due to the test being run\nmultiples times in the same process. This makes us get a BUG due to\nthe repository being reinitialized in the same process.\nBut now resetting the initialization using memset before rerunning\nthe test, the failure is not longer experienced.\n\n1. https://lore.kernel.org/git/43aaec10-2696-44c9-8728-2045b83dc5d3@gmail.com/\n2. https://lore.kernel.org/git/a881499d-e236-4f8e-a217-b6bce69e3e3c@gmail.com/\n3. https://lore.kernel.org/git/8899016f-eeef-404b-8da6-ff3a90e81cea@gmail.com\n4. https://github.com/git/git/actions/runs/21633462506/job/62352191148?pr=2166\n5. https://gitlab.com/gitlab-community/gitlab-org/git/-/pipelines/2303203992\n\nChanges in v7:\n==============\n- Added the code to reset the initialization of the_repository\n  in the fuzz-commit-graph test in Patch 1.\n- Changed the call to rep_config_values_init() in\n  initialze_repository() by passing it the repo config_values_private_\n  struct instead of the accessor function in Patch 1.\n- Modified the commit message in Patch 1\n- Added extra lines between variable declarations and code\n\nOlamide Caleb Bello (3):\n  environment: stop storing `core.attributesFile` globally\n  environment: stop using core.sparseCheckout globally\n  environment: move \"branch.autoSetupMerge\" into `struct\n    repo_config_values`\n\n attr.c                       |  7 ++++---\n branch.h                     |  2 --\n builtin/backfill.c           |  3 ++-\n builtin/branch.c             |  3 ++-\n builtin/checkout.c           |  3 ++-\n builtin/clone.c              |  4 +++-\n builtin/grep.c               |  2 +-\n builtin/mv.c                 |  3 ++-\n builtin/push.c               |  3 ++-\n builtin/sparse-checkout.c    | 31 ++++++++++++++++++++-----------\n builtin/submodule--helper.c  |  3 ++-\n builtin/worktree.c           |  3 ++-\n dir.c                        |  4 +++-\n environment.c                | 28 ++++++++++++++++++----------\n environment.h                | 17 +++++++++++++++--\n oss-fuzz/fuzz-commit-graph.c |  1 +\n repository.c                 | 14 ++++++++++++++\n repository.h                 |  7 +++++++\n sparse-index.c               |  7 +++++--\n unpack-trees.c               |  3 ++-\n wt-status.c                  |  4 +++-\n 21 files changed, 110 insertions(+), 42 deletions(-)\n\nRange diff versus v6:\n=====================\n1:  7e3082125d ! 1:  48821a3848 environment: stop storing `core.attributesFile` globally\n    @@ Commit message\n\n         The `core.attributeFile` config value is parsed in\n         git_default_core_config(), loaded eagerly and stored in the global\n    -    variable `git_attributes_file`. Storing this value in a global variable\n    -    can lead to it being overwritten by another repository when more than one\n    -    Git repository run in the same Git process.\n    +    variable `git_attributes_file`. Storing this value in a global\n    +    variable can lead to it being overwritten by another repository when\n    +    more than one Git repository run in the same Git process.\n\n         Create a new struct `repo_config_values` to hold this value and\n         other repository dependent values parsed by `git_default_config()`.\n         This will ensure the current behaviour remains the same while also\n         enabling the libification of Git.\n\n    -    An accessor function 'repo_config_values()' is created and used to access\n    -    the new struct member of the repository struct.\n    -    This is to ensure that we detect if the struct repository has been\n    -    initialized and also prevent double initialization of the repository.\n    -\n    -    It is important to note that `git_default_config()` is a wrapper to other\n    -    `git_default_*_config()` functions such as `git_default_core_config()`.\n    -    Therefore to access and modify this global variable,\n    -    the change has to be made `git_default_core_config()`.\n    +    An accessor function 'repo_config_values()' to ensure that we do not\n    +    access an uninitialized repository, or an instance of a different\n    +    repository than the current one.\n\n         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>\n         Mentored-by: Christian Couder <christian.couder@gmail.com>\n    @@ environment.h: extern int assume_unchanged;\n      extern int pack_compression_level;\n      extern unsigned long pack_size_limit_cfg;\n\n    + ## oss-fuzz/fuzz-commit-graph.c ##\n    +@@ oss-fuzz/fuzz-commit-graph.c: int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n    + {\n    + \tstruct commit_graph *g;\n    +\n    ++\tmemset(the_repository, 0, sizeof(*the_repository));\n    + \tinitialize_repository(the_repository);\n    +\n    + \t/*\n    +\n      ## repository.c ##\n     @@ repository.c: static void set_default_hash_algo(struct repository *repo)\n      \trepo_set_hash_algo(repo, algo);\n    @@ repository.c: static void set_default_hash_algo(struct repository *repo)\n\n     +struct repo_config_values *repo_config_values(struct repository *repo)\n     +{\n    ++\tif (repo != the_repository)\n    ++\t\tBUG(\"trying to read config from wrong repository instance\");\n     +\tif(!repo->initialized)\n     +\t\tBUG(\"config values from uninitialized repository\");\n     +\treturn &repo->config_values_private_;\n    @@ repository.c: static void set_default_hash_algo(struct repository *repo)\n      \tALLOC_ARRAY(repo->index, 1);\n      \tindex_state_init(repo->index, repo);\n      \trepo->check_deprecated_config = true;\n    -+\trepo_config_values_init(repo_config_values(repo));\n    ++\trepo_config_values_init(&repo->config_values_private_);\n\n      \t/*\n      \t * When a command runs inside a repository, it learns what\n2:  8645b4f595 ! 2:  7d33f1ef0f environment: stop using core.sparseCheckout globally\n    @@ builtin/clone.c: static int git_sparse_checkout_init(const char *repo)\n      \tstruct child_process cmd = CHILD_PROCESS_INIT;\n      \tint result = 0;\n     +\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    ++\n      \tstrvec_pushl(&cmd.args, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n\n      \t/*\n    @@ dir.c: enum pattern_match_result path_matches_pattern_list(\n      {\n     -\tif (!core_apply_sparse_checkout)\n     +\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    ++\n     +\tif (!cfg->apply_sparse_checkout)\n      \t\treturn 1;\n      \tif (istate->sparse_checkout_patterns)\n    @@ sparse-index.c: static int index_has_unmerged_entries(struct index_state *istate\n      {\n     -\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n     +\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n    ++\n     +\tif (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)\n      \t\treturn 0;\n\n3:  60451b93a5 = 3:  c3ecfa63b9 environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`\n\n\n-- \n2.34.1\n\n"},{"id":"536129","messageId":"48821a3848bef25c13038be8377ad73e7c17a924.1771258573.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1771258573.git.belkid98@gmail.com","subject":"[Outreachy PATCH v7 1/3] environment: stop storing `core.attributesFile` globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-16T16:38:25Z","receivedAt":"2026-02-16T16:38:59Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The `core.attributeFile` config value is parsed in\ngit_default_core_config(), loaded eagerly and stored in the global\nvariable `git_attributes_file`. Storing this value in a global\nvariable can lead to it being overwritten by another repository when\nmore than one Git repository run in the same Git process.\n\nCreate a new struct `repo_config_values` to hold this value and\nother repository dependent values parsed by `git_default_config()`.\nThis will ensure the current behaviour remains the same while also\nenabling the libification of Git.\n\nAn accessor function 'repo_config_values()' s created to ensure\nthat we do not access an uninitialized repository, or an instance\nof a different repository than the current one.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n attr.c                       |  7 ++++---\n environment.c                | 12 +++++++++---\n environment.h                | 11 ++++++++++-\n oss-fuzz/fuzz-commit-graph.c |  1 +\n repository.c                 | 14 ++++++++++++++\n repository.h                 |  7 +++++++\n 6 files changed, 45 insertions(+), 7 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 4999b7e09d..75369547b3 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -881,10 +881,11 @@ const char *git_attr_system_file(void)\n \n const char *git_attr_global_file(void)\n {\n-\tif (!git_attributes_file)\n-\t\tgit_attributes_file = xdg_config_home(\"attributes\");\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\tif (!cfg->attributes_file)\n+\t\tcfg->attributes_file = xdg_config_home(\"attributes\");\n \n-\treturn git_attributes_file;\n+\treturn cfg->attributes_file;\n }\n \n int git_attr_system_is_enabled(void)\ndiff --git a/environment.c b/environment.c\nindex a770b5921d..4b5c701e80 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -53,7 +53,6 @@ char *git_commit_encoding;\n char *git_log_output_encoding;\n char *apply_default_whitespace;\n char *apply_default_ignorewhitespace;\n-char *git_attributes_file;\n int zlib_compression_level = Z_BEST_SPEED;\n int pack_compression_level = Z_DEFAULT_COMPRESSION;\n int fsync_object_files = -1;\n@@ -327,6 +326,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char *\n static int git_default_core_config(const char *var, const char *value,\n \t\t\t\t   const struct config_context *ctx, void *cb)\n {\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\n \t/* This needs a better name */\n \tif (!strcmp(var, \"core.filemode\")) {\n \t\ttrust_executable_bit = git_config_bool(var, value);\n@@ -364,8 +365,8 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.attributesfile\")) {\n-\t\tFREE_AND_NULL(git_attributes_file);\n-\t\treturn git_config_pathname(&git_attributes_file, var, value);\n+\t\tFREE_AND_NULL(cfg->attributes_file);\n+\t\treturn git_config_pathname(&cfg->attributes_file, var, value);\n \t}\n \n \tif (!strcmp(var, \"core.bare\")) {\n@@ -756,3 +757,8 @@ int git_default_config(const char *var, const char *value,\n \t/* Add other config variables here and to Documentation/config.adoc. */\n \treturn 0;\n }\n+\n+void repo_config_values_init(struct repo_config_values *cfg)\n+{\n+\tcfg->attributes_file = NULL;\n+}\ndiff --git a/environment.h b/environment.h\nindex 51898c99cd..dfc31b794d 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -84,6 +84,14 @@ extern const char * const local_repo_env[];\n \n struct strvec;\n \n+struct repository;\n+struct repo_config_values {\n+\t/* section \"core\" config values */\n+\tchar *attributes_file;\n+};\n+\n+struct repo_config_values *repo_config_values(struct repository *repo);\n+\n /*\n  * Wrapper of getenv() that returns a strdup value. This value is kept\n  * in argv to be freed later.\n@@ -107,6 +115,8 @@ const char *strip_namespace(const char *namespaced_ref);\n int git_default_config(const char *, const char *,\n \t\t       const struct config_context *, void *);\n \n+void repo_config_values_init(struct repo_config_values *cfg);\n+\n /*\n  * TODO: All the below state either explicitly or implicitly relies on\n  * `the_repository`. We should eventually get rid of these and make the\n@@ -152,7 +162,6 @@ extern int assume_unchanged;\n extern int warn_on_object_refname_ambiguity;\n extern char *apply_default_whitespace;\n extern char *apply_default_ignorewhitespace;\n-extern char *git_attributes_file;\n extern int zlib_compression_level;\n extern int pack_compression_level;\n extern unsigned long pack_size_limit_cfg;\ndiff --git a/oss-fuzz/fuzz-commit-graph.c b/oss-fuzz/fuzz-commit-graph.c\nindex fb8b8787a4..59bbb849d1 100644\n--- a/oss-fuzz/fuzz-commit-graph.c\n+++ b/oss-fuzz/fuzz-commit-graph.c\n@@ -10,6 +10,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n {\n \tstruct commit_graph *g;\n \n+\tmemset(the_repository, 0, sizeof(*the_repository));\n \tinitialize_repository(the_repository);\n \n \t/*\ndiff --git a/repository.c b/repository.c\nindex c7e75215ac..4bbbe6b3ba 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -50,13 +50,27 @@ static void set_default_hash_algo(struct repository *repo)\n \trepo_set_hash_algo(repo, algo);\n }\n \n+struct repo_config_values *repo_config_values(struct repository *repo)\n+{\n+\tif (repo != the_repository)\n+\t\tBUG(\"trying to read config from wrong repository instance\");\n+\tif(!repo->initialized)\n+\t\tBUG(\"config values from uninitialized repository\");\n+\treturn &repo->config_values_private_;\n+}\n+\n void initialize_repository(struct repository *repo)\n {\n+\tif (repo->initialized)\n+\t\tBUG(\"repository initialized already\");\n+\trepo->initialized = true;\n+\n \trepo->remote_state = remote_state_new();\n \trepo->parsed_objects = parsed_object_pool_new(repo);\n \tALLOC_ARRAY(repo->index, 1);\n \tindex_state_init(repo->index, repo);\n \trepo->check_deprecated_config = true;\n+\trepo_config_values_init(&repo->config_values_private_);\n \n \t/*\n \t * When a command runs inside a repository, it learns what\ndiff --git a/repository.h b/repository.h\nindex 6063c4b846..9717e45000 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -3,6 +3,7 @@\n \n #include \"strmap.h\"\n #include \"repo-settings.h\"\n+#include \"environment.h\"\n \n struct config_set;\n struct git_hash_algo;\n@@ -148,6 +149,9 @@ struct repository {\n \t/* Repository's compatibility hash algorithm. */\n \tconst struct git_hash_algo *compat_hash_algo;\n \n+\t/* Repository's config values parsed by git_default_config() */\n+\tstruct repo_config_values config_values_private_;\n+\n \t/* Repository's reference storage format, as serialized on disk. */\n \tenum ref_storage_format ref_storage_format;\n \n@@ -171,6 +175,9 @@ struct repository {\n \n \t/* Should repo_config() check for deprecated settings */\n \tbool check_deprecated_config;\n+\n+\t/* Has this repository instance been initialized? */\n+\tbool initialized;\n };\n \n #ifdef USE_THE_REPOSITORY_VARIABLE\n-- \n2.34.1\n\n"},{"id":"536130","messageId":"7d33f1ef0f8182893d63d49b350984025ab757d4.1771258573.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1771258573.git.belkid98@gmail.com","subject":"[Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-16T16:38:26Z","receivedAt":"2026-02-16T16:39:04Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `core.sparseCheckout` is parsed in\n`git_default_core_config()` and stored globally in\n`core_apply_sparse_checkout`. This could cause it to be overwritten\nby another repository when different Git repositories run in the same\nprocess.\n\nMove the parsed value into `struct repo_config_values` in the_repository\nto retain current behaviours and move towards libifying Git.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n builtin/backfill.c        |  3 ++-\n builtin/clone.c           |  4 +++-\n builtin/grep.c            |  2 +-\n builtin/mv.c              |  3 ++-\n builtin/sparse-checkout.c | 31 ++++++++++++++++++++-----------\n builtin/worktree.c        |  3 ++-\n dir.c                     |  4 +++-\n environment.c             |  4 ++--\n environment.h             |  2 +-\n sparse-index.c            |  7 +++++--\n unpack-trees.c            |  3 ++-\n wt-status.c               |  4 +++-\n 12 files changed, 46 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/backfill.c b/builtin/backfill.c\nindex e80fc1b694..9b2ca57b6a 100644\n--- a/builtin/backfill.c\n+++ b/builtin/backfill.c\n@@ -129,6 +129,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n \t\t\t N_(\"Restrict the missing objects to the current sparse-checkout\")),\n \t\tOPT_END(),\n \t};\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tshow_usage_with_options_if_asked(argc, argv,\n \t\t\t\t\t builtin_backfill_usage, options);\n@@ -139,7 +140,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit\n \trepo_config(repo, git_default_config, NULL);\n \n \tif (ctx.sparse < 0)\n-\t\tctx.sparse = core_apply_sparse_checkout;\n+\t\tctx.sparse = cfg->apply_sparse_checkout;\n \n \tresult = do_backfill(&ctx);\n \tbackfill_context_clear(&ctx);\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..9ba08b86ae 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -617,13 +617,15 @@ static int git_sparse_checkout_init(const char *repo)\n {\n \tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint result = 0;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\n \tstrvec_pushl(&cmd.args, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n \n \t/*\n \t * We must apply the setting in the current process\n \t * for the later checkout to use the sparse-checkout file.\n \t */\n-\tcore_apply_sparse_checkout = 1;\n+\tcfg->apply_sparse_checkout = 1;\n \n \tcmd.git_cmd = 1;\n \tif (run_command(&cmd)) {\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 53cccf2d25..207d340548 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt,\n \t *\t\"forget\" the sparse-index feature switch. As a result, the index\n \t *\tof these submodules are expanded unexpectedly.\n \t *\n-\t * 2. \"core_apply_sparse_checkout\"\n+\t * 2. \"config_values_private_.apply_sparse_checkout\"\n \t *\tWhen running `grep` in the superproject, this setting is\n \t *\tpopulated using the superproject's configs. However, once\n \t *\tinitialized, this config is globally accessible and is read by\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex d43925097b..2215d34e31 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -238,6 +238,7 @@ int cmd_mv(int argc,\n \tstruct hashmap moved_dirs = HASHMAP_INIT(pathmap_cmp, NULL);\n \tstruct strbuf pathbuf = STRBUF_INIT;\n \tint ret;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \trepo_config(the_repository, git_default_config, NULL);\n \n@@ -572,7 +573,7 @@ int cmd_mv(int argc,\n \t\trename_index_entry_at(the_repository->index, pos, dst);\n \n \t\tif (ignore_sparse &&\n-\t\t    core_apply_sparse_checkout &&\n+\t\t    cfg->apply_sparse_checkout &&\n \t\t    core_sparse_checkout_cone) {\n \t\t\t/*\n \t\t\t * NEEDSWORK: we are *not* paying attention to\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..7f3317841e 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -61,9 +61,10 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,\n \tstruct pattern_list pl;\n \tchar *sparse_filename;\n \tint res;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!cfg->apply_sparse_checkout)\n \t\tdie(_(\"this worktree is not sparse\"));\n \n \targc = parse_options(argc, argv, prefix,\n@@ -399,12 +400,14 @@ static int set_config(struct repository *repo,\n }\n \n static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\n \t/* If not specified, use previous definition of cone mode */\n-\tif (*cone_mode == -1 && core_apply_sparse_checkout)\n+\tif (*cone_mode == -1 && cfg->apply_sparse_checkout)\n \t\t*cone_mode = core_sparse_checkout_cone;\n \n \t/* Set cone/non-cone mode appropriately */\n-\tcore_apply_sparse_checkout = 1;\n+\tcfg->apply_sparse_checkout = 1;\n \tif (*cone_mode == 1 || *cone_mode == -1) {\n \t\tcore_sparse_checkout_cone = 1;\n \t\treturn MODE_CONE_PATTERNS;\n@@ -416,9 +419,10 @@ static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {\n static int update_modes(struct repository *repo, int *cone_mode, int *sparse_index)\n {\n \tint mode, record_mode;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \t/* Determine if we need to record the mode; ensure sparse checkout on */\n-\trecord_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;\n+\trecord_mode = (*cone_mode != -1) || !cfg->apply_sparse_checkout;\n \n \tmode = update_cone_mode(cone_mode);\n \tif (record_mode && set_config(repo, mode))\n@@ -684,6 +688,7 @@ static int modify_pattern_list(struct repository *repo,\n \tint result;\n \tint changed_config = 0;\n \tstruct pattern_list *pl = xcalloc(1, sizeof(*pl));\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tswitch (m) {\n \tcase ADD:\n@@ -699,9 +704,9 @@ static int modify_pattern_list(struct repository *repo,\n \t\tbreak;\n \t}\n \n-\tif (!core_apply_sparse_checkout) {\n+\tif (!cfg->apply_sparse_checkout) {\n \t\tset_config(repo, MODE_ALL_PATTERNS);\n-\t\tcore_apply_sparse_checkout = 1;\n+\t\tcfg->apply_sparse_checkout = 1;\n \t\tchanged_config = 1;\n \t}\n \n@@ -796,9 +801,10 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,\n \t};\n \tstruct strvec patterns = STRVEC_INIT;\n \tint ret;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!cfg->apply_sparse_checkout)\n \t\tdie(_(\"no sparse-checkout to add to\"));\n \n \trepo_read_index(repo);\n@@ -905,9 +911,10 @@ static int sparse_checkout_reapply(int argc, const char **argv,\n \t\t\t N_(\"toggle the use of a sparse index\")),\n \t\tOPT_END(),\n \t};\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!cfg->apply_sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to reapply sparsity patterns\"));\n \n \treapply_opts.cone_mode = -1;\n@@ -960,6 +967,7 @@ static int sparse_checkout_clean(int argc, const char **argv,\n \tsize_t worktree_len;\n \tint force = 0, dry_run = 0, verbose = 0;\n \tint require_force = 1;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tstruct option builtin_sparse_checkout_clean_options[] = {\n \t\tOPT__DRY_RUN(&dry_run, N_(\"dry run\")),\n@@ -969,7 +977,7 @@ static int sparse_checkout_clean(int argc, const char **argv,\n \t};\n \n \tsetup_work_tree();\n-\tif (!core_apply_sparse_checkout)\n+\tif (!cfg->apply_sparse_checkout)\n \t\tdie(_(\"must be in a sparse-checkout to clean directories\"));\n \tif (!core_sparse_checkout_cone)\n \t\tdie(_(\"must be in a cone-mode sparse-checkout to clean directories\"));\n@@ -1033,9 +1041,10 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \t\tOPT_END(),\n \t};\n \tstruct pattern_list pl;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \t/*\n-\t * We do not exit early if !core_apply_sparse_checkout; due to the\n+\t * We do not exit early if !repo->config_values.apply_sparse_checkout; due to the\n \t * ability for users to manually muck things up between\n \t *   direct editing of .git/info/sparse-checkout\n \t *   running read-tree -m u HEAD or update-index --skip-worktree\n@@ -1061,7 +1070,7 @@ static int sparse_checkout_disable(int argc, const char **argv,\n \thashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);\n \thashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);\n \tpl.use_cone_patterns = 0;\n-\tcore_apply_sparse_checkout = 1;\n+\tcfg->apply_sparse_checkout = 1;\n \n \tadd_pattern(\"/*\", empty_base, 0, &pl, 0);\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex fbdaf2eb2e..90e56ab495 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -440,6 +440,7 @@ static int add_worktree(const char *path, const char *refname,\n \tstruct strbuf sb_name = STRBUF_INIT;\n \tstruct worktree **worktrees, *wt = NULL;\n \tstruct ref_store *wt_refs;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tworktrees = get_worktrees();\n \tcheck_candidate_path(path, opts->force, worktrees, \"add\");\n@@ -536,7 +537,7 @@ static int add_worktree(const char *path, const char *refname,\n \t * If the current worktree has sparse-checkout enabled, then copy\n \t * the sparse-checkout patterns from the current worktree.\n \t */\n-\tif (core_apply_sparse_checkout)\n+\tif (cfg->apply_sparse_checkout)\n \t\tcopy_sparse_checkout(sb_repo.buf);\n \n \t/*\ndiff --git a/dir.c b/dir.c\nindex b00821f294..026d8516a9 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1551,7 +1551,9 @@ enum pattern_match_result path_matches_pattern_list(\n \n int init_sparse_checkout_patterns(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout)\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\n+\tif (!cfg->apply_sparse_checkout)\n \t\treturn 1;\n \tif (istate->sparse_checkout_patterns)\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex 4b5c701e80..390af1ce54 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -74,7 +74,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #endif\n enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n int grafts_keep_true_parents;\n-int core_apply_sparse_checkout;\n int core_sparse_checkout_cone;\n int sparse_expect_files_outside_of_patterns;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n@@ -546,7 +545,7 @@ static int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.sparsecheckout\")) {\n-\t\tcore_apply_sparse_checkout = git_config_bool(var, value);\n+\t\tcfg->apply_sparse_checkout = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \n@@ -761,4 +760,5 @@ int git_default_config(const char *var, const char *value,\n void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file = NULL;\n+\tcfg->apply_sparse_checkout = 0;\n }\ndiff --git a/environment.h b/environment.h\nindex dfc31b794d..2e24160322 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -88,6 +88,7 @@ struct repository;\n struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n+\tint apply_sparse_checkout;\n };\n \n struct repo_config_values *repo_config_values(struct repository *repo);\n@@ -171,7 +172,6 @@ extern int precomposed_unicode;\n extern int protect_hfs;\n extern int protect_ntfs;\n \n-extern int core_apply_sparse_checkout;\n extern int core_sparse_checkout_cone;\n extern int sparse_expect_files_outside_of_patterns;\n \ndiff --git a/sparse-index.c b/sparse-index.c\nindex 76f90da5f5..386be1d6f1 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -152,7 +152,9 @@ static int index_has_unmerged_entries(struct index_state *istate)\n \n int is_sparse_index_allowed(struct index_state *istate, int flags)\n {\n-\tif (!core_apply_sparse_checkout || !core_sparse_checkout_cone)\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\n+\tif (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone)\n \t\treturn 0;\n \n \tif (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {\n@@ -670,7 +672,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n \n void clear_skip_worktree_from_present_files(struct index_state *istate)\n {\n-\tif (!core_apply_sparse_checkout ||\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\tif (!cfg->apply_sparse_checkout ||\n \t    sparse_expect_files_outside_of_patterns)\n \t\treturn;\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex f38c761ab9..998a1e6dc7 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1888,6 +1888,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tstruct pattern_list pl;\n \tint free_pattern_list = 0;\n \tstruct dir_struct dir = DIR_INIT;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tif (o->reset == UNPACK_RESET_INVALID)\n \t\tBUG(\"o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED\");\n@@ -1924,7 +1925,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tif (o->prefix)\n \t\tupdate_sparsity_for_prefix(o->prefix, o->src_index);\n \n-\tif (!core_apply_sparse_checkout || !o->update)\n+\tif (!cfg->apply_sparse_checkout || !o->update)\n \t\to->skip_sparse_checkout = 1;\n \tif (!o->skip_sparse_checkout) {\n \t\tmemset(&pl, 0, sizeof(pl));\ndiff --git a/wt-status.c b/wt-status.c\nindex e12adb26b9..6609b37cad 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1763,8 +1763,10 @@ static void wt_status_check_sparse_checkout(struct repository *r,\n {\n \tint skip_worktree = 0;\n \tint i;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n-\tif (!core_apply_sparse_checkout || r->index->cache_nr == 0) {\n+\tif (!cfg->apply_sparse_checkout ||\n+\t    r->index->cache_nr == 0) {\n \t\t/*\n \t\t * Don't compute percentage of checked out files if we\n \t\t * aren't in a sparse checkout or would get division by 0.\n-- \n2.34.1\n\n"},{"id":"536131","messageId":"c3ecfa63b918dc508295d23cbf78d5b60f73ff91.1771258573.git.belkid98@gmail.com","threadId":"64782","inReplyTo":"cover.1771258573.git.belkid98@gmail.com","subject":"[Outreachy PATCH v7 3/3] environment: move \"branch.autoSetupMerge\" into `struct repo_config_values`","fromName":"Olamide Caleb Bello","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-16T16:38:27Z","receivedAt":"2026-02-16T16:39:09Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"The config value `branch.autoSetupMerge` is parsed in\n`git_default_branch_config()` and stored in the global variable\n`git_branch_track`. This global variable can be overwritten\nby another repository when multiple Git repos run in the the same process.\n\nMove this value into `struct repo_config_values` in the_repository to\nretain current behaviours and move towards libifying Git.\nSince the variable is no longer a global variable, it has been renamed to\n`branch_track` in the struct `repo_config_values`.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>\nSigned-off-by: Olamide Caleb Bello <belkid98@gmail.com>\n---\n branch.h                    |  2 --\n builtin/branch.c            |  3 ++-\n builtin/checkout.c          |  3 ++-\n builtin/push.c              |  3 ++-\n builtin/submodule--helper.c |  3 ++-\n environment.c               | 12 +++++++-----\n environment.h               |  4 ++++\n 7 files changed, 19 insertions(+), 11 deletions(-)\n\ndiff --git a/branch.h b/branch.h\nindex ec2f35fda4..3dc6e2a0ff 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -15,8 +15,6 @@ enum branch_track {\n \tBRANCH_TRACK_SIMPLE,\n };\n \n-extern enum branch_track git_branch_track;\n-\n /* Functions for acting on the information about branches. */\n \n /**\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex c577b5d20f..a1a43380d0 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -724,6 +724,7 @@ int cmd_branch(int argc,\n \tstatic struct ref_sorting *sorting;\n \tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n \tstruct ref_format format = REF_FORMAT_INIT;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \tint ret;\n \n \tstruct option options[] = {\n@@ -795,7 +796,7 @@ int cmd_branch(int argc,\n \tif (!sorting_options.nr)\n \t\tstring_list_append(&sorting_options, \"refname\");\n \n-\ttrack = git_branch_track;\n+\ttrack = cfg->branch_track;\n \n \thead = refs_resolve_refdup(get_main_ref_store(the_repository), \"HEAD\",\n \t\t\t\t   0, &head_oid, NULL);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 261699e2f5..ea728e733c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1588,6 +1588,7 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,\n static int checkout_branch(struct checkout_opts *opts,\n \t\t\t   struct branch_info *new_branch_info)\n {\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \tint noop_switch = (!new_branch_info->name &&\n \t\t\t   !opts->new_branch &&\n \t\t\t   !opts->force_detach);\n@@ -1631,7 +1632,7 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\t\tdie(_(\"'%s' cannot be used with '%s'\"), \"--detach\", \"-t\");\n \t} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)\n-\t\topts->track = git_branch_track;\n+\t\topts->track = cfg->branch_track;\n \n \tif (new_branch_info->name && !new_branch_info->commit)\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 5b6cebbb85..7100ffba5d 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -151,6 +151,7 @@ static NORETURN void die_push_simple(struct branch *branch,\n \tconst char *advice_pushdefault_maybe = \"\";\n \tconst char *advice_automergesimple_maybe = \"\";\n \tconst char *short_upstream = branch->merge[0]->src;\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \tskip_prefix(short_upstream, \"refs/heads/\", &short_upstream);\n \n@@ -162,7 +163,7 @@ static NORETURN void die_push_simple(struct branch *branch,\n \t\tadvice_pushdefault_maybe = _(\"\\n\"\n \t\t\t\t \"To choose either option permanently, \"\n \t\t\t\t \"see push.default in 'git help config'.\\n\");\n-\tif (git_branch_track != BRANCH_TRACK_SIMPLE)\n+\tif (cfg->branch_track != BRANCH_TRACK_SIMPLE)\n \t\tadvice_automergesimple_maybe = _(\"\\n\"\n \t\t\t\t \"To avoid automatically configuring \"\n \t\t\t\t \"an upstream branch when its name\\n\"\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d537ab087a..594cd107b3 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3126,9 +3126,10 @@ static int module_create_branch(int argc, const char **argv, const char *prefix,\n \t\tN_(\"git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] [-n|--dry-run] <name> <start-oid> <start-name>\"),\n \t\tNULL\n \t};\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n \n \trepo_config(the_repository, git_default_config, NULL);\n-\ttrack = git_branch_track;\n+\ttrack = cfg->branch_track;\n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \n \tif (argc != 3)\ndiff --git a/environment.c b/environment.c\nindex 390af1ce54..1bc3adb75b 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -66,7 +66,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;\n enum eol core_eol = EOL_UNSET;\n int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;\n char *check_roundtrip_encoding;\n-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n enum rebase_setup_type autorebase = AUTOREBASE_NEVER;\n enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #ifndef OBJECT_CREATION_MODE\n@@ -607,18 +606,20 @@ static int git_default_i18n_config(const char *var, const char *value)\n \n static int git_default_branch_config(const char *var, const char *value)\n {\n+\tstruct repo_config_values *cfg = repo_config_values(the_repository);\n+\n \tif (!strcmp(var, \"branch.autosetupmerge\")) {\n \t\tif (value && !strcmp(value, \"always\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_ALWAYS;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_ALWAYS;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n \t\t} else if (value && !strcmp(value, \"simple\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\tcfg->branch_track = BRANCH_TRACK_SIMPLE;\n \t\t\treturn 0;\n \t\t}\n-\t\tgit_branch_track = git_config_bool(var, value);\n+\t\tcfg->branch_track = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"branch.autosetuprebase\")) {\n@@ -761,4 +762,5 @@ void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file = NULL;\n \tcfg->apply_sparse_checkout = 0;\n+\tcfg->branch_track = BRANCH_TRACK_REMOTE;\n }\ndiff --git a/environment.h b/environment.h\nindex 2e24160322..4bfd798757 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -2,6 +2,7 @@\n #define ENVIRONMENT_H\n \n #include \"repo-settings.h\"\n+#include \"branch.h\"\n \n /* Double-check local_repo_env below if you add to this list. */\n #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n@@ -89,6 +90,9 @@ struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n \tint apply_sparse_checkout;\n+\n+\t/* section \"branch\" config values */\n+\tenum branch_track branch_track;\n };\n \n struct repo_config_values *repo_config_values(struct repository *repo);\n-- \n2.34.1\n\n"},{"id":"536222","messageId":"xmqqa4x7b0ba.fsf@gitster.g","threadId":"64782","inReplyTo":"cover.1771258573.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v7 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-17T20:08:09Z","receivedAt":"2026-02-17T20:08:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Olamide Caleb Bello <belkid98@gmail.com> writes:\n\n> Changes in v7:\n> ==============\n> - Added the code to reset the initialization of the_repository\n>   in the fuzz-commit-graph test in Patch 1.\n> - Changed the call to rep_config_values_init() in\n>   initialze_repository() by passing it the repo config_values_private_\n>   struct instead of the accessor function in Patch 1.\n> - Modified the commit message in Patch 1\n> - Added extra lines between variable declarations and code\n\n>      +struct repo_config_values *repo_config_values(struct repository *repo)\n>      +{\n>     ++\tif (repo != the_repository)\n>     ++\t\tBUG(\"trying to read config from wrong repository instance\");\n>      +\tif(!repo->initialized)\n\n$ git clang-format --diff $(git merge-base master HEAD)\n\nwould have pointed out the style issue here.\n\nWill amend while queuing but it would be a bit before I can get to\nthis patch set for real reviews.\n\nThanks.\n\n"},{"id":"536273","messageId":"CAD=f0L-V6bMNHT8=jUDbO3j6mtFjnM97tO60xX9Dwek8AecOZg@mail.gmail.com","threadId":"64782","inReplyTo":"xmqqa4x7b0ba.fsf@gitster.g","subject":"Re: [Outreachy PATCH v7 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-18T11:27:33Z","receivedAt":"2026-02-18T11:27:33Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Tue, 17 Feb 2026 at 21:08, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Olamide Caleb Bello <belkid98@gmail.com> writes:\n>\n> > Changes in v7:\n> > ==============\n> > - Added the code to reset the initialization of the_repository\n> >   in the fuzz-commit-graph test in Patch 1.\n> > - Changed the call to rep_config_values_init() in\n> >   initialze_repository() by passing it the repo config_values_private_\n> >   struct instead of the accessor function in Patch 1.\n> > - Modified the commit message in Patch 1\n> > - Added extra lines between variable declarations and code\n>\n> >      +struct repo_config_values *repo_config_values(struct repository *repo)\n> >      +{\n> >     ++        if (repo != the_repository)\n> >     ++                BUG(\"trying to read config from wrong repository instance\");\n> >      +        if(!repo->initialized)\n>\n> $ git clang-format --diff $(git merge-base master HEAD)\n>\n> would have pointed out the style issue here.\n>\n> Will amend while queuing but it would be a bit before I can get to\n> this patch set for real reviews.\n>\n> Thanks.\n>\n\nSorry about missing this style issue and thank you for your reviews and guidance\nDo I wait for this before continuing to move other repo specific config values\ninto the struct since I might need to rebase?\n\nThanks\n"},{"id":"537185","messageId":"CAP8UFD1c5JgpQwMuTPE-VWS=7-1Lw7mWVaGGXCPwaG5=VYhk2A@mail.gmail.com","threadId":"64782","inReplyTo":"7d33f1ef0f8182893d63d49b350984025ab757d4.1771258573.git.belkid98@gmail.com","subject":"Re: [Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2026-02-26T12:57:45Z","receivedAt":"2026-02-26T12:57:58Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Mon, Feb 16, 2026 at 5:39 PM Olamide Caleb Bello <belkid98@gmail.com> wrote:\n\n> @@ -670,7 +672,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n>\n>  void clear_skip_worktree_from_present_files(struct index_state *istate)\n>  {\n> -       if (!core_apply_sparse_checkout ||\n> +       struct repo_config_values *cfg = repo_config_values(the_repository);\n\nNit: it would be better with a blank line here.\n\n> +       if (!cfg->apply_sparse_checkout ||\n>             sparse_expect_files_outside_of_patterns)\n>                 return;\n"},{"id":"537186","messageId":"CAP8UFD2Rz3NMrTayi9yjRk4-51tStaLWKeBVB9RaJMWmiBLqrQ@mail.gmail.com","threadId":"64782","inReplyTo":"xmqqa4x7b0ba.fsf@gitster.g","subject":"Re: [Outreachy PATCH v7 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2026-02-26T13:03:36Z","receivedAt":"2026-02-26T13:03:51Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Tue, Feb 17, 2026 at 9:08 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Olamide Caleb Bello <belkid98@gmail.com> writes:\n>\n> > Changes in v7:\n> > ==============\n> > - Added the code to reset the initialization of the_repository\n> >   in the fuzz-commit-graph test in Patch 1.\n> > - Changed the call to rep_config_values_init() in\n> >   initialze_repository() by passing it the repo config_values_private_\n> >   struct instead of the accessor function in Patch 1.\n> > - Modified the commit message in Patch 1\n> > - Added extra lines between variable declarations and code\n>\n> >      +struct repo_config_values *repo_config_values(struct repository *repo)\n> >      +{\n> >     ++        if (repo != the_repository)\n> >     ++                BUG(\"trying to read config from wrong repository instance\");\n> >      +        if(!repo->initialized)\n>\n> $ git clang-format --diff $(git merge-base master HEAD)\n>\n> would have pointed out the style issue here.\n>\n> Will amend while queuing but it would be a bit before I can get to\n> this patch set for real reviews.\n\nI took another look at the series and found only another nit (a\nmissing blank line). Otherwise the series looks good to me.\n\nThanks.\n"},{"id":"537195","messageId":"xmqqms0v5y7b.fsf@gitster.g","threadId":"64782","inReplyTo":"CAP8UFD2Rz3NMrTayi9yjRk4-51tStaLWKeBVB9RaJMWmiBLqrQ@mail.gmail.com","subject":"Re: [Outreachy PATCH v7 0/3] store repo specific config values in new `struct repo_config_values`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-26T15:19:52Z","receivedAt":"2026-02-26T15:19:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <christian.couder@gmail.com> writes:\n\n> I took another look at the series and found only another nit (a\n> missing blank line). Otherwise the series looks good to me.\n>\n> Thanks.\n\nThanks for reviewing.\n"},{"id":"537196","messageId":"xmqqikbj5y0y.fsf@gitster.g","threadId":"64782","inReplyTo":"CAP8UFD1c5JgpQwMuTPE-VWS=7-1Lw7mWVaGGXCPwaG5=VYhk2A@mail.gmail.com","subject":"Re: [Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-26T15:23:41Z","receivedAt":"2026-02-26T15:23:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <christian.couder@gmail.com> writes:\n\n> On Mon, Feb 16, 2026 at 5:39 PM Olamide Caleb Bello <belkid98@gmail.com> wrote:\n>\n>> @@ -670,7 +672,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n>>\n>>  void clear_skip_worktree_from_present_files(struct index_state *istate)\n>>  {\n>> -       if (!core_apply_sparse_checkout ||\n>> +       struct repo_config_values *cfg = repo_config_values(the_repository);\n>\n> Nit: it would be better with a blank line here.\n>\n>> +       if (!cfg->apply_sparse_checkout ||\n>>             sparse_expect_files_outside_of_patterns)\n>>                 return;\n\nOK.  Agreed and locally amended.\n\nLet's merge the topic down to 'next'.\n"},{"id":"537204","messageId":"CAD=f0L-_zq5ACD1mbfyBn3bTSu_X2emeMjv+Cfu5B5zyH8=DFw@mail.gmail.com","threadId":"64782","inReplyTo":"xmqqikbj5y0y.fsf@gitster.g","subject":"Re: [Outreachy PATCH v7 2/3] environment: stop using core.sparseCheckout globally","fromName":"Bello Olamide","fromEmail":"belkid98@gmail.com","sentAt":"2026-02-26T16:24:41Z","receivedAt":"2026-02-26T16:24:42Z","isPatch":true,"sender":{"key":"belkid98@gmail.com","avatar":"https://avatars.githubusercontent.com/u/73387291?v=4"},"body":"On Thu, 26 Feb 2026 at 16:23, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Christian Couder <christian.couder@gmail.com> writes:\n>\n> > On Mon, Feb 16, 2026 at 5:39 PM Olamide Caleb Bello <belkid98@gmail.com> wrote:\n> >\n> >> @@ -670,7 +672,8 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista\n> >>\n> >>  void clear_skip_worktree_from_present_files(struct index_state *istate)\n> >>  {\n> >> -       if (!core_apply_sparse_checkout ||\n> >> +       struct repo_config_values *cfg = repo_config_values(the_repository);\n> >\n> > Nit: it would be better with a blank line here.\n> >\n> >> +       if (!cfg->apply_sparse_checkout ||\n> >>             sparse_expect_files_outside_of_patterns)\n> >>                 return;\n>\n> OK.  Agreed and locally amended.\n>\n> Let's merge the topic down to 'next'.\n\nThank you very much for all your assistance.\n"}]}