From 984f182a52a23cf15ab82e0caf624a80bcf19e41 Mon Sep 17 00:00:00 2001 From: Tian Yuchen Date: Mon, 6 Jul 2026 20:46:07 +0800 Subject: [PATCH 1/9] repository: introduce repo_config_values_clear() As part of the ongoing libification effort, dynamically allocated global configuration variables are being moved into 'struct repo_config_values'. To prevent memory leaks, we need a destructor to free these heap-allocated variables when a repository instance is torn down. Introduce 'repo_config_values_clear()' in environment.c and invoke it from 'repo_clear()' in repository.c. As a starting point, update this new function to handle the cleanup of 'attributes_file'. Note: Submodules are currently not supported by repo_config_values(), which explicitly BUG()s out if 'repo != the_repository'. Since repo_clear() cleans up all repository instances, we must bypass them to prevent crashing. Mentored-by: Christian Couder Mentored-by: Ayush Chandekar Mentored-by: Olamide Caleb Bello Signed-off-by: Tian Yuchen --- environment.c | 19 +++++++++++++++++++ environment.h | 9 +++++++++ repository.c | 1 + 3 files changed, 29 insertions(+) diff --git a/environment.c b/environment.c index ba2c60103ff51c..13677484decf77 100644 --- a/environment.c +++ b/environment.c @@ -726,3 +726,22 @@ void repo_config_values_init(struct repo_config_values *cfg) cfg->sparse_expect_files_outside_of_patterns = 0; cfg->warn_on_object_refname_ambiguity = 1; } + +void repo_config_values_clear(struct repository *repo) +{ + struct repo_config_values *cfg; + + /* + * NEEDSWORK: Submodules are currently not supported by + * repo_config_values(), which explicitly BUG()s out if + * repo != the_repository. Since repo_clear() cleans up all + * repository instances, we must bypass them here to prevent + * crashing. + */ + if (repo != the_repository) + return; + + cfg = repo_config_values(repo); + + FREE_AND_NULL(cfg->attributes_file); +} diff --git a/environment.h b/environment.h index 6f182869558395..c4a6a457043638 100644 --- a/environment.h +++ b/environment.h @@ -135,6 +135,15 @@ int git_default_core_config(const char *var, const char *value, void repo_config_values_init(struct repo_config_values *cfg); +/* + * Frees memory allocated for dynamically loaded configuration values + * inside `repo_config_values`. + * + * As dynamically allocated variables are migrated into this struct, + * their FREE_AND_NULL() calls should be appended here. + */ +void repo_config_values_clear(struct repository *repo); + /* * TODO: All the below state either explicitly or implicitly relies on * `the_repository`. We should eventually get rid of these and make the diff --git a/repository.c b/repository.c index 187dd471c4e607..b31f1b785288a8 100644 --- a/repository.c +++ b/repository.c @@ -388,6 +388,7 @@ void repo_clear(struct repository *repo) FREE_AND_NULL(repo->parsed_objects); repo_settings_clear(repo); + repo_config_values_clear(repo); if (repo->config) { git_configset_clear(repo->config); From ddd6e96dc5c5c9203e413df0c01d7914157d76ca Mon Sep 17 00:00:00 2001 From: Tian Yuchen Date: Wed, 24 Jun 2026 00:44:39 +0800 Subject: [PATCH 2/9] environment: move excludes_file into repo_config_values The global variable 'excludes_file' is used to track the path to the global ignore file. If this variable is NULL, 'setup_standard_excludes()' in 'dir.c' forcefully evaluates and assigns the XDG default path to it. Continue the libification effort by encapsulating this lazy-loading fallback logic into a proper getter and moving the variable into 'struct repo_config_values'. Since 'excludes_file' is a dynamically allocated string, it requires proper heap memory management. It is safely freed using the newly introduced `repo_config_values_clear()` function when the repository is torn down. Mentored-by: Christian Couder Mentored-by: Ayush Chandekar Mentored-by: Olamide Caleb Bello Signed-off-by: Tian Yuchen --- dir.c | 4 ++-- environment.c | 15 ++++++++++++--- environment.h | 4 +++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/dir.c b/dir.c index 7a73690fbc7440..4f87a52b3c0597 100644 --- a/dir.c +++ b/dir.c @@ -3481,11 +3481,11 @@ static GIT_PATH_FUNC(git_path_info_exclude, "info/exclude") void setup_standard_excludes(struct dir_struct *dir) { + const char *excludes_file = repo_excludes_file(the_repository); + dir->exclude_per_dir = ".gitignore"; /* core.excludesfile defaulting to $XDG_CONFIG_HOME/git/ignore */ - if (!excludes_file) - excludes_file = xdg_config_home("ignore"); if (excludes_file && !access_or_warn(excludes_file, R_OK, 0)) add_patterns_from_file_1(dir, excludes_file, dir->untracked ? &dir->internal.ss_excludes_file : NULL); diff --git a/environment.c b/environment.c index 13677484decf77..5950592d63cca4 100644 --- a/environment.c +++ b/environment.c @@ -57,7 +57,6 @@ enum fsync_method fsync_method = FSYNC_METHOD_DEFAULT; enum fsync_component fsync_components = FSYNC_COMPONENTS_DEFAULT; char *editor_program; char *askpass_program; -char *excludes_file; enum auto_crlf auto_crlf = AUTO_CRLF_FALSE; enum eol core_eol = EOL_UNSET; int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN; @@ -134,6 +133,14 @@ int is_bare_repository(void) return is_bare_repository_cfg && !repo_get_work_tree(the_repository); } +const char *repo_excludes_file(struct repository *repo) +{ + if (!repo_config_values(repo)->excludes_file) + repo_config_values(repo)->excludes_file = xdg_config_home("ignore"); + + return repo_config_values(repo)->excludes_file; +} + int have_git_dir(void) { return startup_info->have_repository @@ -461,8 +468,8 @@ int git_default_core_config(const char *var, const char *value, } if (!strcmp(var, "core.excludesfile")) { - FREE_AND_NULL(excludes_file); - return git_config_pathname(&excludes_file, var, value); + FREE_AND_NULL(cfg->excludes_file); + return git_config_pathname(&cfg->excludes_file, var, value); } if (!strcmp(var, "core.whitespace")) { @@ -715,6 +722,7 @@ int git_default_config(const char *var, const char *value, void repo_config_values_init(struct repo_config_values *cfg) { cfg->attributes_file = NULL; + cfg->excludes_file = NULL; cfg->apply_sparse_checkout = 0; cfg->branch_track = BRANCH_TRACK_REMOTE; cfg->trust_ctime = 1; @@ -744,4 +752,5 @@ void repo_config_values_clear(struct repository *repo) cfg = repo_config_values(repo); FREE_AND_NULL(cfg->attributes_file); + FREE_AND_NULL(cfg->excludes_file); } diff --git a/environment.h b/environment.h index c4a6a457043638..2e8352de7fb481 100644 --- a/environment.h +++ b/environment.h @@ -90,6 +90,7 @@ struct repository; struct repo_config_values { /* section "core" config values */ char *attributes_file; + char *excludes_file; int apply_sparse_checkout; int trust_ctime; int check_stat; @@ -133,6 +134,8 @@ int git_default_config(const char *, const char *, int git_default_core_config(const char *var, const char *value, const struct config_context *ctx, void *cb); +const char *repo_excludes_file(struct repository *repo); + void repo_config_values_init(struct repo_config_values *cfg); /* @@ -217,7 +220,6 @@ extern char *git_log_output_encoding; extern char *editor_program; extern char *askpass_program; -extern char *excludes_file; /* * The character that begins a commented line in user-editable file From 72f41412626110a9f8210a3d9b773563fe623b52 Mon Sep 17 00:00:00 2001 From: Tian Yuchen Date: Thu, 2 Jul 2026 22:12:04 +0800 Subject: [PATCH 3/9] environment: move editor_program into repo_config_values The global variable 'editor_program' holds the path to the user's preferred editor. Move 'editor_program' into 'struct repo_config_values' to continue the libification effort. There have been discussions on whether external programs like editors truly need to be configured on a per-repository basis within the same process. While a single process might rarely invoke different editors, this migration is necessary for two reasons: 1. Developers frequently use different toolchains for different projects. Per-repo configuration respects this. 2. Moving this string into 'repo_config_values' eliminates mutable global state. As the codebase moves toward becoming a long-running processes managing multiple repositories concurrently must not overwrite each other's program configurations. No standalone getter function is introduced. Callers directly access the field via 'repo_config_values()'. Heap memory is safely reclaimed in 'repo_config_values_clear()'. Mentored-by: Christian Couder Mentored-by: Ayush Chandekar Mentored-by: Olamide Caleb Bello Signed-off-by: Tian Yuchen --- editor.c | 4 ++-- environment.c | 7 ++++--- environment.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/editor.c b/editor.c index fd174e6a034f1c..07d264cba0ae7e 100644 --- a/editor.c +++ b/editor.c @@ -29,8 +29,8 @@ const char *git_editor(void) const char *editor = getenv("GIT_EDITOR"); int terminal_is_dumb = is_terminal_dumb(); - if (!editor && editor_program) - editor = editor_program; + if (!editor && repo_config_values(the_repository)->editor_program) + editor = repo_config_values(the_repository)->editor_program; if (!editor && !terminal_is_dumb) editor = getenv("VISUAL"); if (!editor) diff --git a/environment.c b/environment.c index 5950592d63cca4..0a01f4761ae96d 100644 --- a/environment.c +++ b/environment.c @@ -55,7 +55,6 @@ int fsync_object_files = -1; int use_fsync = -1; enum fsync_method fsync_method = FSYNC_METHOD_DEFAULT; enum fsync_component fsync_components = FSYNC_COMPONENTS_DEFAULT; -char *editor_program; char *askpass_program; enum auto_crlf auto_crlf = AUTO_CRLF_FALSE; enum eol core_eol = EOL_UNSET; @@ -435,8 +434,8 @@ int git_default_core_config(const char *var, const char *value, } if (!strcmp(var, "core.editor")) { - FREE_AND_NULL(editor_program); - return git_config_string(&editor_program, var, value); + FREE_AND_NULL(cfg->editor_program); + return git_config_string(&cfg->editor_program, var, value); } if (!strcmp(var, "core.commentchar") || @@ -723,6 +722,7 @@ void repo_config_values_init(struct repo_config_values *cfg) { cfg->attributes_file = NULL; cfg->excludes_file = NULL; + cfg->editor_program = NULL; cfg->apply_sparse_checkout = 0; cfg->branch_track = BRANCH_TRACK_REMOTE; cfg->trust_ctime = 1; @@ -753,4 +753,5 @@ void repo_config_values_clear(struct repository *repo) FREE_AND_NULL(cfg->attributes_file); FREE_AND_NULL(cfg->excludes_file); + FREE_AND_NULL(cfg->editor_program); } diff --git a/environment.h b/environment.h index 2e8352de7fb481..1ec19149cbda7e 100644 --- a/environment.h +++ b/environment.h @@ -91,6 +91,7 @@ struct repo_config_values { /* section "core" config values */ char *attributes_file; char *excludes_file; + char *editor_program; int apply_sparse_checkout; int trust_ctime; int check_stat; @@ -218,7 +219,6 @@ const char *get_commit_output_encoding(void); extern char *git_commit_encoding; extern char *git_log_output_encoding; -extern char *editor_program; extern char *askpass_program; /* From 8daaa601ef2a3ade2fc38430cd84cd86a6497f92 Mon Sep 17 00:00:00 2001 From: Tian Yuchen Date: Thu, 2 Jul 2026 22:43:03 +0800 Subject: [PATCH 4/9] environment: move pager_program into repo_config_values The 'pager_program' variable is currently defined as a file-scoped static string in pager.c. Move it into 'struct repo_config_values'. The configuration parsing logic remains strictly within pager.c to respect subsystem boundaries. The read/write operations are simply redirected to the repository-specific structure using 'repo_config_values()'. Similar to the recent editor_program migration, no standalone getter is introduced to keep the code minimal. The dynamically allocated memory is now managed by 'repo_config_values_clear()'. Mentored-by: Christian Couder Mentored-by: Ayush Chandekar Mentored-by: Olamide Caleb Bello Signed-off-by: Tian Yuchen --- environment.c | 2 ++ environment.h | 1 + pager.c | 17 ++++++++++------- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/environment.c b/environment.c index 0a01f4761ae96d..a1204fdcb2410d 100644 --- a/environment.c +++ b/environment.c @@ -723,6 +723,7 @@ void repo_config_values_init(struct repo_config_values *cfg) cfg->attributes_file = NULL; cfg->excludes_file = NULL; cfg->editor_program = NULL; + cfg->pager_program = NULL; cfg->apply_sparse_checkout = 0; cfg->branch_track = BRANCH_TRACK_REMOTE; cfg->trust_ctime = 1; @@ -754,4 +755,5 @@ void repo_config_values_clear(struct repository *repo) FREE_AND_NULL(cfg->attributes_file); FREE_AND_NULL(cfg->excludes_file); FREE_AND_NULL(cfg->editor_program); + FREE_AND_NULL(cfg->pager_program); } diff --git a/environment.h b/environment.h index 1ec19149cbda7e..22f6697c52ffcd 100644 --- a/environment.h +++ b/environment.h @@ -92,6 +92,7 @@ struct repo_config_values { char *attributes_file; char *excludes_file; char *editor_program; + char *pager_program; int apply_sparse_checkout; int trust_ctime; int check_stat; diff --git a/pager.c b/pager.c index 35b210e0484f90..450ad053b61a7b 100644 --- a/pager.c +++ b/pager.c @@ -5,6 +5,8 @@ #include "run-command.h" #include "sigchain.h" #include "alias.h" +#include "repository.h" +#include "environment.h" int pager_use_color = 1; @@ -13,7 +15,6 @@ int pager_use_color = 1; #endif static struct child_process pager_process; -static char *pager_program; static int old_fd1 = -1, old_fd2 = -1; /* Is the value coming back from term_columns() just a guess? */ @@ -75,10 +76,12 @@ static void wait_for_pager_signal(int signo) static int core_pager_config(const char *var, const char *value, const struct config_context *ctx UNUSED, - void *data UNUSED) + void *data) { + struct repository *r = data; + if (!strcmp(var, "core.pager")) - return git_config_string(&pager_program, var, value); + return git_config_string(&repo_config_values(r)->pager_program, var, value); return 0; } @@ -91,10 +94,10 @@ const char *git_pager(struct repository *r, int stdout_is_tty) pager = getenv("GIT_PAGER"); if (!pager) { - if (!pager_program) + if (!repo_config_values(r)->pager_program) read_early_config(r, - core_pager_config, NULL); - pager = pager_program; + core_pager_config, r); + pager = repo_config_values(r)->pager_program; } if (!pager) pager = getenv("PAGER"); @@ -303,6 +306,6 @@ int check_pager_config(struct repository *r, const char *cmd) read_early_config(r, pager_command_config, &data); if (data.value) - pager_program = data.value; + repo_config_values(r)->pager_program = data.value; return data.want; } From f6133ca6bee2b9a682ab45f54e9991b6d9253b5c Mon Sep 17 00:00:00 2001 From: Tian Yuchen Date: Fri, 3 Jul 2026 16:35:57 +0800 Subject: [PATCH 5/9] environment: move askpass_program into repo_config_values The global variable 'askpass_program' stores the path to the program used to prompt the user for credentials. Move it into repo_config_values to continue the libification effort. While it is uncommon for a single process to require different askpass programs for different repositories, maintaining this value as a mutable global string is a blocker for libification. Global heap-allocated strings introduce thread-safety issues in a multi-repo environment. Move 'askpass_program' into 'struct repo_config_values' to eliminate this global state. The memory is now safely managed and freed via 'repo_config_values_clear()'. Mentored-by: Christian Couder Mentored-by: Ayush Chandekar Mentored-by: Olamide Caleb Bello Signed-off-by: Tian Yuchen --- environment.c | 6 ++++-- environment.h | 1 + prompt.c | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/environment.c b/environment.c index a1204fdcb2410d..3782bf68aa8105 100644 --- a/environment.c +++ b/environment.c @@ -462,8 +462,8 @@ int git_default_core_config(const char *var, const char *value, } if (!strcmp(var, "core.askpass")) { - FREE_AND_NULL(askpass_program); - return git_config_string(&askpass_program, var, value); + FREE_AND_NULL(cfg->askpass_program); + return git_config_string(&cfg->askpass_program, var, value); } if (!strcmp(var, "core.excludesfile")) { @@ -724,6 +724,7 @@ void repo_config_values_init(struct repo_config_values *cfg) cfg->excludes_file = NULL; cfg->editor_program = NULL; cfg->pager_program = NULL; + cfg->askpass_program = NULL; cfg->apply_sparse_checkout = 0; cfg->branch_track = BRANCH_TRACK_REMOTE; cfg->trust_ctime = 1; @@ -756,4 +757,5 @@ void repo_config_values_clear(struct repository *repo) FREE_AND_NULL(cfg->excludes_file); FREE_AND_NULL(cfg->editor_program); FREE_AND_NULL(cfg->pager_program); + FREE_AND_NULL(cfg->askpass_program); } diff --git a/environment.h b/environment.h index 22f6697c52ffcd..d55b1ba0731395 100644 --- a/environment.h +++ b/environment.h @@ -93,6 +93,7 @@ struct repo_config_values { char *excludes_file; char *editor_program; char *pager_program; + char *askpass_program; int apply_sparse_checkout; int trust_ctime; int check_stat; diff --git a/prompt.c b/prompt.c index 706fba2a5030c7..d8d74c7e379dbe 100644 --- a/prompt.c +++ b/prompt.c @@ -3,6 +3,7 @@ #include "git-compat-util.h" #include "parse.h" #include "environment.h" +#include "repository.h" #include "run-command.h" #include "strbuf.h" #include "prompt.h" @@ -51,7 +52,7 @@ char *git_prompt(const char *prompt, int flags) askpass = getenv("GIT_ASKPASS"); if (!askpass) - askpass = askpass_program; + askpass = repo_config_values(the_repository)->askpass_program; if (!askpass) askpass = getenv("SSH_ASKPASS"); if (askpass && *askpass) From d706bd5cc0828f4b56bbd8a81e7f38bb663ed668 Mon Sep 17 00:00:00 2001 From: Tian Yuchen Date: Fri, 3 Jul 2026 16:02:57 +0800 Subject: [PATCH 6/9] environment: migrate apply_default_whitespace and apply_default_ignorewhitespace The global variables 'apply_default_whitespace' and 'apply_default_ignorewhitespace' are used to store the default whitespace configuration for 'git apply'. Move these variables into 'struct repo_config_values' to continue the libification effort. Dynamically allocated strings fetched via 'repo_config_get_string()' are now tracked per-repository and safely freed in 'repo_config_values_clear()'. As part of this transition, update 'git_apply_config()' to accept a 'struct repository *' argument rather than relying on the 'the_repository' global. Mentored-by: Christian Couder Mentored-by: Ayush Chandekar Mentored-by: Olamide Caleb Bello Signed-off-by: Tian Yuchen --- apply.c | 20 ++++++++++++-------- environment.c | 6 ++++-- environment.h | 4 ++-- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/apply.c b/apply.c index 249248d4f205ca..66db9b76784647 100644 --- a/apply.c +++ b/apply.c @@ -47,11 +47,13 @@ struct gitdiff_data { int p_value; }; -static void git_apply_config(void) +static void git_apply_config(struct repository *repo) { - repo_config_get_string(the_repository, "apply.whitespace", &apply_default_whitespace); - repo_config_get_string(the_repository, "apply.ignorewhitespace", &apply_default_ignorewhitespace); - repo_config(the_repository, git_xmerge_config, NULL); + repo_config_get_string(repo, "apply.whitespace", + &repo_config_values(repo)->apply_default_whitespace); + repo_config_get_string(repo, "apply.ignorewhitespace", + &repo_config_values(repo)->apply_default_ignorewhitespace); + repo_config(repo, git_xmerge_config, NULL); } static int parse_whitespace_option(struct apply_state *state, const char *option) @@ -126,10 +128,12 @@ int init_apply_state(struct apply_state *state, strset_init(&state->kept_symlinks); strbuf_init(&state->root, 0); - git_apply_config(); - if (apply_default_whitespace && parse_whitespace_option(state, apply_default_whitespace)) + git_apply_config(repo); + if (repo_config_values(repo)->apply_default_whitespace && + parse_whitespace_option(state, repo_config_values(repo)->apply_default_whitespace)) return -1; - if (apply_default_ignorewhitespace && parse_ignorewhitespace_option(state, apply_default_ignorewhitespace)) + if (repo_config_values(repo)->apply_default_ignorewhitespace && + parse_ignorewhitespace_option(state, repo_config_values(repo)->apply_default_ignorewhitespace)) return -1; return 0; } @@ -192,7 +196,7 @@ int check_apply_state(struct apply_state *state, int force_apply) static void set_default_whitespace_mode(struct apply_state *state) { - if (!state->whitespace_option && !apply_default_whitespace) + if (!state->whitespace_option && !repo_config_values(state->repo)->apply_default_whitespace) state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error); } diff --git a/environment.c b/environment.c index 3782bf68aa8105..874479021900e9 100644 --- a/environment.c +++ b/environment.c @@ -49,8 +49,6 @@ int assume_unchanged; int is_bare_repository_cfg = -1; /* unspecified */ char *git_commit_encoding; char *git_log_output_encoding; -char *apply_default_whitespace; -char *apply_default_ignorewhitespace; int fsync_object_files = -1; int use_fsync = -1; enum fsync_method fsync_method = FSYNC_METHOD_DEFAULT; @@ -725,6 +723,8 @@ void repo_config_values_init(struct repo_config_values *cfg) cfg->editor_program = NULL; cfg->pager_program = NULL; cfg->askpass_program = NULL; + cfg->apply_default_whitespace = NULL; + cfg->apply_default_ignorewhitespace = NULL; cfg->apply_sparse_checkout = 0; cfg->branch_track = BRANCH_TRACK_REMOTE; cfg->trust_ctime = 1; @@ -758,4 +758,6 @@ void repo_config_values_clear(struct repository *repo) FREE_AND_NULL(cfg->editor_program); FREE_AND_NULL(cfg->pager_program); FREE_AND_NULL(cfg->askpass_program); + FREE_AND_NULL(cfg->apply_default_whitespace); + FREE_AND_NULL(cfg->apply_default_ignorewhitespace); } diff --git a/environment.h b/environment.h index d55b1ba0731395..9aecd64152071f 100644 --- a/environment.h +++ b/environment.h @@ -94,6 +94,8 @@ struct repo_config_values { char *editor_program; char *pager_program; char *askpass_program; + char *apply_default_whitespace; + char *apply_default_ignorewhitespace; int apply_sparse_checkout; int trust_ctime; int check_stat; @@ -182,8 +184,6 @@ extern int has_symlinks; extern int minimum_abbrev, default_abbrev; extern int ignore_case; extern int assume_unchanged; -extern char *apply_default_whitespace; -extern char *apply_default_ignorewhitespace; extern unsigned long pack_size_limit_cfg; extern int protect_hfs; From d4e438ca08a5495b36c7c1145408d5e28f514771 Mon Sep 17 00:00:00 2001 From: Tian Yuchen Date: Fri, 3 Jul 2026 19:34:01 +0800 Subject: [PATCH 7/9] environment: move push_default into repo_config_values The global variable 'push_default' specifies the default behavior of 'git push' when no explicit refspec is provided. Move 'push_default' into 'struct repo_config_values' to continue the libification effort. While 'enum push_default_type' ideally belongs in 'remote.h', moving it there introduces a circular dependency chain: remote.h -> hash.h -> repository.h -> environment.h. Therefore, the enum definition is kept in 'environment.h' just above 'struct repo_config_values' with a NEEDSWORK comment for future cleanup. Modify the configuration parsing in environment.c to update the per-repository structure directly, and update caller across the codebase to access the value via 'repo_config_values()'. Mentored-by: Christian Couder Mentored-by: Ayush Chandekar Mentored-by: Olamide Caleb Bello Signed-off-by: Tian Yuchen --- builtin/push.c | 8 ++++---- environment.c | 16 +++++++++------- environment.h | 26 ++++++++++++++++---------- remote.c | 2 +- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/builtin/push.c b/builtin/push.c index 6021b71d668455..6dc3224b6046cc 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -88,7 +88,7 @@ static void refspec_append_mapped(struct refspec *refspec, const char *ref, } } - if (push_default == PUSH_DEFAULT_UPSTREAM && + if (repo_config_values(the_repository)->push_default == PUSH_DEFAULT_UPSTREAM && skip_prefix(matched->name, "refs/heads/", &branch_name)) { struct branch *branch = branch_get(branch_name); if (branch->merge_nr == 1 && branch->merge[0]->src) { @@ -160,7 +160,7 @@ static NORETURN void die_push_simple(struct branch *branch, * Don't show advice for people who explicitly set * push.default. */ - if (push_default == PUSH_DEFAULT_UNSPECIFIED) + if (cfg->push_default == PUSH_DEFAULT_UNSPECIFIED) advice_pushdefault_maybe = _("\n" "To choose either option permanently, " "see push.default in 'git help config'.\n"); @@ -232,7 +232,7 @@ static void setup_default_push_refspecs(int *flags, struct remote *remote) const char *dst; int same_remote; - switch (push_default) { + switch (repo_config_values(the_repository)->push_default) { case PUSH_DEFAULT_MATCHING: refspec_append(&rs, ":"); return; @@ -252,7 +252,7 @@ static void setup_default_push_refspecs(int *flags, struct remote *remote) dst = branch->refname; same_remote = !strcmp(remote->name, remote_for_branch(branch, NULL)); - switch (push_default) { + switch (repo_config_values(the_repository)->push_default) { default: case PUSH_DEFAULT_UNSPECIFIED: case PUSH_DEFAULT_SIMPLE: diff --git a/environment.c b/environment.c index 874479021900e9..09de2fee87030f 100644 --- a/environment.c +++ b/environment.c @@ -59,7 +59,6 @@ enum eol core_eol = EOL_UNSET; int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN; char *check_roundtrip_encoding; enum rebase_setup_type autorebase = AUTOREBASE_NEVER; -enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED; #ifndef OBJECT_CREATION_MODE #define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS #endif @@ -619,21 +618,23 @@ static int git_default_branch_config(const char *var, const char *value) static int git_default_push_config(const char *var, const char *value) { + struct repo_config_values *cfg = repo_config_values(the_repository); + if (!strcmp(var, "push.default")) { if (!value) return config_error_nonbool(var); else if (!strcmp(value, "nothing")) - push_default = PUSH_DEFAULT_NOTHING; + cfg->push_default = PUSH_DEFAULT_NOTHING; else if (!strcmp(value, "matching")) - push_default = PUSH_DEFAULT_MATCHING; + cfg->push_default = PUSH_DEFAULT_MATCHING; else if (!strcmp(value, "simple")) - push_default = PUSH_DEFAULT_SIMPLE; + cfg->push_default = PUSH_DEFAULT_SIMPLE; else if (!strcmp(value, "upstream")) - push_default = PUSH_DEFAULT_UPSTREAM; + cfg->push_default = PUSH_DEFAULT_UPSTREAM; else if (!strcmp(value, "tracking")) /* deprecated */ - push_default = PUSH_DEFAULT_UPSTREAM; + cfg->push_default = PUSH_DEFAULT_UPSTREAM; else if (!strcmp(value, "current")) - push_default = PUSH_DEFAULT_CURRENT; + cfg->push_default = PUSH_DEFAULT_CURRENT; else { error(_("malformed value for %s: %s"), var, value); return error(_("must be one of nothing, matching, simple, " @@ -725,6 +726,7 @@ void repo_config_values_init(struct repo_config_values *cfg) cfg->askpass_program = NULL; cfg->apply_default_whitespace = NULL; cfg->apply_default_ignorewhitespace = NULL; + cfg->push_default = PUSH_DEFAULT_UNSPECIFIED; cfg->apply_sparse_checkout = 0; cfg->branch_track = BRANCH_TRACK_REMOTE; cfg->trust_ctime = 1; diff --git a/environment.h b/environment.h index 9aecd64152071f..72859b5d767721 100644 --- a/environment.h +++ b/environment.h @@ -87,6 +87,21 @@ extern const char * const local_repo_env[]; struct strvec; struct repository; + +/* + * NEEDSWORK: It would be better if these definitions could be moved to + * other more specific files, but care is needed to avoid circular + * inclusion issues. + */ +enum push_default_type { + PUSH_DEFAULT_NOTHING = 0, + PUSH_DEFAULT_MATCHING, + PUSH_DEFAULT_SIMPLE, + PUSH_DEFAULT_UPSTREAM, + PUSH_DEFAULT_CURRENT, + PUSH_DEFAULT_UNSPECIFIED +}; + struct repo_config_values { /* section "core" config values */ char *attributes_file; @@ -96,6 +111,7 @@ struct repo_config_values { char *askpass_program; char *apply_default_whitespace; char *apply_default_ignorewhitespace; + enum push_default_type push_default; int apply_sparse_checkout; int trust_ctime; int check_stat; @@ -197,16 +213,6 @@ enum rebase_setup_type { }; extern enum rebase_setup_type autorebase; -enum push_default_type { - PUSH_DEFAULT_NOTHING = 0, - PUSH_DEFAULT_MATCHING, - PUSH_DEFAULT_SIMPLE, - PUSH_DEFAULT_UPSTREAM, - PUSH_DEFAULT_CURRENT, - PUSH_DEFAULT_UNSPECIFIED -}; -extern enum push_default_type push_default; - enum object_creation_mode { OBJECT_CREATION_USES_HARDLINKS = 0, OBJECT_CREATION_USES_RENAMES = 1 diff --git a/remote.c b/remote.c index 00723b385e1d52..d48c01d37559df 100644 --- a/remote.c +++ b/remote.c @@ -1933,7 +1933,7 @@ static char *branch_get_push_1(struct repository *repo, if (remote->mirror) return tracking_for_push_dest(remote, branch->refname, err); - switch (push_default) { + switch (repo_config_values(repo)->push_default) { case PUSH_DEFAULT_NOTHING: return error_buf(err, _("push has no destination (push.default is 'nothing')")); From 26bca35a8cd037acc5693d8fec0c12beeaee3987 Mon Sep 17 00:00:00 2001 From: Tian Yuchen Date: Sat, 4 Jul 2026 23:04:57 +0800 Subject: [PATCH 8/9] environment: move autorebase into repo_config_values The global variable 'autorebase' dictates whether a newly created branch should be configured to automatically rebase by default. Move it into 'struct repo_config_values' to continue the libification effort. The 'enum rebase_setup_type' definition is moved higher up in 'environment.h' so that it is visible to the repository-specific structure. The default state AUTOREBASE_NEVER is now correctly initialized in 'repo_config_values_init()'. Configuration parsing in 'git_default_branch_config()' is updated to write directly to the repository's configuration instance. Mentored-by: Christian Couder Mentored-by: Ayush Chandekar Mentored-by: Olamide Caleb Bello Signed-off-by: Tian Yuchen --- branch.c | 2 +- environment.c | 10 +++++----- environment.h | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/branch.c b/branch.c index 243db7d0fc0226..e1c1f8c89dbc56 100644 --- a/branch.c +++ b/branch.c @@ -61,7 +61,7 @@ static int find_tracked_branch(struct remote *remote, void *priv) static int should_setup_rebase(const char *origin) { - switch (autorebase) { + switch (repo_config_values(the_repository)->autorebase) { case AUTOREBASE_NEVER: return 0; case AUTOREBASE_LOCAL: diff --git a/environment.c b/environment.c index 09de2fee87030f..7701aa3bc0be24 100644 --- a/environment.c +++ b/environment.c @@ -58,7 +58,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE; enum eol core_eol = EOL_UNSET; int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN; char *check_roundtrip_encoding; -enum rebase_setup_type autorebase = AUTOREBASE_NEVER; #ifndef OBJECT_CREATION_MODE #define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS #endif @@ -600,13 +599,13 @@ static int git_default_branch_config(const char *var, const char *value) if (!value) return config_error_nonbool(var); else if (!strcmp(value, "never")) - autorebase = AUTOREBASE_NEVER; + cfg->autorebase = AUTOREBASE_NEVER; else if (!strcmp(value, "local")) - autorebase = AUTOREBASE_LOCAL; + cfg->autorebase = AUTOREBASE_LOCAL; else if (!strcmp(value, "remote")) - autorebase = AUTOREBASE_REMOTE; + cfg->autorebase = AUTOREBASE_REMOTE; else if (!strcmp(value, "always")) - autorebase = AUTOREBASE_ALWAYS; + cfg->autorebase = AUTOREBASE_ALWAYS; else return error(_("malformed value for %s"), var); return 0; @@ -727,6 +726,7 @@ void repo_config_values_init(struct repo_config_values *cfg) cfg->apply_default_whitespace = NULL; cfg->apply_default_ignorewhitespace = NULL; cfg->push_default = PUSH_DEFAULT_UNSPECIFIED; + cfg->autorebase = AUTOREBASE_NEVER; cfg->apply_sparse_checkout = 0; cfg->branch_track = BRANCH_TRACK_REMOTE; cfg->trust_ctime = 1; diff --git a/environment.h b/environment.h index 72859b5d767721..464ff73136cee4 100644 --- a/environment.h +++ b/environment.h @@ -102,6 +102,13 @@ enum push_default_type { PUSH_DEFAULT_UNSPECIFIED }; +enum rebase_setup_type { + AUTOREBASE_NEVER = 0, + AUTOREBASE_LOCAL, + AUTOREBASE_REMOTE, + AUTOREBASE_ALWAYS +}; + struct repo_config_values { /* section "core" config values */ char *attributes_file; @@ -112,6 +119,7 @@ struct repo_config_values { char *apply_default_whitespace; char *apply_default_ignorewhitespace; enum push_default_type push_default; + enum rebase_setup_type autorebase; int apply_sparse_checkout; int trust_ctime; int check_stat; @@ -205,14 +213,6 @@ extern unsigned long pack_size_limit_cfg; extern int protect_hfs; extern int protect_ntfs; -enum rebase_setup_type { - AUTOREBASE_NEVER = 0, - AUTOREBASE_LOCAL, - AUTOREBASE_REMOTE, - AUTOREBASE_ALWAYS -}; -extern enum rebase_setup_type autorebase; - enum object_creation_mode { OBJECT_CREATION_USES_HARDLINKS = 0, OBJECT_CREATION_USES_RENAMES = 1 From 9bbaf1aae069e231608f582ef765db85517f8d99 Mon Sep 17 00:00:00 2001 From: Tian Yuchen Date: Sat, 4 Jul 2026 23:22:13 +0800 Subject: [PATCH 9/9] environment: move object_creation_mode into repo_config_values The global variable 'object_creation_mode' controls how Git creates object files, specifically determining whether to use hardlinks or renames when moving temporary files into the object database. Move it into 'struct repo_config_values' to continue the libification effort. Move the 'enum object_creation_mode' definition higher up in 'environment.h' to ensure it is visible to the structure. Initialize the per-repository value to its default macro value OBJECT_CREATION_MODE inside 'repo_config_values_init()'. Update configuration parsing in 'git_default_core_config()' to write directly to the repository-specific configuration structure. Mentored-by: Christian Couder Mentored-by: Ayush Chandekar Mentored-by: Olamide Caleb Bello Signed-off-by: Tian Yuchen --- environment.c | 6 +++--- environment.h | 12 ++++++------ object-file.c | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/environment.c b/environment.c index 7701aa3bc0be24..e50beda918b082 100644 --- a/environment.c +++ b/environment.c @@ -61,7 +61,6 @@ char *check_roundtrip_encoding; #ifndef OBJECT_CREATION_MODE #define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS #endif -enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE; int grafts_keep_true_parents; unsigned long pack_size_limit_cfg; @@ -511,9 +510,9 @@ int git_default_core_config(const char *var, const char *value, if (!value) return config_error_nonbool(var); if (!strcmp(value, "rename")) - object_creation_mode = OBJECT_CREATION_USES_RENAMES; + cfg->object_creation_mode = OBJECT_CREATION_USES_RENAMES; else if (!strcmp(value, "link")) - object_creation_mode = OBJECT_CREATION_USES_HARDLINKS; + cfg->object_creation_mode = OBJECT_CREATION_USES_HARDLINKS; else die(_("invalid mode for object creation: %s"), value); return 0; @@ -727,6 +726,7 @@ void repo_config_values_init(struct repo_config_values *cfg) cfg->apply_default_ignorewhitespace = NULL; cfg->push_default = PUSH_DEFAULT_UNSPECIFIED; cfg->autorebase = AUTOREBASE_NEVER; + cfg->object_creation_mode = OBJECT_CREATION_MODE; cfg->apply_sparse_checkout = 0; cfg->branch_track = BRANCH_TRACK_REMOTE; cfg->trust_ctime = 1; diff --git a/environment.h b/environment.h index 464ff73136cee4..eaa0aba7bc06fa 100644 --- a/environment.h +++ b/environment.h @@ -109,6 +109,11 @@ enum rebase_setup_type { AUTOREBASE_ALWAYS }; +enum object_creation_mode { + OBJECT_CREATION_USES_HARDLINKS = 0, + OBJECT_CREATION_USES_RENAMES = 1 +}; + struct repo_config_values { /* section "core" config values */ char *attributes_file; @@ -120,6 +125,7 @@ struct repo_config_values { char *apply_default_ignorewhitespace; enum push_default_type push_default; enum rebase_setup_type autorebase; + enum object_creation_mode object_creation_mode; int apply_sparse_checkout; int trust_ctime; int check_stat; @@ -213,12 +219,6 @@ extern unsigned long pack_size_limit_cfg; extern int protect_hfs; extern int protect_ntfs; -enum object_creation_mode { - OBJECT_CREATION_USES_HARDLINKS = 0, - OBJECT_CREATION_USES_RENAMES = 1 -}; -extern enum object_creation_mode object_creation_mode; - extern int grafts_keep_true_parents; const char *get_log_output_encoding(void); diff --git a/object-file.c b/object-file.c index 9afa842da2dc8e..cbbfc8f1dc1164 100644 --- a/object-file.c +++ b/object-file.c @@ -415,7 +415,7 @@ int finalize_object_file_flags(struct repository *repo, retry: ret = 0; - if (object_creation_mode == OBJECT_CREATION_USES_RENAMES) + if (repo_config_values(repo)->object_creation_mode == OBJECT_CREATION_USES_RENAMES) goto try_rename; else if (link(tmpfile, filename)) ret = errno;