{"thread":{"id":"65824","subject":"[PATCH 0/2] environment: move ignore_case into repo_config_values","startedAt":"2026-06-17T15:49:39Z","lastAt":"2026-07-10T15:01:32Z","messageCount":18,"participants":["Tian Yuchen","Junio C Hamano","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"545769","messageId":"20260617154929.564498-1-cat@malon.dev","threadId":"65824","inReplyTo":null,"subject":"[PATCH 0/2] environment: move ignore_case into repo_config_values","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-17T15:49:27Z","receivedAt":"2026-06-17T15:49:39Z","isPatch":true,"body":"The 'core.ignorecase' configuration, stored as the global variable\n'ignore_case', acts as a core filesystem capability flag.\n\nThis series continues the ongoing libification effort by moving\nthis global variable into struct 'repo_config_values', tying it\nto the specific repository instance it was read from. This allows\nus to encapsulate the configuration without altering its\neager-parsing behavior.\n\nThe getter function 'repo_get_ignore_case()' is introduced so\nthat we can safely retrieve the configuration value whilst\nmaintaining the correct fallback logic.\n\nRFC Questions:\n\nenvironment.h --- Is the fallback logic for repo_get_ignore_case()\ncorrect? I am unsure whether gitdir should be used here, since it\nmight not be ready when we access it in the early stage of\ninitialization (e.g. git init / git clone).\n\ndir.c --- Performance overhead?\n\ncompat/win32/path-utils.c --- Is it appropriate to include the\nrepository.h header file?\n\nRelated materials:\n\n[1] In this patch to migrate protect_hfs and protect_ntfs, the approach\nof introducing getters has been endorsed.\n[2] Derrick Stolee's previous attempt. The reasons for the failure are\nalso mentioned in [1].\n\nThanks!\n\nMentored-by: Christian Couder christian.couder@gmail.com\nMentored-by: Ayush Chandekar ayu.chandekar@gmail.com\nMentored-by: Olamide Caleb Bello belkid98@gmail.com\nSigned-off-by: Tian Yuchen cat@malon.dev\n\n[1] https://lore.kernel.org/git/20260606143412.15443-1-cat@malon.dev/\n[2] https://lore.kernel.org/git/2b4198c09cb6c04c60608d19072d419503dfe5df.1685716421.git.gitgitgadget@gmail.com/\n\nTian Yuchen (2):\n  environment: move ignore_case into repo_config_values\n  config: use repo_get_ignore_case() to access core.ignorecase\n\n apply.c                             |  2 +-\n builtin/fetch.c                     |  2 +-\n builtin/mv.c                        |  2 +-\n compat/win32/path-utils.c           |  3 ++-\n dir.c                               | 18 +++++++++---------\n environment.c                       | 11 +++++++++--\n environment.h                       |  9 ++++++++-\n fsmonitor.c                         |  2 +-\n name-hash.c                         |  6 +++---\n read-cache.c                        |  6 +++---\n refs/files-backend.c                |  4 ++--\n submodule.c                         |  2 +-\n t/helper/test-lazy-init-name-hash.c |  2 +-\n unpack-trees.c                      |  2 +-\n 14 files changed, 43 insertions(+), 28 deletions(-)\n\n-- \n2.43.0\n\n"},{"id":"545770","messageId":"20260617154929.564498-2-cat@malon.dev","threadId":"65824","inReplyTo":"20260617154929.564498-1-cat@malon.dev","subject":"[PATCH 1/2] environment: move ignore_case into repo_config_values","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-17T15:49:28Z","receivedAt":"2026-06-17T15:49:45Z","isPatch":true,"body":"The 'core.ignorecase' configuration which is stored as the\nglobal variable 'ignore_case' acts as a core filesystem\ncapability flag.\n\nMove this global variable into 'struct repo_config_values' to tie it\nto the specific repository instance it was read from. This reduces\nglobal state and aligns with the ongoing libification effort.\n\nNote that the newly introduced getter, 'repo_get_ignore_case()',\nintentionally avoids checking 'repo->gitdir'. This could safely\naccommodates early dynamic probing of the filesystem during\n'git init' or clone operations, where the 'gitdir' might not be fully\ninitialized but the filesystem capability must be recorded.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>\nMentored-by: Olamide Caleb Bello <belkid98@gmail.com>\nSigned-off-by: Tian Yuchen <cat@malon.dev>\n---\n environment.c | 8 ++++++++\n environment.h | 8 ++++++++\n 2 files changed, 16 insertions(+)\n\ndiff --git a/environment.c b/environment.c\nindex fc3ed8bb1c..c568d3b6fb 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -142,6 +142,13 @@ int is_bare_repository(void)\n \treturn is_bare_repository_cfg && !repo_get_work_tree(the_repository);\n }\n \n+int repo_get_ignore_case(struct repository *repo)\n+{\n+\tif (repo)\n+\t\treturn repo_config_values(repo)->ignore_case;\n+\treturn 0;\n+}\n+\n int have_git_dir(void)\n {\n \treturn startup_info->have_repository\n@@ -720,5 +727,6 @@ void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file = NULL;\n \tcfg->apply_sparse_checkout = 0;\n+\tcfg->ignore_case = 0;\n \tcfg->branch_track = BRANCH_TRACK_REMOTE;\n }\ndiff --git a/environment.h b/environment.h\nindex 9eb97b3869..9e3d94fb80 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -91,6 +91,7 @@ struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n \tint apply_sparse_checkout;\n+\tint ignore_case;\n \n \t/* section \"branch\" config values */\n \tenum branch_track branch_track;\n@@ -123,6 +124,13 @@ int git_default_config(const char *, 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+/*\n+ * Getter for the `ignore_case` field of `struct repo_config_values`.\n+ * It intentionally avoids checking `repo->gitdir` to allow early dynamic\n+ * probing during `git init` or clone.\n+ */\n+int repo_get_ignore_case(struct repository *repo);\n+\n void repo_config_values_init(struct repo_config_values *cfg);\n \n /*\n-- \n2.43.0\n\n"},{"id":"545771","messageId":"20260617154929.564498-3-cat@malon.dev","threadId":"65824","inReplyTo":"20260617154929.564498-1-cat@malon.dev","subject":"[PATCH 2/2] config: use repo_get_ignore_case() to access core.ignorecase","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-17T15:49:29Z","receivedAt":"2026-06-17T15:49:52Z","isPatch":true,"body":"Replace the accesses to the global 'ignore_case' variable with\ncalls to 'repo_get_ignore_case(the_repository)'. This step eliminates\nthe 'ignore_case' global state.\n\nNote on compat/win32/path-utils.c:\nTo eliminate the global state, several helper functions\n(e.g. 'win32_fspathncmp()') now read from\n'repo_get_ignore_case(the_repository)'. While this introduces\ndependency on 'repository.h' into the 'compat/', it avoids massive\nrefactoring of the signatures across the codebase.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>\nMentored-by: Olamide Caleb Bello <belkid98@gmail.com>\nSigned-off-by: Tian Yuchen <cat@malon.dev>\n---\n apply.c                             |  2 +-\n builtin/fetch.c                     |  2 +-\n builtin/mv.c                        |  2 +-\n compat/win32/path-utils.c           |  3 ++-\n dir.c                               | 18 +++++++++---------\n environment.c                       |  3 +--\n environment.h                       |  1 -\n fsmonitor.c                         |  2 +-\n name-hash.c                         |  6 +++---\n read-cache.c                        |  6 +++---\n refs/files-backend.c                |  4 ++--\n submodule.c                         |  2 +-\n t/helper/test-lazy-init-name-hash.c |  2 +-\n unpack-trees.c                      |  2 +-\n 14 files changed, 27 insertions(+), 28 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 249248d4f2..53309b9a09 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -4008,7 +4008,7 @@ static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *na\n \t\t\tstruct cache_entry *ce;\n \n \t\t\tce = index_file_exists(state->repo->index, name->buf,\n-\t\t\t\t\t       name->len, ignore_case);\n+\t\t\t\t\t       name->len, repo_get_ignore_case(the_repository));\n \t\t\tif (ce && S_ISLNK(ce->ce_mode))\n \t\t\t\treturn 1;\n \t\t} else {\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex e4e8a72ed9..67c7df0f3c 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1819,7 +1819,7 @@ static void ref_transaction_rejection_handler(const char *refname,\n {\n \tstruct ref_rejection_data *data = cb_data;\n \n-\tif (err == REF_TRANSACTION_ERROR_CASE_CONFLICT && ignore_case &&\n+\tif (err == REF_TRANSACTION_ERROR_CASE_CONFLICT && repo_get_ignore_case(the_repository) &&\n \t    !data->case_sensitive_msg_shown) {\n \t\terror(_(\"You're on a case-insensitive filesystem, and the remote you are\\n\"\n \t\t\t\"trying to fetch from has references that only differ in casing. It\\n\"\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex 948b330639..0f6f060004 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -419,7 +419,7 @@ int cmd_mv(int argc,\n \t\t\tgoto act_on_entry;\n \t\t}\n \t\tif (lstat(dst, &st) == 0 &&\n-\t\t    (!ignore_case || strcasecmp(src, dst))) {\n+\t\t    (!repo_get_ignore_case(the_repository) || strcasecmp(src, dst))) {\n \t\t\tbad = _(\"destination exists\");\n \t\t\tif (force) {\n \t\t\t\t/*\ndiff --git a/compat/win32/path-utils.c b/compat/win32/path-utils.c\nindex 966ef779b9..4edb033e20 100644\n--- a/compat/win32/path-utils.c\n+++ b/compat/win32/path-utils.c\n@@ -2,6 +2,7 @@\n \n #include \"../../git-compat-util.h\"\n #include \"../../environment.h\"\n+#include \"../../repository.h\"\n \n int win32_has_dos_drive_prefix(const char *path)\n {\n@@ -75,7 +76,7 @@ int win32_fspathncmp(const char *a, const char *b, size_t count)\n \t\t} else if (is_dir_sep(*b))\n \t\t\treturn +1;\n \n-\t\tdiff = ignore_case ?\n+\t\tdiff = repo_get_ignore_case(the_repository) ?\n \t\t\t(unsigned char)tolower(*a) - (int)(unsigned char)tolower(*b) :\n \t\t\t(unsigned char)*a - (int)(unsigned char)*b;\n \t\tif (diff)\ndiff --git a/dir.c b/dir.c\nindex 33c81c256e..7116d65cad 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -126,7 +126,7 @@ int count_slashes(const char *s)\n \n int git_fspathcmp(const char *a, const char *b)\n {\n-\treturn ignore_case ? strcasecmp(a, b) : strcmp(a, b);\n+\treturn repo_get_ignore_case(the_repository) ? strcasecmp(a, b) : strcmp(a, b);\n }\n \n int fspatheq(const char *a, const char *b)\n@@ -136,7 +136,7 @@ int fspatheq(const char *a, const char *b)\n \n int git_fspathncmp(const char *a, const char *b, size_t count)\n {\n-\treturn ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);\n+\treturn repo_get_ignore_case(the_repository) ? strncasecmp(a, b, count) : strncmp(a, b, count);\n }\n \n int paths_collide(const char *a, const char *b)\n@@ -153,7 +153,7 @@ int paths_collide(const char *a, const char *b)\n \n unsigned int fspathhash(const char *str)\n {\n-\treturn ignore_case ? strihash(str) : strhash(str);\n+\treturn repo_get_ignore_case(the_repository) ? strihash(str) : strhash(str);\n }\n \n int git_fnmatch(const struct pathspec_item *item,\n@@ -202,7 +202,7 @@ static int fnmatch_icase_mem(const char *pattern, int patternlen,\n \t\tuse_str = str_buf.buf;\n \t}\n \n-\tif (ignore_case)\n+\tif (repo_get_ignore_case(the_repository))\n \t\tflags |= WM_CASEFOLD;\n \tmatch_status = wildmatch(use_pat, use_str, flags);\n \n@@ -1851,7 +1851,7 @@ static struct dir_entry *dir_add_name(struct dir_struct *dir,\n \t\t\t\t      struct index_state *istate,\n \t\t\t\t      const char *pathname, int len)\n {\n-\tif (index_file_exists(istate, pathname, len, ignore_case))\n+\tif (index_file_exists(istate, pathname, len, repo_get_ignore_case(the_repository)))\n \t\treturn NULL;\n \n \tALLOC_GROW(dir->entries, dir->nr+1, dir->internal.alloc);\n@@ -1888,7 +1888,7 @@ static enum exist_status directory_exists_in_index_icase(struct index_state *ist\n \tif (index_dir_exists(istate, dirname, len))\n \t\treturn index_directory;\n \n-\tce = index_file_exists(istate, dirname, len, ignore_case);\n+\tce = index_file_exists(istate, dirname, len, repo_get_ignore_case(the_repository));\n \tif (ce && S_ISGITLINK(ce->ce_mode))\n \t\treturn index_gitdir;\n \n@@ -1907,7 +1907,7 @@ static enum exist_status directory_exists_in_index(struct index_state *istate,\n {\n \tint pos;\n \n-\tif (ignore_case)\n+\tif (repo_get_ignore_case(the_repository))\n \t\treturn directory_exists_in_index_icase(istate, dirname, len);\n \n \tpos = index_name_pos(istate, dirname, len);\n@@ -2447,7 +2447,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,\n \n \t/* Always exclude indexed files */\n \thas_path_in_index = !!index_file_exists(istate, path->buf, path->len,\n-\t\t\t\t\t\tignore_case);\n+\t\t\t\t\t\trepo_get_ignore_case(the_repository));\n \tif (dtype != DT_DIR && has_path_in_index)\n \t\treturn path_none;\n \n@@ -3201,7 +3201,7 @@ static int cmp_icase(char a, char b)\n {\n \tif (a == b)\n \t\treturn 0;\n-\tif (ignore_case)\n+\tif (repo_get_ignore_case(the_repository))\n \t\treturn toupper(a) - toupper(b);\n \treturn a - b;\n }\ndiff --git a/environment.c b/environment.c\nindex c568d3b6fb..1f548b357c 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -46,7 +46,6 @@ int trust_ctime = 1;\n int check_stat = 1;\n int has_symlinks = 1;\n int minimum_abbrev = 4, default_abbrev = -1;\n-int ignore_case;\n int assume_unchanged;\n int is_bare_repository_cfg = -1; /* unspecified */\n int warn_on_object_refname_ambiguity = 1;\n@@ -342,7 +341,7 @@ int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.ignorecase\")) {\n-\t\tignore_case = git_config_bool(var, value);\n+\t\tcfg->ignore_case = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \ndiff --git a/environment.h b/environment.h\nindex 9e3d94fb80..66fdb1ed20 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -171,7 +171,6 @@ extern int trust_ctime;\n extern int check_stat;\n extern int has_symlinks;\n extern int minimum_abbrev, default_abbrev;\n-extern int ignore_case;\n extern int assume_unchanged;\n extern int warn_on_object_refname_ambiguity;\n extern char *apply_default_whitespace;\ndiff --git a/fsmonitor.c b/fsmonitor.c\nindex d07dc18967..5376e1987a 100644\n--- a/fsmonitor.c\n+++ b/fsmonitor.c\n@@ -453,7 +453,7 @@ static void fsmonitor_refresh_callback(struct index_state *istate, char *name)\n \t * case-insensitive file system, try again using the name-hash\n \t * and dir-name-hash.\n \t */\n-\tif (!nr_in_cone && ignore_case) {\n+\tif (!nr_in_cone && repo_get_ignore_case(the_repository)) {\n \t\tnr_in_cone = handle_using_name_hash_icase(istate, name);\n \t\tif (!nr_in_cone)\n \t\t\tnr_in_cone = handle_using_dir_name_hash_icase(\ndiff --git a/name-hash.c b/name-hash.c\nindex b91e276267..6bb2ecdd05 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -126,7 +126,7 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)\n \t\thashmap_add(&istate->name_hash, &ce->ent);\n \t}\n \n-\tif (ignore_case)\n+\tif (repo_get_ignore_case(the_repository))\n \t\tadd_dir_entry(istate, ce);\n }\n \n@@ -207,7 +207,7 @@ static int lookup_lazy_params(struct index_state *istate)\n \t * code to build the \"istate->name_hash\".  We don't\n \t * need the complexity here.\n \t */\n-\tif (!ignore_case)\n+\tif (!repo_get_ignore_case(the_repository))\n \t\treturn 0;\n \n \tnr_cpus = online_cpus();\n@@ -651,7 +651,7 @@ void remove_name_hash(struct index_state *istate, struct cache_entry *ce)\n \tce->ce_flags &= ~CE_HASHED;\n \thashmap_remove(&istate->name_hash, &ce->ent, ce);\n \n-\tif (ignore_case)\n+\tif (repo_get_ignore_case(the_repository))\n \t\tremove_dir_entry(istate, ce);\n }\n \ndiff --git a/read-cache.c b/read-cache.c\nindex 21829102ae..1409ac00b4 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -760,12 +760,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \t * case of the file being added to the repository matches (is folded into) the existing\n \t * entry's directory case.\n \t */\n-\tif (ignore_case) {\n+\tif (repo_get_ignore_case(the_repository)) {\n \t\tadjust_dirname_case(istate, ce->name);\n \t}\n \tif (!(flags & ADD_CACHE_RENORMALIZE)) {\n \t\talias = index_file_exists(istate, ce->name,\n-\t\t\t\t\t  ce_namelen(ce), ignore_case);\n+\t\t\t\t\t  ce_namelen(ce), repo_get_ignore_case(the_repository));\n \t\tif (alias &&\n \t\t    !ce_stage(alias) &&\n \t\t    !ie_match_stat(istate, alias, st, ce_option)) {\n@@ -786,7 +786,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \t} else\n \t\tset_object_name_for_intent_to_add_entry(ce);\n \n-\tif (ignore_case && alias && different_name(ce, alias))\n+\tif (repo_get_ignore_case(the_repository) && alias && different_name(ce, alias))\n \t\tce = create_alias_ce(istate, ce, alias);\n \tce->ce_flags |= CE_ADDED;\n \ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex a4c7858787..6d89d9817a 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -806,7 +806,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,\n \t\t} else {\n \t\t\tunable_to_lock_message(ref_file.buf, myerr, err);\n \t\t\tif (myerr == EEXIST) {\n-\t\t\t\tif (ignore_case &&\n+\t\t\t\tif (repo_get_ignore_case(the_repository) &&\n \t\t\t\t    transaction_has_case_conflicting_update(transaction, update)) {\n \t\t\t\t\t/*\n \t\t\t\t\t * In case-insensitive filesystems, ensure that conflicts within a\n@@ -920,7 +920,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,\n \t\t * conflicts between 'foo' and 'Foo/bar'. So let's lowercase\n \t\t * the refname.\n \t\t */\n-\t\tif (ignore_case) {\n+\t\tif (repo_get_ignore_case(the_repository)) {\n \t\t\tstruct strbuf lower = STRBUF_INIT;\n \n \t\t\tstrbuf_addstr(&lower, refname);\ndiff --git a/submodule.c b/submodule.c\nindex a939ff5072..32af85d967 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -2389,7 +2389,7 @@ static int validate_submodule_encoded_git_dir(char *git_dir, const char *submodu\n \n \t/* Prevent conflicts on case-folding filesystems */\n \trepo_config_get_bool(the_repository, \"core.ignorecase\", &config_ignorecase);\n-\tif (ignore_case || config_ignorecase) {\n+\tif (repo_get_ignore_case(the_repository) || config_ignorecase) {\n \t\tbool suffixes_match = !strcmp(last_submodule_name, submodule_name);\n \t\treturn check_casefolding_conflict(git_dir, submodule_name,\n \t\t\t\t\t\t  suffixes_match);\ndiff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c\nindex e542985c94..43cead6d7d 100644\n--- a/t/helper/test-lazy-init-name-hash.c\n+++ b/t/helper/test-lazy-init-name-hash.c\n@@ -218,7 +218,7 @@ int cmd__lazy_init_name_hash(int argc, const char **argv)\n \t/*\n \t * istate->dir_hash is only created when ignore_case is set.\n \t */\n-\tignore_case = 1;\n+\trepo_config_values(the_repository)->ignore_case = 1;\n \n \tif (dump) {\n \t\tif (perf || analyze > 0)\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 998a1e6dc7..330c5c0172 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -2428,7 +2428,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \t *\n \t * Ignore that lstat() if it matches.\n \t */\n-\tif (ignore_case && icase_exists(o, name, len, st))\n+\tif (repo_get_ignore_case(the_repository) && icase_exists(o, name, len, st))\n \t\treturn 0;\n \n \tif (o->internal.dir &&\n-- \n2.43.0\n\n"},{"id":"545780","messageId":"xmqqh5n1w0i7.fsf@gitster.g","threadId":"65824","inReplyTo":"20260617154929.564498-2-cat@malon.dev","subject":"Re: [PATCH 1/2] environment: move ignore_case into repo_config_values","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-06-17T17:16:32Z","receivedAt":"2026-06-17T17:16:34Z","isPatch":true,"body":"Tian Yuchen <cat@malon.dev> writes:\n\n> Note that the newly introduced getter, 'repo_get_ignore_case()',\n> intentionally avoids checking 'repo->gitdir'. This could safely\n> accommodates early dynamic probing of the filesystem during\n> 'git init' or clone operations, where the 'gitdir' might not be fully\n> initialized but the filesystem capability must be recorded.\n\nWhy \"could\"?  It either \"safely accommodates\" or it doesn't.\n\nI do not quite understand the logic behind this part.  Why is it OK\nto punt until .gitdir is ready for trust-executable-bit, like it is\ndone in f951ed98 (environment: move trust_executable_bit into\nrepo_config_values, 2026-06-13)\n\ndiff --git a/environment.c b/environment.c\nindex fc3ed8bb1c..75069a884d 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -142,6 +141,13 @@ int is_bare_repository(void)\n \treturn is_bare_repository_cfg && !repo_get_work_tree(the_repository);\n }\n \n+int repo_trust_executable_bit(struct repository *repo)\n+{\n+\treturn repo->gitdir?\n+\t\trepo_config_values(repo)->trust_executable_bit :\n+\t\t1;\n+}\n+\n\nor hfs/ntfs in 71386c21 (environment: move 'protect_hfs' and\n'protect_ntfs' into 'repo_config_values', 2026-06-10)\n\ndiff --git a/environment.c b/environment.c\nindex fc3ed8bb1c..683fe1b4d3 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -142,6 +140,20 @@ int is_bare_repository(void)\n \treturn is_bare_repository_cfg && !repo_get_work_tree(the_repository);\n }\n \n+int repo_protect_ntfs(struct repository *repo)\n+{\n+\treturn repo->gitdir ?\n+\t\trepo_config_values(repo)->protect_ntfs :\n+\t\tPROTECT_NTFS_DEFAULT;\n+}\n+\n+int repo_protect_hfs(struct repository *repo)\n+{\n+\treturn repo->gitdir ?\n+\t\trepo_config_values(repo)->protect_hfs :\n+\t\tPROTECT_HFS_DEFAULT;\n+}\n+\n int have_git_dir(void)\n {\n \treturn startup_info->have_repository\n\nbut not for this bit?\n\n> +int repo_get_ignore_case(struct repository *repo)\n> +{\n> +\tif (repo)\n> +\t\treturn repo_config_values(repo)->ignore_case;\n> +\treturn 0;\n> +}\n\nWhat makes ignore-case so special?  Doesn't the same logic apply to\nthe other three bits?\n\nOr use a more direct\n\n\tif (repo && repo->initialized)\n\t\t...;\n\nfor all three, as repo_config_values(repo) barfs when repo is not\ninitialized?\n\nI dunno.\n"},{"id":"545831","messageId":"dcafaf9d-d422-46ae-96c3-26674ee70e3f@malon.dev","threadId":"65824","inReplyTo":"xmqqh5n1w0i7.fsf@gitster.g","subject":"Re: [PATCH 1/2] environment: move ignore_case into repo_config_values","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-18T10:56:25Z","receivedAt":"2026-06-18T10:56:35Z","isPatch":true,"body":"On 6/18/26 01:16, Junio C Hamano wrote:\n> Tian Yuchen <cat@malon.dev> writes:\n> \n>> Note that the newly introduced getter, 'repo_get_ignore_case()',\n>> intentionally avoids checking 'repo->gitdir'. This could safely\n>> accommodates early dynamic probing of the filesystem during\n>> 'git init' or clone operations, where the 'gitdir' might not be fully\n>> initialized but the filesystem capability must be recorded.\n> \n> Why \"could\"?  It either \"safely accommodates\" or it doesn't.\n> \n\nOkay, will change in the next reroll.\n\n> I do not quite understand the logic behind this part.  Why is it OK\n> to punt until .gitdir is ready for trust-executable-bit, like it is\n> done in f951ed98 (environment: move trust_executable_bit into\n> repo_config_values, 2026-06-13)\n> \n> diff --git a/environment.c b/environment.c\n> index fc3ed8bb1c..75069a884d 100644\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -142,6 +141,13 @@ int is_bare_repository(void)\n>   \treturn is_bare_repository_cfg && !repo_get_work_tree(the_repository);\n>   }\n>   \n> +int repo_trust_executable_bit(struct repository *repo)\n> +{\n> +\treturn repo->gitdir?\n> +\t\trepo_config_values(repo)->trust_executable_bit :\n> +\t\t1;\n> +}\n> +\n> \n> or hfs/ntfs in 71386c21 (environment: move 'protect_hfs' and\n> 'protect_ntfs' into 'repo_config_values', 2026-06-10)\n> \n> diff --git a/environment.c b/environment.c\n> index fc3ed8bb1c..683fe1b4d3 100644\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -142,6 +140,20 @@ int is_bare_repository(void)\n>   \treturn is_bare_repository_cfg && !repo_get_work_tree(the_repository);\n>   }\n>   \n> +int repo_protect_ntfs(struct repository *repo)\n> +{\n> +\treturn repo->gitdir ?\n> +\t\trepo_config_values(repo)->protect_ntfs :\n> +\t\tPROTECT_NTFS_DEFAULT;\n> +}\n> +\n> +int repo_protect_hfs(struct repository *repo)\n> +{\n> +\treturn repo->gitdir ?\n> +\t\trepo_config_values(repo)->protect_hfs :\n> +\t\tPROTECT_HFS_DEFAULT;\n> +}\n> +\n>   int have_git_dir(void)\n>   {\n>   \treturn startup_info->have_repository\n> \n> but not for this bit?\n\nYou're right, I made a mistake.\n\nEarlier, when I was testing using 'repo->gitdir', the CI tests failed, \nand I thought it was because the test script was forcing initial values \ntoo early. I just realized the error was somewhere else. I'll fix it.\n\n> \n>> +int repo_get_ignore_case(struct repository *repo)\n>> +{\n>> +\tif (repo)\n>> +\t\treturn repo_config_values(repo)->ignore_case;\n>> +\treturn 0;\n>> +}\n> \n> What makes ignore-case so special?  Doesn't the same logic apply to\n> the other three bits?\n> \n> Or use a more direct\n> \n> \tif (repo && repo->initialized)\n> \t\t...;\n> \n> for all three, as repo_config_values(repo) barfs when repo is not\n> initialized?\n> \n> I dunno.\n\nThat makes some sense. The reasoning behind using 'repo->gitdir' is that \n\"as long as I know where .git is, I assume the repository has been \ninitialized,\" which may indeed be less precise than 'repo->initialized'.\n\nBtw, I think it's better to change the getter's name from \n'repo_get_ignore_case()' to 'repo_ignore_case()'. This way, it will be \nconsistent with the previous flags.\n\nThanks, yuchen\n"},{"id":"545832","messageId":"20260618114207.605211-1-cat@malon.dev","threadId":"65824","inReplyTo":"20260617154929.564498-1-cat@malon.dev","subject":"[PATCH v2 0/2] environment: move ignore_case into repo_config_values","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-18T11:42:05Z","receivedAt":"2026-06-18T11:42:21Z","isPatch":true,"body":"The 'core.ignorecase' configuration, stored as the global variable\n'ignore_case', acts as a core filesystem capability flag.\n\nThis series continues the ongoing libification effort by moving\nthis global variable into 'struct repo_config_values', tying it\nto the specific repository instance it was read from. This allows\nus to encapsulate the configuration without altering its\neager-parsing behavior.\n\nThe getter function 'repo_ignore_case()' is introduced so\nthat we can safely retrieve the configuration value whilst\nmaintaining the correct fallback logic.\n\nRFC Questions:\n\ndir.c --- Performance overhead?\n\ncompat/win32/path-utils.c --- Is it appropriate to include the\nrepository.h header file?\n\nRelated materials:\n\n [1] In this patch to migrate protect_hfs and protect_ntfs, the approach\nof introducing getters has been endorsed.\n\n [2] Derrick Stolee's previous attempt. The reasons for the failure are\nalso mentioned in [1].\n\nChanges since V1:\n\n - s/repo_get_ignore_case()/repo_ignore_case()\n\n - Use repo->initialized instead of repo->gitdir\n\nThanks!\n\nMentored-by: Christian Couder christian.couder@gmail.com\nMentored-by: Ayush Chandekar ayu.chandekar@gmail.com\nMentored-by: Olamide Caleb Bello belkid98@gmail.com\nSigned-off-by: Tian Yuchen cat@malon.dev\n\n[1] https://lore.kernel.org/git/20260606143412.15443-1-cat@malon.dev/\n[2] https://lore.kernel.org/git/2b4198c09cb6c04c60608d19072d419503dfe5df.1685716421.git.gitgitgadget@gmail.com/\n\nTian Yuchen (2):\n  environment: move ignore_case into repo_config_values\n  config: use repo_ignore_case() to access core.ignorecase\n\n apply.c                             |  2 +-\n builtin/fetch.c                     |  2 +-\n builtin/mv.c                        |  2 +-\n compat/win32/path-utils.c           |  3 ++-\n dir.c                               | 18 +++++++++---------\n environment.c                       | 11 +++++++++--\n environment.h                       |  9 ++++++++-\n fsmonitor.c                         |  2 +-\n name-hash.c                         |  6 +++---\n read-cache.c                        |  6 +++---\n refs/files-backend.c                |  4 ++--\n submodule.c                         |  2 +-\n t/helper/test-lazy-init-name-hash.c |  2 +-\n unpack-trees.c                      |  2 +-\n 14 files changed, 43 insertions(+), 28 deletions(-)\n\n-- \n2.43.0\n\n"},{"id":"545833","messageId":"20260618114207.605211-2-cat@malon.dev","threadId":"65824","inReplyTo":"20260618114207.605211-1-cat@malon.dev","subject":"[PATCH v2 1/2] environment: move ignore_case into repo_config_values","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-18T11:42:06Z","receivedAt":"2026-06-18T11:42:24Z","isPatch":true,"body":"The 'core.ignorecase' configuration which is stored as the\nglobal variable 'ignore_case' acts as a core filesystem\ncapability flag.\n\nMove this global variable into 'struct repo_config_values' to tie it\nto the specific repository instance it was read from. This reduces\nglobal state and aligns with the ongoing libification effort.\n\nTo ensure code readability, the getter function\n'repo_ignore_case()' is introduced.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>\nMentored-by: Olamide Caleb Bello <belkid98@gmail.com>\nSigned-off-by: Tian Yuchen <cat@malon.dev>\n---\n environment.c | 8 ++++++++\n environment.h | 8 ++++++++\n 2 files changed, 16 insertions(+)\n\ndiff --git a/environment.c b/environment.c\nindex fc3ed8bb1c..bfa3cb3045 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -142,6 +142,13 @@ int is_bare_repository(void)\n \treturn is_bare_repository_cfg && !repo_get_work_tree(the_repository);\n }\n \n+int repo_ignore_case(struct repository *repo)\n+{\n+\treturn (repo && repo->initialized) ?\n+\t\trepo_config_values(repo)->ignore_case :\n+\t\t0;\n+}\n+\n int have_git_dir(void)\n {\n \treturn startup_info->have_repository\n@@ -720,5 +727,6 @@ void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file = NULL;\n \tcfg->apply_sparse_checkout = 0;\n+\tcfg->ignore_case = 0;\n \tcfg->branch_track = BRANCH_TRACK_REMOTE;\n }\ndiff --git a/environment.h b/environment.h\nindex 9eb97b3869..39a8bf0b49 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -91,6 +91,7 @@ struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n \tint apply_sparse_checkout;\n+\tint ignore_case;\n \n \t/* section \"branch\" config values */\n \tenum branch_track branch_track;\n@@ -123,6 +124,13 @@ int git_default_config(const char *, 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+/*\n+ * Getter for the `ignore_case` field of `struct repo_config_values`.\n+ * It checks `repo->initialized` to prevent calling repo_config_values()`\n+ * before the repository setup is fully complete or in non-git environments.\n+ */\n+int repo_ignore_case(struct repository *repo);\n+\n void repo_config_values_init(struct repo_config_values *cfg);\n \n /*\n-- \n2.43.0\n\n"},{"id":"545834","messageId":"20260618114207.605211-3-cat@malon.dev","threadId":"65824","inReplyTo":"20260618114207.605211-1-cat@malon.dev","subject":"[PATCH v2 2/2] config: use repo_ignore_case() to access core.ignorecase","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-18T11:42:07Z","receivedAt":"2026-06-18T11:42:30Z","isPatch":true,"body":"Replace the accesses to the global 'ignore_case' variable with\ncalls to 'repo_ignore_case(the_repository)'. This step eliminates\nthe 'ignore_case' global state.\n\nNote on compat/win32/path-utils.c:\nTo eliminate the global state, several helper functions\n(e.g. 'win32_fspathncmp()') now read from\n'repo_ignore_case(the_repository)'. While this introduces\ndependency on 'repository.h' into the 'compat/', it avoids massive\nrefactoring of the signatures across the codebase.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>\nMentored-by: Olamide Caleb Bello <belkid98@gmail.com>\nSigned-off-by: Tian Yuchen <cat@malon.dev>\n---\n apply.c                             |  2 +-\n builtin/fetch.c                     |  2 +-\n builtin/mv.c                        |  2 +-\n compat/win32/path-utils.c           |  3 ++-\n dir.c                               | 18 +++++++++---------\n environment.c                       |  3 +--\n environment.h                       |  1 -\n fsmonitor.c                         |  2 +-\n name-hash.c                         |  6 +++---\n read-cache.c                        |  6 +++---\n refs/files-backend.c                |  4 ++--\n submodule.c                         |  2 +-\n t/helper/test-lazy-init-name-hash.c |  2 +-\n unpack-trees.c                      |  2 +-\n 14 files changed, 27 insertions(+), 28 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 249248d4f2..620c88d2a0 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -4008,7 +4008,7 @@ static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *na\n \t\t\tstruct cache_entry *ce;\n \n \t\t\tce = index_file_exists(state->repo->index, name->buf,\n-\t\t\t\t\t       name->len, ignore_case);\n+\t\t\t\t\t       name->len, repo_ignore_case(the_repository));\n \t\t\tif (ce && S_ISLNK(ce->ce_mode))\n \t\t\t\treturn 1;\n \t\t} else {\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex e4e8a72ed9..073e716bc4 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1819,7 +1819,7 @@ static void ref_transaction_rejection_handler(const char *refname,\n {\n \tstruct ref_rejection_data *data = cb_data;\n \n-\tif (err == REF_TRANSACTION_ERROR_CASE_CONFLICT && ignore_case &&\n+\tif (err == REF_TRANSACTION_ERROR_CASE_CONFLICT && repo_ignore_case(the_repository) &&\n \t    !data->case_sensitive_msg_shown) {\n \t\terror(_(\"You're on a case-insensitive filesystem, and the remote you are\\n\"\n \t\t\t\"trying to fetch from has references that only differ in casing. It\\n\"\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex 948b330639..d60582262c 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -419,7 +419,7 @@ int cmd_mv(int argc,\n \t\t\tgoto act_on_entry;\n \t\t}\n \t\tif (lstat(dst, &st) == 0 &&\n-\t\t    (!ignore_case || strcasecmp(src, dst))) {\n+\t\t    (!repo_ignore_case(the_repository) || strcasecmp(src, dst))) {\n \t\t\tbad = _(\"destination exists\");\n \t\t\tif (force) {\n \t\t\t\t/*\ndiff --git a/compat/win32/path-utils.c b/compat/win32/path-utils.c\nindex 966ef779b9..f779f367cf 100644\n--- a/compat/win32/path-utils.c\n+++ b/compat/win32/path-utils.c\n@@ -2,6 +2,7 @@\n \n #include \"../../git-compat-util.h\"\n #include \"../../environment.h\"\n+#include \"../../repository.h\"\n \n int win32_has_dos_drive_prefix(const char *path)\n {\n@@ -75,7 +76,7 @@ int win32_fspathncmp(const char *a, const char *b, size_t count)\n \t\t} else if (is_dir_sep(*b))\n \t\t\treturn +1;\n \n-\t\tdiff = ignore_case ?\n+\t\tdiff = repo_ignore_case(the_repository) ?\n \t\t\t(unsigned char)tolower(*a) - (int)(unsigned char)tolower(*b) :\n \t\t\t(unsigned char)*a - (int)(unsigned char)*b;\n \t\tif (diff)\ndiff --git a/dir.c b/dir.c\nindex 33c81c256e..540dd372c1 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -126,7 +126,7 @@ int count_slashes(const char *s)\n \n int git_fspathcmp(const char *a, const char *b)\n {\n-\treturn ignore_case ? strcasecmp(a, b) : strcmp(a, b);\n+\treturn repo_ignore_case(the_repository) ? strcasecmp(a, b) : strcmp(a, b);\n }\n \n int fspatheq(const char *a, const char *b)\n@@ -136,7 +136,7 @@ int fspatheq(const char *a, const char *b)\n \n int git_fspathncmp(const char *a, const char *b, size_t count)\n {\n-\treturn ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);\n+\treturn repo_ignore_case(the_repository) ? strncasecmp(a, b, count) : strncmp(a, b, count);\n }\n \n int paths_collide(const char *a, const char *b)\n@@ -153,7 +153,7 @@ int paths_collide(const char *a, const char *b)\n \n unsigned int fspathhash(const char *str)\n {\n-\treturn ignore_case ? strihash(str) : strhash(str);\n+\treturn repo_ignore_case(the_repository) ? strihash(str) : strhash(str);\n }\n \n int git_fnmatch(const struct pathspec_item *item,\n@@ -202,7 +202,7 @@ static int fnmatch_icase_mem(const char *pattern, int patternlen,\n \t\tuse_str = str_buf.buf;\n \t}\n \n-\tif (ignore_case)\n+\tif (repo_ignore_case(the_repository))\n \t\tflags |= WM_CASEFOLD;\n \tmatch_status = wildmatch(use_pat, use_str, flags);\n \n@@ -1851,7 +1851,7 @@ static struct dir_entry *dir_add_name(struct dir_struct *dir,\n \t\t\t\t      struct index_state *istate,\n \t\t\t\t      const char *pathname, int len)\n {\n-\tif (index_file_exists(istate, pathname, len, ignore_case))\n+\tif (index_file_exists(istate, pathname, len, repo_ignore_case(the_repository)))\n \t\treturn NULL;\n \n \tALLOC_GROW(dir->entries, dir->nr+1, dir->internal.alloc);\n@@ -1888,7 +1888,7 @@ static enum exist_status directory_exists_in_index_icase(struct index_state *ist\n \tif (index_dir_exists(istate, dirname, len))\n \t\treturn index_directory;\n \n-\tce = index_file_exists(istate, dirname, len, ignore_case);\n+\tce = index_file_exists(istate, dirname, len, repo_ignore_case(the_repository));\n \tif (ce && S_ISGITLINK(ce->ce_mode))\n \t\treturn index_gitdir;\n \n@@ -1907,7 +1907,7 @@ static enum exist_status directory_exists_in_index(struct index_state *istate,\n {\n \tint pos;\n \n-\tif (ignore_case)\n+\tif (repo_ignore_case(the_repository))\n \t\treturn directory_exists_in_index_icase(istate, dirname, len);\n \n \tpos = index_name_pos(istate, dirname, len);\n@@ -2447,7 +2447,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,\n \n \t/* Always exclude indexed files */\n \thas_path_in_index = !!index_file_exists(istate, path->buf, path->len,\n-\t\t\t\t\t\tignore_case);\n+\t\t\t\t\t\trepo_ignore_case(the_repository));\n \tif (dtype != DT_DIR && has_path_in_index)\n \t\treturn path_none;\n \n@@ -3201,7 +3201,7 @@ static int cmp_icase(char a, char b)\n {\n \tif (a == b)\n \t\treturn 0;\n-\tif (ignore_case)\n+\tif (repo_ignore_case(the_repository))\n \t\treturn toupper(a) - toupper(b);\n \treturn a - b;\n }\ndiff --git a/environment.c b/environment.c\nindex bfa3cb3045..c288c3613d 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -46,7 +46,6 @@ int trust_ctime = 1;\n int check_stat = 1;\n int has_symlinks = 1;\n int minimum_abbrev = 4, default_abbrev = -1;\n-int ignore_case;\n int assume_unchanged;\n int is_bare_repository_cfg = -1; /* unspecified */\n int warn_on_object_refname_ambiguity = 1;\n@@ -342,7 +341,7 @@ int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.ignorecase\")) {\n-\t\tignore_case = git_config_bool(var, value);\n+\t\tcfg->ignore_case = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \ndiff --git a/environment.h b/environment.h\nindex 39a8bf0b49..c15121db65 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -171,7 +171,6 @@ extern int trust_ctime;\n extern int check_stat;\n extern int has_symlinks;\n extern int minimum_abbrev, default_abbrev;\n-extern int ignore_case;\n extern int assume_unchanged;\n extern int warn_on_object_refname_ambiguity;\n extern char *apply_default_whitespace;\ndiff --git a/fsmonitor.c b/fsmonitor.c\nindex d07dc18967..107767527e 100644\n--- a/fsmonitor.c\n+++ b/fsmonitor.c\n@@ -453,7 +453,7 @@ static void fsmonitor_refresh_callback(struct index_state *istate, char *name)\n \t * case-insensitive file system, try again using the name-hash\n \t * and dir-name-hash.\n \t */\n-\tif (!nr_in_cone && ignore_case) {\n+\tif (!nr_in_cone && repo_ignore_case(the_repository)) {\n \t\tnr_in_cone = handle_using_name_hash_icase(istate, name);\n \t\tif (!nr_in_cone)\n \t\t\tnr_in_cone = handle_using_dir_name_hash_icase(\ndiff --git a/name-hash.c b/name-hash.c\nindex b91e276267..83757db874 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -126,7 +126,7 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)\n \t\thashmap_add(&istate->name_hash, &ce->ent);\n \t}\n \n-\tif (ignore_case)\n+\tif (repo_ignore_case(the_repository))\n \t\tadd_dir_entry(istate, ce);\n }\n \n@@ -207,7 +207,7 @@ static int lookup_lazy_params(struct index_state *istate)\n \t * code to build the \"istate->name_hash\".  We don't\n \t * need the complexity here.\n \t */\n-\tif (!ignore_case)\n+\tif (!repo_ignore_case(the_repository))\n \t\treturn 0;\n \n \tnr_cpus = online_cpus();\n@@ -651,7 +651,7 @@ void remove_name_hash(struct index_state *istate, struct cache_entry *ce)\n \tce->ce_flags &= ~CE_HASHED;\n \thashmap_remove(&istate->name_hash, &ce->ent, ce);\n \n-\tif (ignore_case)\n+\tif (repo_ignore_case(the_repository))\n \t\tremove_dir_entry(istate, ce);\n }\n \ndiff --git a/read-cache.c b/read-cache.c\nindex 21829102ae..fcdf0e5ef1 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -760,12 +760,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \t * case of the file being added to the repository matches (is folded into) the existing\n \t * entry's directory case.\n \t */\n-\tif (ignore_case) {\n+\tif (repo_ignore_case(the_repository)) {\n \t\tadjust_dirname_case(istate, ce->name);\n \t}\n \tif (!(flags & ADD_CACHE_RENORMALIZE)) {\n \t\talias = index_file_exists(istate, ce->name,\n-\t\t\t\t\t  ce_namelen(ce), ignore_case);\n+\t\t\t\t\t  ce_namelen(ce), repo_ignore_case(the_repository));\n \t\tif (alias &&\n \t\t    !ce_stage(alias) &&\n \t\t    !ie_match_stat(istate, alias, st, ce_option)) {\n@@ -786,7 +786,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \t} else\n \t\tset_object_name_for_intent_to_add_entry(ce);\n \n-\tif (ignore_case && alias && different_name(ce, alias))\n+\tif (repo_ignore_case(the_repository) && alias && different_name(ce, alias))\n \t\tce = create_alias_ce(istate, ce, alias);\n \tce->ce_flags |= CE_ADDED;\n \ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex a4c7858787..c1da06b1d5 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -806,7 +806,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,\n \t\t} else {\n \t\t\tunable_to_lock_message(ref_file.buf, myerr, err);\n \t\t\tif (myerr == EEXIST) {\n-\t\t\t\tif (ignore_case &&\n+\t\t\t\tif (repo_ignore_case(the_repository) &&\n \t\t\t\t    transaction_has_case_conflicting_update(transaction, update)) {\n \t\t\t\t\t/*\n \t\t\t\t\t * In case-insensitive filesystems, ensure that conflicts within a\n@@ -920,7 +920,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,\n \t\t * conflicts between 'foo' and 'Foo/bar'. So let's lowercase\n \t\t * the refname.\n \t\t */\n-\t\tif (ignore_case) {\n+\t\tif (repo_ignore_case(the_repository)) {\n \t\t\tstruct strbuf lower = STRBUF_INIT;\n \n \t\t\tstrbuf_addstr(&lower, refname);\ndiff --git a/submodule.c b/submodule.c\nindex a939ff5072..6e7f8b9f7c 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -2389,7 +2389,7 @@ static int validate_submodule_encoded_git_dir(char *git_dir, const char *submodu\n \n \t/* Prevent conflicts on case-folding filesystems */\n \trepo_config_get_bool(the_repository, \"core.ignorecase\", &config_ignorecase);\n-\tif (ignore_case || config_ignorecase) {\n+\tif (repo_ignore_case(the_repository) || config_ignorecase) {\n \t\tbool suffixes_match = !strcmp(last_submodule_name, submodule_name);\n \t\treturn check_casefolding_conflict(git_dir, submodule_name,\n \t\t\t\t\t\t  suffixes_match);\ndiff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c\nindex e542985c94..43cead6d7d 100644\n--- a/t/helper/test-lazy-init-name-hash.c\n+++ b/t/helper/test-lazy-init-name-hash.c\n@@ -218,7 +218,7 @@ int cmd__lazy_init_name_hash(int argc, const char **argv)\n \t/*\n \t * istate->dir_hash is only created when ignore_case is set.\n \t */\n-\tignore_case = 1;\n+\trepo_config_values(the_repository)->ignore_case = 1;\n \n \tif (dump) {\n \t\tif (perf || analyze > 0)\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 998a1e6dc7..d13b004f71 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -2428,7 +2428,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \t *\n \t * Ignore that lstat() if it matches.\n \t */\n-\tif (ignore_case && icase_exists(o, name, len, st))\n+\tif (repo_ignore_case(the_repository) && icase_exists(o, name, len, st))\n \t\treturn 0;\n \n \tif (o->internal.dir &&\n-- \n2.43.0\n\n"},{"id":"545844","messageId":"xmqqldcct2hx.fsf@gitster.g","threadId":"65824","inReplyTo":"20260618114207.605211-1-cat@malon.dev","subject":"Re: [PATCH v2 0/2] environment: move ignore_case into repo_config_values","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-06-18T13:14:02Z","receivedAt":"2026-06-18T13:14:05Z","isPatch":true,"body":"Tian Yuchen <cat@malon.dev> writes:\n\n> Related materials:\n>\n>  [1] In this patch to migrate protect_hfs and protect_ntfs, the approach\n> of introducing getters has been endorsed.\n>\n>  [2] Derrick Stolee's previous attempt. The reasons for the failure are\n> also mentioned in [1].\n\n[1] here refers to the starting message of the whole hfs/ntfs thing.\nDo you mean that people must read the entire thread to find out what\nthe reasons for the failure was?  For that matter, it is not clear,\nunless readers read the whole thread, where the approach of using\ngetters was \"endorsed\", either.\n\n> [1] https://lore.kernel.org/git/20260606143412.15443-1-cat@malon.dev/\n> [2] https://lore.kernel.org/git/2b4198c09cb6c04c60608d19072d419503dfe5df.1685716421.git.gitgitgadget@gmail.com/\n\n> Changes since V1:\n>\n>  - s/repo_get_ignore_case()/repo_ignore_case()\n>\n>  - Use repo->initialized instead of repo->gitdir\n\nI do not think I have any objections to these changes from the\nprevious iteration.  There may be some other things in the new\niteration but I'll have to go in and read the patches to find them\nout (if they exist).\n\nThanks.\n"},{"id":"545982","messageId":"20260619155152.642760-1-cat@malon.dev","threadId":"65824","inReplyTo":"20260618114207.605211-1-cat@malon.dev","subject":"[PATCH v3 0/2] environment: move ignore_case into repo_config_values","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-19T15:51:50Z","receivedAt":"2026-06-19T15:52:02Z","isPatch":true,"body":"This series continues the ongoing libification effort by moving\nthis global variable into 'struct repo_config_values', tying it\nto the specific repository instance it was read from. This allows\nus to encapsulate the configuration without altering its\neager-parsing behavior.\n\nThe getter function 'repo_ignore_case()' is introduced so\nthat we can safely retrieve the configuration value whilst\nmaintaining the correct fallback logic.\n\nRFC Questions:\n\ndir.c --- Performance overhead?\n\ncompat/win32/path-utils.c --- Is it appropriate to include the\nrepository.h header file?\n\nRelated materials:\n\n [1] The practice of introducing getters for filesystem flags\n to ensure safe access was previously introduced in this patch\n to migrate 'protect_hfs' and 'protect_ntfs'.\n\n When migrating 'ignore_case', the same approach is strictly followed.\n\n [2] Derrick Stolee's previous attempt. This patch series attempted\n to wrap this kind of filesystem-level variable using a lazy-loaded\n global accessor get_int_config_global().\n\n However, as Glen Choo pointed out in his review of that\n series, it is strongly preferred to use plain fields in a\n repository-scoped struct over global lazy-loaders, provided\n those fields are properly initialized during the setup process.\n\nChanges since V2:\n\n - Revise the cover letter to clarify what the links lead to.\n\nThanks!\n\nMentored-by: Christian Couder christian.couder@gmail.com\nMentored-by: Ayush Chandekar ayu.chandekar@gmail.com\nMentored-by: Olamide Caleb Bello belkid98@gmail.com\nSigned-off-by: Tian Yuchen cat@malon.dev\n\n[1] https://lore.kernel.org/git/20260606143412.15443-1-cat@malon.dev/\n[2] https://lore.kernel.org/git/2b4198c09cb6c04c60608d19072d419503dfe5df.1685716421.git.gitgitgadget@gmail.com/\n\nTian Yuchen (2):\n  environment: move ignore_case into repo_config_values\n  config: use repo_ignore_case() to access core.ignorecase\n\n apply.c                             |  2 +-\n builtin/fetch.c                     |  2 +-\n builtin/mv.c                        |  2 +-\n compat/win32/path-utils.c           |  3 ++-\n dir.c                               | 18 +++++++++---------\n environment.c                       | 11 +++++++++--\n environment.h                       |  9 ++++++++-\n fsmonitor.c                         |  2 +-\n name-hash.c                         |  6 +++---\n read-cache.c                        |  6 +++---\n refs/files-backend.c                |  4 ++--\n submodule.c                         |  2 +-\n t/helper/test-lazy-init-name-hash.c |  2 +-\n unpack-trees.c                      |  2 +-\n 14 files changed, 43 insertions(+), 28 deletions(-)\n\n-- \n2.43.0\n\n"},{"id":"545983","messageId":"20260619155152.642760-2-cat@malon.dev","threadId":"65824","inReplyTo":"20260619155152.642760-1-cat@malon.dev","subject":"[PATCH v3 1/2] environment: move ignore_case into repo_config_values","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-19T15:51:51Z","receivedAt":"2026-06-19T15:52:06Z","isPatch":true,"body":"The 'core.ignorecase' configuration which is stored as the\nglobal variable 'ignore_case' acts as a core filesystem\ncapability flag.\n\nMove this global variable into 'struct repo_config_values' to tie it\nto the specific repository instance it was read from. This reduces\nglobal state and aligns with the ongoing libification effort.\n\nTo ensure code readability, the getter function\n'repo_ignore_case()' is introduced.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>\nMentored-by: Olamide Caleb Bello <belkid98@gmail.com>\nSigned-off-by: Tian Yuchen <cat@malon.dev>\n---\n environment.c | 8 ++++++++\n environment.h | 8 ++++++++\n 2 files changed, 16 insertions(+)\n\ndiff --git a/environment.c b/environment.c\nindex fc3ed8bb1c..bfa3cb3045 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -142,6 +142,13 @@ int is_bare_repository(void)\n \treturn is_bare_repository_cfg && !repo_get_work_tree(the_repository);\n }\n \n+int repo_ignore_case(struct repository *repo)\n+{\n+\treturn (repo && repo->initialized) ?\n+\t\trepo_config_values(repo)->ignore_case :\n+\t\t0;\n+}\n+\n int have_git_dir(void)\n {\n \treturn startup_info->have_repository\n@@ -720,5 +727,6 @@ void repo_config_values_init(struct repo_config_values *cfg)\n {\n \tcfg->attributes_file = NULL;\n \tcfg->apply_sparse_checkout = 0;\n+\tcfg->ignore_case = 0;\n \tcfg->branch_track = BRANCH_TRACK_REMOTE;\n }\ndiff --git a/environment.h b/environment.h\nindex 9eb97b3869..39a8bf0b49 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -91,6 +91,7 @@ struct repo_config_values {\n \t/* section \"core\" config values */\n \tchar *attributes_file;\n \tint apply_sparse_checkout;\n+\tint ignore_case;\n \n \t/* section \"branch\" config values */\n \tenum branch_track branch_track;\n@@ -123,6 +124,13 @@ int git_default_config(const char *, 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+/*\n+ * Getter for the `ignore_case` field of `struct repo_config_values`.\n+ * It checks `repo->initialized` to prevent calling repo_config_values()`\n+ * before the repository setup is fully complete or in non-git environments.\n+ */\n+int repo_ignore_case(struct repository *repo);\n+\n void repo_config_values_init(struct repo_config_values *cfg);\n \n /*\n-- \n2.43.0\n\n"},{"id":"545984","messageId":"20260619155152.642760-3-cat@malon.dev","threadId":"65824","inReplyTo":"20260619155152.642760-1-cat@malon.dev","subject":"[PATCH v3 2/2] config: use repo_ignore_case() to access core.ignorecase","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-19T15:51:52Z","receivedAt":"2026-06-19T15:52:11Z","isPatch":true,"body":"Replace the accesses to the global 'ignore_case' variable with\ncalls to 'repo_ignore_case(the_repository)'. This step eliminates\nthe 'ignore_case' global state.\n\nNote on compat/win32/path-utils.c:\nTo eliminate the global state, several helper functions\n(e.g. 'win32_fspathncmp()') now read from\n'repo_ignore_case(the_repository)'. While this introduces\ndependency on 'repository.h' into the 'compat/', it avoids massive\nrefactoring of the signatures across the codebase.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>\nMentored-by: Olamide Caleb Bello <belkid98@gmail.com>\nSigned-off-by: Tian Yuchen <cat@malon.dev>\n---\n apply.c                             |  2 +-\n builtin/fetch.c                     |  2 +-\n builtin/mv.c                        |  2 +-\n compat/win32/path-utils.c           |  3 ++-\n dir.c                               | 18 +++++++++---------\n environment.c                       |  3 +--\n environment.h                       |  1 -\n fsmonitor.c                         |  2 +-\n name-hash.c                         |  6 +++---\n read-cache.c                        |  6 +++---\n refs/files-backend.c                |  4 ++--\n submodule.c                         |  2 +-\n t/helper/test-lazy-init-name-hash.c |  2 +-\n unpack-trees.c                      |  2 +-\n 14 files changed, 27 insertions(+), 28 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 249248d4f2..620c88d2a0 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -4008,7 +4008,7 @@ static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *na\n \t\t\tstruct cache_entry *ce;\n \n \t\t\tce = index_file_exists(state->repo->index, name->buf,\n-\t\t\t\t\t       name->len, ignore_case);\n+\t\t\t\t\t       name->len, repo_ignore_case(the_repository));\n \t\t\tif (ce && S_ISLNK(ce->ce_mode))\n \t\t\t\treturn 1;\n \t\t} else {\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex e4e8a72ed9..073e716bc4 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1819,7 +1819,7 @@ static void ref_transaction_rejection_handler(const char *refname,\n {\n \tstruct ref_rejection_data *data = cb_data;\n \n-\tif (err == REF_TRANSACTION_ERROR_CASE_CONFLICT && ignore_case &&\n+\tif (err == REF_TRANSACTION_ERROR_CASE_CONFLICT && repo_ignore_case(the_repository) &&\n \t    !data->case_sensitive_msg_shown) {\n \t\terror(_(\"You're on a case-insensitive filesystem, and the remote you are\\n\"\n \t\t\t\"trying to fetch from has references that only differ in casing. It\\n\"\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex 948b330639..d60582262c 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -419,7 +419,7 @@ int cmd_mv(int argc,\n \t\t\tgoto act_on_entry;\n \t\t}\n \t\tif (lstat(dst, &st) == 0 &&\n-\t\t    (!ignore_case || strcasecmp(src, dst))) {\n+\t\t    (!repo_ignore_case(the_repository) || strcasecmp(src, dst))) {\n \t\t\tbad = _(\"destination exists\");\n \t\t\tif (force) {\n \t\t\t\t/*\ndiff --git a/compat/win32/path-utils.c b/compat/win32/path-utils.c\nindex 966ef779b9..f779f367cf 100644\n--- a/compat/win32/path-utils.c\n+++ b/compat/win32/path-utils.c\n@@ -2,6 +2,7 @@\n \n #include \"../../git-compat-util.h\"\n #include \"../../environment.h\"\n+#include \"../../repository.h\"\n \n int win32_has_dos_drive_prefix(const char *path)\n {\n@@ -75,7 +76,7 @@ int win32_fspathncmp(const char *a, const char *b, size_t count)\n \t\t} else if (is_dir_sep(*b))\n \t\t\treturn +1;\n \n-\t\tdiff = ignore_case ?\n+\t\tdiff = repo_ignore_case(the_repository) ?\n \t\t\t(unsigned char)tolower(*a) - (int)(unsigned char)tolower(*b) :\n \t\t\t(unsigned char)*a - (int)(unsigned char)*b;\n \t\tif (diff)\ndiff --git a/dir.c b/dir.c\nindex 33c81c256e..540dd372c1 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -126,7 +126,7 @@ int count_slashes(const char *s)\n \n int git_fspathcmp(const char *a, const char *b)\n {\n-\treturn ignore_case ? strcasecmp(a, b) : strcmp(a, b);\n+\treturn repo_ignore_case(the_repository) ? strcasecmp(a, b) : strcmp(a, b);\n }\n \n int fspatheq(const char *a, const char *b)\n@@ -136,7 +136,7 @@ int fspatheq(const char *a, const char *b)\n \n int git_fspathncmp(const char *a, const char *b, size_t count)\n {\n-\treturn ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);\n+\treturn repo_ignore_case(the_repository) ? strncasecmp(a, b, count) : strncmp(a, b, count);\n }\n \n int paths_collide(const char *a, const char *b)\n@@ -153,7 +153,7 @@ int paths_collide(const char *a, const char *b)\n \n unsigned int fspathhash(const char *str)\n {\n-\treturn ignore_case ? strihash(str) : strhash(str);\n+\treturn repo_ignore_case(the_repository) ? strihash(str) : strhash(str);\n }\n \n int git_fnmatch(const struct pathspec_item *item,\n@@ -202,7 +202,7 @@ static int fnmatch_icase_mem(const char *pattern, int patternlen,\n \t\tuse_str = str_buf.buf;\n \t}\n \n-\tif (ignore_case)\n+\tif (repo_ignore_case(the_repository))\n \t\tflags |= WM_CASEFOLD;\n \tmatch_status = wildmatch(use_pat, use_str, flags);\n \n@@ -1851,7 +1851,7 @@ static struct dir_entry *dir_add_name(struct dir_struct *dir,\n \t\t\t\t      struct index_state *istate,\n \t\t\t\t      const char *pathname, int len)\n {\n-\tif (index_file_exists(istate, pathname, len, ignore_case))\n+\tif (index_file_exists(istate, pathname, len, repo_ignore_case(the_repository)))\n \t\treturn NULL;\n \n \tALLOC_GROW(dir->entries, dir->nr+1, dir->internal.alloc);\n@@ -1888,7 +1888,7 @@ static enum exist_status directory_exists_in_index_icase(struct index_state *ist\n \tif (index_dir_exists(istate, dirname, len))\n \t\treturn index_directory;\n \n-\tce = index_file_exists(istate, dirname, len, ignore_case);\n+\tce = index_file_exists(istate, dirname, len, repo_ignore_case(the_repository));\n \tif (ce && S_ISGITLINK(ce->ce_mode))\n \t\treturn index_gitdir;\n \n@@ -1907,7 +1907,7 @@ static enum exist_status directory_exists_in_index(struct index_state *istate,\n {\n \tint pos;\n \n-\tif (ignore_case)\n+\tif (repo_ignore_case(the_repository))\n \t\treturn directory_exists_in_index_icase(istate, dirname, len);\n \n \tpos = index_name_pos(istate, dirname, len);\n@@ -2447,7 +2447,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,\n \n \t/* Always exclude indexed files */\n \thas_path_in_index = !!index_file_exists(istate, path->buf, path->len,\n-\t\t\t\t\t\tignore_case);\n+\t\t\t\t\t\trepo_ignore_case(the_repository));\n \tif (dtype != DT_DIR && has_path_in_index)\n \t\treturn path_none;\n \n@@ -3201,7 +3201,7 @@ static int cmp_icase(char a, char b)\n {\n \tif (a == b)\n \t\treturn 0;\n-\tif (ignore_case)\n+\tif (repo_ignore_case(the_repository))\n \t\treturn toupper(a) - toupper(b);\n \treturn a - b;\n }\ndiff --git a/environment.c b/environment.c\nindex bfa3cb3045..c288c3613d 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -46,7 +46,6 @@ int trust_ctime = 1;\n int check_stat = 1;\n int has_symlinks = 1;\n int minimum_abbrev = 4, default_abbrev = -1;\n-int ignore_case;\n int assume_unchanged;\n int is_bare_repository_cfg = -1; /* unspecified */\n int warn_on_object_refname_ambiguity = 1;\n@@ -342,7 +341,7 @@ int git_default_core_config(const char *var, const char *value,\n \t}\n \n \tif (!strcmp(var, \"core.ignorecase\")) {\n-\t\tignore_case = git_config_bool(var, value);\n+\t\tcfg->ignore_case = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \ndiff --git a/environment.h b/environment.h\nindex 39a8bf0b49..c15121db65 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -171,7 +171,6 @@ extern int trust_ctime;\n extern int check_stat;\n extern int has_symlinks;\n extern int minimum_abbrev, default_abbrev;\n-extern int ignore_case;\n extern int assume_unchanged;\n extern int warn_on_object_refname_ambiguity;\n extern char *apply_default_whitespace;\ndiff --git a/fsmonitor.c b/fsmonitor.c\nindex d07dc18967..107767527e 100644\n--- a/fsmonitor.c\n+++ b/fsmonitor.c\n@@ -453,7 +453,7 @@ static void fsmonitor_refresh_callback(struct index_state *istate, char *name)\n \t * case-insensitive file system, try again using the name-hash\n \t * and dir-name-hash.\n \t */\n-\tif (!nr_in_cone && ignore_case) {\n+\tif (!nr_in_cone && repo_ignore_case(the_repository)) {\n \t\tnr_in_cone = handle_using_name_hash_icase(istate, name);\n \t\tif (!nr_in_cone)\n \t\t\tnr_in_cone = handle_using_dir_name_hash_icase(\ndiff --git a/name-hash.c b/name-hash.c\nindex b91e276267..83757db874 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -126,7 +126,7 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)\n \t\thashmap_add(&istate->name_hash, &ce->ent);\n \t}\n \n-\tif (ignore_case)\n+\tif (repo_ignore_case(the_repository))\n \t\tadd_dir_entry(istate, ce);\n }\n \n@@ -207,7 +207,7 @@ static int lookup_lazy_params(struct index_state *istate)\n \t * code to build the \"istate->name_hash\".  We don't\n \t * need the complexity here.\n \t */\n-\tif (!ignore_case)\n+\tif (!repo_ignore_case(the_repository))\n \t\treturn 0;\n \n \tnr_cpus = online_cpus();\n@@ -651,7 +651,7 @@ void remove_name_hash(struct index_state *istate, struct cache_entry *ce)\n \tce->ce_flags &= ~CE_HASHED;\n \thashmap_remove(&istate->name_hash, &ce->ent, ce);\n \n-\tif (ignore_case)\n+\tif (repo_ignore_case(the_repository))\n \t\tremove_dir_entry(istate, ce);\n }\n \ndiff --git a/read-cache.c b/read-cache.c\nindex 21829102ae..fcdf0e5ef1 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -760,12 +760,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \t * case of the file being added to the repository matches (is folded into) the existing\n \t * entry's directory case.\n \t */\n-\tif (ignore_case) {\n+\tif (repo_ignore_case(the_repository)) {\n \t\tadjust_dirname_case(istate, ce->name);\n \t}\n \tif (!(flags & ADD_CACHE_RENORMALIZE)) {\n \t\talias = index_file_exists(istate, ce->name,\n-\t\t\t\t\t  ce_namelen(ce), ignore_case);\n+\t\t\t\t\t  ce_namelen(ce), repo_ignore_case(the_repository));\n \t\tif (alias &&\n \t\t    !ce_stage(alias) &&\n \t\t    !ie_match_stat(istate, alias, st, ce_option)) {\n@@ -786,7 +786,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \t} else\n \t\tset_object_name_for_intent_to_add_entry(ce);\n \n-\tif (ignore_case && alias && different_name(ce, alias))\n+\tif (repo_ignore_case(the_repository) && alias && different_name(ce, alias))\n \t\tce = create_alias_ce(istate, ce, alias);\n \tce->ce_flags |= CE_ADDED;\n \ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex a4c7858787..c1da06b1d5 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -806,7 +806,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,\n \t\t} else {\n \t\t\tunable_to_lock_message(ref_file.buf, myerr, err);\n \t\t\tif (myerr == EEXIST) {\n-\t\t\t\tif (ignore_case &&\n+\t\t\t\tif (repo_ignore_case(the_repository) &&\n \t\t\t\t    transaction_has_case_conflicting_update(transaction, update)) {\n \t\t\t\t\t/*\n \t\t\t\t\t * In case-insensitive filesystems, ensure that conflicts within a\n@@ -920,7 +920,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,\n \t\t * conflicts between 'foo' and 'Foo/bar'. So let's lowercase\n \t\t * the refname.\n \t\t */\n-\t\tif (ignore_case) {\n+\t\tif (repo_ignore_case(the_repository)) {\n \t\t\tstruct strbuf lower = STRBUF_INIT;\n \n \t\t\tstrbuf_addstr(&lower, refname);\ndiff --git a/submodule.c b/submodule.c\nindex a939ff5072..6e7f8b9f7c 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -2389,7 +2389,7 @@ static int validate_submodule_encoded_git_dir(char *git_dir, const char *submodu\n \n \t/* Prevent conflicts on case-folding filesystems */\n \trepo_config_get_bool(the_repository, \"core.ignorecase\", &config_ignorecase);\n-\tif (ignore_case || config_ignorecase) {\n+\tif (repo_ignore_case(the_repository) || config_ignorecase) {\n \t\tbool suffixes_match = !strcmp(last_submodule_name, submodule_name);\n \t\treturn check_casefolding_conflict(git_dir, submodule_name,\n \t\t\t\t\t\t  suffixes_match);\ndiff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c\nindex e542985c94..43cead6d7d 100644\n--- a/t/helper/test-lazy-init-name-hash.c\n+++ b/t/helper/test-lazy-init-name-hash.c\n@@ -218,7 +218,7 @@ int cmd__lazy_init_name_hash(int argc, const char **argv)\n \t/*\n \t * istate->dir_hash is only created when ignore_case is set.\n \t */\n-\tignore_case = 1;\n+\trepo_config_values(the_repository)->ignore_case = 1;\n \n \tif (dump) {\n \t\tif (perf || analyze > 0)\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 998a1e6dc7..d13b004f71 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -2428,7 +2428,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \t *\n \t * Ignore that lstat() if it matches.\n \t */\n-\tif (ignore_case && icase_exists(o, name, len, st))\n+\tif (repo_ignore_case(the_repository) && icase_exists(o, name, len, st))\n \t\treturn 0;\n \n \tif (o->internal.dir &&\n-- \n2.43.0\n\n"},{"id":"545986","messageId":"b0ad1eba-a4e2-418b-816f-5861b029cf23@malon.dev","threadId":"65824","inReplyTo":"xmqqldcct2hx.fsf@gitster.g","subject":"Re: [PATCH v2 0/2] environment: move ignore_case into repo_config_values","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-19T16:01:57Z","receivedAt":"2026-06-19T16:02:05Z","isPatch":true,"body":"On 6/18/26 21:14, Junio C Hamano wrote:\n> Tian Yuchen <cat@malon.dev> writes:\n> \n>> Related materials:\n>>\n>>   [1] In this patch to migrate protect_hfs and protect_ntfs, the approach\n>> of introducing getters has been endorsed.\n>>\n>>   [2] Derrick Stolee's previous attempt. The reasons for the failure are\n>> also mentioned in [1].\n> \n> [1] here refers to the starting message of the whole hfs/ntfs thing.\n> Do you mean that people must read the entire thread to find out what\n> the reasons for the failure was?  For that matter, it is not clear,\n> unless readers read the whole thread, where the approach of using\n> getters was \"endorsed\", either.\n>\n\nWhat I meant to say was that the reasons were already mentioned in the \n_cover-letter_ for the patch which the link refers to. It’s certainly my \nfault for not expressing myself clearly. Nevertheless, I’ve briefly \nexplained the content of the link directly in the cover-letter for V3.\n\n>> [1] https://lore.kernel.org/git/20260606143412.15443-1-cat@malon.dev/\n>> [2] https://lore.kernel.org/git/2b4198c09cb6c04c60608d19072d419503dfe5df.1685716421.git.gitgitgadget@gmail.com/\n> \n>> Changes since V1:\n>>\n>>   - s/repo_get_ignore_case()/repo_ignore_case()\n>>\n>>   - Use repo->initialized instead of repo->gitdir\n> \n> I do not think I have any objections to these changes from the\n> previous iteration.  There may be some other things in the new\n> iteration but I'll have to go in and read the patches to find them\n> out (if they exist).\n> \n> Thanks.\n\nAdditionally, I’ll change 'repo->gitdir' to using 'repo->initialized' \nfor the previous three bits as well.\n\nRegards, yuchen\n"},{"id":"546086","messageId":"xmqqjyrr7ipf.fsf@gitster.g","threadId":"65824","inReplyTo":"20260619155152.642760-1-cat@malon.dev","subject":"Re: [PATCH v3 0/2] environment: move ignore_case into repo_config_values","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-06-21T20:16:12Z","receivedAt":"2026-06-21T20:16:15Z","isPatch":true,"body":"Tian Yuchen <cat@malon.dev> writes:\n\n> This series continues the ongoing libification effort by moving\n> this global variable into 'struct repo_config_values', tying it\n> to the specific repository instance it was read from. This allows\n> us to encapsulate the configuration without altering its\n> eager-parsing behavior.\n\nLooks good.\n\n> compat/win32/path-utils.c --- Is it appropriate to include the\n> repository.h header file?\n\nAs the compat/ layer is not meant as a general purpose POSIX\nemulation wrapper that is generally reusable to projects other than\nus, if we have a knob settable by end users to affect behaviours of\nlower layer in compat/, it is natural to make repo-settings\navailable to them.\n\nWhat is the perceived problem you have in mind, and what are your\nproposed alternatives?\n"},{"id":"546192","messageId":"b5a9115a-c909-405f-b150-f956d866b1eb@malon.dev","threadId":"65824","inReplyTo":"xmqqjyrr7ipf.fsf@gitster.g","subject":"Re: [PATCH v3 0/2] environment: move ignore_case into repo_config_values","fromName":"Tian Yuchen","fromEmail":"cat@malon.dev","sentAt":"2026-06-22T16:45:46Z","receivedAt":"2026-06-22T16:46:06Z","isPatch":true,"body":"On 6/22/26 04:16, Junio C Hamano wrote:\n> As the compat/ layer is not meant as a general purpose POSIX\n> emulation wrapper that is generally reusable to projects other than\n> us, if we have a knob settable by end users to affect behaviours of\n> lower layer in compat/, it is natural to make repo-settings\n> available to them.\n\nI see.\n\n> What is the perceived problem you have in mind, and what are your\n> proposed alternatives?\n\nActually, my reason for showing this question wasn’t because I thought \nthere were any architectural problem, but because I felt that for a file \nin compat/win32, which is more on the _downstream_ side (is that \ncorrect?), we need to exercise extra caution and confirm with its \nmaintainer whether the changes are appropriate. That’s why I CC'd \nJohannes Schindelin on this.\n\nWas that the right thing to do?\n\nRegards, yuchen\n"},{"id":"546199","messageId":"xmqqzf0mzc7j.fsf@gitster.g","threadId":"65824","inReplyTo":"b5a9115a-c909-405f-b150-f956d866b1eb@malon.dev","subject":"Re: [PATCH v3 0/2] environment: move ignore_case into repo_config_values","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-06-22T18:01:20Z","receivedAt":"2026-06-22T18:01:24Z","isPatch":true,"body":"Tian Yuchen <cat@malon.dev> writes:\n\n> On 6/22/26 04:16, Junio C Hamano wrote:\n>> As the compat/ layer is not meant as a general purpose POSIX\n>> emulation wrapper that is generally reusable to projects other than\n>> us, if we have a knob settable by end users to affect behaviours of\n>> lower layer in compat/, it is natural to make repo-settings\n>> available to them.\n>\n> I see.\n>\n>> What is the perceived problem you have in mind, and what are your\n>> proposed alternatives?\n>\n> Actually, my reason for showing this question wasn’t because I thought \n> there were any architectural problem, but because I felt that for a file \n> in compat/win32, which is more on the _downstream_ side (is that \n> correct?), we need to exercise extra caution and confirm with its \n> maintainer whether the changes are appropriate. That’s why I CC'd \n> Johannes Schindelin on this.\n>\n> Was that the right thing to do?\n\nYup, Dscho is the right person to decide on the design issues on\nWindows build.\n"},{"id":"547708","messageId":"9ade3ca2-fdd9-c5da-3d87-a754a0643d6f@gmx.de","threadId":"65824","inReplyTo":"20260619155152.642760-1-cat@malon.dev","subject":"Re: [PATCH v3 0/2] environment: move ignore_case into repo_config_values","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2026-07-10T10:29:47Z","receivedAt":"2026-07-10T10:29:49Z","isPatch":true,"body":"Hi Yuchen,\n\nOn Fri, 10 Jul 2026, Tian Yuchen wrote:\n\n> compat/win32/path-utils.c --- Is it appropriate to include the\n> repository.h header file?\n\nSince path-utils.c implements logic that is repository-dependent (as your\npatch points out), including that header is appropriate.\n\nCiao,\nJohannes\n"},{"id":"547739","messageId":"xmqqechaga7p.fsf@gitster.g","threadId":"65824","inReplyTo":"9ade3ca2-fdd9-c5da-3d87-a754a0643d6f@gmx.de","subject":"Re: [PATCH v3 0/2] environment: move ignore_case into repo_config_values","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-10T15:01:30Z","receivedAt":"2026-07-10T15:01:32Z","isPatch":true,"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi Yuchen,\n>\n> On Fri, 10 Jul 2026, Tian Yuchen wrote:\n>\n>> compat/win32/path-utils.c --- Is it appropriate to include the\n>> repository.h header file?\n>\n> Since path-utils.c implements logic that is repository-dependent (as your\n> patch points out), including that header is appropriate.\n\nThanks, both.  Let's merge the topic down to 'next' then.\n"}]}