diff --git a/builtin/config.c b/builtin/config.c index f70d6354772259..24b5cf61827f09 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -768,7 +768,16 @@ static void location_options_init(struct config_location_options *opts, } if (opts->use_global_config) { + // Used for reading config: + opts->options.ignore_repo = 1; + opts->options.ignore_cmdline= 1; + opts->options.ignore_worktree = 1; + opts->options.ignore_system = 1; + opts->source.scope = CONFIG_SCOPE_GLOBAL; + + // Used for writing config: opts->source.file = opts->file_to_free = git_global_config(); + // todo(delilahwu): Do we need to move this error handling somewhere else? if (!opts->source.file) /* * It is unknown if HOME/.gitconfig exists, so @@ -833,6 +842,22 @@ static void display_options_init(struct config_display_options *opts) } } +static void handle_nonzero_config_with_options(const struct config_location_options + *location_opts) +{ + // todo(delilahwu): Either un-refactor this back to its original state from + // `master` or clean it up. + const char *err_file = location_opts->source.file; /* ? + location_opts->source.file : + location_opts->source.user_config ? + location_opts->source.user_config : + NULL; */ + if (err_file) + die_errno(_("unable to read config file '%s'"), err_file); + else + die(_("error processing config file(s)")); +} + static int cmd_config_list(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { @@ -858,11 +883,7 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix, if (config_with_options(show_all_config, &display_opts, &location_opts.source, the_repository, &location_opts.options) < 0) { - if (location_opts.source.file) - die_errno(_("unable to read config file '%s'"), - location_opts.source.file); - else - die(_("error processing config file(s)")); + handle_nonzero_config_with_options(&location_opts); } location_options_release(&location_opts); @@ -1282,11 +1303,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix) if (config_with_options(show_all_config, &display_opts, &location_opts.source, the_repository, &location_opts.options) < 0) { - if (location_opts.source.file) - die_errno(_("unable to read config file '%s'"), - location_opts.source.file); - else - die(_("error processing config file(s)")); + handle_nonzero_config_with_options(&location_opts); } } else if (actions == ACTION_EDIT) { diff --git a/config.c b/config.c index b18b5617fcd05d..dbf06bc3d3cda2 100644 --- a/config.c +++ b/config.c @@ -2019,11 +2019,13 @@ int git_config_system(void) } static int do_git_config_sequence(const struct config_options *opts, - const struct repository *repo, - config_fn_t fn, void *data) + const struct repository *repo, config_fn_t fn, + void *data, enum config_scope scope) { int ret = 0; char *system_config = git_system_config(); + int access_success_count = 0; + int nonzero_ret_on_global_access_error = scope == CONFIG_SCOPE_GLOBAL; char *xdg_config = NULL; char *user_config = NULL; char *repo_config; @@ -2045,22 +2047,45 @@ static int do_git_config_sequence(const struct config_options *opts, worktree_config = NULL; } - if (git_config_system() && system_config && + if (!opts->ignore_system && git_config_system() && system_config && !access_or_die(system_config, R_OK, opts->system_gently ? ACCESS_EACCES_OK : 0)) ret += git_config_from_file_with_options(fn, system_config, data, CONFIG_SCOPE_SYSTEM, NULL); - git_global_config_paths(&user_config, &xdg_config); + if (!opts->ignore_global) { + git_global_config_paths(&user_config, &xdg_config); - if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) - ret += git_config_from_file_with_options(fn, xdg_config, data, - CONFIG_SCOPE_GLOBAL, NULL); + if (xdg_config) { + if (!access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) { + ret += git_config_from_file_with_options( + fn, xdg_config, data, + CONFIG_SCOPE_GLOBAL, NULL); + if (nonzero_ret_on_global_access_error && !ret) { + ++access_success_count; + } + } + } - if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) - ret += git_config_from_file_with_options(fn, user_config, data, - CONFIG_SCOPE_GLOBAL, NULL); + if (user_config) { + if (!access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) { + ret += git_config_from_file_with_options( + fn, user_config, data, + CONFIG_SCOPE_GLOBAL, NULL); + if (nonzero_ret_on_global_access_error && !ret) { + ++access_success_count; + } + } + } + + if (nonzero_ret_on_global_access_error && !access_success_count) { + --ret; + } + + free(xdg_config); + free(user_config); + } if (!opts->ignore_repo && repo_config && !access_or_die(repo_config, R_OK, 0)) @@ -2079,8 +2104,6 @@ static int do_git_config_sequence(const struct config_options *opts, die(_("unable to parse command-line config")); free(system_config); - free(xdg_config); - free(user_config); free(repo_config); free(worktree_config); return ret; @@ -2110,7 +2133,8 @@ int config_with_options(config_fn_t fn, void *data, */ if (config_source && config_source->use_stdin) { ret = git_config_from_stdin(fn, data, config_source->scope); - } else if (config_source && config_source->file) { + } else if (config_source && config_source->file && + config_source->scope != CONFIG_SCOPE_GLOBAL) { ret = git_config_from_file_with_options(fn, config_source->file, data, config_source->scope, NULL); @@ -2118,7 +2142,10 @@ int config_with_options(config_fn_t fn, void *data, ret = git_config_from_blob_ref(fn, repo, config_source->blob, data, config_source->scope); } else { - ret = do_git_config_sequence(opts, repo, fn, data); + ret = do_git_config_sequence(opts, repo, fn, data, + config_source ? + config_source->scope : + CONFIG_SCOPE_UNKNOWN); } if (inc.remote_urls) { diff --git a/config.h b/config.h index 29a027748375f1..d7e0c4430d8e04 100644 --- a/config.h +++ b/config.h @@ -87,6 +87,8 @@ typedef int (*config_parser_event_fn_t)(enum config_event_t type, struct config_options { unsigned int respect_includes : 1; + unsigned int ignore_system : 1; // From /etc/gitconfig + unsigned int ignore_global : 1; // From a combination of `~/.gitconfig` and `XDG_CONFIG_HOME/git/config` unsigned int ignore_repo : 1; unsigned int ignore_worktree : 1; unsigned int ignore_cmdline : 1; diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000000000..065e5e50fcb221 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1747676747, + "narHash": "sha256-LXkWBVqilgx7Pohwqu/ABxDVw+Cmi5/Mj2S2mpUH0Fw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "72841a4a8761d1aed92ef6169a636872c986c76d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000000000..ac196bb9d0c389 --- /dev/null +++ b/flake.nix @@ -0,0 +1,41 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; + flake-utils.url = "github:numtide/flake-utils"; + }; + outputs = + { + self, + nixpkgs, + flake-utils, + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + overlays = [ ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + in + with pkgs; + { + devShell = mkShell { + buildInputs = [ + # Build tools + autoconf + clang-tools_19 + clang_19 + cmake + ninja + perl + lldb_19 + + # Deps + curl + zlib + expat + ]; + }; + } + ); +} diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 51a85e83c27b13..524c2e5ff6fef3 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -2367,6 +2367,76 @@ test_expect_success '--show-scope with --default' ' test_cmp expect actual ' +test_expect_success 'list with nonexistent global config' ' + rm -rf "$HOME"/.config/git/config && + rm -f .git/config && + git config ${mode_prefix}list --show-scope +' + +test_expect_success 'list --global with nonexistent global config' ' + rm -rf "$HOME"/.config/git/config && + rm -f .git/config && + test_must_fail git config ${mode_prefix}list --global --show-scope +' + +test_expect_success 'list --global with only home' ' + rm -rf "$HOME"/.config/git/config && + rm -f .git/config && + + test_when_finished rm -f \"\$HOME\"/.gitconfig && + cat >"$HOME"/.gitconfig <<-EOF && + [home] + config = true + EOF + + cat >expect <<-EOF && + global home.config=true + EOF + git config ${mode_prefix}list --global --show-scope >output && + test_cmp expect output +' + +test_expect_success 'list --global with only xdg' ' + rm -f "$HOME"/.gitconfig .git/config && + + test_when_finished rm -rf \"\$HOME\"/.config/git && + mkdir -p "$HOME"/.config/git && + cat >"$HOME"/.config/git/config <<-EOF && + [xdg] + config = true + EOF + + cat >expect <<-EOF && + global xdg.config=true + EOF + git config ${mode_prefix}list --global --show-scope >output && + test_cmp expect output +' + +test_expect_success 'list --global with both home and xdg' ' + rm -f .git/config && + + test_when_finished rm -f \"\$HOME\"/.gitconfig && + cat >"$HOME"/.gitconfig <<-EOF && + [home] + config = true + EOF + + test_when_finished rm -rf \"\$HOME\"/.config/git && + mkdir -p "$HOME"/.config/git && + cat >"$HOME"/.config/git/config <<-EOF && + [xdg] + config = true + EOF + + cat >expect <<-EOF && + global xdg.config=true + global home.config=true + EOF + git config ${mode_prefix}list --global --show-scope >output && + test_cmp expect output +' + test_expect_success 'override global and system config' ' test_when_finished rm -f \"\$HOME\"/.gitconfig && cat >"$HOME"/.gitconfig <<-EOF &&