Merge branch 'mr/show-config-scope'

"git config" learned to show in which "scope", in addition to in
which file, each config setting comes from.

* mr/show-config-scope:
  config: add '--show-scope' to print the scope of a config value
  submodule-config: add subomdule config scope
  config: teach git_config_source to remember its scope
  config: preserve scope in do_git_config_sequence
  config: clarify meaning of command line scoping
  config: split repo scope to local and worktree
  config: make scope_name non-static and rename it
  t1300: create custom config file without special characters
  t1300: fix over-indented HERE-DOCs
  config: fix typo in variable name
This commit is contained in:
Junio C Hamano 2020-02-17 13:22:17 -08:00
commit 5d55554b1d
10 changed files with 248 additions and 139 deletions

View File

@ -9,18 +9,18 @@ git-config - Get and set repository or global options
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] name [value [value_regex]] 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]
'git config' [<file-option>] [--type=<type>] --add name value 'git config' [<file-option>] [--type=<type>] --add name value
'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex] 'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex]
'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] --get name [value_regex] 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get name [value_regex]
'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] --get-all name [value_regex] 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get-all name [value_regex]
'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex] 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]
'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL 'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL
'git config' [<file-option>] --unset name [value_regex] 'git config' [<file-option>] --unset name [value_regex]
'git config' [<file-option>] --unset-all name [value_regex] 'git config' [<file-option>] --unset-all name [value_regex]
'git config' [<file-option>] --rename-section old_name new_name 'git config' [<file-option>] --rename-section old_name new_name
'git config' [<file-option>] --remove-section name 'git config' [<file-option>] --remove-section name
'git config' [<file-option>] [--show-origin] [-z|--null] [--name-only] -l | --list 'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list
'git config' [<file-option>] --get-color name [default] 'git config' [<file-option>] --get-color name [default]
'git config' [<file-option>] --get-colorbool name [stdout-is-tty] 'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
'git config' [<file-option>] -e | --edit 'git config' [<file-option>] -e | --edit
@ -222,6 +222,11 @@ Valid `<type>`'s include:
the actual origin (config file path, ref, or blob id if the actual origin (config file path, ref, or blob id if
applicable). applicable).
--show-scope::
Similar to `--show-origin` in that it augments the output of
all queried config options with the scope of that value
(local, global, system, command).
--get-colorbool name [stdout-is-tty]:: --get-colorbool name [stdout-is-tty]::
Find the color setting for `name` (e.g. `color.diff`) and output Find the color setting for `name` (e.g. `color.diff`) and output

View File

@ -29,10 +29,11 @@ static int use_worktree_config;
static struct git_config_source given_config_source; static struct git_config_source given_config_source;
static int actions, type; static int actions, type;
static char *default_value; static char *default_value;
static int end_null; static int end_nul;
static int respect_includes_opt = -1; static int respect_includes_opt = -1;
static struct config_options config_options; static struct config_options config_options;
static int show_origin; static int show_origin;
static int show_scope;
#define ACTION_GET (1<<0) #define ACTION_GET (1<<0)
#define ACTION_GET_ALL (1<<1) #define ACTION_GET_ALL (1<<1)
@ -151,10 +152,11 @@ static struct option builtin_config_options[] = {
OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH), OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE), OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
OPT_GROUP(N_("Other")), OPT_GROUP(N_("Other")),
OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")), OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")),
OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")), OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")), OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")), OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")), OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
OPT_END(), OPT_END(),
}; };
@ -178,22 +180,34 @@ static void check_argc(int argc, int min, int max)
static void show_config_origin(struct strbuf *buf) static void show_config_origin(struct strbuf *buf)
{ {
const char term = end_null ? '\0' : '\t'; const char term = end_nul ? '\0' : '\t';
strbuf_addstr(buf, current_config_origin_type()); strbuf_addstr(buf, current_config_origin_type());
strbuf_addch(buf, ':'); strbuf_addch(buf, ':');
if (end_null) if (end_nul)
strbuf_addstr(buf, current_config_name()); strbuf_addstr(buf, current_config_name());
else else
quote_c_style(current_config_name(), buf, NULL, 0); quote_c_style(current_config_name(), buf, NULL, 0);
strbuf_addch(buf, term); strbuf_addch(buf, term);
} }
static void show_config_scope(struct strbuf *buf)
{
const char term = end_nul ? '\0' : '\t';
const char *scope = config_scope_name(current_config_scope());
strbuf_addstr(buf, N_(scope));
strbuf_addch(buf, term);
}
static int show_all_config(const char *key_, const char *value_, void *cb) static int show_all_config(const char *key_, const char *value_, void *cb)
{ {
if (show_origin) { if (show_origin || show_scope) {
struct strbuf buf = STRBUF_INIT; struct strbuf buf = STRBUF_INIT;
show_config_origin(&buf); if (show_scope)
show_config_scope(&buf);
if (show_origin)
show_config_origin(&buf);
/* Use fwrite as "buf" can contain \0's if "end_null" is set. */ /* Use fwrite as "buf" can contain \0's if "end_null" is set. */
fwrite(buf.buf, 1, buf.len, stdout); fwrite(buf.buf, 1, buf.len, stdout);
strbuf_release(&buf); strbuf_release(&buf);
@ -213,6 +227,8 @@ struct strbuf_list {
static int format_config(struct strbuf *buf, const char *key_, const char *value_) static int format_config(struct strbuf *buf, const char *key_, const char *value_)
{ {
if (show_scope)
show_config_scope(buf);
if (show_origin) if (show_origin)
show_config_origin(buf); show_config_origin(buf);
if (show_keys) if (show_keys)
@ -622,6 +638,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
!strcmp(given_config_source.file, "-")) { !strcmp(given_config_source.file, "-")) {
given_config_source.file = NULL; given_config_source.file = NULL;
given_config_source.use_stdin = 1; given_config_source.use_stdin = 1;
given_config_source.scope = CONFIG_SCOPE_COMMAND;
} }
if (use_global_config) { if (use_global_config) {
@ -637,6 +654,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
*/ */
die(_("$HOME not set")); die(_("$HOME not set"));
given_config_source.scope = CONFIG_SCOPE_GLOBAL;
if (access_or_warn(user_config, R_OK, 0) && if (access_or_warn(user_config, R_OK, 0) &&
xdg_config && !access_or_warn(xdg_config, R_OK, 0)) { xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
given_config_source.file = xdg_config; given_config_source.file = xdg_config;
@ -646,11 +665,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
free(xdg_config); free(xdg_config);
} }
} }
else if (use_system_config) else if (use_system_config) {
given_config_source.file = git_etc_gitconfig(); given_config_source.file = git_etc_gitconfig();
else if (use_local_config) given_config_source.scope = CONFIG_SCOPE_SYSTEM;
} else if (use_local_config) {
given_config_source.file = git_pathdup("config"); given_config_source.file = git_pathdup("config");
else if (use_worktree_config) { given_config_source.scope = CONFIG_SCOPE_LOCAL;
} else if (use_worktree_config) {
struct worktree **worktrees = get_worktrees(0); struct worktree **worktrees = get_worktrees(0);
if (repository_format_worktree_config) if (repository_format_worktree_config)
given_config_source.file = git_pathdup("config.worktree"); given_config_source.file = git_pathdup("config.worktree");
@ -662,13 +683,18 @@ int cmd_config(int argc, const char **argv, const char *prefix)
"section in \"git help worktree\" for details")); "section in \"git help worktree\" for details"));
else else
given_config_source.file = git_pathdup("config"); given_config_source.file = git_pathdup("config");
given_config_source.scope = CONFIG_SCOPE_LOCAL;
free_worktrees(worktrees); free_worktrees(worktrees);
} else if (given_config_source.file) { } else if (given_config_source.file) {
if (!is_absolute_path(given_config_source.file) && prefix) if (!is_absolute_path(given_config_source.file) && prefix)
given_config_source.file = given_config_source.file =
prefix_filename(prefix, given_config_source.file); prefix_filename(prefix, given_config_source.file);
given_config_source.scope = CONFIG_SCOPE_COMMAND;
} else if (given_config_source.blob) {
given_config_source.scope = CONFIG_SCOPE_COMMAND;
} }
if (respect_includes_opt == -1) if (respect_includes_opt == -1)
config_options.respect_includes = !given_config_source.file; config_options.respect_includes = !given_config_source.file;
else else
@ -678,7 +704,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
config_options.git_dir = get_git_dir(); config_options.git_dir = get_git_dir();
} }
if (end_null) { if (end_nul) {
term = '\0'; term = '\0';
delim = '\n'; delim = '\n';
key_delim = '\n'; key_delim = '\n';

View File

@ -1702,6 +1702,7 @@ static int do_git_config_sequence(const struct config_options *opts,
char *xdg_config = xdg_config_home("config"); char *xdg_config = xdg_config_home("config");
char *user_config = expand_user_path("~/.gitconfig", 0); char *user_config = expand_user_path("~/.gitconfig", 0);
char *repo_config; char *repo_config;
enum config_scope prev_parsing_scope = current_parsing_scope;
if (opts->commondir) if (opts->commondir)
repo_config = mkpathdup("%s/config", opts->commondir); repo_config = mkpathdup("%s/config", opts->commondir);
@ -1724,15 +1725,12 @@ static int do_git_config_sequence(const struct config_options *opts,
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
ret += git_config_from_file(fn, user_config, data); ret += git_config_from_file(fn, user_config, data);
current_parsing_scope = CONFIG_SCOPE_REPO; current_parsing_scope = CONFIG_SCOPE_LOCAL;
if (!opts->ignore_repo && repo_config && if (!opts->ignore_repo && repo_config &&
!access_or_die(repo_config, R_OK, 0)) !access_or_die(repo_config, R_OK, 0))
ret += git_config_from_file(fn, repo_config, data); ret += git_config_from_file(fn, repo_config, data);
/* current_parsing_scope = CONFIG_SCOPE_WORKTREE;
* Note: this should have a new scope, CONFIG_SCOPE_WORKTREE.
* But let's not complicate things before it's actually needed.
*/
if (!opts->ignore_worktree && repository_format_worktree_config) { if (!opts->ignore_worktree && repository_format_worktree_config) {
char *path = git_pathdup("config.worktree"); char *path = git_pathdup("config.worktree");
if (!access_or_die(path, R_OK, 0)) if (!access_or_die(path, R_OK, 0))
@ -1740,11 +1738,11 @@ static int do_git_config_sequence(const struct config_options *opts,
free(path); free(path);
} }
current_parsing_scope = CONFIG_SCOPE_CMDLINE; current_parsing_scope = CONFIG_SCOPE_COMMAND;
if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0) if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
die(_("unable to parse command-line config")); die(_("unable to parse command-line config"));
current_parsing_scope = CONFIG_SCOPE_UNKNOWN; current_parsing_scope = prev_parsing_scope;
free(xdg_config); free(xdg_config);
free(user_config); free(user_config);
free(repo_config); free(repo_config);
@ -1765,6 +1763,9 @@ int config_with_options(config_fn_t fn, void *data,
data = &inc; data = &inc;
} }
if (config_source)
current_parsing_scope = config_source->scope;
/* /*
* If we have a specific filename, use it. Otherwise, follow the * If we have a specific filename, use it. Otherwise, follow the
* regular lookup sequence. * regular lookup sequence.
@ -3297,6 +3298,26 @@ const char *current_config_origin_type(void)
} }
} }
const char *config_scope_name(enum config_scope scope)
{
switch (scope) {
case CONFIG_SCOPE_SYSTEM:
return "system";
case CONFIG_SCOPE_GLOBAL:
return "global";
case CONFIG_SCOPE_LOCAL:
return "local";
case CONFIG_SCOPE_WORKTREE:
return "worktree";
case CONFIG_SCOPE_COMMAND:
return "command";
case CONFIG_SCOPE_SUBMODULE:
return "submodule";
default:
return "unknown";
}
}
const char *current_config_name(void) const char *current_config_name(void)
{ {
const char *name; const char *name;

View File

@ -35,10 +35,22 @@ struct object_id;
#define CONFIG_REGEX_NONE ((void *)1) #define CONFIG_REGEX_NONE ((void *)1)
enum config_scope {
CONFIG_SCOPE_UNKNOWN = 0,
CONFIG_SCOPE_SYSTEM,
CONFIG_SCOPE_GLOBAL,
CONFIG_SCOPE_LOCAL,
CONFIG_SCOPE_WORKTREE,
CONFIG_SCOPE_COMMAND,
CONFIG_SCOPE_SUBMODULE,
};
const char *config_scope_name(enum config_scope scope);
struct git_config_source { struct git_config_source {
unsigned int use_stdin:1; unsigned int use_stdin:1;
const char *file; const char *file;
const char *blob; const char *blob;
enum config_scope scope;
}; };
enum config_origin_type { enum config_origin_type {
@ -294,14 +306,6 @@ int config_error_nonbool(const char *);
int git_config_parse_parameter(const char *, config_fn_t fn, void *data); int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
enum config_scope {
CONFIG_SCOPE_UNKNOWN = 0,
CONFIG_SCOPE_SYSTEM,
CONFIG_SCOPE_GLOBAL,
CONFIG_SCOPE_REPO,
CONFIG_SCOPE_CMDLINE,
};
enum config_scope current_config_scope(void); enum config_scope current_config_scope(void);
const char *current_config_origin_type(void); const char *current_config_origin_type(void);
const char *current_config_name(void); const char *current_config_name(void);

View File

@ -369,7 +369,8 @@ static int handle_config(const char *key, const char *value, void *cb)
} }
remote = make_remote(name, namelen); remote = make_remote(name, namelen);
remote->origin = REMOTE_CONFIG; remote->origin = REMOTE_CONFIG;
if (current_config_scope() == CONFIG_SCOPE_REPO) if (current_config_scope() == CONFIG_SCOPE_LOCAL ||
current_config_scope() == CONFIG_SCOPE_WORKTREE)
remote->configured_in_repo = 1; remote->configured_in_repo = 1;
if (!strcmp(subkey, "mirror")) if (!strcmp(subkey, "mirror"))
remote->mirror = git_config_bool(key, value); remote->mirror = git_config_bool(key, value);

View File

@ -635,7 +635,9 @@ static void submodule_cache_check_init(struct repository *repo)
static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void *data) static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void *data)
{ {
if (repo->worktree) { if (repo->worktree) {
struct git_config_source config_source = { 0 }; struct git_config_source config_source = {
0, .scope = CONFIG_SCOPE_SUBMODULE
};
const struct config_options opts = { 0 }; const struct config_options opts = { 0 };
struct object_id oid; struct object_id oid;
char *file; char *file;

View File

@ -37,21 +37,6 @@
* *
*/ */
static const char *scope_name(enum config_scope scope)
{
switch (scope) {
case CONFIG_SCOPE_SYSTEM:
return "system";
case CONFIG_SCOPE_GLOBAL:
return "global";
case CONFIG_SCOPE_REPO:
return "repo";
case CONFIG_SCOPE_CMDLINE:
return "cmdline";
default:
return "unknown";
}
}
static int iterate_cb(const char *var, const char *value, void *data) static int iterate_cb(const char *var, const char *value, void *data)
{ {
static int nr; static int nr;
@ -63,7 +48,7 @@ static int iterate_cb(const char *var, const char *value, void *data)
printf("value=%s\n", value ? value : "(null)"); printf("value=%s\n", value ? value : "(null)");
printf("origin=%s\n", current_config_origin_type()); printf("origin=%s\n", current_config_origin_type());
printf("name=%s\n", current_config_name()); printf("name=%s\n", current_config_name());
printf("scope=%s\n", scope_name(current_config_scope())); printf("scope=%s\n", config_scope_name(current_config_scope()));
return 0; return 0;
} }

View File

@ -1191,47 +1191,47 @@ test_expect_success 'old-fashioned settings are case insensitive' '
test_when_finished "rm -f testConfig testConfig_expect testConfig_actual" && test_when_finished "rm -f testConfig testConfig_expect testConfig_actual" &&
cat >testConfig_actual <<-EOF && cat >testConfig_actual <<-EOF &&
[V.A] [V.A]
r = value1 r = value1
EOF EOF
q_to_tab >testConfig_expect <<-EOF && q_to_tab >testConfig_expect <<-EOF &&
[V.A] [V.A]
Qr = value2 Qr = value2
EOF EOF
git config -f testConfig_actual "v.a.r" value2 && git config -f testConfig_actual "v.a.r" value2 &&
test_cmp testConfig_expect testConfig_actual && test_cmp testConfig_expect testConfig_actual &&
cat >testConfig_actual <<-EOF && cat >testConfig_actual <<-EOF &&
[V.A] [V.A]
r = value1 r = value1
EOF EOF
q_to_tab >testConfig_expect <<-EOF && q_to_tab >testConfig_expect <<-EOF &&
[V.A] [V.A]
QR = value2 QR = value2
EOF EOF
git config -f testConfig_actual "V.a.R" value2 && git config -f testConfig_actual "V.a.R" value2 &&
test_cmp testConfig_expect testConfig_actual && test_cmp testConfig_expect testConfig_actual &&
cat >testConfig_actual <<-EOF && cat >testConfig_actual <<-EOF &&
[V.A] [V.A]
r = value1 r = value1
EOF EOF
q_to_tab >testConfig_expect <<-EOF && q_to_tab >testConfig_expect <<-EOF &&
[V.A] [V.A]
r = value1 r = value1
Qr = value2 Qr = value2
EOF EOF
git config -f testConfig_actual "V.A.r" value2 && git config -f testConfig_actual "V.A.r" value2 &&
test_cmp testConfig_expect testConfig_actual && test_cmp testConfig_expect testConfig_actual &&
cat >testConfig_actual <<-EOF && cat >testConfig_actual <<-EOF &&
[V.A] [V.A]
r = value1 r = value1
EOF EOF
q_to_tab >testConfig_expect <<-EOF && q_to_tab >testConfig_expect <<-EOF &&
[V.A] [V.A]
r = value1 r = value1
Qr = value2 Qr = value2
EOF EOF
git config -f testConfig_actual "v.A.r" value2 && git config -f testConfig_actual "v.A.r" value2 &&
test_cmp testConfig_expect testConfig_actual test_cmp testConfig_expect testConfig_actual
@ -1241,26 +1241,26 @@ test_expect_success 'setting different case sensitive subsections ' '
test_when_finished "rm -f testConfig testConfig_expect testConfig_actual" && test_when_finished "rm -f testConfig testConfig_expect testConfig_actual" &&
cat >testConfig_actual <<-EOF && cat >testConfig_actual <<-EOF &&
[V "A"] [V "A"]
R = v1 R = v1
[K "E"] [K "E"]
Y = v1 Y = v1
[a "b"] [a "b"]
c = v1 c = v1
[d "e"] [d "e"]
f = v1 f = v1
EOF EOF
q_to_tab >testConfig_expect <<-EOF && q_to_tab >testConfig_expect <<-EOF &&
[V "A"] [V "A"]
Qr = v2 Qr = v2
[K "E"] [K "E"]
Qy = v2 Qy = v2
[a "b"] [a "b"]
Qc = v2 Qc = v2
[d "e"] [d "e"]
f = v1 f = v1
[d "E"] [d "E"]
Qf = v2 Qf = v2
EOF EOF
# exact match # exact match
git config -f testConfig_actual a.b.c v2 && git config -f testConfig_actual a.b.c v2 &&
@ -1622,40 +1622,40 @@ test_expect_success 'set up --show-origin tests' '
INCLUDE_DIR="$HOME/include" && INCLUDE_DIR="$HOME/include" &&
mkdir -p "$INCLUDE_DIR" && mkdir -p "$INCLUDE_DIR" &&
cat >"$INCLUDE_DIR"/absolute.include <<-\EOF && cat >"$INCLUDE_DIR"/absolute.include <<-\EOF &&
[user] [user]
absolute = include absolute = include
EOF EOF
cat >"$INCLUDE_DIR"/relative.include <<-\EOF && cat >"$INCLUDE_DIR"/relative.include <<-\EOF &&
[user] [user]
relative = include relative = include
EOF EOF
cat >"$HOME"/.gitconfig <<-EOF && cat >"$HOME"/.gitconfig <<-EOF &&
[user] [user]
global = true global = true
override = global override = global
[include] [include]
path = "$INCLUDE_DIR/absolute.include" path = "$INCLUDE_DIR/absolute.include"
EOF EOF
cat >.git/config <<-\EOF cat >.git/config <<-\EOF
[user] [user]
local = true local = true
override = local override = local
[include] [include]
path = ../include/relative.include path = ../include/relative.include
EOF EOF
' '
test_expect_success '--show-origin with --list' ' test_expect_success '--show-origin with --list' '
cat >expect <<-EOF && cat >expect <<-EOF &&
file:$HOME/.gitconfig user.global=true file:$HOME/.gitconfig user.global=true
file:$HOME/.gitconfig user.override=global file:$HOME/.gitconfig user.override=global
file:$HOME/.gitconfig include.path=$INCLUDE_DIR/absolute.include file:$HOME/.gitconfig include.path=$INCLUDE_DIR/absolute.include
file:$INCLUDE_DIR/absolute.include user.absolute=include file:$INCLUDE_DIR/absolute.include user.absolute=include
file:.git/config user.local=true file:.git/config user.local=true
file:.git/config user.override=local file:.git/config user.override=local
file:.git/config include.path=../include/relative.include file:.git/config include.path=../include/relative.include
file:.git/../include/relative.include user.relative=include file:.git/../include/relative.include user.relative=include
command line: user.cmdline=true command line: user.cmdline=true
EOF EOF
git -c user.cmdline=true config --list --show-origin >output && git -c user.cmdline=true config --list --show-origin >output &&
test_cmp expect output test_cmp expect output
@ -1663,16 +1663,16 @@ test_expect_success '--show-origin with --list' '
test_expect_success '--show-origin with --list --null' ' test_expect_success '--show-origin with --list --null' '
cat >expect <<-EOF && cat >expect <<-EOF &&
file:$HOME/.gitconfigQuser.global file:$HOME/.gitconfigQuser.global
trueQfile:$HOME/.gitconfigQuser.override trueQfile:$HOME/.gitconfigQuser.override
globalQfile:$HOME/.gitconfigQinclude.path globalQfile:$HOME/.gitconfigQinclude.path
$INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute $INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute
includeQfile:.git/configQuser.local includeQfile:.git/configQuser.local
trueQfile:.git/configQuser.override trueQfile:.git/configQuser.override
localQfile:.git/configQinclude.path localQfile:.git/configQinclude.path
../include/relative.includeQfile:.git/../include/relative.includeQuser.relative ../include/relative.includeQfile:.git/../include/relative.includeQuser.relative
includeQcommand line:Quser.cmdline includeQcommand line:Quser.cmdline
trueQ trueQ
EOF EOF
git -c user.cmdline=true config --null --list --show-origin >output.raw && git -c user.cmdline=true config --null --list --show-origin >output.raw &&
nul_to_q <output.raw >output && nul_to_q <output.raw >output &&
@ -1684,9 +1684,9 @@ test_expect_success '--show-origin with --list --null' '
test_expect_success '--show-origin with single file' ' test_expect_success '--show-origin with single file' '
cat >expect <<-\EOF && cat >expect <<-\EOF &&
file:.git/config user.local=true file:.git/config user.local=true
file:.git/config user.override=local file:.git/config user.override=local
file:.git/config include.path=../include/relative.include file:.git/config include.path=../include/relative.include
EOF EOF
git config --local --list --show-origin >output && git config --local --list --show-origin >output &&
test_cmp expect output test_cmp expect output
@ -1694,8 +1694,8 @@ test_expect_success '--show-origin with single file' '
test_expect_success '--show-origin with --get-regexp' ' test_expect_success '--show-origin with --get-regexp' '
cat >expect <<-EOF && cat >expect <<-EOF &&
file:$HOME/.gitconfig user.global true file:$HOME/.gitconfig user.global true
file:.git/config user.local true file:.git/config user.local true
EOF EOF
git config --show-origin --get-regexp "user\.[g|l].*" >output && git config --show-origin --get-regexp "user\.[g|l].*" >output &&
test_cmp expect output test_cmp expect output
@ -1703,31 +1703,36 @@ test_expect_success '--show-origin with --get-regexp' '
test_expect_success '--show-origin getting a single key' ' test_expect_success '--show-origin getting a single key' '
cat >expect <<-\EOF && cat >expect <<-\EOF &&
file:.git/config local file:.git/config local
EOF EOF
git config --show-origin user.override >output && git config --show-origin user.override >output &&
test_cmp expect output test_cmp expect output
' '
test_expect_success 'set up custom config file' ' test_expect_success 'set up custom config file' '
CUSTOM_CONFIG_FILE="file\" (dq) and spaces.conf" && CUSTOM_CONFIG_FILE="custom.conf" &&
cat >"$CUSTOM_CONFIG_FILE" <<-\EOF cat >"$CUSTOM_CONFIG_FILE" <<-\EOF
[user] [user]
custom = true custom = true
EOF EOF
' '
test_expect_success !MINGW 'set up custom config file with special name characters' '
WEIRDLY_NAMED_FILE="file\" (dq) and spaces.conf" &&
cp "$CUSTOM_CONFIG_FILE" "$WEIRDLY_NAMED_FILE"
'
test_expect_success !MINGW '--show-origin escape special file name characters' ' test_expect_success !MINGW '--show-origin escape special file name characters' '
cat >expect <<-\EOF && cat >expect <<-\EOF &&
file:"file\" (dq) and spaces.conf" user.custom=true file:"file\" (dq) and spaces.conf" user.custom=true
EOF EOF
git config --file "$CUSTOM_CONFIG_FILE" --show-origin --list >output && git config --file "$WEIRDLY_NAMED_FILE" --show-origin --list >output &&
test_cmp expect output test_cmp expect output
' '
test_expect_success '--show-origin stdin' ' test_expect_success '--show-origin stdin' '
cat >expect <<-\EOF && cat >expect <<-\EOF &&
standard input: user.custom=true standard input: user.custom=true
EOF EOF
git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output && git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output &&
test_cmp expect output test_cmp expect output
@ -1735,11 +1740,11 @@ test_expect_success '--show-origin stdin' '
test_expect_success '--show-origin stdin with file include' ' test_expect_success '--show-origin stdin with file include' '
cat >"$INCLUDE_DIR"/stdin.include <<-EOF && cat >"$INCLUDE_DIR"/stdin.include <<-EOF &&
[user] [user]
stdin = include stdin = include
EOF EOF
cat >expect <<-EOF && cat >expect <<-EOF &&
file:$INCLUDE_DIR/stdin.include include file:$INCLUDE_DIR/stdin.include include
EOF EOF
echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" | echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" |
git config --show-origin --includes --file - user.stdin >output && git config --show-origin --includes --file - user.stdin >output &&
@ -1747,18 +1752,18 @@ test_expect_success '--show-origin stdin with file include' '
test_cmp expect output test_cmp expect output
' '
test_expect_success !MINGW '--show-origin blob' ' test_expect_success '--show-origin blob' '
blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") && blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
cat >expect <<-EOF && cat >expect <<-EOF &&
blob:$blob user.custom=true blob:$blob user.custom=true
EOF EOF
git config --blob=$blob --show-origin --list >output && git config --blob=$blob --show-origin --list >output &&
test_cmp expect output test_cmp expect output
' '
test_expect_success !MINGW '--show-origin blob ref' ' test_expect_success '--show-origin blob ref' '
cat >expect <<-\EOF && cat >expect <<-\EOF &&
blob:"master:file\" (dq) and spaces.conf" user.custom=true blob:master:custom.conf user.custom=true
EOF EOF
git add "$CUSTOM_CONFIG_FILE" && git add "$CUSTOM_CONFIG_FILE" &&
git commit -m "new config file" && git commit -m "new config file" &&
@ -1766,6 +1771,65 @@ test_expect_success !MINGW '--show-origin blob ref' '
test_cmp expect output test_cmp expect output
' '
test_expect_success '--show-scope with --list' '
cat >expect <<-EOF &&
global user.global=true
global user.override=global
global include.path=$INCLUDE_DIR/absolute.include
global user.absolute=include
local user.local=true
local user.override=local
local include.path=../include/relative.include
local user.relative=include
command user.cmdline=true
EOF
git -c user.cmdline=true config --list --show-scope >output &&
test_cmp expect output
'
test_expect_success !MINGW '--show-scope with --blob' '
blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
cat >expect <<-EOF &&
command user.custom=true
EOF
git config --blob=$blob --show-scope --list >output &&
test_cmp expect output
'
test_expect_success '--show-scope with --local' '
cat >expect <<-\EOF &&
local user.local=true
local user.override=local
local include.path=../include/relative.include
EOF
git config --local --list --show-scope >output &&
test_cmp expect output
'
test_expect_success '--show-scope getting a single value' '
cat >expect <<-\EOF &&
local true
EOF
git config --show-scope --get user.local >output &&
test_cmp expect output
'
test_expect_success '--show-scope with --show-origin' '
cat >expect <<-EOF &&
global file:$HOME/.gitconfig user.global=true
global file:$HOME/.gitconfig user.override=global
global file:$HOME/.gitconfig include.path=$INCLUDE_DIR/absolute.include
global file:$INCLUDE_DIR/absolute.include user.absolute=include
local file:.git/config user.local=true
local file:.git/config user.override=local
local file:.git/config include.path=../include/relative.include
local file:.git/../include/relative.include user.relative=include
command command line: user.cmdline=true
EOF
git -c user.cmdline=true config --list --show-origin --show-scope >output &&
test_cmp expect output
'
test_expect_success '--local requires a repo' ' test_expect_success '--local requires a repo' '
# we expect 128 to ensure that we do not simply # we expect 128 to ensure that we do not simply
# fail to find anything and return code "1" # fail to find anything and return code "1"

View File

@ -259,13 +259,13 @@ test_expect_success 'iteration shows correct origins' '
value=from-repo value=from-repo
origin=file origin=file
name=.git/config name=.git/config
scope=repo scope=local
key=foo.bar key=foo.bar
value=from-cmdline value=from-cmdline
origin=command line origin=command line
name= name=
scope=cmdline scope=command
EOF EOF
GIT_CONFIG_PARAMETERS=$cmdline_config test-tool config iterate >actual && GIT_CONFIG_PARAMETERS=$cmdline_config test-tool config iterate >actual &&
test_cmp expect actual test_cmp expect actual

View File

@ -1073,7 +1073,8 @@ static int upload_pack_config(const char *var, const char *value, void *unused)
precomposed_unicode = git_config_bool(var, value); precomposed_unicode = git_config_bool(var, value);
} }
if (current_config_scope() != CONFIG_SCOPE_REPO) { if (current_config_scope() != CONFIG_SCOPE_LOCAL &&
current_config_scope() != CONFIG_SCOPE_WORKTREE) {
if (!strcmp("uploadpack.packobjectshook", var)) if (!strcmp("uploadpack.packobjectshook", var))
return git_config_string(&pack_objects_hook, var, value); return git_config_string(&pack_objects_hook, var, value);
} }