Skip to content

Commit 639bc7b

Browse files
committed
config: read from config sequence for global scope
The output of `git config list --global` should include both `$HOME/.gitconfig` and `$XDG_CONFIG_HOME/git/config`, but it only reads from the former. We've assumed each config scope corresponds to a single config file location. Because of this assumption, `git config list --global` reads the global config by calling `git_config_from_file_with_options(..., "~/.gitconfig", ...)`. This function usage restricts us to a single config file. Since the global scope includes more than one file, we should use another method to read the global config. The output of `git config list --show-scope --show-origin` correctly includes both the home and XDG config files. So there's existing code that respects both locations, namely the `do_git_config_sequence()` function which reads from all scopes. Introduce flags that make it possible to ignore all but the global scope (i.e. ignore system, local, worktree, and cmdline). Then, reuse this function for reading only the global scope. This was the suggested solution in the original bug report thread [1]. However, this change introduces a regression. If the global config file does not exist or is unreadable, `git config list --global` should fail. Since `do_git_config_sequence()` reads from all scopes, it will still succeed when the global config file is missing. This regression will be addressed in the next patch. Modify the tests as follows: A. Assert that the output of `git config list --global` now includes both the home and XDG config. B. Expect success even when the global config file is missing or unreadable, marking them as a "TODO known breakage". Note 1: The `ignore_global` flag is not set anywhere, so the `if (!opts->ignore_global)` condition is always met. We can remove this flag if desired. Note 2: It's assumed that `config_source->scope == CONFIG_SCOPE_GLOBAL` iff `--global` is specified. This comparison determines whether to call `do_git_config_sequence()` for the global scope, or to keep calling `git_config_from_file_with_options()` for other scopes. Note 3: Keep populating `opts->source.file` in `builtin/config.c` because it is used as the destination file for write operations. The proposed changes could convolute the code because there is no single source of truth for the config file locations in the global scope. Add a comment to help clarify this. Please let me know if it's unclear. [1]: https://lore.kernel.org/git/[email protected]. Reported-by: Jade Lovelace <[email protected]> Helped-by: Derrick Stolee <[email protected]> Suggested-by: Glen Choo <[email protected]> Signed-off-by: Delilah Ashley Wu <[email protected]>
1 parent 2fcfdd1 commit 639bc7b

File tree

5 files changed

+33
-15
lines changed

5 files changed

+33
-15
lines changed

builtin/config.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,18 @@ static void location_options_init(struct config_location_options *opts,
768768
}
769769

770770
if (opts->use_global_config) {
771+
/*
772+
* Reading config is handled by `config.c#do_git_config_sequence()`
773+
* because the global config can be sourced from multiple files.
774+
* Writing config should point to a single destination as set in
775+
* `opts->source.file` below.
776+
*/
777+
opts->options.ignore_repo = 1;
778+
opts->options.ignore_cmdline= 1;
779+
opts->options.ignore_worktree = 1;
780+
opts->options.ignore_system = 1;
781+
opts->source.scope = CONFIG_SCOPE_GLOBAL;
782+
771783
opts->source.file = opts->file_to_free = git_global_config();
772784
if (!opts->source.file)
773785
/*

config.c

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,22 +1524,27 @@ static int do_git_config_sequence(const struct config_options *opts,
15241524
worktree_config = NULL;
15251525
}
15261526

1527-
if (git_config_system() && system_config &&
1527+
if (!opts->ignore_system && git_config_system() && system_config &&
15281528
!access_or_die(system_config, R_OK,
15291529
opts->system_gently ? ACCESS_EACCES_OK : 0))
15301530
ret += git_config_from_file_with_options(fn, system_config,
15311531
data, CONFIG_SCOPE_SYSTEM,
15321532
NULL);
15331533

1534-
git_global_config_paths(&user_config, &xdg_config);
1534+
if (!opts->ignore_global) {
1535+
git_global_config_paths(&user_config, &xdg_config);
1536+
1537+
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
1538+
ret += git_config_from_file_with_options(fn, xdg_config, data,
1539+
CONFIG_SCOPE_GLOBAL, NULL);
15351540

1536-
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
1537-
ret += git_config_from_file_with_options(fn, xdg_config, data,
1538-
CONFIG_SCOPE_GLOBAL, NULL);
1541+
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
1542+
ret += git_config_from_file_with_options(fn, user_config, data,
1543+
CONFIG_SCOPE_GLOBAL, NULL);
15391544

1540-
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
1541-
ret += git_config_from_file_with_options(fn, user_config, data,
1542-
CONFIG_SCOPE_GLOBAL, NULL);
1545+
free(xdg_config);
1546+
free(user_config);
1547+
}
15431548

15441549
if (!opts->ignore_repo && repo_config &&
15451550
!access_or_die(repo_config, R_OK, 0))
@@ -1558,8 +1563,6 @@ static int do_git_config_sequence(const struct config_options *opts,
15581563
die(_("unable to parse command-line config"));
15591564

15601565
free(system_config);
1561-
free(xdg_config);
1562-
free(user_config);
15631566
free(repo_config);
15641567
free(worktree_config);
15651568
return ret;
@@ -1589,7 +1592,8 @@ int config_with_options(config_fn_t fn, void *data,
15891592
*/
15901593
if (config_source && config_source->use_stdin) {
15911594
ret = git_config_from_stdin(fn, data, config_source->scope);
1592-
} else if (config_source && config_source->file) {
1595+
} else if (config_source && config_source->file &&
1596+
config_source->scope != CONFIG_SCOPE_GLOBAL) {
15931597
ret = git_config_from_file_with_options(fn, config_source->file,
15941598
data, config_source->scope,
15951599
NULL);

config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ typedef int (*config_parser_event_fn_t)(enum config_event_t type,
8787

8888
struct config_options {
8989
unsigned int respect_includes : 1;
90+
unsigned int ignore_system : 1;
91+
unsigned int ignore_global : 1;
9092
unsigned int ignore_repo : 1;
9193
unsigned int ignore_worktree : 1;
9294
unsigned int ignore_cmdline : 1;

t/t1300-config.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2372,7 +2372,7 @@ test_expect_success 'list with nonexistent global config' '
23722372
git config ${mode_prefix}list --show-scope
23732373
'
23742374

2375-
test_expect_success 'list --global with nonexistent global config' '
2375+
test_expect_failure 'list --global with nonexistent global config' '
23762376
rm -rf "$HOME"/.gitconfig "$HOME"/.config/git/config &&
23772377
test_must_fail git config ${mode_prefix}list --global --show-scope
23782378
'
@@ -2429,7 +2429,7 @@ test_expect_success 'list --global with both home and xdg' '
24292429
global file:$HOME/.gitconfig home.config=true
24302430
EOF
24312431
git config ${mode_prefix}list --global --show-scope --show-origin >output &&
2432-
! test_cmp expect output
2432+
test_cmp expect output
24332433
'
24342434

24352435
test_expect_success 'override global and system config' '
@@ -2483,7 +2483,7 @@ test_expect_success 'override global and system config' '
24832483
test_cmp expect output
24842484
'
24852485

2486-
test_expect_success 'override global and system config with missing file' '
2486+
test_expect_failure 'override global and system config with missing file' '
24872487
test_must_fail env GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=/dev/null git config ${mode_prefix}list --global &&
24882488
test_must_fail env GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=does-not-exist git config ${mode_prefix}list --system &&
24892489
GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=does-not-exist git version

t/t1306-xdg-files.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists'
7171
echo user.name=read_config >expected &&
7272
echo user.name=read_gitconfig >>expected &&
7373
git config --global --list >actual &&
74-
! test_cmp expected actual
74+
test_cmp expected actual
7575
'
7676

7777

0 commit comments

Comments
 (0)