diff mbox series

[09/21] builtin/config: move location options into local variables

Message ID a96c122280ad025f94c1343cca85c67c7c617c8f.1715339393.git.ps@pks.im (mailing list archive)
State Superseded
Headers show
Series builtin/config: remove global state | expand

Commit Message

Patrick Steinhardt May 10, 2024, 11:25 a.m. UTC
The location options are tracked via a set of global variables. Move
them into a self-contained structure so that we can easily parse all
relevant options and hand them over to the various functions that
require them.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/config.c | 311 ++++++++++++++++++++++++++---------------------
 1 file changed, 174 insertions(+), 137 deletions(-)

Comments

Patrick Steinhardt May 10, 2024, 11:34 a.m. UTC | #1
On Fri, May 10, 2024 at 01:25:07PM +0200, Patrick Steinhardt wrote:
> @@ -638,34 +657,40 @@ static char *default_user_config(void)
[snip]
> -	if (use_global_config) {
> -		given_config_source.file = git_global_config();
> -		if (!given_config_source.file)
> +	if (opts->use_global_config) {
> +		opts->source.file = xstrdup_or_null(git_global_config());
> +		if (!opts->source.file)

This needs the following on top to plug a memory leak -- I didn't
realize that `git_global_config()` transfers ownership of the string to
the caller already.

diff --git a/builtin/config.c b/builtin/config.c
index 127bc097b2..30a49d07d7 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -761,7 +761,7 @@ static void location_options_init(struct config_location_options *opts,
        }

        if (opts->use_global_config) {
-               opts->source.file = xstrdup_or_null(git_global_config());
+               opts->source.file = git_global_config();
                if (!opts->source.file)
                        /*
                         * It is unknown if HOME/.gitconfig exists, so

Will fix in v2.

Patrick
diff mbox series

Patch

diff --git a/builtin/config.c b/builtin/config.c
index 9b90eda328..5346a782d0 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -62,6 +62,25 @@  static const char *const builtin_config_edit_usage[] = {
 	NULL
 };
 
+#define CONFIG_LOCATION_OPTIONS(opts) \
+	OPT_GROUP(N_("Config file location")), \
+	OPT_BOOL(0, "global", &opts.use_global_config, N_("use global config file")), \
+	OPT_BOOL(0, "system", &opts.use_system_config, N_("use system config file")), \
+	OPT_BOOL(0, "local", &opts.use_local_config, N_("use repository config file")), \
+	OPT_BOOL(0, "worktree", &opts.use_worktree_config, N_("use per-worktree config file")), \
+	OPT_STRING('f', "file", &opts.source.file, N_("file"), N_("use given config file")), \
+	OPT_STRING(0, "blob", &opts.source.blob, N_("blob-id"), N_("read config from given blob object"))
+
+struct config_location_options {
+	struct git_config_source source;
+	struct config_options options;
+	int use_global_config;
+	int use_system_config;
+	int use_local_config;
+	int use_worktree_config;
+};
+#define CONFIG_LOCATION_OPTIONS_INIT {0}
+
 static char *key;
 static regex_t *key_regexp;
 static const char *value_pattern;
@@ -75,14 +94,10 @@  static char delim = '=';
 static char key_delim = ' ';
 static char term = '\n';
 
-static int use_global_config, use_system_config, use_local_config;
-static int use_worktree_config;
-static struct git_config_source given_config_source;
 static int type;
 static char *default_value;
 static int end_nul;
 static int respect_includes_opt = -1;
-static struct config_options config_options;
 static int show_origin;
 static int show_scope;
 static int fixed_value;
@@ -298,7 +313,8 @@  static int collect_config(const char *key_, const char *value_,
 	return format_config(&values->items[values->nr++], key_, value_, kvi);
 }
 
-static int get_value(const char *key_, const char *regex_, unsigned flags)
+static int get_value(const struct config_location_options *opts,
+		     const char *key_, const char *regex_, unsigned flags)
 {
 	int ret = CONFIG_GENERIC_ERROR;
 	struct strbuf_list values = {NULL};
@@ -353,8 +369,8 @@  static int get_value(const char *key_, const char *regex_, unsigned flags)
 	}
 
 	config_with_options(collect_config, &values,
-			    &given_config_source, the_repository,
-			    &config_options);
+			    &opts->source, the_repository,
+			    &opts->options);
 
 	if (!values.nr && default_value) {
 		struct key_value_info kvi = KVI_INIT;
@@ -464,14 +480,15 @@  static int git_get_color_config(const char *var, const char *value,
 	return 0;
 }
 
-static void get_color(const char *var, const char *def_color)
+static void get_color(const struct config_location_options *opts,
+		      const char *var, const char *def_color)
 {
 	get_color_slot = var;
 	get_color_found = 0;
 	parsed_color[0] = '\0';
 	config_with_options(git_get_color_config, NULL,
-			    &given_config_source, the_repository,
-			    &config_options);
+			    &opts->source, the_repository,
+			    &opts->options);
 
 	if (!get_color_found && def_color) {
 		if (color_parse(def_color, parsed_color) < 0)
@@ -497,15 +514,16 @@  static int git_get_colorbool_config(const char *var, const char *value,
 	return 0;
 }
 
-static int get_colorbool(const char *var, int print)
+static int get_colorbool(const struct config_location_options *opts,
+			 const char *var, int print)
 {
 	get_colorbool_slot = var;
 	get_colorbool_found = -1;
 	get_diff_color_found = -1;
 	get_color_ui_found = -1;
 	config_with_options(git_get_colorbool_config, NULL,
-			    &given_config_source, the_repository,
-			    &config_options);
+			    &opts->source, the_repository,
+			    &opts->options);
 
 	if (get_colorbool_found < 0) {
 		if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -527,15 +545,15 @@  static int get_colorbool(const char *var, int print)
 		return get_colorbool_found ? 0 : 1;
 }
 
-static void check_write(void)
+static void check_write(const struct git_config_source *source)
 {
-	if (!given_config_source.file && !startup_info->have_repository)
+	if (!source->file && !startup_info->have_repository)
 		die(_("not in a git directory"));
 
-	if (given_config_source.use_stdin)
+	if (source->use_stdin)
 		die(_("writing to stdin is not supported"));
 
-	if (given_config_source.blob)
+	if (source->blob)
 		die(_("writing config blobs is not supported"));
 }
 
@@ -572,7 +590,8 @@  static int urlmatch_collect_fn(const char *var, const char *value,
 	return 0;
 }
 
-static int get_urlmatch(const char *var, const char *url)
+static int get_urlmatch(const struct config_location_options *opts,
+			const char *var, const char *url)
 {
 	int ret;
 	char *section_tail;
@@ -599,8 +618,8 @@  static int get_urlmatch(const char *var, const char *url)
 	}
 
 	config_with_options(urlmatch_config_entry, &config,
-			    &given_config_source, the_repository,
-			    &config_options);
+			    &opts->source, the_repository,
+			    &opts->options);
 
 	ret = !values.nr;
 
@@ -638,34 +657,40 @@  static char *default_user_config(void)
 	return strbuf_detach(&buf, NULL);
 }
 
-static void handle_config_location(const char *prefix)
+static void location_options_init(struct config_location_options *opts,
+				  const char *prefix)
 {
-	if (use_global_config + use_system_config + use_local_config +
-	    use_worktree_config +
-	    !!given_config_source.file + !!given_config_source.blob > 1) {
+	if (!opts->source.file)
+		opts->source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
+	else
+		opts->source.file = xstrdup(opts->source.file);
+
+	if (opts->use_global_config + opts->use_system_config +
+	    opts->use_local_config + opts->use_worktree_config +
+	    !!opts->source.file + !!opts->source.blob > 1) {
 		error(_("only one config file at a time"));
 		exit(129);
 	}
 
 	if (!startup_info->have_repository) {
-		if (use_local_config)
+		if (opts->use_local_config)
 			die(_("--local can only be used inside a git repository"));
-		if (given_config_source.blob)
+		if (opts->source.blob)
 			die(_("--blob can only be used inside a git repository"));
-		if (use_worktree_config)
+		if (opts->use_worktree_config)
 			die(_("--worktree can only be used inside a git repository"));
 	}
 
-	if (given_config_source.file &&
-			!strcmp(given_config_source.file, "-")) {
-		given_config_source.file = NULL;
-		given_config_source.use_stdin = 1;
-		given_config_source.scope = CONFIG_SCOPE_COMMAND;
+	if (opts->source.file &&
+			!strcmp(opts->source.file, "-")) {
+		opts->source.file = NULL;
+		opts->source.use_stdin = 1;
+		opts->source.scope = CONFIG_SCOPE_COMMAND;
 	}
 
-	if (use_global_config) {
-		given_config_source.file = git_global_config();
-		if (!given_config_source.file)
+	if (opts->use_global_config) {
+		opts->source.file = xstrdup_or_null(git_global_config());
+		if (!opts->source.file)
 			/*
 			 * It is unknown if HOME/.gitconfig exists, so
 			 * we do not know if we should write to XDG
@@ -673,17 +698,17 @@  static void handle_config_location(const char *prefix)
 			 * is set and points at a sane location.
 			 */
 			die(_("$HOME not set"));
-		given_config_source.scope = CONFIG_SCOPE_GLOBAL;
-	} else if (use_system_config) {
-		given_config_source.file = git_system_config();
-		given_config_source.scope = CONFIG_SCOPE_SYSTEM;
-	} else if (use_local_config) {
-		given_config_source.file = git_pathdup("config");
-		given_config_source.scope = CONFIG_SCOPE_LOCAL;
-	} else if (use_worktree_config) {
+		opts->source.scope = CONFIG_SCOPE_GLOBAL;
+	} else if (opts->use_system_config) {
+		opts->source.file = xstrdup_or_null(git_system_config());
+		opts->source.scope = CONFIG_SCOPE_SYSTEM;
+	} else if (opts->use_local_config) {
+		opts->source.file = xstrdup_or_null(git_pathdup("config"));
+		opts->source.scope = CONFIG_SCOPE_LOCAL;
+	} else if (opts->use_worktree_config) {
 		struct worktree **worktrees = get_worktrees();
 		if (the_repository->repository_format_worktree_config)
-			given_config_source.file = git_pathdup("config.worktree");
+			opts->source.file = git_pathdup("config.worktree");
 		else if (worktrees[0] && worktrees[1])
 			die(_("--worktree cannot be used with multiple "
 			      "working trees unless the config\n"
@@ -691,28 +716,33 @@  static void handle_config_location(const char *prefix)
 			      "Please read \"CONFIGURATION FILE\"\n"
 			      "section in \"git help worktree\" for details"));
 		else
-			given_config_source.file = git_pathdup("config");
-		given_config_source.scope = CONFIG_SCOPE_LOCAL;
+			opts->source.file = git_pathdup("config");
+		opts->source.scope = CONFIG_SCOPE_LOCAL;
 		free_worktrees(worktrees);
-	} else if (given_config_source.file) {
-		if (!is_absolute_path(given_config_source.file) && prefix)
-			given_config_source.file =
-				prefix_filename(prefix, given_config_source.file);
-		given_config_source.scope = CONFIG_SCOPE_COMMAND;
-	} else if (given_config_source.blob) {
-		given_config_source.scope = CONFIG_SCOPE_COMMAND;
+	} else if (opts->source.file) {
+		if (!is_absolute_path(opts->source.file) && prefix)
+			opts->source.file =
+				prefix_filename(prefix, opts->source.file);
+		opts->source.scope = CONFIG_SCOPE_COMMAND;
+	} else if (opts->source.blob) {
+		opts->source.scope = CONFIG_SCOPE_COMMAND;
 	}
 
 	if (respect_includes_opt == -1)
-		config_options.respect_includes = !given_config_source.file;
+		opts->options.respect_includes = !opts->source.file;
 	else
-		config_options.respect_includes = respect_includes_opt;
+		opts->options.respect_includes = respect_includes_opt;
 	if (startup_info->have_repository) {
-		config_options.commondir = get_git_common_dir();
-		config_options.git_dir = get_git_dir();
+		opts->options.commondir = get_git_common_dir();
+		opts->options.git_dir = get_git_dir();
 	}
 }
 
+static void location_options_release(struct config_location_options *opts)
+{
+	free((char *) opts->source.file);
+}
+
 static void handle_nul(void) {
 	if (end_nul) {
 		term = '\0';
@@ -721,15 +751,6 @@  static void handle_nul(void) {
 	}
 }
 
-#define CONFIG_LOCATION_OPTIONS \
-	OPT_GROUP(N_("Config file location")), \
-	OPT_BOOL(0, "global", &use_global_config, N_("use global config file")), \
-	OPT_BOOL(0, "system", &use_system_config, N_("use system config file")), \
-	OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")), \
-	OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")), \
-	OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")), \
-	OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object"))
-
 #define CONFIG_TYPE_OPTIONS \
 	OPT_GROUP(N_("Type")), \
 	OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type), \
@@ -749,8 +770,9 @@  static void handle_nul(void) {
 
 static int cmd_config_list(int argc, const char **argv, const char *prefix)
 {
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
 	struct option opts[] = {
-		CONFIG_LOCATION_OPTIONS,
+		CONFIG_LOCATION_OPTIONS(location_opts),
 		CONFIG_DISPLAY_OPTIONS,
 		OPT_GROUP(N_("Other")),
 		OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
@@ -760,30 +782,32 @@  static int cmd_config_list(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, opts, builtin_config_list_usage, 0);
 	check_argc(argc, 0, 0);
 
-	handle_config_location(prefix);
+	location_options_init(&location_opts, prefix);
 	handle_nul();
 
 	setup_auto_pager("config", 1);
 
 	if (config_with_options(show_all_config, NULL,
-				&given_config_source, the_repository,
-				&config_options) < 0) {
-		if (given_config_source.file)
+				&location_opts.source, the_repository,
+				&location_opts.options) < 0) {
+		if (location_opts.source.file)
 			die_errno(_("unable to read config file '%s'"),
-				  given_config_source.file);
+				  location_opts.source.file);
 		else
 			die(_("error processing config file(s)"));
 	}
 
+	location_options_release(&location_opts);
 	return 0;
 }
 
 static int cmd_config_get(int argc, const char **argv, const char *prefix)
 {
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
 	const char *value_pattern = NULL, *url = NULL;
 	int flags = 0;
 	struct option opts[] = {
-		CONFIG_LOCATION_OPTIONS,
+		CONFIG_LOCATION_OPTIONS(location_opts),
 		CONFIG_TYPE_OPTIONS,
 		OPT_GROUP(N_("Filter options")),
 		OPT_BOOL(0, "all", &do_all, N_("return all values for multi-valued config options")),
@@ -811,26 +835,28 @@  static int cmd_config_get(int argc, const char **argv, const char *prefix)
 	if (url && (do_all || use_key_regexp || value_pattern))
 		die(_("--url= cannot be used with --all, --regexp or --value"));
 
-	handle_config_location(prefix);
+	location_options_init(&location_opts, prefix);
 	handle_nul();
 
 	setup_auto_pager("config", 1);
 
 	if (url)
-		ret = get_urlmatch(argv[0], url);
+		ret = get_urlmatch(&location_opts, argv[0], url);
 	else
-		ret = get_value(argv[0], value_pattern, flags);
+		ret = get_value(&location_opts, argv[0], value_pattern, flags);
 
+	location_options_release(&location_opts);
 	return ret;
 }
 
 static int cmd_config_set(int argc, const char **argv, const char *prefix)
 {
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
 	const char *value_pattern = NULL, *comment_arg = NULL;
 	char *comment = NULL;
 	int flags = 0, append = 0;
 	struct option opts[] = {
-		CONFIG_LOCATION_OPTIONS,
+		CONFIG_LOCATION_OPTIONS(location_opts),
 		CONFIG_TYPE_OPTIONS,
 		OPT_GROUP(N_("Filter")),
 		OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
@@ -858,23 +884,24 @@  static int cmd_config_set(int argc, const char **argv, const char *prefix)
 
 	comment = git_config_prepare_comment_string(comment_arg);
 
-	handle_config_location(prefix);
-	check_write();
+	location_options_init(&location_opts, prefix);
+	check_write(&location_opts.source);
 
 	value = normalize_value(argv[0], argv[1], &default_kvi);
 
 	if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {
-		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
 							     argv[0], value, value_pattern,
 							     comment, flags);
 	} else {
-		ret = git_config_set_in_file_gently(given_config_source.file,
+		ret = git_config_set_in_file_gently(location_opts.source.file,
 						    argv[0], comment, value);
 		if (ret == CONFIG_NOTHING_SET)
 			error(_("cannot overwrite multiple values with a single value\n"
 			"       Use a regexp, --add or --replace-all to change %s."), argv[0]);
 	}
 
+	location_options_release(&location_opts);
 	free(comment);
 	free(value);
 	return ret;
@@ -882,10 +909,11 @@  static int cmd_config_set(int argc, const char **argv, const char *prefix)
 
 static int cmd_config_unset(int argc, const char **argv, const char *prefix)
 {
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
 	const char *value_pattern = NULL;
 	int flags = 0;
 	struct option opts[] = {
-		CONFIG_LOCATION_OPTIONS,
+		CONFIG_LOCATION_OPTIONS(location_opts),
 		OPT_GROUP(N_("Filter")),
 		OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
 		OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
@@ -901,24 +929,26 @@  static int cmd_config_unset(int argc, const char **argv, const char *prefix)
 	if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
 		die(_("--fixed-value only applies with 'value-pattern'"));
 
-	handle_config_location(prefix);
-	check_write();
+	location_options_init(&location_opts, prefix);
+	check_write(&location_opts.source);
 
 	if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)
-		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
 							     argv[0], NULL, value_pattern,
 							     NULL, flags);
 	else
-		ret = git_config_set_in_file_gently(given_config_source.file, argv[0],
+		ret = git_config_set_in_file_gently(location_opts.source.file, argv[0],
 						    NULL, NULL);
 
+	location_options_release(&location_opts);
 	return ret;
 }
 
 static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)
 {
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
 	struct option opts[] = {
-		CONFIG_LOCATION_OPTIONS,
+		CONFIG_LOCATION_OPTIONS(location_opts),
 		OPT_END(),
 	};
 	int ret;
@@ -927,10 +957,10 @@  static int cmd_config_rename_section(int argc, const char **argv, const char *pr
 			     PARSE_OPT_STOP_AT_NON_OPTION);
 	check_argc(argc, 2, 2);
 
-	handle_config_location(prefix);
-	check_write();
+	location_options_init(&location_opts, prefix);
+	check_write(&location_opts.source);
 
-	ret = git_config_rename_section_in_file(given_config_source.file,
+	ret = git_config_rename_section_in_file(location_opts.source.file,
 						argv[0], argv[1]);
 	if (ret < 0)
 		goto out;
@@ -939,13 +969,15 @@  static int cmd_config_rename_section(int argc, const char **argv, const char *pr
 	ret = 0;
 
 out:
+	location_options_release(&location_opts);
 	return ret;
 }
 
 static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)
 {
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
 	struct option opts[] = {
-		CONFIG_LOCATION_OPTIONS,
+		CONFIG_LOCATION_OPTIONS(location_opts),
 		OPT_END(),
 	};
 	int ret;
@@ -954,10 +986,10 @@  static int cmd_config_remove_section(int argc, const char **argv, const char *pr
 			     PARSE_OPT_STOP_AT_NON_OPTION);
 	check_argc(argc, 1, 1);
 
-	handle_config_location(prefix);
-	check_write();
+	location_options_init(&location_opts, prefix);
+	check_write(&location_opts.source);
 
-	ret = git_config_rename_section_in_file(given_config_source.file,
+	ret = git_config_rename_section_in_file(location_opts.source.file,
 						argv[0], NULL);
 	if (ret < 0)
 		goto out;
@@ -966,24 +998,25 @@  static int cmd_config_remove_section(int argc, const char **argv, const char *pr
 	ret = 0;
 
 out:
+	location_options_release(&location_opts);
 	return ret;
 }
 
-static int show_editor(void)
+static int show_editor(struct config_location_options *opts)
 {
 	char *config_file;
 
-	if (!given_config_source.file && !startup_info->have_repository)
+	if (!opts->source.file && !startup_info->have_repository)
 		die(_("not in a git directory"));
-	if (given_config_source.use_stdin)
+	if (opts->source.use_stdin)
 		die(_("editing stdin is not supported"));
-	if (given_config_source.blob)
+	if (opts->source.blob)
 		die(_("editing blobs is not supported"));
 	git_config(git_default_config, NULL);
-	config_file = given_config_source.file ?
-			xstrdup(given_config_source.file) :
+	config_file = opts->source.file ?
+			xstrdup(opts->source.file) :
 			git_pathdup("config");
-	if (use_global_config) {
+	if (opts->use_global_config) {
 		int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
 		if (fd >= 0) {
 			char *content = default_user_config();
@@ -1002,18 +1035,22 @@  static int show_editor(void)
 
 static int cmd_config_edit(int argc, const char **argv, const char *prefix)
 {
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
 	struct option opts[] = {
-		CONFIG_LOCATION_OPTIONS,
+		CONFIG_LOCATION_OPTIONS(location_opts),
 		OPT_END(),
 	};
+	int ret;
 
 	argc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);
 	check_argc(argc, 0, 0);
 
-	handle_config_location(prefix);
-	check_write();
+	location_options_init(&location_opts, prefix);
+	check_write(&location_opts.source);
 
-	return show_editor();
+	ret = show_editor(&location_opts);
+	location_options_release(&location_opts);
+	return ret;
 }
 
 static int cmd_config_actions(int argc, const char **argv, const char *prefix)
@@ -1036,10 +1073,11 @@  static int cmd_config_actions(int argc, const char **argv, const char *prefix)
 		ACTION_GET_COLORBOOL = (1<<14),
 		ACTION_GET_URLMATCH = (1<<15),
 	};
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
 	const char *comment_arg = NULL;
 	int actions = 0;
 	struct option opts[] = {
-		CONFIG_LOCATION_OPTIONS,
+		CONFIG_LOCATION_OPTIONS(location_opts),
 		OPT_GROUP(N_("Action")),
 		OPT_CMDMODE(0, "get", &actions, N_("get value: name [<value-pattern>]"), ACTION_GET),
 		OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [<value-pattern>]"), ACTION_GET_ALL),
@@ -1073,7 +1111,7 @@  static int cmd_config_actions(int argc, const char **argv, const char *prefix)
 			     builtin_config_usage,
 			     PARSE_OPT_STOP_AT_NON_OPTION);
 
-	handle_config_location(prefix);
+	location_options_init(&location_opts, prefix);
 	handle_nul();
 
 	if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {
@@ -1158,94 +1196,94 @@  static int cmd_config_actions(int argc, const char **argv, const char *prefix)
 	if (actions == ACTION_LIST) {
 		check_argc(argc, 0, 0);
 		if (config_with_options(show_all_config, NULL,
-					&given_config_source, the_repository,
-					&config_options) < 0) {
-			if (given_config_source.file)
+					&location_opts.source, the_repository,
+					&location_opts.options) < 0) {
+			if (location_opts.source.file)
 				die_errno(_("unable to read config file '%s'"),
-					  given_config_source.file);
+					  location_opts.source.file);
 			else
 				die(_("error processing config file(s)"));
 		}
 	}
 	else if (actions == ACTION_EDIT) {
-		ret = show_editor();
+		ret = show_editor(&location_opts);
 	}
 	else if (actions == ACTION_SET) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 2, 2);
 		value = normalize_value(argv[0], argv[1], &default_kvi);
-		ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);
+		ret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value);
 		if (ret == CONFIG_NOTHING_SET)
 			error(_("cannot overwrite multiple values with a single value\n"
 			"       Use a regexp, --add or --replace-all to change %s."), argv[0]);
 	}
 	else if (actions == ACTION_SET_ALL) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 2, 3);
 		value = normalize_value(argv[0], argv[1], &default_kvi);
-		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
 							     argv[0], value, argv[2],
 							     comment, flags);
 	}
 	else if (actions == ACTION_ADD) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 2, 2);
 		value = normalize_value(argv[0], argv[1], &default_kvi);
-		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
 							     argv[0], value,
 							     CONFIG_REGEX_NONE,
 							     comment, flags);
 	}
 	else if (actions == ACTION_REPLACE_ALL) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 2, 3);
 		value = normalize_value(argv[0], argv[1], &default_kvi);
-		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
 							     argv[0], value, argv[2],
 							     comment, flags | CONFIG_FLAGS_MULTI_REPLACE);
 	}
 	else if (actions == ACTION_GET) {
 		check_argc(argc, 1, 2);
-		ret = get_value(argv[0], argv[1], flags);
+		ret = get_value(&location_opts, argv[0], argv[1], flags);
 	}
 	else if (actions == ACTION_GET_ALL) {
 		do_all = 1;
 		check_argc(argc, 1, 2);
-		ret = get_value(argv[0], argv[1], flags);
+		ret = get_value(&location_opts, argv[0], argv[1], flags);
 	}
 	else if (actions == ACTION_GET_REGEXP) {
 		show_keys = 1;
 		use_key_regexp = 1;
 		do_all = 1;
 		check_argc(argc, 1, 2);
-		ret = get_value(argv[0], argv[1], flags);
+		ret = get_value(&location_opts, argv[0], argv[1], flags);
 	}
 	else if (actions == ACTION_GET_URLMATCH) {
 		check_argc(argc, 2, 2);
-		ret = get_urlmatch(argv[0], argv[1]);
+		ret = get_urlmatch(&location_opts, argv[0], argv[1]);
 	}
 	else if (actions == ACTION_UNSET) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 1, 2);
 		if (argc == 2)
-			ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+			ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
 								     argv[0], NULL, argv[1],
 								     NULL, flags);
 		else
-			ret = git_config_set_in_file_gently(given_config_source.file,
+			ret = git_config_set_in_file_gently(location_opts.source.file,
 							    argv[0], NULL, NULL);
 	}
 	else if (actions == ACTION_UNSET_ALL) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 1, 2);
-		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
 							     argv[0], NULL, argv[1],
 							     NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
 	}
 	else if (actions == ACTION_RENAME_SECTION) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 2, 2);
-		ret = git_config_rename_section_in_file(given_config_source.file,
+		ret = git_config_rename_section_in_file(location_opts.source.file,
 							argv[0], argv[1]);
 		if (ret < 0)
 			goto out;
@@ -1255,9 +1293,9 @@  static int cmd_config_actions(int argc, const char **argv, const char *prefix)
 			ret = 0;
 	}
 	else if (actions == ACTION_REMOVE_SECTION) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 1, 1);
-		ret = git_config_rename_section_in_file(given_config_source.file,
+		ret = git_config_rename_section_in_file(location_opts.source.file,
 							argv[0], NULL);
 		if (ret < 0)
 			goto out;
@@ -1268,16 +1306,17 @@  static int cmd_config_actions(int argc, const char **argv, const char *prefix)
 	}
 	else if (actions == ACTION_GET_COLOR) {
 		check_argc(argc, 1, 2);
-		get_color(argv[0], argv[1]);
+		get_color(&location_opts, argv[0], argv[1]);
 	}
 	else if (actions == ACTION_GET_COLORBOOL) {
 		check_argc(argc, 1, 2);
 		if (argc == 2)
 			color_stdout_is_tty = git_config_bool("command line", argv[1]);
-		ret = get_colorbool(argv[0], argc == 2);
+		ret = get_colorbool(&location_opts, argv[0], argc == 2);
 	}
 
 out:
+	location_options_release(&location_opts);
 	free(comment);
 	free(value);
 	return ret;
@@ -1297,8 +1336,6 @@  int cmd_config(int argc, const char **argv, const char *prefix)
 		OPT_END(),
 	};
 
-	given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
-
 	/*
 	 * This is somewhat hacky: we first parse the command line while
 	 * keeping all args intact in order to determine whether a subcommand