git@vger.kernel.org mailing list mirror (one of many)
 help / Atom feed
* [PATCH 0/6] Add "git config --move-to"
@ 2018-12-27 15:56 Nguyễn Thái Ngọc Duy
  2018-12-27 15:56 ` [PATCH 1/6] config.c: avoid git_path() in do_git_config_sequence() Nguyễn Thái Ngọc Duy
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2018-12-27 15:56 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This is a continuation of nd/per-worktree-config, fixing is small
things from that series and prepares to make submodules work in
multiple worktrees.

The first thing is adding API for updating per-worktree config in
03/06. When submodule code wants to write to a per-worktree config,
calling this function takes care of both single and multi worktree
setups, similar to "git config --worktree <name> <value>"

The second thing is "git config --move-to" and friends. This helps
the config transition right after enabling extensions.worktreeConfig,
when the user has to make sure per-worktree config stays in
per-worktree file.

Manual moving config from one file to another is possible with a
couple keys like core.worktree, but once you have to move multiple
groups (i.e.  "submodule.*.*" keys) then it becomes a lot less fun and
more pain. This simplifies that work (and also migration
documentation)

Note that I added --move-glob-to because I think glob fits much better
than regex for matching keys because of the dots in config keys. I
added --move-regex-to anyway because we have --get-regexp. Maybe I
should add --get-glob too (but probably not now).

There is also a NEEDSWORK in 03/06. But I hope it could be addressed
after this series is merged (so that I could continue pushing
submodule changes out)

Nguyễn Thái Ngọc Duy (6):
  config.c: avoid git_path() in do_git_config_sequence()
  worktree.c: add get_worktree_config()
  config.c: add repo_config_set_worktree_gently()
  config: use OPT_FILENAME()
  config: factor out set_config_source_file()
  config: add --move-to

 Documentation/git-config.txt   |  12 ++
 Documentation/git-worktree.txt |  16 ++-
 builtin/config.c               | 244 ++++++++++++++++++++++++---------
 config.c                       |  55 +++++++-
 config.h                       |   3 +
 t/t1300-config.sh              |  54 ++++++++
 worktree.c                     |  16 +++
 worktree.h                     |   7 +
 8 files changed, 330 insertions(+), 77 deletions(-)

-- 
2.20.0.482.g66447595a7


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/6] config.c: avoid git_path() in do_git_config_sequence()
  2018-12-27 15:56 [PATCH 0/6] Add "git config --move-to" Nguyễn Thái Ngọc Duy
@ 2018-12-27 15:56 ` Nguyễn Thái Ngọc Duy
  2019-01-10 21:38   ` Junio C Hamano
  2018-12-27 15:56 ` [PATCH 2/6] worktree.c: add get_worktree_config() Nguyễn Thái Ngọc Duy
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2018-12-27 15:56 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This function has both $GIT_COMMON_DIR and $GIT_DIR in "opts". Use it
to construct config.worktree path instead because git_pathdup() is
tied to the current worktree, but the given $GIT_DIR could be from
another one.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 config.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/config.c b/config.c
index ff521eb27a..79fbe65da8 100644
--- a/config.c
+++ b/config.c
@@ -1665,6 +1665,7 @@ static int do_git_config_sequence(const struct config_options *opts,
 	char *xdg_config = xdg_config_home("config");
 	char *user_config = expand_user_path("~/.gitconfig", 0);
 	char *repo_config;
+	char *worktree_config;
 
 	if (opts->commondir)
 		repo_config = mkpathdup("%s/config", opts->commondir);
@@ -1672,6 +1673,10 @@ static int do_git_config_sequence(const struct config_options *opts,
 		BUG("git_dir without commondir");
 	else
 		repo_config = NULL;
+	if (repository_format_worktree_config)
+		worktree_config = mkpathdup("%s/config.worktree", opts->git_dir);
+	else
+		worktree_config = NULL;
 
 	current_parsing_scope = CONFIG_SCOPE_SYSTEM;
 	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
@@ -1693,12 +1698,8 @@ static int do_git_config_sequence(const struct config_options *opts,
 	 * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE.
 	 * But let's not complicate things before it's actually needed.
 	 */
-	if (repository_format_worktree_config) {
-		char *path = git_pathdup("config.worktree");
-		if (!access_or_die(path, R_OK, 0))
-			ret += git_config_from_file(fn, path, data);
-		free(path);
-	}
+	if (worktree_config && !access_or_die(worktree_config, R_OK, 0))
+		ret += git_config_from_file(fn, worktree_config, data);
 
 	current_parsing_scope = CONFIG_SCOPE_CMDLINE;
 	if (git_config_from_parameters(fn, data) < 0)
@@ -1708,6 +1709,7 @@ static int do_git_config_sequence(const struct config_options *opts,
 	free(xdg_config);
 	free(user_config);
 	free(repo_config);
+	free(worktree_config);
 	return ret;
 }
 
-- 
2.20.0.482.g66447595a7


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 2/6] worktree.c: add get_worktree_config()
  2018-12-27 15:56 [PATCH 0/6] Add "git config --move-to" Nguyễn Thái Ngọc Duy
  2018-12-27 15:56 ` [PATCH 1/6] config.c: avoid git_path() in do_git_config_sequence() Nguyễn Thái Ngọc Duy
@ 2018-12-27 15:56 ` Nguyễn Thái Ngọc Duy
  2018-12-27 20:36   ` Eric Sunshine
  2018-12-27 15:56 ` [PATCH 3/6] config.c: add repo_config_set_worktree_gently() Nguyễn Thái Ngọc Duy
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2018-12-27 15:56 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

"git config --worktree" can write to the write file whether
extensions.worktreeConfig is enabled or not. In order to do the same
using config API, we need to determine the right file to write to. Add
this function for that purpose. This is the basis for the coming
repo_config_set_worktree()

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/config.c |  9 ++-------
 worktree.c       | 16 ++++++++++++++++
 worktree.h       |  7 +++++++
 3 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/builtin/config.c b/builtin/config.c
index 84385ef165..771cfa54bd 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -650,18 +650,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 	else if (use_local_config)
 		given_config_source.file = git_pathdup("config");
 	else if (use_worktree_config) {
-		struct worktree **worktrees = get_worktrees(0);
-		if (repository_format_worktree_config)
-			given_config_source.file = git_pathdup("config.worktree");
-		else if (worktrees[0] && worktrees[1])
+		given_config_source.file = get_worktree_config(the_repository);
+		if (!given_config_source.file)
 			die(_("--worktree cannot be used with multiple "
 			      "working trees unless the config\n"
 			      "extension worktreeConfig is enabled. "
 			      "Please read \"CONFIGURATION FILE\"\n"
 			      "section in \"git help worktree\" for details"));
-		else
-			given_config_source.file = git_pathdup("config");
-		free_worktrees(worktrees);
 	} else if (given_config_source.file) {
 		if (!is_absolute_path(given_config_source.file) && prefix)
 			given_config_source.file =
diff --git a/worktree.c b/worktree.c
index d6a0ee7f73..d335bdf28a 100644
--- a/worktree.c
+++ b/worktree.c
@@ -581,3 +581,19 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
 	free_worktrees(worktrees);
 	return ret;
 }
+
+char *get_worktree_config(struct repository *r)
+{
+	struct worktree **worktrees = get_worktrees(0);
+	char *path;
+
+	if (repository_format_worktree_config)
+		path = repo_git_path(r, "config.worktree");
+	else if (worktrees[0] && worktrees[1])
+		path = NULL;
+	else
+		path = repo_git_path(r, "config");
+
+	free_worktrees(worktrees);
+	return path;
+}
diff --git a/worktree.h b/worktree.h
index 9e3b0b7b6f..4c41002d31 100644
--- a/worktree.h
+++ b/worktree.h
@@ -132,4 +132,11 @@ void strbuf_worktree_ref(const struct worktree *wt,
 const char *worktree_ref(const struct worktree *wt,
 			 const char *refname);
 
+/*
+ * Return the path to config file that can contain worktree-specific
+ * config (or NULL in unsupported setups). The caller must free the
+ * return value.
+ */
+char *get_worktree_config(struct repository *r);
+
 #endif
-- 
2.20.0.482.g66447595a7


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/6] config.c: add repo_config_set_worktree_gently()
  2018-12-27 15:56 [PATCH 0/6] Add "git config --move-to" Nguyễn Thái Ngọc Duy
  2018-12-27 15:56 ` [PATCH 1/6] config.c: avoid git_path() in do_git_config_sequence() Nguyễn Thái Ngọc Duy
  2018-12-27 15:56 ` [PATCH 2/6] worktree.c: add get_worktree_config() Nguyễn Thái Ngọc Duy
@ 2018-12-27 15:56 ` Nguyễn Thái Ngọc Duy
  2018-12-27 15:56 ` [PATCH 4/6] config: use OPT_FILENAME() Nguyễn Thái Ngọc Duy
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2018-12-27 15:56 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This is C equivalent of "git config --set --worktree". In other words,
it will

- write to $GIT_DIR/config in single-worktree setup

- write to $GIT_COMMON_DIR/worktrees/<x>/config.worktree or
  $GIT_COMMON_DIR/config.worktree (for main worktree)
  if extensions.worktreeConfig is enabled

- return error in multiple-worktree setup if extensions.worktreeConfig
  is not enabled.

While at there, also add repo_config_set*() for writing to
$GIT_COMMON_DIR/config.

Note, since git_config_set_multivar_in_file_gently() only invalidates
the config set of the_repository, anybody who uses these functions on
submodules must do additional invalidation if needed.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 config.c | 41 ++++++++++++++++++++++++++++++++++++++++-
 config.h |  3 +++
 2 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/config.c b/config.c
index 79fbe65da8..151d28664e 100644
--- a/config.c
+++ b/config.c
@@ -19,6 +19,7 @@
 #include "utf8.h"
 #include "dir.h"
 #include "color.h"
+#include "worktree.h"
 
 struct config_source {
 	struct config_source *prev;
@@ -2137,6 +2138,39 @@ int repo_config_get_pathname(struct repository *repo,
 	return ret;
 }
 
+int repo_config_set_gently(struct repository *r,
+			   const char *key, const char *value)
+{
+	char *path = repo_git_path(r, "config");
+	int ret = git_config_set_multivar_in_file_gently(path, key, value, NULL, 0);
+	free(path);
+	return ret;
+}
+
+void repo_config_set(struct repository *r, const char *key, const char *value)
+{
+	if (!repo_config_set_gently(r, key, value))
+		return;
+	if (value)
+		die(_("could not set '%s' to '%s'"), key, value);
+	else
+		die(_("could not unset '%s'"), key);
+}
+
+int repo_config_set_worktree_gently(struct repository *r,
+				    const char *key, const char *value)
+{
+	char *path;
+	int ret;
+
+	path = get_worktree_config(r);
+	if (!path)
+		return CONFIG_INVALID_FILE;
+	ret = git_config_set_multivar_in_file_gently(path, key, value, NULL, 0);
+	free(path);
+	return ret;
+}
+
 /* Functions used historically to read configuration from 'the_repository' */
 void git_config(config_fn_t fn, void *data)
 {
@@ -2912,7 +2946,12 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
 
 	ret = 0;
 
-	/* Invalidate the config cache */
+	/*
+	 * Invalidate the config cache
+	 *
+	 * NEEDSWORK: invalidate _all_ existing config caches, not
+	 * just one from the_repository
+	 */
 	git_config_clear();
 
 out_free:
diff --git a/config.h b/config.h
index ee5d3fa7b4..62204dc252 100644
--- a/config.h
+++ b/config.h
@@ -103,6 +103,9 @@ extern int git_config_color(char *, const char *, const char *);
 extern int git_config_set_in_file_gently(const char *, const char *, const char *);
 extern void git_config_set_in_file(const char *, const char *, const char *);
 extern int git_config_set_gently(const char *, const char *);
+extern int repo_config_set_gently(struct repository *, const char *, const char *);
+extern void repo_config_set(struct repository *, const char *, const char *);
+extern int repo_config_set_worktree_gently(struct repository *, const char *, const char *);
 extern void git_config_set(const char *, const char *);
 extern int git_config_parse_key(const char *, char **, int *);
 extern int git_config_key_is_valid(const char *key);
-- 
2.20.0.482.g66447595a7


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 4/6] config: use OPT_FILENAME()
  2018-12-27 15:56 [PATCH 0/6] Add "git config --move-to" Nguyễn Thái Ngọc Duy
                   ` (2 preceding siblings ...)
  2018-12-27 15:56 ` [PATCH 3/6] config.c: add repo_config_set_worktree_gently() Nguyễn Thái Ngọc Duy
@ 2018-12-27 15:56 ` Nguyễn Thái Ngọc Duy
  2018-12-27 20:40   ` Eric Sunshine
  2018-12-27 15:56 ` [PATCH 5/6] config: factor out set_config_source_file() Nguyễn Thái Ngọc Duy
  2018-12-27 15:56 ` [PATCH 6/6] config: add --move-to Nguyễn Thái Ngọc Duy
  5 siblings, 1 reply; 10+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2018-12-27 15:56 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

Do not handle prefix directly. It's simpler to use OPT_FILENAME()
instead. The othe reason for doing this is because this code (where
the deleted code is) will be factored out and called when "prefix" is
not available.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/config.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/builtin/config.c b/builtin/config.c
index 771cfa54bd..c22d25de12 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -126,7 +126,7 @@ static struct option builtin_config_options[] = {
 	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_FILENAME('f', "file", &given_config_source.file, N_("use given config file")),
 	OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
 	OPT_GROUP(N_("Action")),
 	OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
@@ -657,10 +657,6 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 			      "extension worktreeConfig is enabled. "
 			      "Please read \"CONFIGURATION FILE\"\n"
 			      "section in \"git help worktree\" for details"));
-	} 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);
 	}
 
 	if (respect_includes_opt == -1)
-- 
2.20.0.482.g66447595a7


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 5/6] config: factor out set_config_source_file()
  2018-12-27 15:56 [PATCH 0/6] Add "git config --move-to" Nguyễn Thái Ngọc Duy
                   ` (3 preceding siblings ...)
  2018-12-27 15:56 ` [PATCH 4/6] config: use OPT_FILENAME() Nguyễn Thái Ngọc Duy
@ 2018-12-27 15:56 ` Nguyễn Thái Ngọc Duy
  2018-12-27 15:56 ` [PATCH 6/6] config: add --move-to Nguyễn Thái Ngọc Duy
  5 siblings, 0 replies; 10+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2018-12-27 15:56 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

In the next patch, "config" is taught to move some config variables
from one file to another. We need to specify two locations, source and
destination, and the plan is reusing the same --global/--local/...
option. Factor this code out for reuse later.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/config.c | 113 +++++++++++++++++++++++++----------------------
 1 file changed, 59 insertions(+), 54 deletions(-)

diff --git a/builtin/config.c b/builtin/config.c
index c22d25de12..61a6a5a0e1 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -71,6 +71,64 @@ static int show_origin;
 
 static NORETURN void usage_builtin_config(void);
 
+static void set_config_source_file(void)
+{
+	int nongit = !startup_info->have_repository;
+
+	if (use_global_config + use_system_config + use_local_config +
+	    use_worktree_config +
+	    !!given_config_source.file + !!given_config_source.blob > 1)
+		die(_("only one config file at a time"));
+
+	if (use_local_config && nongit)
+		die(_("--local can only be used inside a git repository"));
+
+	if (given_config_source.blob && nongit)
+		die(_("--blob 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;
+	}
+
+	if (use_global_config) {
+		char *user_config = expand_user_path("~/.gitconfig", 0);
+		char *xdg_config = xdg_config_home("config");
+
+		if (!user_config)
+			/*
+			 * It is unknown if HOME/.gitconfig exists, so
+			 * we do not know if we should write to XDG
+			 * location; error out even if XDG_CONFIG_HOME
+			 * is set and points at a sane location.
+			 */
+			die(_("$HOME not set"));
+
+		if (access_or_warn(user_config, R_OK, 0) &&
+		    xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
+			given_config_source.file = xdg_config;
+			free(user_config);
+		} else {
+			given_config_source.file = user_config;
+			free(xdg_config);
+		}
+	}
+	else if (use_system_config)
+		given_config_source.file = git_etc_gitconfig();
+	else if (use_local_config)
+		given_config_source.file = git_pathdup("config");
+	else if (use_worktree_config) {
+		given_config_source.file = get_worktree_config(the_repository);
+		if (!given_config_source.file)
+			die(_("--worktree cannot be used with multiple "
+			      "working trees unless the config\n"
+			      "extension worktreeConfig is enabled. "
+			      "Please read \"CONFIGURATION FILE\"\n"
+			      "section in \"git help worktree\" for details"));
+	}
+}
+
 static int option_parse_type(const struct option *opt, const char *arg,
 			     int unset)
 {
@@ -604,60 +662,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 			     builtin_config_usage,
 			     PARSE_OPT_STOP_AT_NON_OPTION);
 
-	if (use_global_config + use_system_config + use_local_config +
-	    use_worktree_config +
-	    !!given_config_source.file + !!given_config_source.blob > 1) {
-		error(_("only one config file at a time"));
-		usage_builtin_config();
-	}
-
-	if (use_local_config && nongit)
-		die(_("--local can only be used inside a git repository"));
-
-	if (given_config_source.blob && nongit)
-		die(_("--blob 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;
-	}
-
-	if (use_global_config) {
-		char *user_config = expand_user_path("~/.gitconfig", 0);
-		char *xdg_config = xdg_config_home("config");
-
-		if (!user_config)
-			/*
-			 * It is unknown if HOME/.gitconfig exists, so
-			 * we do not know if we should write to XDG
-			 * location; error out even if XDG_CONFIG_HOME
-			 * is set and points at a sane location.
-			 */
-			die(_("$HOME not set"));
-
-		if (access_or_warn(user_config, R_OK, 0) &&
-		    xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
-			given_config_source.file = xdg_config;
-			free(user_config);
-		} else {
-			given_config_source.file = user_config;
-			free(xdg_config);
-		}
-	}
-	else if (use_system_config)
-		given_config_source.file = git_etc_gitconfig();
-	else if (use_local_config)
-		given_config_source.file = git_pathdup("config");
-	else if (use_worktree_config) {
-		given_config_source.file = get_worktree_config(the_repository);
-		if (!given_config_source.file)
-			die(_("--worktree cannot be used with multiple "
-			      "working trees unless the config\n"
-			      "extension worktreeConfig is enabled. "
-			      "Please read \"CONFIGURATION FILE\"\n"
-			      "section in \"git help worktree\" for details"));
-	}
+	set_config_source_file();
 
 	if (respect_includes_opt == -1)
 		config_options.respect_includes = !given_config_source.file;
-- 
2.20.0.482.g66447595a7


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 6/6] config: add --move-to
  2018-12-27 15:56 [PATCH 0/6] Add "git config --move-to" Nguyễn Thái Ngọc Duy
                   ` (4 preceding siblings ...)
  2018-12-27 15:56 ` [PATCH 5/6] config: factor out set_config_source_file() Nguyễn Thái Ngọc Duy
@ 2018-12-27 15:56 ` Nguyễn Thái Ngọc Duy
  5 siblings, 0 replies; 10+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2018-12-27 15:56 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This option can be used to move one or multiple variables from one
place to another, e.g. to move some aliases from repo config to user
config, or from repo config to per-worktree config.

This will be useful for moving config variables around when
extensions.worktreeConfig is enabled. E.g.

    git config --local --move-to --worktree core.worktree
    git config --local --move-glob-to --worktree 'submodule.*.*'

The implementation is definitely not the best. We could for example
lock both source and destination files before doing any update, and
perhaps edit these files just once instead of once per key.

But it adds a lot more complication to config update code. Let's stay
with something simple for now. It's not worse than scripting using
"git config". Optimization could be done later.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-config.txt   |  12 ++++
 Documentation/git-worktree.txt |  16 +++--
 builtin/config.c               | 120 +++++++++++++++++++++++++++++++++
 t/t1300-config.sh              |  54 +++++++++++++++
 4 files changed, 196 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 1bfe9f56a7..495bb57416 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -20,6 +20,9 @@ SYNOPSIS
 '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>] --move-to name
+'git config' [<file-option>] --move-regexp-to name-regexp
+'git config' [<file-option>] --move-glob-to name-glob
 'git config' [<file-option>] [--show-origin] [-z|--null] [--name-only] -l | --list
 'git config' [<file-option>] --get-color name [default]
 'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
@@ -161,6 +164,15 @@ See also <<FILES>>.
 --unset-all::
 	Remove all lines matching the key from config file.
 
+--move-to::
+--move-regexp-to::
+--move-glob-to::
+	Move a config variable (or multiple variables matching the
+	given regular expression or glob pattern) to a new file. Any
+	option about the config file location after `--move-to` or
+	`--move-to-regexp` specifies the move destination. Existing
+	config of the same name remains.
+
 -l::
 --list::
 	List all variables set in config file, along with their values.
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index cb86318f3e..aae8e1d8b2 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -252,13 +252,17 @@ rev-parse --git-path config.worktree`. You can add or update
 configuration in this file with `git config --worktree`. Older Git
 versions will refuse to access repositories with this extension.
 
-Note that in this file, the exception for `core.bare` and `core.worktree`
-is gone. If you have them in $GIT_DIR/config before, you must move
-them to the `config.worktree` of the main working tree. You may also
-take this opportunity to review and move other configuration that you
-do not want to share to all working trees:
+Note that in this file, the exception for `core.bare` and
+`core.worktree` is gone. If you have them in $GIT_DIR/config before,
+you must move them to the `config.worktree` of the main working tree.
 
- - `core.worktree` and `core.bare` should never be shared
+------------
+$ git config --local --move-to --worktree core.bare
+$ git config --local --move-to --worktree core.worktree
+------------
+
+You may also take this opportunity to review and move other
+configuration that you do not want to share to all working trees:
 
  - `core.sparseCheckout` is recommended per working tree, unless you
    are sure you always use sparse checkout for all working trees.
diff --git a/builtin/config.c b/builtin/config.c
index 61a6a5a0e1..20266824f3 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -26,6 +26,7 @@ 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 move_source;
 static struct git_config_source given_config_source;
 static int actions, type;
 static char *default_value;
@@ -50,6 +51,9 @@ static int show_origin;
 #define ACTION_GET_COLOR (1<<13)
 #define ACTION_GET_COLORBOOL (1<<14)
 #define ACTION_GET_URLMATCH (1<<15)
+#define ACTION_MOVE (1<<16)
+#define ACTION_MOVE_REGEXP (1<<17)
+#define ACTION_MOVE_GLOB (1<<18)
 
 /*
  * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
@@ -178,6 +182,25 @@ static int option_parse_type(const struct option *opt, const char *arg,
 	return 0;
 }
 
+static int option_move_cb(const struct option *opt,
+			  const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+
+	set_config_source_file();
+	memcpy(&move_source, &given_config_source, sizeof(move_source));
+
+	memset(&given_config_source, 0, sizeof(given_config_source));
+	use_global_config = 0;
+	use_system_config = 0;
+	use_local_config = 0;
+	use_worktree_config = 0;
+
+	actions = opt->defval;
+	return 0;
+}
+
 static struct option builtin_config_options[] = {
 	OPT_GROUP(N_("Config file location")),
 	OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
@@ -197,6 +220,18 @@ static struct option builtin_config_options[] = {
 	OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL),
 	OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
 	OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
+	{ OPTION_CALLBACK, 0, "move-to", NULL, NULL,
+	  N_("move a variable to a different config file"),
+	  PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+	  option_move_cb, ACTION_MOVE },
+	{ OPTION_CALLBACK, 0, "move-regexp-to", NULL, NULL,
+	  N_("move matching variables to a different config file"),
+	  PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+	  option_move_cb, ACTION_MOVE_REGEXP },
+	{ OPTION_CALLBACK, 0, "move-glob-to", NULL, NULL,
+	  N_("move matching variables to a different config file"),
+	  PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+	  option_move_cb, ACTION_MOVE_GLOB },
 	OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
 	OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
 	OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
@@ -426,6 +461,84 @@ static int get_value(const char *key_, const char *regex_)
 	return ret;
 }
 
+struct move_config_cb {
+	struct string_list keys;
+	const char *key;
+	regex_t key_re;
+};
+
+static int collect_move_config(const char *key, const char *value, void *cb)
+{
+	struct move_config_cb *data = cb;
+
+	switch (actions) {
+	case ACTION_MOVE:
+		if (strcasecmp(data->key, key))
+			return 0;
+		break;
+	case ACTION_MOVE_REGEXP:
+		if (regexec(&data->key_re, key, 0, NULL, 0))
+			return 0;
+		break;
+	case ACTION_MOVE_GLOB:
+		if (wildmatch(data->key, key, WM_CASEFOLD))
+			return 0;
+		break;
+	default:
+		BUG("action %d cannot get here", actions);
+	}
+
+	string_list_append(&data->keys, key)->util = xstrdup(value);
+	return 0;
+}
+
+static int move_config(const char *key)
+{
+	struct move_config_cb cb;
+	int i, ret = 0;
+
+	config_options.respect_includes = 0;
+	if (!move_source.file && !move_source.use_stdin && !move_source.blob)
+		die(_("unknown config source"));
+
+	string_list_init(&cb.keys, 1);
+	cb.key = key;
+	if (actions == ACTION_MOVE_REGEXP &&
+	    regcomp(&cb.key_re, key, REG_EXTENDED | REG_ICASE))
+		die(_("invalid key pattern: %s"), key);
+
+	config_with_options(collect_move_config, &cb,
+			    &move_source, &config_options);
+
+	for (i = 0; i < cb.keys.nr && !ret; i++) {
+		const char *key = cb.keys.items[i].string;
+		const char *value = cb.keys.items[i].util;
+		const char *dest = given_config_source.file;
+
+		ret = git_config_set_multivar_in_file_gently(
+			dest, key, value, CONFIG_REGEX_NONE, 0);
+	}
+
+	/*
+	 * OK all keys have been copied successfully, time to delete
+	 * old ones
+	 */
+	if (!ret && move_source.file) {
+		for (i = 0; i < cb.keys.nr; i++) {
+			const char *key = cb.keys.items[i].string;
+			const char *src = move_source.file;
+
+			git_config_set_multivar_in_file_gently(
+				src, key, NULL, NULL, 1);
+		}
+	}
+
+	string_list_clear(&cb.keys, 1);
+	if (actions == ACTION_MOVE_REGEXP)
+		regfree(&cb.key_re);
+	return ret;
+}
+
 static char *normalize_value(const char *key, const char *value)
 {
 	if (!value)
@@ -862,6 +975,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 			color_stdout_is_tty = git_config_bool("command line", argv[1]);
 		return get_colorbool(argv[0], argc == 2);
 	}
+	else if (actions == ACTION_MOVE ||
+		 actions == ACTION_MOVE_REGEXP ||
+		 actions == ACTION_MOVE_GLOB) {
+		check_write();
+		check_argc(argc, 1, 1);
+		return move_config(argv[0]);
+	}
 
 	return 0;
 }
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 9652b241c7..6969e6092b 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -1844,4 +1844,58 @@ test_expect_success '--replace-all does not invent newlines' '
 	test_cmp expect .git/config
 '
 
+test_expect_success '--move-to moves keys' '
+	test_when_finished rm -f dest &&
+	git config single.foo bar &&
+	git config multi.foo bar1 &&
+	git config --add multi.foo bar2 &&
+	git config --local --move-to -f dest SINGLE.foo &&
+	! git config single.foo &&
+	test_cmp_config bar -f dest single.foo &&
+	git config --local --move-to -f dest multi.FOO &&
+	! git config multi.foo &&
+	git config -f dest --get-all multi.foo | sort >actual &&
+	cat >expected <<-\EOF &&
+	bar1
+	bar2
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success '--move-regexp-to moves keys' '
+	test_when_finished rm -f dest &&
+	git config single.foo bar &&
+	git config multi.foo bar1 &&
+	git config --add multi.foo bar2 &&
+	git config --local --move-regexp-to -f dest "S.*OO" &&
+	! git config single.foo &&
+	test_cmp_config bar -f dest single.foo &&
+	git config --local --move-regexp-to -f dest ^multi &&
+	! git config multi.foo &&
+	git config -f dest --get-all multi.foo | sort >actual &&
+	cat >expected <<-\EOF &&
+	bar1
+	bar2
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success '--move-glob-to moves keys' '
+	test_when_finished rm -f dest &&
+	git config single.foo bar &&
+	git config multi.foo bar1 &&
+	git config --add multi.foo bar2 &&
+	git config --local --move-glob-to -f dest "SINGLE.*" &&
+	! git config single.foo &&
+	test_cmp_config bar -f dest single.foo &&
+	git config --local --move-glob-to -f dest "m*.foo" &&
+	! git config multi.foo &&
+	git config -f dest --get-all multi.foo | sort >actual &&
+	cat >expected <<-\EOF &&
+	bar1
+	bar2
+	EOF
+	test_cmp expected actual
+'
+
 test_done
-- 
2.20.0.482.g66447595a7


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/6] worktree.c: add get_worktree_config()
  2018-12-27 15:56 ` [PATCH 2/6] worktree.c: add get_worktree_config() Nguyễn Thái Ngọc Duy
@ 2018-12-27 20:36   ` Eric Sunshine
  0 siblings, 0 replies; 10+ messages in thread
From: Eric Sunshine @ 2018-12-27 20:36 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git List

On Thu, Dec 27, 2018 at 10:56 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> "git config --worktree" can write to the write file whether

s/write file/right file/

> extensions.worktreeConfig is enabled or not. In order to do the same
> using config API, we need to determine the right file to write to. Add
> this function for that purpose. This is the basis for the coming
> repo_config_set_worktree()
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/6] config: use OPT_FILENAME()
  2018-12-27 15:56 ` [PATCH 4/6] config: use OPT_FILENAME() Nguyễn Thái Ngọc Duy
@ 2018-12-27 20:40   ` Eric Sunshine
  0 siblings, 0 replies; 10+ messages in thread
From: Eric Sunshine @ 2018-12-27 20:40 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git List

On Thu, Dec 27, 2018 at 10:56 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> Do not handle prefix directly. It's simpler to use OPT_FILENAME()
> instead. The othe reason for doing this is because this code (where

s/othe/other/

> the deleted code is) will be factored out and called when "prefix" is
> not available.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/6] config.c: avoid git_path() in do_git_config_sequence()
  2018-12-27 15:56 ` [PATCH 1/6] config.c: avoid git_path() in do_git_config_sequence() Nguyễn Thái Ngọc Duy
@ 2019-01-10 21:38   ` Junio C Hamano
  0 siblings, 0 replies; 10+ messages in thread
From: Junio C Hamano @ 2019-01-10 21:38 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> This function has both $GIT_COMMON_DIR and $GIT_DIR in "opts". Use it
> to construct config.worktree path instead because git_pathdup() is
> tied to the current worktree, but the given $GIT_DIR could be from
> another one.

Given that git_pathdup() does

	do_git_path(the_repository, NULL, pathname, fmt, args);

I am not sure what you mean by "tied to the current worktree"
above.  As do_git_path() uses strbuf_worktree_gitdir() that does

	if (!wt)
		strbuf_addstr(buf, repo->gitdir);

you meant s/current worktree/current repository/, perhaps?  In which
case I think the description makes sense to me.

Thanks.


>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  config.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
>
> diff --git a/config.c b/config.c
> index ff521eb27a..79fbe65da8 100644
> --- a/config.c
> +++ b/config.c
> @@ -1665,6 +1665,7 @@ static int do_git_config_sequence(const struct config_options *opts,
>  	char *xdg_config = xdg_config_home("config");
>  	char *user_config = expand_user_path("~/.gitconfig", 0);
>  	char *repo_config;
> +	char *worktree_config;
>  
>  	if (opts->commondir)
>  		repo_config = mkpathdup("%s/config", opts->commondir);
> @@ -1672,6 +1673,10 @@ static int do_git_config_sequence(const struct config_options *opts,
>  		BUG("git_dir without commondir");
>  	else
>  		repo_config = NULL;
> +	if (repository_format_worktree_config)
> +		worktree_config = mkpathdup("%s/config.worktree", opts->git_dir);
> +	else
> +		worktree_config = NULL;
>  
>  	current_parsing_scope = CONFIG_SCOPE_SYSTEM;
>  	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
> @@ -1693,12 +1698,8 @@ static int do_git_config_sequence(const struct config_options *opts,
>  	 * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE.
>  	 * But let's not complicate things before it's actually needed.
>  	 */
> -	if (repository_format_worktree_config) {
> -		char *path = git_pathdup("config.worktree");
> -		if (!access_or_die(path, R_OK, 0))
> -			ret += git_config_from_file(fn, path, data);
> -		free(path);
> -	}
> +	if (worktree_config && !access_or_die(worktree_config, R_OK, 0))
> +		ret += git_config_from_file(fn, worktree_config, data);
>  
>  	current_parsing_scope = CONFIG_SCOPE_CMDLINE;
>  	if (git_config_from_parameters(fn, data) < 0)
> @@ -1708,6 +1709,7 @@ static int do_git_config_sequence(const struct config_options *opts,
>  	free(xdg_config);
>  	free(user_config);
>  	free(repo_config);
> +	free(worktree_config);
>  	return ret;
>  }

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, back to index

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-27 15:56 [PATCH 0/6] Add "git config --move-to" Nguyễn Thái Ngọc Duy
2018-12-27 15:56 ` [PATCH 1/6] config.c: avoid git_path() in do_git_config_sequence() Nguyễn Thái Ngọc Duy
2019-01-10 21:38   ` Junio C Hamano
2018-12-27 15:56 ` [PATCH 2/6] worktree.c: add get_worktree_config() Nguyễn Thái Ngọc Duy
2018-12-27 20:36   ` Eric Sunshine
2018-12-27 15:56 ` [PATCH 3/6] config.c: add repo_config_set_worktree_gently() Nguyễn Thái Ngọc Duy
2018-12-27 15:56 ` [PATCH 4/6] config: use OPT_FILENAME() Nguyễn Thái Ngọc Duy
2018-12-27 20:40   ` Eric Sunshine
2018-12-27 15:56 ` [PATCH 5/6] config: factor out set_config_source_file() Nguyễn Thái Ngọc Duy
2018-12-27 15:56 ` [PATCH 6/6] config: add --move-to Nguyễn Thái Ngọc Duy

git@vger.kernel.org mailing list mirror (one of many)

Archives are clonable:
	git clone --mirror https://public-inbox.org/git
	git clone --mirror http://ou63pmih66umazou.onion/git
	git clone --mirror http://czquwvybam4bgbro.onion/git
	git clone --mirror http://hjrcffqmbrq6wope.onion/git

Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.version-control.git
	nntp://ou63pmih66umazou.onion/inbox.comp.version-control.git
	nntp://czquwvybam4bgbro.onion/inbox.comp.version-control.git
	nntp://hjrcffqmbrq6wope.onion/inbox.comp.version-control.git
	nntp://news.gmane.org/gmane.comp.version-control.git

 note: .onion URLs require Tor: https://www.torproject.org/
       or Tor2web: https://www.tor2web.org/

AGPL code for this site: git clone https://public-inbox.org/ public-inbox