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 <peff@peff.net> Signed-off-by: Lars Schneider <larsxschneider@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Lars Schneider committed
Feb 19, 2016 at 10:16 UTC
70bd879ab66aeee809306908e3551d50cdf06802
3 files changed
+191
-5
Documentation/git-config.txt
+11
-5
@@ -9,18 +9,18 @@ git-config - Get and set repository or global options
9
SYNOPSIS
10
--------
11
[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]]
13
'git config' [<file-option>] [type] --add name value
14
'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]
18
'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL
19
'git config' [<file-option>] --unset name [value_regex]
20
'git config' [<file-option>] --unset-all name [value_regex]
21
'git config' [<file-option>] --rename-section old_name new_name
22
'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
24
'git config' [<file-option>] --get-color name [default]
25
'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
26
'git config' [<file-option>] -e | --edit
@@ -194,6 +194,12 @@ See also <<FILES>>.
194
Output only the names of config variables for `--list` or
195
`--get-regexp`.
196
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
+
203
--get-colorbool name [stdout-is-tty]::
204
205
Find the color setting for `name` (e.g. `color.diff`) and output
builtin/config.c
+33
@@ -3,6 +3,7 @@
3
#include "color.h"
4
#include "parse-options.h"
5
#include "urlmatch.h"
6
+#include "quote.h"
7
8
static const char *const builtin_config_usage[] = {
9
N_("git config [<options>]"),
@@ -27,6 +28,7 @@ static int actions, types;
28
static const char *get_color_slot, *get_colorbool_slot;
29
static int end_null;
30
static int respect_includes = -1;
31
+static int show_origin;
32
33
#define ACTION_GET (1<<0)
34
#define ACTION_GET_ALL (1<<1)
@@ -81,6 +83,7 @@ static struct option builtin_config_options[] = {
83
OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
84
OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
85
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)")),
87
OPT_END(),
88
};
89
@@ -91,8 +94,28 @@ static void check_argc(int argc, int min, int max) {
94
usage_with_options(builtin_config_usage, builtin_config_options);
95
}
96
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
+
110
static int show_all_config(const char *key_, const char *value_, void *cb)
111
{
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
+ }
119
if (!omit_values && value_)
120
printf("%s%c%s%c", key_, delim, value_, term);
121
else
@@ -108,6 +131,8 @@ struct strbuf_list {
131
132
static int format_config(struct strbuf *buf, const char *key_, const char *value_)
133
{
134
+ if (show_origin)
135
+ show_config_origin(buf);
136
if (show_keys)
137
strbuf_addstr(buf, key_);
138
if (!omit_values) {
@@ -538,6 +563,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
563
error("--name-only is only applicable to --list or --get-regexp");
564
usage_with_options(builtin_config_usage, builtin_config_options);
565
}
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
+
574
if (actions == ACTION_LIST) {
575
check_argc(argc, 0, 0);
576
if (git_config_with_options(show_all_config, NULL,
t/t1300-repo-config.sh
+147
@@ -1209,4 +1209,151 @@ test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
1209
"die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
1210
'
1211
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
+
1359
test_done