From 329e6ec39726594117dbc95a9f38aa145add78d3 Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Fri, 24 Jan 2020 00:21:01 +0000 Subject: [PATCH 01/10] config: fix typo in variable name In git config use of the end_null variable to determine if we should be null terminating our output. While it is correct to say a string is "null terminated" the character is actually the "nul" character, so this malapropism is being fixed. Signed-off-by: Matthew Rogers Signed-off-by: Junio C Hamano --- builtin/config.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/builtin/config.c b/builtin/config.c index 98d65bc0ad..52a904cfb1 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -29,7 +29,7 @@ static int use_worktree_config; static struct git_config_source given_config_source; static int actions, type; static char *default_value; -static int end_null; +static int end_nul; static int respect_includes_opt = -1; static struct config_options config_options; static int show_origin; @@ -151,7 +151,7 @@ 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, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE), 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, "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)")), @@ -178,11 +178,11 @@ static void check_argc(int argc, int min, int max) 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_addch(buf, ':'); - if (end_null) + if (end_nul) strbuf_addstr(buf, current_config_name()); else quote_c_style(current_config_name(), buf, NULL, 0); @@ -678,7 +678,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) config_options.git_dir = get_git_dir(); } - if (end_null) { + if (end_nul) { term = '\0'; delim = '\n'; key_delim = '\n'; From 3de7ee369b198ea3c6a096a6f499fb3c8a368a9a Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Fri, 24 Jan 2020 00:21:02 +0000 Subject: [PATCH 02/10] t1300: fix over-indented HERE-DOCs Prepare for the following patches by removing extraneous indents from HERE-DOCs used in config tests. Signed-off-by: Matthew Rogers Signed-off-by: Junio C Hamano --- t/t1300-config.sh | 168 +++++++++++++++++++++++----------------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 983a0a1583..e8b4575758 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -1191,47 +1191,47 @@ test_expect_success 'old-fashioned settings are case insensitive' ' test_when_finished "rm -f testConfig testConfig_expect testConfig_actual" && cat >testConfig_actual <<-EOF && - [V.A] - r = value1 + [V.A] + r = value1 EOF q_to_tab >testConfig_expect <<-EOF && - [V.A] - Qr = value2 + [V.A] + Qr = value2 EOF git config -f testConfig_actual "v.a.r" value2 && test_cmp testConfig_expect testConfig_actual && cat >testConfig_actual <<-EOF && - [V.A] - r = value1 + [V.A] + r = value1 EOF q_to_tab >testConfig_expect <<-EOF && - [V.A] - QR = value2 + [V.A] + QR = value2 EOF git config -f testConfig_actual "V.a.R" value2 && test_cmp testConfig_expect testConfig_actual && cat >testConfig_actual <<-EOF && - [V.A] - r = value1 + [V.A] + r = value1 EOF q_to_tab >testConfig_expect <<-EOF && - [V.A] - r = value1 - Qr = value2 + [V.A] + r = value1 + Qr = value2 EOF git config -f testConfig_actual "V.A.r" value2 && test_cmp testConfig_expect testConfig_actual && cat >testConfig_actual <<-EOF && - [V.A] - r = value1 + [V.A] + r = value1 EOF q_to_tab >testConfig_expect <<-EOF && - [V.A] - r = value1 - Qr = value2 + [V.A] + r = value1 + Qr = value2 EOF git config -f testConfig_actual "v.A.r" value2 && 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" && cat >testConfig_actual <<-EOF && - [V "A"] - R = v1 - [K "E"] - Y = v1 - [a "b"] - c = v1 - [d "e"] - f = v1 + [V "A"] + R = v1 + [K "E"] + Y = v1 + [a "b"] + c = v1 + [d "e"] + f = v1 EOF q_to_tab >testConfig_expect <<-EOF && - [V "A"] - Qr = v2 - [K "E"] - Qy = v2 - [a "b"] - Qc = v2 - [d "e"] - f = v1 - [d "E"] - Qf = v2 + [V "A"] + Qr = v2 + [K "E"] + Qy = v2 + [a "b"] + Qc = v2 + [d "e"] + f = v1 + [d "E"] + Qf = v2 EOF # exact match 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" && mkdir -p "$INCLUDE_DIR" && cat >"$INCLUDE_DIR"/absolute.include <<-\EOF && - [user] - absolute = include + [user] + absolute = include EOF cat >"$INCLUDE_DIR"/relative.include <<-\EOF && - [user] - relative = include + [user] + relative = include EOF cat >"$HOME"/.gitconfig <<-EOF && - [user] - global = true - override = global - [include] - path = "$INCLUDE_DIR/absolute.include" + [user] + global = true + override = global + [include] + path = "$INCLUDE_DIR/absolute.include" EOF cat >.git/config <<-\EOF - [user] - local = true - override = local - [include] - path = ../include/relative.include + [user] + local = true + override = local + [include] + path = ../include/relative.include EOF ' test_expect_success '--show-origin with --list' ' cat >expect <<-EOF && - file:$HOME/.gitconfig user.global=true - file:$HOME/.gitconfig user.override=global - file:$HOME/.gitconfig include.path=$INCLUDE_DIR/absolute.include - file:$INCLUDE_DIR/absolute.include user.absolute=include - file:.git/config user.local=true - file:.git/config user.override=local - file:.git/config include.path=../include/relative.include - file:.git/../include/relative.include user.relative=include - command line: user.cmdline=true + file:$HOME/.gitconfig user.global=true + file:$HOME/.gitconfig user.override=global + file:$HOME/.gitconfig include.path=$INCLUDE_DIR/absolute.include + file:$INCLUDE_DIR/absolute.include user.absolute=include + file:.git/config user.local=true + file:.git/config user.override=local + file:.git/config include.path=../include/relative.include + file:.git/../include/relative.include user.relative=include + command line: user.cmdline=true EOF git -c user.cmdline=true config --list --show-origin >output && test_cmp expect output @@ -1663,16 +1663,16 @@ test_expect_success '--show-origin with --list' ' test_expect_success '--show-origin with --list --null' ' cat >expect <<-EOF && - file:$HOME/.gitconfigQuser.global - trueQfile:$HOME/.gitconfigQuser.override - globalQfile:$HOME/.gitconfigQinclude.path - $INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute - includeQfile:.git/configQuser.local - trueQfile:.git/configQuser.override - localQfile:.git/configQinclude.path - ../include/relative.includeQfile:.git/../include/relative.includeQuser.relative - includeQcommand line:Quser.cmdline - trueQ + file:$HOME/.gitconfigQuser.global + trueQfile:$HOME/.gitconfigQuser.override + globalQfile:$HOME/.gitconfigQinclude.path + $INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute + includeQfile:.git/configQuser.local + trueQfile:.git/configQuser.override + localQfile:.git/configQinclude.path + ../include/relative.includeQfile:.git/../include/relative.includeQuser.relative + includeQcommand line:Quser.cmdline + trueQ EOF git -c user.cmdline=true config --null --list --show-origin >output.raw && nul_to_q output && @@ -1684,9 +1684,9 @@ test_expect_success '--show-origin with --list --null' ' test_expect_success '--show-origin with single file' ' cat >expect <<-\EOF && - file:.git/config user.local=true - file:.git/config user.override=local - file:.git/config include.path=../include/relative.include + file:.git/config user.local=true + file:.git/config user.override=local + file:.git/config include.path=../include/relative.include EOF git config --local --list --show-origin >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' ' cat >expect <<-EOF && - file:$HOME/.gitconfig user.global true - file:.git/config user.local true + file:$HOME/.gitconfig user.global true + file:.git/config user.local true EOF git config --show-origin --get-regexp "user\.[g|l].*" >output && test_cmp expect output @@ -1703,7 +1703,7 @@ test_expect_success '--show-origin with --get-regexp' ' test_expect_success '--show-origin getting a single key' ' cat >expect <<-\EOF && - file:.git/config local + file:.git/config local EOF git config --show-origin user.override >output && test_cmp expect output @@ -1712,14 +1712,14 @@ test_expect_success '--show-origin getting a single key' ' test_expect_success 'set up custom config file' ' CUSTOM_CONFIG_FILE="file\" (dq) and spaces.conf" && cat >"$CUSTOM_CONFIG_FILE" <<-\EOF - [user] - custom = true + [user] + custom = true EOF ' test_expect_success !MINGW '--show-origin escape special file name characters' ' cat >expect <<-\EOF && - file:"file\" (dq) and spaces.conf" user.custom=true + file:"file\" (dq) and spaces.conf" user.custom=true EOF git config --file "$CUSTOM_CONFIG_FILE" --show-origin --list >output && test_cmp expect output @@ -1727,7 +1727,7 @@ test_expect_success !MINGW '--show-origin escape special file name characters' ' test_expect_success '--show-origin stdin' ' cat >expect <<-\EOF && - standard input: user.custom=true + standard input: user.custom=true EOF git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output && test_cmp expect output @@ -1735,11 +1735,11 @@ test_expect_success '--show-origin stdin' ' test_expect_success '--show-origin stdin with file include' ' cat >"$INCLUDE_DIR"/stdin.include <<-EOF && - [user] - stdin = include + [user] + stdin = include EOF cat >expect <<-EOF && - file:$INCLUDE_DIR/stdin.include include + file:$INCLUDE_DIR/stdin.include include EOF echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" | git config --show-origin --includes --file - user.stdin >output && @@ -1750,7 +1750,7 @@ test_expect_success '--show-origin stdin with file include' ' test_expect_success !MINGW '--show-origin blob' ' blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") && cat >expect <<-EOF && - blob:$blob user.custom=true + blob:$blob user.custom=true EOF git config --blob=$blob --show-origin --list >output && test_cmp expect output @@ -1758,7 +1758,7 @@ test_expect_success !MINGW '--show-origin blob' ' test_expect_success !MINGW '--show-origin blob ref' ' cat >expect <<-\EOF && - blob:"master:file\" (dq) and spaces.conf" user.custom=true + blob:"master:file\" (dq) and spaces.conf" user.custom=true EOF git add "$CUSTOM_CONFIG_FILE" && git commit -m "new config file" && From 417be08d02ef5df72996972e105d386028c34653 Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Fri, 24 Jan 2020 00:21:03 +0000 Subject: [PATCH 03/10] t1300: create custom config file without special characters Tests that required a custom configuration file to be created previously used a file with non-alphanumeric characters including escaped double quotes. This is not really necessary for the majority of tests involving custom config files, and decreases test coverage on systems that dissallow such filenames (Windows, etc.). Create two files, one appropriate for testing quoting and one appropriate for general use. Signed-off-by: Matthew Rogers Signed-off-by: Junio C Hamano --- t/t1300-config.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/t/t1300-config.sh b/t/t1300-config.sh index e8b4575758..e5fb9114f6 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -1710,18 +1710,23 @@ test_expect_success '--show-origin getting a single key' ' ' 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 [user] custom = true 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' ' cat >expect <<-\EOF && file:"file\" (dq) and spaces.conf" user.custom=true 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 ' @@ -1747,7 +1752,7 @@ test_expect_success '--show-origin stdin with file include' ' 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") && cat >expect <<-EOF && blob:$blob user.custom=true @@ -1756,9 +1761,9 @@ test_expect_success !MINGW '--show-origin blob' ' test_cmp expect output ' -test_expect_success !MINGW '--show-origin blob ref' ' +test_expect_success '--show-origin blob ref' ' cat >expect <<-\EOF && - blob:"master:file\" (dq) and spaces.conf" user.custom=true + blob:master:custom.conf user.custom=true EOF git add "$CUSTOM_CONFIG_FILE" && git commit -m "new config file" && From a5cb4204b63b57e99ae3a44f23a7945f146fe078 Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Mon, 10 Feb 2020 00:30:53 +0000 Subject: [PATCH 04/10] config: make scope_name non-static and rename it To prepare for the upcoming --show-scope option, we require the ability to convert a config_scope enum to a string. As this was originally implemented as a static function 'scope_name()' in t/helper/test-config.c, we expose it via config.h and give it a less ambiguous name 'config_scope_name()' Signed-off-by: Matthew Rogers Signed-off-by: Junio C Hamano --- config.c | 16 ++++++++++++++++ config.h | 1 + t/helper/test-config.c | 17 +---------------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/config.c b/config.c index d75f88ca0c..a922b136e5 100644 --- a/config.c +++ b/config.c @@ -3297,6 +3297,22 @@ 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_REPO: + return "repo"; + case CONFIG_SCOPE_CMDLINE: + return "cmdline"; + default: + return "unknown"; + } +} + const char *current_config_name(void) { const char *name; diff --git a/config.h b/config.h index 91fd4c5e96..c063f33ff6 100644 --- a/config.h +++ b/config.h @@ -301,6 +301,7 @@ enum config_scope { CONFIG_SCOPE_REPO, CONFIG_SCOPE_CMDLINE, }; +const char *config_scope_name(enum config_scope scope); enum config_scope current_config_scope(void); const char *current_config_origin_type(void); diff --git a/t/helper/test-config.c b/t/helper/test-config.c index 214003d5b2..1e3bc7c8f4 100644 --- a/t/helper/test-config.c +++ b/t/helper/test-config.c @@ -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 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("origin=%s\n", current_config_origin_type()); 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; } From 6dc905d97431d683b418978819629a2626555b2e Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Mon, 10 Feb 2020 00:30:54 +0000 Subject: [PATCH 05/10] config: split repo scope to local and worktree Previously when iterating through git config variables, worktree config and local config were both considered "CONFIG_SCOPE_REPO". This was never a problem before as no one had needed to differentiate between the two cases, but future functionality may care whether or not the config options come from a worktree or from the repository's actual local config file. For example, the planned feature to add a '--show-scope' to config to allow a user to see which scope listed config options come from would confuse users if it just printed 'repo' rather than 'local' or 'worktree' as the documentation would lead them to expect. As well as the additional benefit of making the implementation look more like how the documentation describes the interface. To accomplish this we split out what was previously considered repo scope to be local and worktree. The clients of 'current_config_scope()' who cared about CONFIG_SCOPE_REPO are also modified to similarly care about CONFIG_SCOPE_WORKTREE and CONFIG_SCOPE_LOCAL to preserve previous behavior. Signed-off-by: Matthew Rogers Signed-off-by: Junio C Hamano --- config.c | 13 ++++++------- config.h | 3 ++- remote.c | 3 ++- t/t1308-config-set.sh | 2 +- upload-pack.c | 3 ++- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/config.c b/config.c index a922b136e5..f68eec766f 100644 --- a/config.c +++ b/config.c @@ -1724,15 +1724,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)) 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 && !access_or_die(repo_config, R_OK, 0)) ret += git_config_from_file(fn, repo_config, data); - /* - * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE. - * But let's not complicate things before it's actually needed. - */ + current_parsing_scope = CONFIG_SCOPE_WORKTREE; if (!opts->ignore_worktree && repository_format_worktree_config) { char *path = git_pathdup("config.worktree"); if (!access_or_die(path, R_OK, 0)) @@ -3304,8 +3301,10 @@ const char *config_scope_name(enum config_scope scope) return "system"; case CONFIG_SCOPE_GLOBAL: return "global"; - case CONFIG_SCOPE_REPO: - return "repo"; + case CONFIG_SCOPE_LOCAL: + return "local"; + case CONFIG_SCOPE_WORKTREE: + return "worktree"; case CONFIG_SCOPE_CMDLINE: return "cmdline"; default: diff --git a/config.h b/config.h index c063f33ff6..49297b5077 100644 --- a/config.h +++ b/config.h @@ -298,7 +298,8 @@ enum config_scope { CONFIG_SCOPE_UNKNOWN = 0, CONFIG_SCOPE_SYSTEM, CONFIG_SCOPE_GLOBAL, - CONFIG_SCOPE_REPO, + CONFIG_SCOPE_LOCAL, + CONFIG_SCOPE_WORKTREE, CONFIG_SCOPE_CMDLINE, }; const char *config_scope_name(enum config_scope scope); diff --git a/remote.c b/remote.c index 5c4666b53a..593ce297ed 100644 --- a/remote.c +++ b/remote.c @@ -369,7 +369,8 @@ static int handle_config(const char *key, const char *value, void *cb) } remote = make_remote(name, namelen); 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; if (!strcmp(subkey, "mirror")) remote->mirror = git_config_bool(key, value); diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index 7b4e1a63eb..90196e2862 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -259,7 +259,7 @@ test_expect_success 'iteration shows correct origins' ' value=from-repo origin=file name=.git/config - scope=repo + scope=local key=foo.bar value=from-cmdline diff --git a/upload-pack.c b/upload-pack.c index a00d7ece6b..c53249cac1 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -1073,7 +1073,8 @@ static int upload_pack_config(const char *var, const char *value, void *unused) 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)) return git_config_string(&pack_objects_hook, var, value); } From 6766e41b8aae21927625adbb8cdc87804153639a Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Mon, 10 Feb 2020 00:30:55 +0000 Subject: [PATCH 06/10] config: clarify meaning of command line scoping CONFIG_SCOPE_CMDLINE is generally used in the code to refer to config values passed in via the -c option. Options passed in using this mechanism share similar scoping characteristics with the --file and --blob options of the 'config' command, namely that they are only in use for that single invocation of git, and that they supersede the normal system/global/local hierarchy. This patch introduces CONFIG_SCOPE_COMMAND to reflect this new idea, which also makes CONFIG_SCOPE_CMDLINE redundant. Signed-off-by: Matthew Rogers Signed-off-by: Junio C Hamano --- config.c | 6 +++--- config.h | 2 +- t/t1308-config-set.sh | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config.c b/config.c index f68eec766f..fe1e44a43a 100644 --- a/config.c +++ b/config.c @@ -1737,7 +1737,7 @@ static int do_git_config_sequence(const struct config_options *opts, 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) die(_("unable to parse command-line config")); @@ -3305,8 +3305,8 @@ const char *config_scope_name(enum config_scope scope) return "local"; case CONFIG_SCOPE_WORKTREE: return "worktree"; - case CONFIG_SCOPE_CMDLINE: - return "cmdline"; + case CONFIG_SCOPE_COMMAND: + return "command"; default: return "unknown"; } diff --git a/config.h b/config.h index 49297b5077..397ba4063c 100644 --- a/config.h +++ b/config.h @@ -300,7 +300,7 @@ enum config_scope { CONFIG_SCOPE_GLOBAL, CONFIG_SCOPE_LOCAL, CONFIG_SCOPE_WORKTREE, - CONFIG_SCOPE_CMDLINE, + CONFIG_SCOPE_COMMAND, }; const char *config_scope_name(enum config_scope scope); diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index 90196e2862..fba0abe429 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -265,7 +265,7 @@ test_expect_success 'iteration shows correct origins' ' value=from-cmdline origin=command line name= - scope=cmdline + scope=command EOF GIT_CONFIG_PARAMETERS=$cmdline_config test-tool config iterate >actual && test_cmp expect actual From 5c105a842eae59a3271f5db861ef8d85de6bc2f8 Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Mon, 10 Feb 2020 00:30:56 +0000 Subject: [PATCH 07/10] config: preserve scope in do_git_config_sequence do_git_config_sequence operated under the assumption that it was correct to set current_parsing_scope to CONFIG_SCOPE_UNKNOWN as part of the cleanup it does after it finishes execution. This is incorrect, as it blows away the current_parsing_scope if do_git_config_sequence is called recursively. As such situations are rare (git config running with the '--blob' option is one example) this has yet to cause a problem, but the upcoming '--show-scope' option will experience issues in that case, lets teach do_git_config_sequence to preserve the current_parsing_scope from before it started execution. Signed-off-by: Matthew Rogers Signed-off-by: Junio C Hamano --- config.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index fe1e44a43a..0e2c693e78 100644 --- a/config.c +++ b/config.c @@ -1702,6 +1702,7 @@ static int do_git_config_sequence(const struct config_options *opts, char *xdg_config = xdg_config_home("config"); char *user_config = expand_user_path("~/.gitconfig", 0); char *repo_config; + enum config_scope prev_parsing_scope = current_parsing_scope; if (opts->commondir) repo_config = mkpathdup("%s/config", opts->commondir); @@ -1741,7 +1742,7 @@ static int do_git_config_sequence(const struct config_options *opts, if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0) die(_("unable to parse command-line config")); - current_parsing_scope = CONFIG_SCOPE_UNKNOWN; + current_parsing_scope = prev_parsing_scope; free(xdg_config); free(user_config); free(repo_config); From e37efa40e122c4408c89c437e8a375df2147feac Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Mon, 10 Feb 2020 00:30:57 +0000 Subject: [PATCH 08/10] config: teach git_config_source to remember its scope There are many situations where the scope of a config command is known beforehand, such as passing of '--local', '--file', etc. to an invocation of git config. However, this information is lost when moving from builtin/config.c to /config.c. This historically hasn't been a big deal, but to prepare for the upcoming --show-scope option we teach git_config_source to keep track of the source and the config machinery to use that information to set current_parsing_scope appropriately. Signed-off-by: Matthew Rogers Signed-off-by: Junio C Hamano --- builtin/config.c | 16 +++++++++++++--- config.c | 3 +++ config.h | 21 +++++++++++---------- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/builtin/config.c b/builtin/config.c index 52a904cfb1..0a9778b714 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -622,6 +622,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) !strcmp(given_config_source.file, "-")) { given_config_source.file = NULL; given_config_source.use_stdin = 1; + given_config_source.scope = CONFIG_SCOPE_COMMAND; } if (use_global_config) { @@ -637,6 +638,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) */ die(_("$HOME not set")); + given_config_source.scope = CONFIG_SCOPE_GLOBAL; + if (access_or_warn(user_config, R_OK, 0) && xdg_config && !access_or_warn(xdg_config, R_OK, 0)) { given_config_source.file = xdg_config; @@ -646,11 +649,13 @@ int cmd_config(int argc, const char **argv, const char *prefix) free(xdg_config); } } - else if (use_system_config) + else if (use_system_config) { 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"); - else if (use_worktree_config) { + given_config_source.scope = CONFIG_SCOPE_LOCAL; + } else if (use_worktree_config) { struct worktree **worktrees = get_worktrees(0); if (repository_format_worktree_config) given_config_source.file = git_pathdup("config.worktree"); @@ -662,13 +667,18 @@ int cmd_config(int argc, const char **argv, const char *prefix) "section in \"git help worktree\" for details")); else given_config_source.file = git_pathdup("config"); + given_config_source.scope = CONFIG_SCOPE_LOCAL; free_worktrees(worktrees); } else if (given_config_source.file) { if (!is_absolute_path(given_config_source.file) && 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) config_options.respect_includes = !given_config_source.file; else diff --git a/config.c b/config.c index 0e2c693e78..9b6afca210 100644 --- a/config.c +++ b/config.c @@ -1763,6 +1763,9 @@ int config_with_options(config_fn_t fn, void *data, data = &inc; } + if (config_source) + current_parsing_scope = config_source->scope; + /* * If we have a specific filename, use it. Otherwise, follow the * regular lookup sequence. diff --git a/config.h b/config.h index 397ba4063c..165cacb7da 100644 --- a/config.h +++ b/config.h @@ -35,10 +35,21 @@ struct object_id; #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, +}; +const char *config_scope_name(enum config_scope scope); + struct git_config_source { unsigned int use_stdin:1; const char *file; const char *blob; + enum config_scope scope; }; enum config_origin_type { @@ -294,16 +305,6 @@ int config_error_nonbool(const char *); 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_LOCAL, - CONFIG_SCOPE_WORKTREE, - CONFIG_SCOPE_COMMAND, -}; -const char *config_scope_name(enum config_scope scope); - enum config_scope current_config_scope(void); const char *current_config_origin_type(void); const char *current_config_name(void); From 9a83d088ee00dcdab171b2020ab334e369437a33 Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Mon, 10 Feb 2020 00:30:58 +0000 Subject: [PATCH 09/10] submodule-config: add subomdule config scope Before the changes to teach git_config_source to remember scope information submodule-config.c never needed to consider the question of config scope. Even though zeroing out git_config_source is still correct and preserved the previous behavior of setting the scope to CONFIG_SCOPE_UNKNOWN, it's better to be explicit about such situations by explicitly setting the scope. As none of the current config_scope enumerations make sense we create CONFIG_SCOPE_SUBMODULE to describe the situation. Signed-off-by: Matthew Rogers Signed-off-by: Junio C Hamano --- config.c | 2 ++ config.h | 1 + submodule-config.c | 4 +++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index 9b6afca210..18a6bdd9ff 100644 --- a/config.c +++ b/config.c @@ -3311,6 +3311,8 @@ const char *config_scope_name(enum config_scope scope) return "worktree"; case CONFIG_SCOPE_COMMAND: return "command"; + case CONFIG_SCOPE_SUBMODULE: + return "submodule"; default: return "unknown"; } diff --git a/config.h b/config.h index 165cacb7da..fe0addb0dc 100644 --- a/config.h +++ b/config.h @@ -42,6 +42,7 @@ enum config_scope { CONFIG_SCOPE_LOCAL, CONFIG_SCOPE_WORKTREE, CONFIG_SCOPE_COMMAND, + CONFIG_SCOPE_SUBMODULE, }; const char *config_scope_name(enum config_scope scope); diff --git a/submodule-config.c b/submodule-config.c index 85064810b2..b8e97d8ae8 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -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) { 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 }; struct object_id oid; char *file; From 145d59f48233c64cb8a9262c9f1451cc7d66b530 Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Mon, 10 Feb 2020 00:30:59 +0000 Subject: [PATCH 10/10] config: add '--show-scope' to print the scope of a config value When a user queries config values with --show-origin, often it's difficult to determine what the actual "scope" (local, global, etc.) of a given value is based on just the origin file. Teach 'git config' the '--show-scope' option to print the scope of all displayed config values. Note that we should never see anything of "submodule" scope as that is only ever used by submodule-config.c when parsing the '.gitmodules' file. Signed-off-by: Matthew Rogers Signed-off-by: Junio C Hamano --- Documentation/git-config.txt | 15 ++++++--- builtin/config.c | 20 ++++++++++-- t/t1300-config.sh | 59 ++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 899e92a1c9..7573160f21 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -9,18 +9,18 @@ git-config - Get and set repository or global options SYNOPSIS -------- [verse] -'git config' [] [--type=] [--show-origin] [-z|--null] name [value [value_regex]] +'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]] 'git config' [] [--type=] --add name value 'git config' [] [--type=] --replace-all name value [value_regex] -'git config' [] [--type=] [--show-origin] [-z|--null] --get name [value_regex] -'git config' [] [--type=] [--show-origin] [-z|--null] --get-all name [value_regex] -'git config' [] [--type=] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex] +'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] --get name [value_regex] +'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] --get-all name [value_regex] +'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value_regex] 'git config' [] [--type=] [-z|--null] --get-urlmatch name URL 'git config' [] --unset name [value_regex] 'git config' [] --unset-all name [value_regex] 'git config' [] --rename-section old_name new_name 'git config' [] --remove-section name -'git config' [] [--show-origin] [-z|--null] [--name-only] -l | --list +'git config' [] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list 'git config' [] --get-color name [default] 'git config' [] --get-colorbool name [stdout-is-tty] 'git config' [] -e | --edit @@ -222,6 +222,11 @@ Valid ``'s include: the actual origin (config file path, ref, or blob id if 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]:: Find the color setting for `name` (e.g. `color.diff`) and output diff --git a/builtin/config.c b/builtin/config.c index 0a9778b714..ee4aef6a35 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -33,6 +33,7 @@ static int end_nul; static int respect_includes_opt = -1; static struct config_options config_options; static int show_origin; +static int show_scope; #define ACTION_GET (1<<0) #define ACTION_GET_ALL (1<<1) @@ -155,6 +156,7 @@ static struct option builtin_config_options[] = { 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, "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_END(), }; @@ -189,11 +191,23 @@ static void show_config_origin(struct strbuf *buf) 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) { - if (show_origin) { + if (show_origin || show_scope) { 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. */ fwrite(buf.buf, 1, buf.len, stdout); strbuf_release(&buf); @@ -213,6 +227,8 @@ struct strbuf_list { static int format_config(struct strbuf *buf, const char *key_, const char *value_) { + if (show_scope) + show_config_scope(buf); if (show_origin) show_config_origin(buf); if (show_keys) diff --git a/t/t1300-config.sh b/t/t1300-config.sh index e5fb9114f6..5464c46c18 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -1771,6 +1771,65 @@ test_expect_success '--show-origin blob ref' ' 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' ' # we expect 128 to ensure that we do not simply # fail to find anything and return code "1"