Merge branch 'dw/config-global-list' into seen

"git config --list --global", unlike "git config --list", did not
consult both of the two possible per-user sources of the
configuration files, i.e. $HOME/.gitconfig and the XDG one, which
has been corrected.

* dw/config-global-list:
  config: keep bailing on unreadable global files
  config: read global scope via config_sequence
  config: test home and xdg files in `list --global`
  cleanup_path: force forward slashes on Windows
seen
Junio C Hamano 2026-01-13 11:55:23 -08:00
commit d019f53678
6 changed files with 128 additions and 18 deletions

View File

@ -803,6 +803,18 @@ static void location_options_init(struct config_location_options *opts,
}

if (opts->use_global_config) {
/*
* Since global config is sourced from more than one location,
* use `config.c#do_git_config_sequence()` with `opts->options`
* to read it. However, writing global config should point to a
* single destination, set in `opts->source.file`.
*/
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;

opts->source.file = opts->file_to_free = git_global_config();
if (!opts->source.file)
/*

View File

@ -1513,8 +1513,8 @@ 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();
@ -1539,22 +1539,46 @@ 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) {
int global_config_success_count = 0;
int nonzero_ret_on_global_config_error = scope == CONFIG_SCOPE_GLOBAL;

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);
git_global_config_paths(&user_config, &xdg_config);

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 (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 (!ret)
global_config_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 (!ret)
global_config_success_count++;
}

if (nonzero_ret_on_global_config_error &&
!global_config_success_count)
--ret;

free(xdg_config);
free(user_config);
}

if (!opts->ignore_repo && repo_config &&
!access_or_die(repo_config, R_OK, 0))
@ -1573,8 +1597,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;
@ -1604,7 +1626,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);
@ -1612,7 +1635,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) {

View File

@ -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;
unsigned int ignore_global : 1;
unsigned int ignore_repo : 1;
unsigned int ignore_worktree : 1;
unsigned int ignore_cmdline : 1;

10
path.c
View File

@ -40,13 +40,17 @@ static struct strbuf *get_pathname(void)
return sb;
}

static const char *cleanup_path(const char *path)
static char *cleanup_path(char *path)
{
/* Clean it up */
if (skip_prefix(path, "./", &path)) {
if (skip_prefix(path, "./", (const char **)&path))
while (*path == '/')
path++;
}

#ifdef GIT_WINDOWS_NATIVE
convert_slashes(path);
#endif

return path;
}


View File

@ -2362,6 +2362,71 @@ test_expect_success '--show-scope with --default' '
test_cmp expect actual
'

test_expect_success 'list with nonexistent global config' '
rm -rf "$HOME"/.gitconfig "$HOME"/.config/git/config &&
git config ${mode_prefix}list --show-scope
'

test_expect_success 'list --global with nonexistent global config' '
rm -rf "$HOME"/.gitconfig "$HOME"/.config/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 &&

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 &&

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' '
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 file:$HOME/.config/git/config xdg.config=true
global file:$HOME/.gitconfig home.config=true
EOF
git config ${mode_prefix}list --global --show-scope --show-origin >output &&
test_cmp expect output
'

test_expect_success 'override global and system config' '
test_when_finished rm -f \"\$HOME\"/.gitconfig &&
cat >"$HOME"/.gitconfig <<-EOF &&

View File

@ -68,7 +68,8 @@ test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists'
>.gitconfig &&
echo "[user]" >.gitconfig &&
echo " name = read_gitconfig" >>.gitconfig &&
echo user.name=read_gitconfig >expected &&
echo user.name=read_config >expected &&
echo user.name=read_gitconfig >>expected &&
git config --global --list >actual &&
test_cmp expected actual
'