Skip to content

Commit eb3a952

Browse files
delilahwgitster
authored andcommitted
config: read global scope via config_sequence
The output of `git config list --global` should include both the home (`$HOME/.gitconfig`) and XDG (`$XDG_CONFIG_HOME/git/config`) configs, but it only reads from the former. We assumed each config scope corresponds to a single config file. Under 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. Because the global scope includes two files, we should read the configs via another method. The output of `git config list --show-scope --show-origin` (without `--global`) 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 to make it possible to ignore all but the global scope (i.e. ignore system, local, worktree, and cmdline). Then, reuse the function to read only the global scope when `--global` is specified. This was the suggested solution in the bug report: https://lore.kernel.org/git/[email protected]. Then, modify the tests to check that `git config list --global` includes both home and XDG configs. This patch introduces a regression. If both global config files are unreadable, then `git config list --global` should exit non-zero. This is no longer the case, so mark the corresponding test as a "TODO known breakage" and address the issue in the next patch, config: keep bailing on unreadable global files. Implementation notes: 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. 2. I've 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. 3. Keep populating `opts->source.file` in `builtin/config.c` because it is used as the destination config 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. Reported-by: Jade Lovelace <[email protected]> Suggested-by: Glen Choo <[email protected]> Helped-by: Derrick Stolee <[email protected]> Signed-off-by: Delilah Ashley Wu <[email protected]> Reviewed-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent eea0169 commit eb3a952

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
@@ -776,6 +776,18 @@ static void location_options_init(struct config_location_options *opts,
776776
}
777777

778778
if (opts->use_global_config) {
779+
/*
780+
* Since global config is sourced from more than one location,
781+
* use `config.c#do_git_config_sequence()` with `opts->options`
782+
* to read it. However, writing global config should point to a
783+
* single destination, set in `opts->source.file`.
784+
*/
785+
opts->options.ignore_repo = 1;
786+
opts->options.ignore_cmdline= 1;
787+
opts->options.ignore_worktree = 1;
788+
opts->options.ignore_system = 1;
789+
opts->source.scope = CONFIG_SCOPE_GLOBAL;
790+
779791
opts->source.file = opts->file_to_free = git_global_config();
780792
if (!opts->source.file)
781793
/*

config.c

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

1541-
if (git_config_system() && system_config &&
1541+
if (!opts->ignore_system && git_config_system() && system_config &&
15421542
!access_or_die(system_config, R_OK,
15431543
opts->system_gently ? ACCESS_EACCES_OK : 0))
15441544
ret += git_config_from_file_with_options(fn, system_config,
15451545
data, CONFIG_SCOPE_SYSTEM,
15461546
NULL);
15471547

1548-
git_global_config_paths(&user_config, &xdg_config);
1548+
if (!opts->ignore_global) {
1549+
git_global_config_paths(&user_config, &xdg_config);
1550+
1551+
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
1552+
ret += git_config_from_file_with_options(fn, xdg_config, data,
1553+
CONFIG_SCOPE_GLOBAL, NULL);
15491554

1550-
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
1551-
ret += git_config_from_file_with_options(fn, xdg_config, data,
1552-
CONFIG_SCOPE_GLOBAL, NULL);
1555+
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
1556+
ret += git_config_from_file_with_options(fn, user_config, data,
1557+
CONFIG_SCOPE_GLOBAL, NULL);
15531558

1554-
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
1555-
ret += git_config_from_file_with_options(fn, user_config, data,
1556-
CONFIG_SCOPE_GLOBAL, NULL);
1559+
free(xdg_config);
1560+
free(user_config);
1561+
}
15571562

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

15741579
free(system_config);
1575-
free(xdg_config);
1576-
free(user_config);
15771580
free(repo_config);
15781581
free(worktree_config);
15791582
return ret;
@@ -1603,7 +1606,8 @@ int config_with_options(config_fn_t fn, void *data,
16031606
*/
16041607
if (config_source && config_source->use_stdin) {
16051608
ret = git_config_from_stdin(fn, data, config_source->scope);
1606-
} else if (config_source && config_source->file) {
1609+
} else if (config_source && config_source->file &&
1610+
config_source->scope != CONFIG_SCOPE_GLOBAL) {
16071611
ret = git_config_from_file_with_options(fn, config_source->file,
16081612
data, config_source->scope,
16091613
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
@@ -2367,7 +2367,7 @@ test_expect_success 'list with nonexistent global config' '
23672367
git config ${mode_prefix}list --show-scope
23682368
'
23692369

2370-
test_expect_success 'list --global with nonexistent global config' '
2370+
test_expect_failure 'list --global with nonexistent global config' '
23712371
rm -rf "$HOME"/.gitconfig "$HOME"/.config/git/config &&
23722372
test_must_fail git config ${mode_prefix}list --global --show-scope
23732373
'
@@ -2424,7 +2424,7 @@ test_expect_success 'list --global with both home and xdg' '
24242424
global file:$HOME/.gitconfig home.config=true
24252425
EOF
24262426
git config ${mode_prefix}list --global --show-scope --show-origin >output &&
2427-
! test_cmp expect output
2427+
test_cmp expect output
24282428
'
24292429

24302430
test_expect_success 'override global and system config' '
@@ -2478,7 +2478,7 @@ test_expect_success 'override global and system config' '
24782478
test_cmp expect output
24792479
'
24802480

2481-
test_expect_success 'override global and system config with missing file' '
2481+
test_expect_failure 'override global and system config with missing file' '
24822482
test_must_fail env GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=/dev/null git config ${mode_prefix}list --global &&
24832483
test_must_fail env GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=does-not-exist git config ${mode_prefix}list --system &&
24842484
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)