diff options
-rw-r--r-- | Documentation/git-config.txt | 16 | ||||
-rw-r--r-- | builtin/config.c | 33 | ||||
-rw-r--r-- | cache.h | 6 | ||||
-rw-r--r-- | config.c | 36 | ||||
-rw-r--r-- | submodule-config.c | 4 | ||||
-rwxr-xr-x | t/t1300-repo-config.sh | 161 | ||||
-rwxr-xr-x | t/t1308-config-set.sh | 4 | ||||
-rwxr-xr-x | t/t7008-grep-binary.sh | 3 |
8 files changed, 237 insertions, 26 deletions
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 242fa5d1bb..153b2d89b5 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' [<file-option>] [type] [-z|--null] name [value [value_regex]] +'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]] 'git config' [<file-option>] [type] --add name value 'git config' [<file-option>] [type] --replace-all name value [value_regex] -'git config' [<file-option>] [type] [-z|--null] --get name [value_regex] -'git config' [<file-option>] [type] [-z|--null] --get-all name [value_regex] -'git config' [<file-option>] [type] [-z|--null] [--name-only] --get-regexp name_regex [value_regex] +'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex] +'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex] +'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex] 'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL 'git config' [<file-option>] --unset 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>] --remove-section name -'git config' [<file-option>] [-z|--null] [--name-only] -l | --list +'git config' [<file-option>] [--show-origin] [-z|--null] [--name-only] -l | --list 'git config' [<file-option>] --get-color name [default] 'git config' [<file-option>] --get-colorbool name [stdout-is-tty] 'git config' [<file-option>] -e | --edit @@ -194,6 +194,12 @@ See also <<FILES>>. Output only the names of config variables for `--list` or `--get-regexp`. +--show-origin:: + Augment the output of all queried config options with the + origin type (file, standard input, blob, command line) and + the actual origin (config file path, ref, or blob id if + applicable). + --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 adc772786a..0f1422006f 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -3,6 +3,7 @@ #include "color.h" #include "parse-options.h" #include "urlmatch.h" +#include "quote.h" static const char *const builtin_config_usage[] = { N_("git config [<options>]"), @@ -27,6 +28,7 @@ static int actions, types; static const char *get_color_slot, *get_colorbool_slot; static int end_null; static int respect_includes = -1; +static int show_origin; #define ACTION_GET (1<<0) #define ACTION_GET_ALL (1<<1) @@ -81,6 +83,7 @@ static struct option builtin_config_options[] = { OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")), OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")), OPT_BOOL(0, "includes", &respect_includes, 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_END(), }; @@ -91,8 +94,28 @@ static void check_argc(int argc, int min, int max) { usage_with_options(builtin_config_usage, builtin_config_options); } +static void show_config_origin(struct strbuf *buf) +{ + const char term = end_null ? '\0' : '\t'; + + strbuf_addstr(buf, current_config_origin_type()); + strbuf_addch(buf, ':'); + if (end_null) + strbuf_addstr(buf, current_config_name()); + else + quote_c_style(current_config_name(), buf, NULL, 0); + strbuf_addch(buf, term); +} + static int show_all_config(const char *key_, const char *value_, void *cb) { + if (show_origin) { + struct strbuf buf = STRBUF_INIT; + 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); + } if (!omit_values && value_) printf("%s%c%s%c", key_, delim, value_, term); else @@ -108,6 +131,8 @@ struct strbuf_list { static int format_config(struct strbuf *buf, const char *key_, const char *value_) { + if (show_origin) + show_config_origin(buf); if (show_keys) strbuf_addstr(buf, key_); if (!omit_values) { @@ -538,6 +563,14 @@ int cmd_config(int argc, const char **argv, const char *prefix) error("--name-only is only applicable to --list or --get-regexp"); usage_with_options(builtin_config_usage, builtin_config_options); } + + if (show_origin && !(actions & + (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) { + error("--show-origin is only applicable to --get, --get-all, " + "--get-regexp, and --list."); + usage_with_options(builtin_config_usage, builtin_config_options); + } + if (actions == ACTION_LIST) { check_argc(argc, 0, 0); if (git_config_with_options(show_all_config, NULL, @@ -1508,8 +1508,8 @@ struct git_config_source { typedef int (*config_fn_t)(const char *, const char *, void *); extern int git_default_config(const char *, const char *, void *); extern int git_config_from_file(config_fn_t fn, const char *, void *); -extern int git_config_from_buf(config_fn_t fn, const char *name, - const char *buf, size_t len, void *data); +extern int git_config_from_mem(config_fn_t fn, const char *origin_type, + const char *name, const char *buf, size_t len, void *data); extern void git_config_push_parameter(const char *text); extern int git_config_from_parameters(config_fn_t fn, void *data); extern void git_config(config_fn_t fn, void *); @@ -1548,6 +1548,8 @@ extern const char *get_log_output_encoding(void); extern const char *get_commit_output_encoding(void); extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data); +extern const char *current_config_origin_type(void); +extern const char *current_config_name(void); struct config_include_data { int depth; @@ -24,6 +24,7 @@ struct config_source { size_t pos; } buf; } u; + const char *origin_type; const char *name; const char *path; int die_on_error; @@ -471,9 +472,9 @@ static int git_parse_source(config_fn_t fn, void *data) break; } if (cf->die_on_error) - die(_("bad config file line %d in %s"), cf->linenr, cf->name); + die(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name); else - return error(_("bad config file line %d in %s"), cf->linenr, cf->name); + return error(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name); } static int parse_unit_factor(const char *end, uintmax_t *val) @@ -588,9 +589,9 @@ static void die_bad_number(const char *name, const char *value) if (!value) value = ""; - if (cf && cf->name) - die(_("bad numeric config value '%s' for '%s' in %s: %s"), - value, name, cf->name, reason); + if (cf && cf->origin_type && cf->name) + die(_("bad numeric config value '%s' for '%s' in %s %s: %s"), + value, name, cf->origin_type, cf->name, reason); die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason); } @@ -1061,11 +1062,13 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data) } static int do_config_from_file(config_fn_t fn, - const char *name, const char *path, FILE *f, void *data) + const char *origin_type, const char *name, const char *path, FILE *f, + void *data) { struct config_source top; top.u.file = f; + top.origin_type = origin_type; top.name = name; top.path = path; top.die_on_error = 1; @@ -1078,7 +1081,7 @@ static int do_config_from_file(config_fn_t fn, static int git_config_from_stdin(config_fn_t fn, void *data) { - return do_config_from_file(fn, "<stdin>", NULL, stdin, data); + return do_config_from_file(fn, "standard input", "", NULL, stdin, data); } int git_config_from_file(config_fn_t fn, const char *filename, void *data) @@ -1089,21 +1092,22 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data) f = fopen(filename, "r"); if (f) { flockfile(f); - ret = do_config_from_file(fn, filename, filename, f, data); + ret = do_config_from_file(fn, "file", filename, filename, f, data); funlockfile(f); fclose(f); } return ret; } -int git_config_from_buf(config_fn_t fn, const char *name, const char *buf, - size_t len, void *data) +int git_config_from_mem(config_fn_t fn, const char *origin_type, + const char *name, const char *buf, size_t len, void *data) { struct config_source top; top.u.buf.buf = buf; top.u.buf.len = len; top.u.buf.pos = 0; + top.origin_type = origin_type; top.name = name; top.path = NULL; top.die_on_error = 0; @@ -1132,7 +1136,7 @@ static int git_config_from_blob_sha1(config_fn_t fn, return error("reference '%s' does not point to a blob", name); } - ret = git_config_from_buf(fn, name, buf, size, data); + ret = git_config_from_mem(fn, "blob", name, buf, size, data); free(buf); return ret; @@ -2407,3 +2411,13 @@ int parse_config_key(const char *var, return 0; } + +const char *current_config_origin_type(void) +{ + return cf && cf->origin_type ? cf->origin_type : "command line"; +} + +const char *current_config_name(void) +{ + return cf && cf->name ? cf->name : ""; +} diff --git a/submodule-config.c b/submodule-config.c index fe8ceabf30..92502b594d 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -427,8 +427,8 @@ static const struct submodule *config_from(struct submodule_cache *cache, parameter.commit_sha1 = commit_sha1; parameter.gitmodules_sha1 = sha1; parameter.overwrite = 0; - git_config_from_buf(parse_config, rev.buf, config, config_size, - ¶meter); + git_config_from_mem(parse_config, "submodule-blob", rev.buf, + config, config_size, ¶meter); free(config); switch (lookup_type) { diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 52678e7d0a..8867ce10f8 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -700,12 +700,18 @@ test_expect_success 'invalid unit' ' git config aninvalid.unit >actual && test_cmp expect actual && cat >expect <<-\EOF && - fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in .git/config: invalid unit + fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in file .git/config: invalid unit EOF test_must_fail git config --int --get aninvalid.unit 2>actual && test_i18ncmp expect actual ' +test_expect_success 'invalid stdin config' ' + echo "fatal: bad config line 1 in standard input " >expect && + echo "[broken" | test_must_fail git config --list --file - >output 2>&1 && + test_cmp expect output +' + cat > expect << EOF true false @@ -957,13 +963,15 @@ Qsection.sub=section.val4 Qsection.sub=section.val5Q EOF test_expect_success '--null --list' ' - git config --null --list | nul_to_q >result && + git config --null --list >result.raw && + nul_to_q <result.raw >result && echo >>result && test_cmp expect result ' test_expect_success '--null --get-regexp' ' - git config --null --get-regexp "val[0-9]" | nul_to_q >result && + git config --null --get-regexp "val[0-9]" >result.raw && + nul_to_q <result.raw >result && echo >>result && test_cmp expect result ' @@ -1201,4 +1209,151 @@ test_expect_success POSIXPERM,PERL 'preserves existing permissions' ' "die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600" ' +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 + EOF + cat >"$INCLUDE_DIR"/relative.include <<-\EOF && + [user] + relative = include + EOF + cat >"$HOME"/.gitconfig <<-EOF && + [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 + 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 + EOF + git -c user.cmdline=true config --list --show-origin >output && + test_cmp expect output +' + +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 + EOF + git -c user.cmdline=true config --null --list --show-origin >output.raw && + nul_to_q <output.raw >output && + # The here-doc above adds a newline that the --null output would not + # include. Add it here to make the two comparable. + echo >>output && + test_cmp expect output +' + +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 + EOF + git config --local --list --show-origin >output && + test_cmp expect output +' + +test_expect_success '--show-origin with --get-regexp' ' + cat >expect <<-EOF && + 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 +' + +test_expect_success '--show-origin getting a single key' ' + cat >expect <<-\EOF && + file:.git/config local + EOF + git config --show-origin user.override >output && + test_cmp expect output +' + +test_expect_success 'set up custom config file' ' + CUSTOM_CONFIG_FILE="file\" (dq) and spaces.conf" && + cat >"$CUSTOM_CONFIG_FILE" <<-\EOF + [user] + custom = true + EOF +' + +test_expect_success '--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 && + test_cmp expect output +' + +test_expect_success '--show-origin stdin' ' + cat >expect <<-\EOF && + standard input: user.custom=true + EOF + git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output && + test_cmp expect output +' + +test_expect_success '--show-origin stdin with file include' ' + cat >"$INCLUDE_DIR"/stdin.include <<-EOF && + [user] + stdin = include + EOF + cat >expect <<-EOF && + file:$INCLUDE_DIR/stdin.include include + EOF + echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" \ + | git config --show-origin --includes --file - user.stdin >output && + test_cmp expect output +' + +test_expect_success '--show-origin blob' ' + cat >expect <<-\EOF && + blob:a9d9f9e555b5c6f07cbe09d3f06fe3df11e09c08 user.custom=true + EOF + blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") && + git config --blob=$blob --show-origin --list >output && + test_cmp expect output +' + +test_expect_success '--show-origin blob ref' ' + cat >expect <<-\EOF && + blob:"master:file\" (dq) and spaces.conf" user.custom=true + EOF + git add "$CUSTOM_CONFIG_FILE" && + git commit -m "new config file" && + git config --blob=master:"$CUSTOM_CONFIG_FILE" --show-origin --list >output && + test_cmp expect output +' + test_done diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index 91235b76ba..82f82a16d9 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -195,14 +195,14 @@ test_expect_success 'proper error on error in default config files' ' cp .git/config .git/config.old && test_when_finished "mv .git/config.old .git/config" && echo "[" >>.git/config && - echo "fatal: bad config file line 34 in .git/config" >expect && + echo "fatal: bad config line 34 in file .git/config" >expect && test_expect_code 128 test-config get_value foo.bar 2>actual && test_cmp expect actual ' test_expect_success 'proper error on error in custom config files' ' echo "[" >>syntax-error && - echo "fatal: bad config file line 1 in syntax-error" >expect && + echo "fatal: bad config line 1 in file syntax-error" >expect && test_expect_code 128 test-config configset_get_value foo.bar syntax-error 2>actual && test_cmp expect actual ' diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh index b146406e9c..9c9c378119 100755 --- a/t/t7008-grep-binary.sh +++ b/t/t7008-grep-binary.sh @@ -141,7 +141,8 @@ test_expect_success 'grep respects not-binary diff attribute' ' test_cmp expect actual && echo "b diff" >.gitattributes && echo "b:binQary" >expect && - git grep bin b | nul_to_q >actual && + git grep bin b >actual.raw && + nul_to_q <actual.raw >actual && test_cmp expect actual ' |