Skip to content

Commit e309303

Browse files
committed
Merge branch 'dw/config-read-both-global' into jch
The git config --global read operations have been updated to respect both $HOME/.gitconfig and $XDG_CONFIG_HOME/git/config, fixing an inconsistency where only the former was read when both configuration files are present. * dw/config-read-both-global: config: read global scope via config_sequence config: let sequence require a successful file path: use forward slashes in XDG config on Windows
2 parents c691610 + d2319a0 commit e309303

6 files changed

Lines changed: 194 additions & 31 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: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,11 +1553,27 @@ int git_config_system(void)
15531553
return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
15541554
}
15551555

1556+
static void attempt_git_config_from_file_with_options(config_fn_t fn,
1557+
const char *filename,
1558+
void *data,
1559+
enum config_scope scope,
1560+
const struct config_options *opts,
1561+
int *success_count,
1562+
int *cumulative_ret)
1563+
{
1564+
int ret = git_config_from_file_with_options(fn, filename, data,
1565+
scope, opts);
1566+
if (!ret)
1567+
(*success_count)++;
1568+
*cumulative_ret += ret;
1569+
}
1570+
15561571
static int do_git_config_sequence(const struct config_options *opts,
1557-
const struct repository *repo,
1558-
config_fn_t fn, void *data)
1572+
const struct repository *repo, config_fn_t fn,
1573+
void *data, int require_successful_config)
15591574
{
15601575
int ret = 0;
1576+
int success_count = 0;
15611577
char *system_config = git_system_config();
15621578
char *xdg_config = NULL;
15631579
char *user_config = NULL;
@@ -1580,44 +1596,54 @@ static int do_git_config_sequence(const struct config_options *opts,
15801596
worktree_config = NULL;
15811597
}
15821598

1583-
if (git_config_system() && system_config &&
1599+
if (!opts->ignore_system && git_config_system() && system_config &&
15841600
!access_or_die(system_config, R_OK,
15851601
opts->system_gently ? ACCESS_EACCES_OK : 0))
1586-
ret += git_config_from_file_with_options(fn, system_config,
1587-
data, CONFIG_SCOPE_SYSTEM,
1588-
NULL);
1602+
attempt_git_config_from_file_with_options(fn, system_config, data,
1603+
CONFIG_SCOPE_SYSTEM, NULL,
1604+
&success_count, &ret);
15891605

1590-
git_global_config_paths(&user_config, &xdg_config);
1606+
if (!opts->ignore_global) {
1607+
git_global_config_paths(&user_config, &xdg_config);
15911608

1592-
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
1593-
ret += git_config_from_file_with_options(fn, xdg_config, data,
1594-
CONFIG_SCOPE_GLOBAL, NULL);
1609+
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
1610+
attempt_git_config_from_file_with_options(fn, xdg_config,
1611+
data,
1612+
CONFIG_SCOPE_GLOBAL,
1613+
NULL, &success_count, &ret);
15951614

1596-
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
1597-
ret += git_config_from_file_with_options(fn, user_config, data,
1598-
CONFIG_SCOPE_GLOBAL, NULL);
1615+
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
1616+
attempt_git_config_from_file_with_options(fn, user_config,
1617+
data,
1618+
CONFIG_SCOPE_GLOBAL,
1619+
NULL, &success_count, &ret);
1620+
1621+
free(xdg_config);
1622+
free(user_config);
1623+
}
15991624

16001625
if (!opts->ignore_repo && repo_config &&
16011626
!access_or_die(repo_config, R_OK, 0))
1602-
ret += git_config_from_file_with_options(fn, repo_config, data,
1603-
CONFIG_SCOPE_LOCAL, NULL);
1627+
attempt_git_config_from_file_with_options(fn, repo_config, data,
1628+
CONFIG_SCOPE_LOCAL, NULL, &success_count, &ret);
16041629

16051630
if (!opts->ignore_worktree && worktree_config &&
16061631
repo && repo->repository_format_worktree_config &&
1607-
!access_or_die(worktree_config, R_OK, 0)) {
1608-
ret += git_config_from_file_with_options(fn, worktree_config, data,
1609-
CONFIG_SCOPE_WORKTREE,
1610-
NULL);
1611-
}
1632+
!access_or_die(worktree_config, R_OK, 0))
1633+
attempt_git_config_from_file_with_options(fn, worktree_config, data,
1634+
CONFIG_SCOPE_WORKTREE,
1635+
NULL, &success_count, &ret);
16121636

16131637
if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
16141638
die(_("unable to parse command-line config"));
16151639

16161640
free(system_config);
1617-
free(xdg_config);
1618-
free(user_config);
16191641
free(repo_config);
16201642
free(worktree_config);
1643+
1644+
if (require_successful_config && !success_count && !ret)
1645+
ret = -1;
1646+
16211647
return ret;
16221648
}
16231649

@@ -1645,15 +1671,17 @@ int config_with_options(config_fn_t fn, void *data,
16451671
*/
16461672
if (config_source && config_source->use_stdin) {
16471673
ret = git_config_from_stdin(fn, data, config_source->scope);
1648-
} else if (config_source && config_source->file) {
1674+
} else if (config_source && config_source->file &&
1675+
config_source->scope != CONFIG_SCOPE_GLOBAL) {
16491676
ret = git_config_from_file_with_options(fn, config_source->file,
16501677
data, config_source->scope,
16511678
NULL);
16521679
} else if (config_source && config_source->blob) {
16531680
ret = git_config_from_blob_ref(fn, repo, config_source->blob,
16541681
data, config_source->scope);
16551682
} else {
1656-
ret = do_git_config_sequence(opts, repo, fn, data);
1683+
ret = do_git_config_sequence(opts, repo, fn, data,
1684+
config_source && config_source->scope == CONFIG_SCOPE_GLOBAL);
16571685
}
16581686

16591687
if (inc.remote_urls) {

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;

path.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,19 +1544,23 @@ int looks_like_command_line_option(const char *str)
15441544

15451545
char *xdg_config_home_for(const char *subdir, const char *filename)
15461546
{
1547+
char *ret;
15471548
const char *home, *config_home;
15481549

15491550
assert(subdir);
15501551
assert(filename);
15511552
config_home = getenv("XDG_CONFIG_HOME");
15521553
if (config_home && *config_home)
1553-
return mkpathdup("%s/%s/%s", config_home, subdir, filename);
1554-
1555-
home = getenv("HOME");
1556-
if (home)
1557-
return mkpathdup("%s/.config/%s/%s", home, subdir, filename);
1554+
ret = mkpathdup("%s/%s/%s", config_home, subdir, filename);
1555+
else if ((home = getenv("HOME")))
1556+
ret = mkpathdup("%s/.config/%s/%s", home, subdir, filename);
1557+
else
1558+
return NULL;
15581559

1559-
return NULL;
1560+
#ifdef GIT_WINDOWS_NATIVE
1561+
convert_slashes(ret);
1562+
#endif
1563+
return ret;
15601564
}
15611565

15621566
char *xdg_config_home(const char *filename)

t/t1300-config.sh

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2350,6 +2350,38 @@ test_expect_success '--show-origin with --default' '
23502350
test_cmp expect actual
23512351
'
23522352

2353+
test_expect_success 'set up xdg config --show-origin tests' '
2354+
mkdir -p "$HOME"/.config/git &&
2355+
cat >"$HOME"/.config/git/config <<-EOF
2356+
[xdg]
2357+
config = true
2358+
EOF
2359+
'
2360+
2361+
test_expect_success MINGW '--show-origin converts backslashes in xdg path to forward slashes on Windows' '
2362+
backslash_home="$(echo "$HOME" | tr / \\\\)" &&
2363+
echo "file:$HOME/.config/git/config true" >expect &&
2364+
2365+
(
2366+
sane_unset XDG_CONFIG_HOME &&
2367+
HOME="$backslash_home" git config ${mode_get} --show-origin xdg.config >actual
2368+
) &&
2369+
test_cmp expect actual &&
2370+
2371+
XDG_CONFIG_HOME="$backslash_home\\.config" git config ${mode_get} --show-origin xdg.config >actual &&
2372+
test_cmp expect actual
2373+
'
2374+
2375+
test_expect_success '--show-origin with default xdg path' '
2376+
echo "file:$HOME/.config/git/config true" >expect &&
2377+
git config ${mode_get} --show-origin xdg.config >actual &&
2378+
test_cmp expect actual
2379+
'
2380+
2381+
test_expect_success 'clean up xdg config --show-origin tests' '
2382+
rm -rf "$HOME"/.config/git
2383+
'
2384+
23532385
test_expect_success '--show-scope with --list' '
23542386
cat >expect <<-EOF &&
23552387
global user.global=true
@@ -2425,6 +2457,89 @@ test_expect_success '--show-scope with --default' '
24252457
test_cmp expect actual
24262458
'
24272459

2460+
test_expect_success 'list with nonexistent global config gracefully exits' '
2461+
rm -f "$HOME"/.gitconfig "$HOME"/.config/git/config &&
2462+
git config ${mode_prefix}list &&
2463+
git config ${mode_prefix}list --show-scope
2464+
'
2465+
2466+
test_expect_success 'list --global with nonexistent global config fails' '
2467+
rm -f "$HOME"/.gitconfig "$HOME"/.config/git/config &&
2468+
test_must_fail git config ${mode_prefix}list --global &&
2469+
test_must_fail git config ${mode_prefix}list --global --show-scope
2470+
'
2471+
2472+
test_expect_success 'list and get --global with only home' '
2473+
rm -f "$HOME"/.config/git/config &&
2474+
2475+
test_when_finished rm -f \"\$HOME\"/.gitconfig &&
2476+
cat >"$HOME"/.gitconfig <<-EOF &&
2477+
[home]
2478+
config = true
2479+
EOF
2480+
2481+
cat >expect <<-EOF &&
2482+
global home.config=true
2483+
EOF
2484+
git config ${mode_prefix}list --global --show-scope >actual &&
2485+
test_cmp expect actual &&
2486+
2487+
echo true >expect &&
2488+
git config ${mode_get} --global home.config >actual &&
2489+
test_cmp expect actual
2490+
'
2491+
2492+
test_expect_success 'list and get --global with only xdg' '
2493+
rm -f "$HOME"/.gitconfig &&
2494+
2495+
test_when_finished rm -rf \"\$HOME\"/.config/git &&
2496+
mkdir -p "$HOME"/.config/git &&
2497+
cat >"$HOME"/.config/git/config <<-EOF &&
2498+
[xdg]
2499+
config = true
2500+
EOF
2501+
2502+
cat >expect <<-EOF &&
2503+
global xdg.config=true
2504+
EOF
2505+
git config ${mode_prefix}list --global --show-scope >actual &&
2506+
test_cmp expect actual &&
2507+
2508+
echo true >expect &&
2509+
git config ${mode_get} --global xdg.config >actual &&
2510+
test_cmp expect actual
2511+
'
2512+
2513+
test_expect_success 'list and get --global with both home and xdg' '
2514+
test_when_finished rm -f \"\$HOME\"/.gitconfig &&
2515+
cat >"$HOME"/.gitconfig <<-EOF &&
2516+
[home]
2517+
config = home
2518+
EOF
2519+
2520+
test_when_finished rm -rf \"\$HOME\"/.config/git &&
2521+
mkdir -p "$HOME"/.config/git &&
2522+
cat >"$HOME"/.config/git/config <<-EOF &&
2523+
[xdg]
2524+
config = xdg
2525+
EOF
2526+
2527+
cat >expect <<-EOF &&
2528+
global file:$HOME/.config/git/config xdg.config=xdg
2529+
global file:$HOME/.gitconfig home.config=home
2530+
EOF
2531+
git config ${mode_prefix}list --global --show-scope --show-origin >actual &&
2532+
test_cmp expect actual &&
2533+
2534+
echo xdg >expect &&
2535+
git config ${mode_get} --global xdg.config >actual &&
2536+
test_cmp expect actual &&
2537+
2538+
echo home >expect &&
2539+
git config ${mode_get} --global home.config >actual &&
2540+
test_cmp expect actual
2541+
'
2542+
24282543
test_expect_success 'override global and system config' '
24292544
test_when_finished rm -f \"\$HOME\"/.gitconfig &&
24302545
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)