Skip to content

Commit 70bd879

Browse files
larsxschneidergitster
authored andcommitted
config: add '--show-origin' option to print the origin of a config value
If config values are queried using 'git config' (e.g. via --get, --get-all, --get-regexp, or --list flag) then it is sometimes hard to find the configuration file where the values were defined. Teach 'git config' the '--show-origin' option to print the source configuration file for every printed value. Based-on-patch-by: Jeff King <[email protected]> Signed-off-by: Lars Schneider <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 473166b commit 70bd879

File tree

3 files changed

+191
-5
lines changed

3 files changed

+191
-5
lines changed

Documentation/git-config.txt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@ git-config - Get and set repository or global options
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git config' [<file-option>] [type] [-z|--null] name [value [value_regex]]
12+
'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]]
1313
'git config' [<file-option>] [type] --add name value
1414
'git config' [<file-option>] [type] --replace-all name value [value_regex]
15-
'git config' [<file-option>] [type] [-z|--null] --get name [value_regex]
16-
'git config' [<file-option>] [type] [-z|--null] --get-all name [value_regex]
17-
'git config' [<file-option>] [type] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]
15+
'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex]
16+
'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex]
17+
'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]
1818
'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL
1919
'git config' [<file-option>] --unset name [value_regex]
2020
'git config' [<file-option>] --unset-all name [value_regex]
2121
'git config' [<file-option>] --rename-section old_name new_name
2222
'git config' [<file-option>] --remove-section name
23-
'git config' [<file-option>] [-z|--null] [--name-only] -l | --list
23+
'git config' [<file-option>] [--show-origin] [-z|--null] [--name-only] -l | --list
2424
'git config' [<file-option>] --get-color name [default]
2525
'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
2626
'git config' [<file-option>] -e | --edit
@@ -194,6 +194,12 @@ See also <<FILES>>.
194194
Output only the names of config variables for `--list` or
195195
`--get-regexp`.
196196

197+
--show-origin::
198+
Augment the output of all queried config options with the
199+
origin type (file, standard input, blob, command line) and
200+
the actual origin (config file path, ref, or blob id if
201+
applicable).
202+
197203
--get-colorbool name [stdout-is-tty]::
198204

199205
Find the color setting for `name` (e.g. `color.diff`) and output

builtin/config.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "color.h"
44
#include "parse-options.h"
55
#include "urlmatch.h"
6+
#include "quote.h"
67

78
static const char *const builtin_config_usage[] = {
89
N_("git config [<options>]"),
@@ -27,6 +28,7 @@ static int actions, types;
2728
static const char *get_color_slot, *get_colorbool_slot;
2829
static int end_null;
2930
static int respect_includes = -1;
31+
static int show_origin;
3032

3133
#define ACTION_GET (1<<0)
3234
#define ACTION_GET_ALL (1<<1)
@@ -81,6 +83,7 @@ static struct option builtin_config_options[] = {
8183
OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
8284
OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
8385
OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
86+
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
8487
OPT_END(),
8588
};
8689

@@ -91,8 +94,28 @@ static void check_argc(int argc, int min, int max) {
9194
usage_with_options(builtin_config_usage, builtin_config_options);
9295
}
9396

97+
static void show_config_origin(struct strbuf *buf)
98+
{
99+
const char term = end_null ? '\0' : '\t';
100+
101+
strbuf_addstr(buf, current_config_origin_type());
102+
strbuf_addch(buf, ':');
103+
if (end_null)
104+
strbuf_addstr(buf, current_config_name());
105+
else
106+
quote_c_style(current_config_name(), buf, NULL, 0);
107+
strbuf_addch(buf, term);
108+
}
109+
94110
static int show_all_config(const char *key_, const char *value_, void *cb)
95111
{
112+
if (show_origin) {
113+
struct strbuf buf = STRBUF_INIT;
114+
show_config_origin(&buf);
115+
/* Use fwrite as "buf" can contain \0's if "end_null" is set. */
116+
fwrite(buf.buf, 1, buf.len, stdout);
117+
strbuf_release(&buf);
118+
}
96119
if (!omit_values && value_)
97120
printf("%s%c%s%c", key_, delim, value_, term);
98121
else
@@ -108,6 +131,8 @@ struct strbuf_list {
108131

109132
static int format_config(struct strbuf *buf, const char *key_, const char *value_)
110133
{
134+
if (show_origin)
135+
show_config_origin(buf);
111136
if (show_keys)
112137
strbuf_addstr(buf, key_);
113138
if (!omit_values) {
@@ -538,6 +563,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
538563
error("--name-only is only applicable to --list or --get-regexp");
539564
usage_with_options(builtin_config_usage, builtin_config_options);
540565
}
566+
567+
if (show_origin && !(actions &
568+
(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
569+
error("--show-origin is only applicable to --get, --get-all, "
570+
"--get-regexp, and --list.");
571+
usage_with_options(builtin_config_usage, builtin_config_options);
572+
}
573+
541574
if (actions == ACTION_LIST) {
542575
check_argc(argc, 0, 0);
543576
if (git_config_with_options(show_all_config, NULL,

t/t1300-repo-config.sh

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,4 +1209,151 @@ test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
12091209
"die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
12101210
'
12111211

1212+
test_expect_success 'set up --show-origin tests' '
1213+
INCLUDE_DIR="$HOME/include" &&
1214+
mkdir -p "$INCLUDE_DIR" &&
1215+
cat >"$INCLUDE_DIR"/absolute.include <<-\EOF &&
1216+
[user]
1217+
absolute = include
1218+
EOF
1219+
cat >"$INCLUDE_DIR"/relative.include <<-\EOF &&
1220+
[user]
1221+
relative = include
1222+
EOF
1223+
cat >"$HOME"/.gitconfig <<-EOF &&
1224+
[user]
1225+
global = true
1226+
override = global
1227+
[include]
1228+
path = "$INCLUDE_DIR/absolute.include"
1229+
EOF
1230+
cat >.git/config <<-\EOF
1231+
[user]
1232+
local = true
1233+
override = local
1234+
[include]
1235+
path = ../include/relative.include
1236+
EOF
1237+
'
1238+
1239+
test_expect_success '--show-origin with --list' '
1240+
cat >expect <<-EOF &&
1241+
file:$HOME/.gitconfig user.global=true
1242+
file:$HOME/.gitconfig user.override=global
1243+
file:$HOME/.gitconfig include.path=$INCLUDE_DIR/absolute.include
1244+
file:$INCLUDE_DIR/absolute.include user.absolute=include
1245+
file:.git/config user.local=true
1246+
file:.git/config user.override=local
1247+
file:.git/config include.path=../include/relative.include
1248+
file:.git/../include/relative.include user.relative=include
1249+
command line: user.cmdline=true
1250+
EOF
1251+
git -c user.cmdline=true config --list --show-origin >output &&
1252+
test_cmp expect output
1253+
'
1254+
1255+
test_expect_success '--show-origin with --list --null' '
1256+
cat >expect <<-EOF &&
1257+
file:$HOME/.gitconfigQuser.global
1258+
trueQfile:$HOME/.gitconfigQuser.override
1259+
globalQfile:$HOME/.gitconfigQinclude.path
1260+
$INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute
1261+
includeQfile:.git/configQuser.local
1262+
trueQfile:.git/configQuser.override
1263+
localQfile:.git/configQinclude.path
1264+
../include/relative.includeQfile:.git/../include/relative.includeQuser.relative
1265+
includeQcommand line:Quser.cmdline
1266+
trueQ
1267+
EOF
1268+
git -c user.cmdline=true config --null --list --show-origin >output.raw &&
1269+
nul_to_q <output.raw >output &&
1270+
# The here-doc above adds a newline that the --null output would not
1271+
# include. Add it here to make the two comparable.
1272+
echo >>output &&
1273+
test_cmp expect output
1274+
'
1275+
1276+
test_expect_success '--show-origin with single file' '
1277+
cat >expect <<-\EOF &&
1278+
file:.git/config user.local=true
1279+
file:.git/config user.override=local
1280+
file:.git/config include.path=../include/relative.include
1281+
EOF
1282+
git config --local --list --show-origin >output &&
1283+
test_cmp expect output
1284+
'
1285+
1286+
test_expect_success '--show-origin with --get-regexp' '
1287+
cat >expect <<-EOF &&
1288+
file:$HOME/.gitconfig user.global true
1289+
file:.git/config user.local true
1290+
EOF
1291+
git config --show-origin --get-regexp "user\.[g|l].*" >output &&
1292+
test_cmp expect output
1293+
'
1294+
1295+
test_expect_success '--show-origin getting a single key' '
1296+
cat >expect <<-\EOF &&
1297+
file:.git/config local
1298+
EOF
1299+
git config --show-origin user.override >output &&
1300+
test_cmp expect output
1301+
'
1302+
1303+
test_expect_success 'set up custom config file' '
1304+
CUSTOM_CONFIG_FILE="file\" (dq) and spaces.conf" &&
1305+
cat >"$CUSTOM_CONFIG_FILE" <<-\EOF
1306+
[user]
1307+
custom = true
1308+
EOF
1309+
'
1310+
1311+
test_expect_success '--show-origin escape special file name characters' '
1312+
cat >expect <<-\EOF &&
1313+
file:"file\" (dq) and spaces.conf" user.custom=true
1314+
EOF
1315+
git config --file "$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
1316+
test_cmp expect output
1317+
'
1318+
1319+
test_expect_success '--show-origin stdin' '
1320+
cat >expect <<-\EOF &&
1321+
standard input: user.custom=true
1322+
EOF
1323+
git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output &&
1324+
test_cmp expect output
1325+
'
1326+
1327+
test_expect_success '--show-origin stdin with file include' '
1328+
cat >"$INCLUDE_DIR"/stdin.include <<-EOF &&
1329+
[user]
1330+
stdin = include
1331+
EOF
1332+
cat >expect <<-EOF &&
1333+
file:$INCLUDE_DIR/stdin.include include
1334+
EOF
1335+
echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" \
1336+
| git config --show-origin --includes --file - user.stdin >output &&
1337+
test_cmp expect output
1338+
'
1339+
1340+
test_expect_success '--show-origin blob' '
1341+
cat >expect <<-\EOF &&
1342+
blob:a9d9f9e555b5c6f07cbe09d3f06fe3df11e09c08 user.custom=true
1343+
EOF
1344+
blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
1345+
git config --blob=$blob --show-origin --list >output &&
1346+
test_cmp expect output
1347+
'
1348+
1349+
test_expect_success '--show-origin blob ref' '
1350+
cat >expect <<-\EOF &&
1351+
blob:"master:file\" (dq) and spaces.conf" user.custom=true
1352+
EOF
1353+
git add "$CUSTOM_CONFIG_FILE" &&
1354+
git commit -m "new config file" &&
1355+
git config --blob=master:"$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
1356+
test_cmp expect output
1357+
'
1358+
12121359
test_done

0 commit comments

Comments
 (0)