From 1c4faa7a367baf95d9940f4e9caa1904321601c7 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 98d65bc0ad4fd4..52a904cfb1ee65 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 2c38c72f898bff2b5d552ce25f4593622f4b6c8e 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 983a0a15839acf..e8b4575758a653 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 c832293ca7ee3cb5013fe927cb2b97734e5039c4 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 e8b4575758a653..e5fb9114f6187a 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 14b0f278196ab9ab130402c2ef79adb0543655ef Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Sat, 8 Feb 2020 10:17:07 -0500 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 --- config.c | 16 ++++++++++++++++ config.h | 2 ++ t/helper/test-config.c | 17 +---------------- t/t1308-config-set.sh | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/config.c b/config.c index d75f88ca0ce31f..83bb98d65ece4f 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 "command line"; + default: + return "unknown"; + } +} + const char *current_config_name(void) { const char *name; diff --git a/config.h b/config.h index 91fd4c5e96ae79..dcb8c274d4d603 100644 --- a/config.h +++ b/config.h @@ -35,6 +35,7 @@ struct object_id; #define CONFIG_REGEX_NONE ((void *)1) + struct git_config_source { unsigned int use_stdin:1; const char *file; @@ -301,6 +302,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 214003d5b2f9bb..1e3bc7c8f4a57e 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; } diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index 7b4e1a63eba2ff..5f3e71a160974b 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 line EOF GIT_CONFIG_PARAMETERS=$cmdline_config test-tool config iterate >actual && test_cmp expect actual From 1af0237b8e76f0fd84ccac1e29d0cdc326303d7e Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Fri, 24 Jan 2020 00:21:04 +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 83bb98d65ece4f..7422bdebb15418 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 "command line"; default: diff --git a/config.h b/config.h index dcb8c274d4d603..d3ed41ef8e276d 100644 --- a/config.h +++ b/config.h @@ -299,7 +299,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 5c4666b53abc0c..593ce297ed2ad4 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 5f3e71a160974b..728a2b87ce10ca 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 a00d7ece6b9c9a..c53249cac19a33 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 64c20d0556e9e72663940da7f58a8e55a75fa9d0 Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Fri, 24 Jan 2020 00:21:05 +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 7422bdebb15418..fe1e44a43aa01f 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 "command line"; + case CONFIG_SCOPE_COMMAND: + return "command"; default: return "unknown"; } diff --git a/config.h b/config.h index d3ed41ef8e276d..b570f4ce435921 100644 --- a/config.h +++ b/config.h @@ -301,7 +301,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 728a2b87ce10ca..fba0abe429ace8 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=command line + scope=command EOF GIT_CONFIG_PARAMETERS=$cmdline_config test-tool config iterate >actual && test_cmp expect actual From f61985375c5096b863dc99ef613d864bfd010d96 Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Sat, 8 Feb 2020 10:26:40 -0500 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 --- config.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index fe1e44a43aa01f..0e2c693e7831f9 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 6c59c5cace7e1f8fc16737c2c9dcf2016397caa1 Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Sat, 8 Feb 2020 11:06:37 -0500 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 --- builtin/config.c | 16 +++++++++++++--- config.c | 3 +++ config.h | 20 ++++++++++---------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/builtin/config.c b/builtin/config.c index 52a904cfb1ee65..0a9778b714ce11 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 0e2c693e7831f9..9b6afca21016ab 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 b570f4ce435921..165cacb7daacdc 100644 --- a/config.h +++ b/config.h @@ -35,11 +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 { @@ -295,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 dd376246ec75e50b2e07f10ae7ec1e59c1e0572f Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Sat, 8 Feb 2020 11:25:04 -0500 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 --- 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 9b6afca21016ab..18a6bdd9ffe587 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 165cacb7daacdc..fe0addb0dc96cd 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 85064810b20adc..b8e97d8ae84a06 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 f76463ee90c1bc294ed503af19a47190f72778a8 Mon Sep 17 00:00:00 2001 From: Matthew Rogers Date: Sat, 8 Feb 2020 11:30:27 -0500 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 --- 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 899e92a1c93c1a..7573160f215395 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 0a9778b714ce11..ee4aef6a355576 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 e5fb9114f6187a..5464c46c185f39 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"