From 99febf0858fde149009cff7d031f229eff8a92e1 Mon Sep 17 00:00:00 2001
From: Alex Riesen <raa.lkml@gmail.com>
Date: Fri, 19 Mar 2010 15:19:24 +0100
Subject: [PATCH] Allow passing of configuration parameters in the command line

The values passed this way will override whatever is defined
in the config files.

Signed-off-by: Alex Riesen <raa.lkml@gmail.com>
---
 Documentation/git.txt  |    7 +++++
 builtin/config.c       |    6 +++-
 cache.h                |    2 +
 config.c               |   71 ++++++++++++++++++++++++++++++++++++++++++++++++
 git.c                  |    9 ++++++
 t/t1300-repo-config.sh |    7 +++++
 6 files changed, 101 insertions(+), 1 deletions(-)

diff --git a/Documentation/git.txt b/Documentation/git.txt
index 35c0c79..755fa4d 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -12,6 +12,7 @@ SYNOPSIS
 'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
     [-p|--paginate|--no-pager] [--no-replace-objects]
     [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
+    [-c name=value]
     [--help] COMMAND [ARGS]
 
 DESCRIPTION
@@ -219,6 +220,12 @@ displayed. See linkgit:git-help[1] for more information,
 because `git --help ...` is converted internally into `git
 help ...`.
 
+-c <name>=<value>::
+	Pass a configuration parameter to the command. The value
+	given will override values from configuration files.
+	The <name> is expected in the same format as listed by
+	'git config' (subkeys separated by dots).
+
 --exec-path::
 	Path to wherever your core git programs are installed.
 	This can also be controlled by setting the GIT_EXEC_PATH
diff --git a/builtin/config.c b/builtin/config.c
index 4bc46b1..f3d1660 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -197,7 +197,11 @@ static int get_value(const char *key_, const char *regex_)
 		git_config_from_file(show_config, system_wide, NULL);
 	if (do_all && global)
 		git_config_from_file(show_config, global, NULL);
-	git_config_from_file(show_config, local, NULL);
+	if (do_all)
+		git_config_from_file(show_config, local, NULL);
+	git_config_from_parameters(show_config, NULL);
+	if (!do_all && !seen)
+		git_config_from_file(show_config, local, NULL);
 	if (!do_all && !seen && global)
 		git_config_from_file(show_config, global, NULL);
 	if (!do_all && !seen && system_wide)
diff --git a/cache.h b/cache.h
index 89f6a40..4a0c75d 100644
--- a/cache.h
+++ b/cache.h
@@ -932,6 +932,8 @@ extern int update_server_info(int);
 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_parse_parameter(const char *text);
+extern int git_config_from_parameters();
 extern int git_config(config_fn_t fn, void *);
 extern int git_parse_ulong(const char *, unsigned long *);
 extern int git_config_int(const char *, const char *);
diff --git a/config.c b/config.c
index 6963fbe..2376ae9 100644
--- a/config.c
+++ b/config.c
@@ -18,6 +18,62 @@ static int zlib_compression_seen;
 
 const char *config_exclusive_filename = NULL;
 
+struct config_item
+{
+	struct config_item *next;
+	char *value;
+	char name[1];
+};
+static struct config_item *config_parameters;
+static struct config_item **config_parametes_tail = &config_parameters;
+
+static void lowercase(char *p)
+{
+	for (; *p; ++p)
+		*p = tolower(*p);
+}
+static char *skip_space(const char *p)
+{
+	for (; *p; ++p)
+		if (!isspace(*p))
+			break;
+	return (char *)p;
+}
+static char *trailing_space(const char *begin, const char *p)
+{
+	while (p-- > begin)
+		if (!isspace(*p))
+			break;
+	return (char *)p + 1;
+}
+
+int git_config_parse_parameter(const char *text)
+{
+	struct config_item *ct;
+	const char *name;
+	const char *val;
+	name = skip_space(text);
+	text = val = strchr(name, '=');
+	if (!text)
+		text = name + strlen(name);
+	text = trailing_space(name, text);
+	if (text <= name)
+		return -1;
+	ct = calloc(1, sizeof(struct config_item) + (text - name));
+	memcpy(ct->name, name, text - name);
+	lowercase(ct->name);
+	if (!val)
+		ct->value = NULL;
+	else {
+		val = skip_space(++val /* skip "=" */);
+		text = trailing_space(val, val + strlen(val));
+		ct->value = strndup(val, text - val);
+	}
+	*config_parametes_tail = ct;
+	config_parametes_tail = &ct->next;
+	return 0;
+}
+
 static int get_next_char(void)
 {
 	int c;
@@ -699,6 +755,15 @@ int git_config_global(void)
 	return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0);
 }
 
+int git_config_from_parameters(config_fn_t fn, void *data)
+{
+	const struct config_item *ct;
+	for (ct = config_parameters; ct; ct = ct->next)
+		if (fn(ct->name, ct->value, data) < 0)
+			return -1;
+	return 0;
+}
+
 int git_config(config_fn_t fn, void *data)
 {
 	int ret = 0, found = 0;
@@ -730,6 +795,12 @@ int git_config(config_fn_t fn, void *data)
 		found += 1;
 	}
 	free(repo_config);
+
+	if (config_parameters) {
+		ret += git_config_from_parameters(fn, data);
+		found += 1;
+	}
+
 	if (found == 0)
 		return -1;
 	return ret;
diff --git a/git.c b/git.c
index 6bae305..99f0363 100644
--- a/git.c
+++ b/git.c
@@ -8,6 +8,7 @@ const char git_usage_string[] =
 	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
 	"           [-p|--paginate|--no-pager] [--no-replace-objects]\n"
 	"           [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
+	"           [-c name=value\n"
 	"           [--help] COMMAND [ARGS]";
 
 const char git_more_info_string[] =
@@ -130,6 +131,14 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 			setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0);
 			if (envchanged)
 				*envchanged = 1;
+		} else if (!strcmp(cmd, "-c")) {
+			if (*argc < 2) {
+				fprintf(stderr, "-c expects a configuration string\n" );
+				usage(git_usage_string);
+			}
+			git_config_parse_parameter((*argv)[1]);
+			(*argv)++;
+			(*argc)--;
 		} else {
 			fprintf(stderr, "Unknown option: %s\n", cmd);
 			usage(git_usage_string);
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index f11f98c..d7c5501 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -824,4 +824,11 @@ test_expect_success 'check split_cmdline return' "
 	test_must_fail git merge master
 	"
 
+test_expect_success 'git -c "key=value" support' '
+	test "z$(git -c name=value config name)" = zvalue &&
+	test "z$(git -c core.name=value config core.name)" = zvalue &&
+	test "z$(git -c flag config --bool flag)" = ztrue &&
+	test_must_fail git -c core.name=value config name
+'
+
 test_done
-- 
1.7.0.2.318.g49250

