Skip to content

Commit 33d79b9

Browse files
committed
config: read global scope via config_sequence
When both `$HOME/.gitconfig` and `$XDG_CONFIG_HOME/git/config` exist, `git config list --global` and `git config --get --global` read the home configuration file but ignore the XDG file. Bug reporters expected these commands to read both files and be consistent with the documentation and the behaviour of the unscoped `git config list` and `git config --get` commands [1][2], which read from both files (in addition to system-wide and repository-specific entries, of course). We assume each scope corresponds to a single configuration file. So, during `--global` operations, Git selects a single path and passes it to `git_config_from_file_with_options(path)`. Since global configuration comes from two files, we should read the configuration files with another method. Running `git config list --show-scope --show-origin` (without `--global`) reads both the home and XDG files. So there's existing code that respects both locations, namely `do_git_config_sequence()` which reads from all scopes. Introduce flags 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 [3] in the original bug report. Modify tests to check that both configuration files are respected during `--global` read operations and add tests to ensure we do not introduce regressions. Specifically: - The home config should take precedence over the XDG config. - `git config list` should not fail on non-existent global config. - `git config list --global` should fail when both global config files are non-existent. It should not fail if at least one of them exists. Implementation notes: - The `ignore_global` flag is not set anywhere, so the `if (!opts->ignore_global)` condition is always met. Include the flag for completeness. - 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 clarify this. [1] https://lore.kernel.org/git/CAFA9we-QLQRzJdGMMCPatmfrk1oHeiUu9msMRXXk1MLE5HRxBQ@mail.gmail.com/ [2] https://lore.kernel.org/git/CAAdFe9yhBk-WecVzCTsjQ-4Z3AZAbpP+w+B076ouM3qX6d1WAg@mail.gmail.com/ [3] https://lore.kernel.org/git/kl6ly1oze7wb.fsf@chooglen-macbookpro.roam.corp.google.com Reported-by: Jade Lovelace <lists@jade.fyi> Reported-by: Nils Fahldieck <nils@fahldieck.de> Suggested-by: Glen Choo <glencbz@gmail.com> Helped-by: Derrick Stolee <stolee@gmail.com> Helped-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Delilah Ashley Wu <delilahwu@microsoft.com>
1 parent 25af55b commit 33d79b9

5 files changed

Lines changed: 107 additions & 16 deletions

File tree

‎builtin/config.c‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,17 @@ static void location_options_init(struct config_location_options *opts,
957957
}
958958

959959
if (opts->use_global_config) {
960+
/*
961+
* Since global config is sourced from more than one location,
962+
* read it using `do_git_config_sequence()` with other scopes
963+
* ignored. However, writing global config should point to a
964+
* single destination, set in `opts->source.file`.
965+
*/
966+
opts->options.ignore_repo = 1;
967+
opts->options.ignore_cmdline = 1;
968+
opts->options.ignore_worktree = 1;
969+
opts->options.ignore_system = 1;
970+
960971
opts->source.file = opts->file_to_free = git_global_config();
961972
if (!opts->source.file)
962973
/*

‎config.c‎

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1586,26 +1586,31 @@ static int do_git_config_sequence(const struct config_options *opts,
15861586
worktree_config = NULL;
15871587
}
15881588

1589-
if (git_config_system() && system_config &&
1589+
if (!opts->ignore_system && git_config_system() && system_config &&
15901590
!access_or_die(system_config, R_OK,
15911591
opts->system_gently ? ACCESS_EACCES_OK : 0))
15921592
attempt_git_config_from_file_with_options(fn, system_config, data,
15931593
CONFIG_SCOPE_SYSTEM, NULL,
15941594
&success_count, &ret);
15951595

1596-
git_global_config_paths(&user_config, &xdg_config);
1596+
if (!opts->ignore_global) {
1597+
git_global_config_paths(&user_config, &xdg_config);
15971598

1598-
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
1599-
attempt_git_config_from_file_with_options(fn, xdg_config,
1600-
data,
1601-
CONFIG_SCOPE_GLOBAL,
1602-
NULL, &success_count, &ret);
1599+
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
1600+
attempt_git_config_from_file_with_options(fn, xdg_config,
1601+
data,
1602+
CONFIG_SCOPE_GLOBAL,
1603+
NULL, &success_count, &ret);
16031604

1604-
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
1605-
attempt_git_config_from_file_with_options(fn, user_config,
1606-
data,
1607-
CONFIG_SCOPE_GLOBAL,
1608-
NULL, &success_count, &ret);
1605+
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
1606+
attempt_git_config_from_file_with_options(fn, user_config,
1607+
data,
1608+
CONFIG_SCOPE_GLOBAL,
1609+
NULL, &success_count, &ret);
1610+
1611+
free(xdg_config);
1612+
free(user_config);
1613+
}
16091614

16101615
if (!opts->ignore_repo && repo_config &&
16111616
!access_or_die(repo_config, R_OK, 0))
@@ -1623,8 +1628,6 @@ static int do_git_config_sequence(const struct config_options *opts,
16231628
die(_("unable to parse command-line config"));
16241629

16251630
free(system_config);
1626-
free(xdg_config);
1627-
free(user_config);
16281631
free(repo_config);
16291632
free(worktree_config);
16301633

@@ -1658,7 +1661,8 @@ int config_with_options(config_fn_t fn, void *data,
16581661
*/
16591662
if (config_source && config_source->use_stdin) {
16601663
ret = git_config_from_stdin(fn, data, config_source->scope);
1661-
} else if (config_source && config_source->file) {
1664+
} else if (config_source && config_source->file &&
1665+
config_source->scope != CONFIG_SCOPE_GLOBAL) {
16621666
ret = git_config_from_file_with_options(fn, config_source->file,
16631667
data, config_source->scope,
16641668
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: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2437,6 +2437,77 @@ test_expect_success 'list --global with nonexistent global config fails' '
24372437
test_must_fail git config ${mode_prefix}list --global --show-scope
24382438
'
24392439

2440+
test_expect_success 'list and get --global with only home' '
2441+
rm -f "$HOME"/.config/git/config &&
2442+
2443+
test_when_finished rm -f \"\$HOME\"/.gitconfig &&
2444+
cat >"$HOME"/.gitconfig <<-EOF &&
2445+
[home]
2446+
config = true
2447+
EOF
2448+
2449+
cat >expect <<-EOF &&
2450+
global home.config=true
2451+
EOF
2452+
git config ${mode_prefix}list --global --show-scope >actual &&
2453+
test_cmp expect actual &&
2454+
2455+
echo true >expect &&
2456+
git config ${mode_get} --global home.config >actual &&
2457+
test_cmp expect actual
2458+
'
2459+
2460+
test_expect_success 'list and get --global with only xdg' '
2461+
rm -f "$HOME"/.gitconfig &&
2462+
2463+
test_when_finished rm -rf \"\$HOME\"/.config/git &&
2464+
mkdir -p "$HOME"/.config/git &&
2465+
cat >"$HOME"/.config/git/config <<-EOF &&
2466+
[xdg]
2467+
config = true
2468+
EOF
2469+
2470+
cat >expect <<-EOF &&
2471+
global xdg.config=true
2472+
EOF
2473+
git config ${mode_prefix}list --global --show-scope >actual &&
2474+
test_cmp expect actual &&
2475+
2476+
echo true >expect &&
2477+
git config ${mode_get} --global xdg.config >actual &&
2478+
test_cmp expect actual
2479+
'
2480+
2481+
test_expect_success 'list and get --global with both home and xdg' '
2482+
test_when_finished rm -f \"\$HOME\"/.gitconfig &&
2483+
cat >"$HOME"/.gitconfig <<-EOF &&
2484+
[home]
2485+
config = home
2486+
EOF
2487+
2488+
test_when_finished rm -rf \"\$HOME\"/.config/git &&
2489+
mkdir -p "$HOME"/.config/git &&
2490+
cat >"$HOME"/.config/git/config <<-EOF &&
2491+
[xdg]
2492+
config = xdg
2493+
EOF
2494+
2495+
cat >expect <<-EOF &&
2496+
global file:$HOME/.config/git/config xdg.config=xdg
2497+
global file:$HOME/.gitconfig home.config=home
2498+
EOF
2499+
git config ${mode_prefix}list --global --show-scope --show-origin >actual &&
2500+
test_cmp expect actual &&
2501+
2502+
echo xdg >expect &&
2503+
git config ${mode_get} --global xdg.config >actual &&
2504+
test_cmp expect actual &&
2505+
2506+
echo home >expect &&
2507+
git config ${mode_get} --global home.config >actual &&
2508+
test_cmp expect actual
2509+
'
2510+
24402511
test_expect_success 'override global and system config' '
24412512
test_when_finished rm -f \"\$HOME\"/.gitconfig &&
24422513
cat >"$HOME"/.gitconfig <<-EOF &&

‎t/t1306-xdg-files.sh‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ test_expect_success 'read with --get: xdg file exists and ~/.gitconfig exists' '
5252
echo " name = read_gitconfig" >>.gitconfig &&
5353
echo read_gitconfig >expected &&
5454
git config --get user.name >actual &&
55+
test_cmp expected actual &&
56+
git config --global --get user.name >actual &&
5557
test_cmp expected actual
5658
'
5759

@@ -68,7 +70,8 @@ test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists'
6870
>.gitconfig &&
6971
echo "[user]" >.gitconfig &&
7072
echo " name = read_gitconfig" >>.gitconfig &&
71-
echo user.name=read_gitconfig >expected &&
73+
echo user.name=read_config >expected &&
74+
echo user.name=read_gitconfig >>expected &&
7275
git config --global --list >actual &&
7376
test_cmp expected actual
7477
'

0 commit comments

Comments
 (0)