git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2] config: add '--sources' option to print the source of a config value

From
larsxschneider@gmail.com <larsxschneider@gmail.com>
Date
Feb 10, 2016, 10:13 UTC
Message-ID
<1455099198-11515-1-git-send-email-larsxschneider@gmail.com>
From: Lars Schneider <larsxschneider@gmail.com>

If config values are queried using 'git config' (e.g. via '--list' flag or the '--get*' flags) then it is sometimes hard to find the configuration file where the values were defined.

Teach 'git config' the '--sources' 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>
---
diff to v1:
* add documention
* produce a consistent (tab-delimited) format that can be parsed
* adhere declaration-after-statement style
* prefix every source line with the source type (file, stdin, blob, cmd)
* add relative path test case
* add blob ref test case
* add "git config --local" test case (Note: I think I found a bug there that I
  plan to fix/investigate in a seperate patch. Is it ok to leave the TODO?)
* add a test case to check funny character escapes (file name with tabs)

Sebastian suggested "--show-origin" as a better option name over "--sources". I still believe "--sources" might be slightly better as I fear that users could somehow related "origin" to "remote" kind of configs. However, I am happy to change that if a majority prefers "--show-origin".

Thanks to Peff and Sebastian for the review, Lars

 Documentation/git-config.txt |  15 ++++--
 builtin/config.c             |  66 ++++++++++++++++++++++++++
 cache.h                      |   1 +
 config.c                     |   9 +++-
 t/t1300-repo-config.sh       | 109 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 194 insertions(+), 6 deletions(-)
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 2608ca7..8c4969f 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] [--sources] [-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] [--sources] [-z|--null] --get name [value_regex]
+'git config' [<file-option>] [type] [--sources] [-z|--null] --get-all name [value_regex]
+'git config' [<file-option>] [type] [--sources] [-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>] [--sources] [-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,11 @@ See also <<FILES>>.
 	Output only the names of config variables for `--list` or
 	`--get-regexp`.

+--sources::
+	Augment the output of all queried config options with the
+	source type (file, stdin, blob, cmd) and the actual source
+	(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 adc7727..78f3344 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_sources;

 #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, "sources", &show_sources, N_("show source filenames of config")),
 	OPT_END(),
 };

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

+/* output to either fp or buf; only one should be non-NULL */
+static void show_config_source(struct strbuf *buf, FILE *fp)
+{
+	char term = '\t';
+	char *prefix;
+	const char *fn = current_config_filename();
+
+	if (end_null)
+		term = '\0';
+
+	if (fn) {
+		if (given_config_source.use_stdin)
+			prefix = "stdin";
+		else if (given_config_source.blob)
+			prefix = "blob";
+		else
+			prefix = "file";
+	} else {
+		fn = "";
+		prefix = "cmd";
+	}
+
+	if (fp)
+		fprintf(fp, "%s", prefix);
+	else {
+		strbuf_addstr(buf, prefix);
+	}
+
+	if (fp)
+		fputc(term, fp);
+	else
+		strbuf_addch(buf, term);
+
+	if (!end_null)
+		quote_c_style(fn, buf, fp, 0);
+	else {
+		if (fp)
+			fprintf(fp, "%s", fn);
+		else
+			strbuf_addstr(buf, fn);
+	}
+
+	if (fp)
+		fputc(term, fp);
+	else
+		strbuf_addch(buf, term);
+}
+
 static int show_all_config(const char *key_, const char *value_, void *cb)
 {
+	if (show_sources)
+		show_config_source(NULL, stdout);
 	if (!omit_values && value_)
 		printf("%s%c%s%c", key_, delim, value_, term);
 	else
@@ -108,6 +161,8 @@ struct strbuf_list {

 static int format_config(struct strbuf *buf, const char *key_, const char *value_)
 {
+	if (show_sources)
+		show_config_source(buf, NULL);
 	if (show_keys)
 		strbuf_addstr(buf, key_);
 	if (!omit_values) {
@@ -451,6 +506,7 @@ static char *default_user_config(void)
 int cmd_config(int argc, const char **argv, const char *prefix)
 {
 	int nongit = !startup_info->have_repository;
+	int is_query_action = 0;
 	char *value;

 	given_config_source.file = getenv(CONFIG_ENVIRONMENT);
@@ -538,6 +594,16 @@ 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);
 	}
+
+	is_query_action = actions & (
+		ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST
+	);
+
+	if (show_sources && !is_query_action) {
+		error("--sources is only applicable to --list or --get-* actions");
+		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,
diff --git a/cache.h b/cache.h
index c63fcc1..c5111ea 100644
--- a/cache.h
+++ b/cache.h
@@ -1525,6 +1525,7 @@ 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_filename(void);

 struct config_include_data {
 	int depth;
diff --git a/config.c b/config.c
index 86a5eb2..204e6b7 100644
--- a/config.c
+++ b/config.c
@@ -1078,7 +1078,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, "", NULL, stdin, data);
 }

 int git_config_from_file(config_fn_t fn, const char *filename, void *data)
@@ -2385,3 +2385,10 @@ int parse_config_key(const char *var,

 	return 0;
 }
+
+const char *current_config_filename(void)
+{
+	if (cf && cf->name)
+		return cf->name;
+	return NULL;
+}
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 52678e7..d69974e 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -1201,4 +1201,113 @@ test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
 	  "die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
 '

+test_expect_success '--sources' '
+	>.git/config &&
+	>"$HOME"/.gitconfig &&
+	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
+	CUSTOM_CONFIG_FILE="file\twith\ttabs.conf" &&
+	cat >"$CUSTOM_CONFIG_FILE" <<-EOF &&
+		[user]
+			custom = true
+	EOF
+	test_config_global user.global "true" &&
+	test_config_global user.override "global" &&
+	test_config_global include.path "$INCLUDE_DIR"/absolute.include &&
+	test_config include.path ../include/relative.include &&
+	test_config user.local "true" &&
+	test_config user.override "local" &&
+
+	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	include.path=../include/relative.include
+		file	.git/../include/relative.include	user.relative=include
+		file	.git/config	user.local=true
+		file	.git/config	user.override=local
+		cmd		user.cmdline=true
+	EOF
+	git -c user.cmdline=true config --list --sources >output &&
+	test_cmp expect output &&
+
+	cat >expect <<-EOF &&
+		fileQ$HOME/.gitconfigQuser.global
+		trueQfileQ$HOME/.gitconfigQuser.override
+		globalQfileQ$HOME/.gitconfigQinclude.path
+		$INCLUDE_DIR/absolute.includeQfileQ$INCLUDE_DIR/absolute.includeQuser.absolute
+		includeQfileQ.git/configQinclude.path
+		../include/relative.includeQfileQ.git/../include/relative.includeQuser.relative
+		includeQfileQ.git/configQuser.local
+		trueQfileQ.git/configQuser.override
+		localQcmdQQuser.cmdline
+		trueQ
+	EOF
+	git -c user.cmdline=true config --null --list --sources | nul_to_q >output &&
+	echo >>output &&
+	test_cmp expect output &&
+
+	# TODO:
+	# The locally included config value is not printed below. I think
+	# this is a bug.
+	# "file	.git/../include/relative.include	user.relative=include"
+	#
+	cat >expect <<-EOF &&
+		file	.git/config	include.path=../include/relative.include
+		file	.git/config	user.local=true
+		file	.git/config	user.override=local
+		EOF
+	git config --local --list --sources >output &&
+	test_cmp expect output &&
+
+	cat >expect <<-EOF &&
+		file	$HOME/.gitconfig	user.global true
+		file	.git/config	user.local true
+	EOF
+	git config --sources --get-regexp "user\.[g|l].*" >output &&
+	test_cmp expect output &&
+
+	cat >expect <<-EOF &&
+		file	.git/config	local
+	EOF
+	git config --sources user.override >output &&
+	test_cmp expect output &&
+
+	cat >expect <<-EOF &&
+		file	"file\\\twith\\\ttabs.conf"	user.custom=true
+	EOF
+	git config --file "$CUSTOM_CONFIG_FILE" --sources --list >output &&
+	test_cmp expect output &&
+
+	cat >expect <<-EOF &&
+		stdin		user.custom=true
+	EOF
+	git config --file - --sources --list <"$CUSTOM_CONFIG_FILE" >output &&
+	test_cmp expect output &&
+
+	cat >expect <<-EOF &&
+		blob	a9d9f9e555b5c6f07cbe09d3f06fe3df11e09c08	user.custom=true
+	EOF
+	blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
+	git config --blob=$blob --sources --list >output &&
+	test_cmp expect output &&
+
+	cat >expect <<-EOF &&
+		blob	"master:file\\\twith\\\ttabs.conf"	user.custom=true
+	EOF
+	git add "$CUSTOM_CONFIG_FILE" &&
+	git commit -m "blubb" &&
+	git config --blob=master:"$CUSTOM_CONFIG_FILE" --sources --list >output &&
+	test_cmp expect output
+'
+
 test_done
--
2.5.1
Next: Ramsay Jones
Message 1 of 10 in “config: add '--sources' option to print the source of a config value”
  1. config: add '--sources' option to print the source of a config valuelarsxschneider@gmail.com, Feb 10, 2016
  2. Ramsay JonesFeb 10, 2016
  3. Sebastian SchuberthFeb 10, 2016
  4. Ramsay JonesFeb 10, 2016
  5. Jeff KingFeb 10, 2016
  6. Sebastian SchuberthFeb 10, 2016
  7. Jeff KingFeb 10, 2016
  8. Sebastian SchuberthFeb 10, 2016
  9. Jeff KingFeb 10, 2016
  10. Eric SunshineFeb 10, 2016

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.