git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Michael J Gruber" <git@drmicha.warpmail.net>,
	"Max Kirillov" <max@max630.net>,
	"Jens Lehmann" <Jens.Lehmann@web.de>,
	"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH 2/5] config.c: move worktree-specific variables to .git/worktrees/...
Date: Wed,  2 Dec 2015 20:13:43 +0100	[thread overview]
Message-ID: <1449083626-20075-3-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1449083626-20075-1-git-send-email-pclouds@gmail.com>

.git/info/config.worktree is a pattern list that splits .git/config in
to sets: the worktree set matches the patterns, the commmon set does
not.

In normal worktrees, both sets are stored in .git/config. The
config.worktree has no effect. Nothing is changed.

In linked worktrees, the common and worktree sets are read from and
saved to .git/config and .git/config.worktree respectively. Config
keys in .git/config that belong to the worktree set is ignored. Those
are for the main worktree only. Similarly, keys not matching the
patterns come from .git/config, duplicate keys from
.git/config.worktree are ignored.

The effect is similar to the $GIT_DIR/$GIT_COMMON_DIR split, we can
define that some vars can be shared and some cannot. And as a result
of the $GIT_DIR/$GIT_COMMON_DIR split, config.worktree is actually
found at .git/worktrees/<id>/config.worktree.

Throwing the exclude mechanism into this means reading config files
will be slower. But unless somebody reads thousands of keys, it should
not be noticable. The nice thing is we don't have to introduce yet
another pattern syntax.

In future, we might want to have a shared config file to contain
common worktree-specific settings, so that we have some good defaults,
but still allow customization. Or we could twist the above logic a
bit: for linked worktrees, read _all_ variables in config.worktree
regardless of the patterns. But let's wait and see..

Helped-by: Max Kirillov <max@max630.net>
Helped-by: Jens Lehmann <Jens.Lehmann@web.de>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/config.txt               |   4 +
 Documentation/gitrepository-layout.txt |  12 +++
 builtin/config.c                       |   9 +++
 cache.h                                |   2 +-
 config.c                               | 143 +++++++++++++++++++++++++++++++--
 dir.c                                  |   2 +-
 dir.h                                  |   1 +
 setup.c                                |   2 +-
 t/t2025-worktree-add.sh                |  26 ++++++
 9 files changed, 191 insertions(+), 10 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 391a0c3..5c6cd4b 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -8,6 +8,10 @@ is used to store the configuration for that repository, and
 fallback values for the `.git/config` file. The file `/etc/gitconfig`
 can be used to store a system-wide default configuration.
 
+Linked worktrees (see linkgit:git-worktree[1]) also have a
+worktree-specific file "config.worktree". See
+linkgit:gitrepository-layout[5] for more information.
+
 The configuration variables are used by both the Git plumbing
 and the porcelains. The variables are divided into sections, wherein
 the fully qualified variable name of the variable itself is the last
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 577ee84..62f7e33 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -212,6 +212,12 @@ info/sparse-checkout::
 	This file stores sparse checkout patterns.
 	See also: linkgit:git-read-tree[1].
 
+info/config.worktree:
+	This file stores the configuration variable pattern list where
+	$GIT_DIR/config.worktree is used as storage instead of
+	$GIT_DIR/config. The syntax is the same as .gitignore except
+	that '.' is considered the separator instead of '/'.
+
 remotes::
 	Stores shorthands for URL and default refnames for use
 	when interacting with remote repositories via 'git fetch',
@@ -276,6 +282,12 @@ worktrees/<id>/link::
 	file. It is used to detect if the linked repository is
 	manually removed.
 
+worktrees/<id>/config.worktree::
+	This file contains worktree-specific configuration
+	variables. The list of variables is controlled by
+	$GIT_COMMON_DIR/info/config.worktree. The same variables in
+	$GIT_COMMON_DIR/config are ignored.
+
 SEE ALSO
 --------
 linkgit:git-init[1],
diff --git a/builtin/config.c b/builtin/config.c
index adc7727..2b4d56e 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -533,6 +533,15 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 		default:
 			usage_with_options(builtin_config_usage, builtin_config_options);
 		}
+
+	/*
+	 * For set operations, --local could be either config or
+	 * config.worktree. Let config.c determine the path based on
+	 * config keys.
+	 */
+	if (use_local_config && actions != ACTION_LIST)
+		given_config_source.file = NULL;
+
 	if (omit_values &&
 	    !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {
 		error("--name-only is only applicable to --list or --get-regexp");
diff --git a/cache.h b/cache.h
index 3ba0b8f..27bc3bc 100644
--- a/cache.h
+++ b/cache.h
@@ -1468,7 +1468,7 @@ extern void git_config(config_fn_t fn, void *);
 extern int git_config_with_options(config_fn_t fn, void *,
 				   struct git_config_source *config_source,
 				   int respect_includes);
-extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
+extern int git_config_early(config_fn_t fn, void *, const char *repo_config, const char *worktree_config);
 extern int git_parse_ulong(const char *, unsigned long *);
 extern int git_parse_maybe_bool(const char *);
 extern int git_config_int(const char *, const char *);
diff --git a/config.c b/config.c
index 248a21a..75c45e1 100644
--- a/config.c
+++ b/config.c
@@ -13,6 +13,7 @@
 #include "hashmap.h"
 #include "string-list.h"
 #include "utf8.h"
+#include "dir.h"
 
 struct config_source {
 	struct config_source *prev;
@@ -38,6 +39,7 @@ struct config_source {
 };
 
 static struct config_source *cf;
+static struct exclude_list config_local;
 
 static int zlib_compression_seen;
 
@@ -89,6 +91,73 @@ static long config_buf_ftell(struct config_source *conf)
 	return conf->u.buf.pos;
 }
 
+static void load_info_config_worktree(void)
+{
+	struct exclude_list *el = &config_local;
+	struct strbuf sb = STRBUF_INIT;
+	int i, lineno = 1;
+	char *buf, *entry;
+	size_t size;
+
+	clear_exclude_list(el);
+
+	if (strbuf_read_file(&sb,
+			     git_path("info/config.worktree"),
+			     128) <= 0) {
+		strbuf_release(&sb);
+		return;
+	}
+	strbuf_addch(&sb, '\n');
+	el->filebuf = buf = strbuf_detach(&sb, &size);
+
+	for (i = 0; i < size; i++)
+		if (buf[i] == '.')
+			buf[i] = '/';
+		else
+			buf[i] = tolower(buf[i]);
+
+	entry = buf;
+	for (i = 0; i < size; i++) {
+		if (buf[i] == '\n') {
+			if (entry != buf + i && entry[0] != '#') {
+				buf[i - (i && buf[i-1] == '\r')] = 0;
+				trim_trailing_spaces(entry);
+				add_exclude(entry, "", 0, el, lineno);
+			}
+			lineno++;
+			entry = buf + i + 1;
+		}
+	}
+
+	/*
+	 * avoid base name matching because it may confusion in
+	 * non-directory context.
+	 */
+	for (i = 0; i < el->nr; i++)
+		el->excludes[i]->flags &= ~EXC_FLAG_NODIR;
+}
+
+static int is_config_local(const char *key_)
+{
+	static struct strbuf key = STRBUF_INIT;
+	int i, dtype;
+
+	if (!config_local.nr)
+		return 0;
+
+	strbuf_reset(&key);
+	strbuf_addstr(&key, key_);
+	for (i = 0; i < key.len; i++) {
+		if (key.buf[i] == '.')
+			key.buf[i] = '/';
+		else
+			key.buf[i] = tolower(key.buf[i]);
+	}
+	dtype = DT_REG;
+	return is_excluded_from_list(key.buf, key.len, "", &dtype,
+				     &config_local) > 0;
+}
+
 #define MAX_INCLUDE_DEPTH 10
 static const char include_depth_advice[] =
 "exceeded maximum include depth (%d) while including\n"
@@ -1184,7 +1253,29 @@ int git_config_system(void)
 	return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
 }
 
-int git_config_early(config_fn_t fn, void *data, const char *repo_config)
+static int config_worktree_filter_in(const char *var,
+				     const char *value, void *data)
+{
+	struct config_include_data *inc = data;
+
+	if (!is_config_local(var))
+		return error("%s in config.worktree is ignored", var);
+	return inc->fn(var, value, inc->data);
+}
+
+static int config_worktree_filter_out(const char *var,
+				      const char *value, void *data)
+{
+	struct config_include_data *inc = data;
+
+	if (is_config_local(var))
+		return 0;	/* these are for main worktree only */
+
+	return inc->fn(var, value, inc->data);
+}
+
+int git_config_early(config_fn_t fn, void *data, const char *repo_config,
+		     const char *worktree_config)
 {
 	int ret = 0, found = 0;
 	char *xdg_config = xdg_config_home("config");
@@ -1206,7 +1297,25 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 		found += 1;
 	}
 
-	if (repo_config && !access_or_die(repo_config, R_OK, 0)) {
+	if (worktree_config) {
+		struct config_include_data inc = CONFIG_INCLUDE_INIT;
+
+		load_info_config_worktree();
+
+		inc.fn = fn;
+		inc.data = data;
+		if (!access_or_die(worktree_config, R_OK, 0)) {
+			ret += git_config_from_file(config_worktree_filter_in,
+						    worktree_config, &inc);
+			found += 1;
+		}
+
+		if (repo_config && !access_or_die(repo_config, R_OK, 0)) {
+			ret += git_config_from_file(config_worktree_filter_out,
+						    repo_config, &inc);
+			found += 1;
+		}
+	} else if (repo_config && !access_or_die(repo_config, R_OK, 0)) {
 		ret += git_config_from_file(fn, repo_config, data);
 		found += 1;
 	}
@@ -1232,6 +1341,7 @@ int git_config_with_options(config_fn_t fn, void *data,
 			    int respect_includes)
 {
 	char *repo_config = NULL;
+	char *worktree_config = NULL;
 	int ret;
 	struct config_include_data inc = CONFIG_INCLUDE_INIT;
 
@@ -1254,9 +1364,11 @@ int git_config_with_options(config_fn_t fn, void *data,
 		return git_config_from_blob_ref(fn, config_source->blob, data);
 
 	repo_config = git_pathdup("config");
-	ret = git_config_early(fn, data, repo_config);
-	if (repo_config)
-		free(repo_config);
+	if (git_common_dir_env)
+		worktree_config = git_pathdup("config.worktree");
+	ret = git_config_early(fn, data, repo_config, worktree_config);
+	free(repo_config);
+	free(worktree_config);
 	return ret;
 }
 
@@ -1925,6 +2037,24 @@ int git_config_key_is_valid(const char *key)
 	return !git_config_parse_key_1(key, NULL, NULL, 1);
 }
 
+static const char *get_config_filename(const char *config_filename,
+				       const char *key,
+				       char **filename_buf)
+{
+	if (config_filename)
+		return config_filename;
+	if (!git_common_dir_env) {
+		config_filename = *filename_buf = git_pathdup("config");
+		return config_filename;
+	}
+	load_info_config_worktree();
+	if (!is_config_local(key))
+		config_filename = *filename_buf = git_pathdup("config");
+	else
+		config_filename = *filename_buf = git_pathdup("config.worktree");
+	return config_filename;
+}
+
 /*
  * If value==NULL, unset in (remove from) config,
  * if value_regex!=NULL, disregard key/value pairs where value does not match.
@@ -1968,8 +2098,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
 
 	store.multi_replace = multi_replace;
 
-	if (!config_filename)
-		config_filename = filename_buf = git_pathdup("config");
+	config_filename = get_config_filename(config_filename, key, &filename_buf);
 
 	/*
 	 * The lock serves a purpose in addition to locking: the new
diff --git a/dir.c b/dir.c
index b8b4576..9522b89 100644
--- a/dir.c
+++ b/dir.c
@@ -567,7 +567,7 @@ void clear_exclude_list(struct exclude_list *el)
 	memset(el, 0, sizeof(*el));
 }
 
-static void trim_trailing_spaces(char *buf)
+void trim_trailing_spaces(char *buf)
 {
 	char *p, *last_space = NULL;
 
diff --git a/dir.h b/dir.h
index 7b5855d..52ded83 100644
--- a/dir.h
+++ b/dir.h
@@ -248,6 +248,7 @@ extern void clear_exclude_list(struct exclude_list *el);
 extern void clear_directory(struct dir_struct *dir);
 extern int file_exists(const char *);
 
+extern void trim_trailing_spaces(char *buf);
 extern int is_inside_dir(const char *dir);
 extern int dir_inside_of(const char *subdir, const char *dir);
 
diff --git a/setup.c b/setup.c
index d343725..0047d40 100644
--- a/setup.c
+++ b/setup.c
@@ -404,7 +404,7 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
 	 * Use a gentler version of git_config() to check if this repo
 	 * is a good one.
 	 */
-	git_config_early(fn, NULL, repo_config);
+	git_config_early(fn, NULL, repo_config, NULL);
 	if (GIT_REPO_VERSION_READ < repository_format_version) {
 		if (!nongit_ok)
 			die ("Expected git repo version <= %d, found %d",
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index 3694174..e8d8da4 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -198,4 +198,30 @@ test_expect_success 'local clone from linked checkout' '
 	( cd here-clone && git fsck )
 '
 
+test_expect_success 'setting worktree.foo goes to config.worktree' '
+	echo worKtree.Foo >> .git/info/config.worktree &&
+	git worktree add wt.foo HEAD &&
+	git config woRKtree.FOO barrrr &&
+	git --git-dir=wt.foo/.git config woRKtree.FOO bar &&
+	cat >expect <<\EOF &&
+[woRKtree]
+	FOO = bar
+EOF
+	test_cmp expect .git/worktrees/wt.foo/config.worktree &&
+	git --git-dir=wt.foo/.git config woRktree.foo >actual2 &&
+	echo bar >expect2 &&
+	test_cmp expect2 actual2 &&
+	test_path_is_missing .git/config.worktree &&
+	git config WORKTREE.FOO >actual3 &&
+	echo barrrr >expect3 &&
+	test_cmp expect3 actual3
+'
+
+test_expect_success 'shared config still goes to config' '
+	git config random.key randomValue &&
+	git --git-dir=wt.foo/.git config random.key >actual &&
+	echo randomValue >expect &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.2.0.513.g477eb31

  parent reply	other threads:[~2015-12-02 19:14 UTC|newest]

Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-12-02 19:13 [PATCH 0/5] Split .git/config in multiple worktree setup Nguyễn Thái Ngọc Duy
2015-12-02 19:13 ` [PATCH 1/5] dir.c: clean the entire struct in clear_exclude_list() Nguyễn Thái Ngọc Duy
2015-12-02 19:13 ` Nguyễn Thái Ngọc Duy [this message]
2015-12-06  7:47   ` [PATCH 2/5] config.c: move worktree-specific variables to .git/worktrees/ Eric Sunshine
2015-12-06 10:22     ` Duy Nguyen
2015-12-02 19:13 ` [PATCH 3/5] setup.c: remove special case of core.worktree and core.bare Nguyễn Thái Ngọc Duy
2015-12-02 19:13 ` [PATCH 4/5] worktree: make core.sparseCheckout and core.ignoreStat per-worktree Nguyễn Thái Ngọc Duy
2015-12-02 19:13 ` [PATCH 5/5] git-worktree.txt: mention about the config file split Nguyễn Thái Ngọc Duy
2015-12-06  8:02   ` Eric Sunshine
2015-12-03  6:15 ` [PATCH 0/5] Split .git/config in multiple worktree setup Max Kirillov
2015-12-03  8:07   ` Duy Nguyen
2015-12-03 19:52     ` Junio C Hamano
2015-12-03 21:00       ` Max Kirillov
2015-12-03 20:53     ` Max Kirillov
2015-12-04 15:57       ` Duy Nguyen
2015-12-27  3:14 ` [PATCH v2 0/6] " Nguyễn Thái Ngọc Duy
2015-12-27  3:14   ` [PATCH v2 1/6] Define new repo extension to manage multiple worktree behaviors Nguyễn Thái Ngọc Duy
2015-12-27  3:14   ` [PATCH v2 2/6] config.c: move worktree-specific variables to .git/worktrees/ Nguyễn Thái Ngọc Duy
2015-12-27  3:14   ` [PATCH v2 3/6] setup.c: remove special case of core.worktree and core.bare Nguyễn Thái Ngọc Duy
2015-12-27  3:14   ` [PATCH v2 4/6] worktree: make core.sparseCheckout and core.ignoreStat per-worktree Nguyễn Thái Ngọc Duy
2015-12-27  3:14   ` [PATCH v2 5/6] config.c: allow to un-share certain config in multi-worktree setup Nguyễn Thái Ngọc Duy
2015-12-27  3:14   ` [PATCH v2 6/6] worktree: bump worktree version to 1 on "worktree add" Nguyễn Thái Ngọc Duy
2016-01-11 22:43   ` [PATCH v2 0/6] Split .git/config in multiple worktree setup Max Kirillov
2016-01-26 11:44   ` [PATCH v3 " Nguyễn Thái Ngọc Duy
2016-01-26 11:44     ` [PATCH v3 1/6] worktree: new repo extension to manage worktree behaviors Nguyễn Thái Ngọc Duy
2016-01-27 22:12       ` Junio C Hamano
2016-01-28 12:11         ` Duy Nguyen
2016-01-30 14:20         ` Max Kirillov
2016-01-31 16:42           ` Junio C Hamano
2016-02-01  2:41             ` Stefan Monnier
2016-02-01  2:47               ` Stefan Monnier
2016-02-01  5:23               ` Duy Nguyen
2016-02-01 18:19                 ` Junio C Hamano
2016-02-04 18:12                 ` git worktree (was: [PATCH v3 1/6] worktree: new repo extension to manage worktree behaviors) Stefan Monnier
2016-02-01 18:39         ` [PATCH v3 1/6] worktree: new repo extension to manage worktree behaviors Dennis Kaarsemaker
2016-01-30 13:59       ` Max Kirillov
2016-01-26 11:44     ` [PATCH v3 2/6] path.c: new (identical) list for worktree v1 Nguyễn Thái Ngọc Duy
2016-01-27 22:18       ` Junio C Hamano
2016-01-30 14:45       ` Max Kirillov
2016-01-26 11:44     ` [PATCH v3 3/6] worktree: share .git/common in v1 Nguyễn Thái Ngọc Duy
2016-01-26 11:44     ` [PATCH v3 4/6] worktree: new config file hierarchy Nguyễn Thái Ngọc Duy
2016-01-27 22:22       ` Junio C Hamano
2016-01-28 12:03         ` Duy Nguyen
2016-01-28 18:45           ` Junio C Hamano
2016-02-01  5:09             ` Duy Nguyen
2016-01-26 11:44     ` [PATCH v3 5/6] config: select .git/common/config with --repo Nguyễn Thái Ngọc Duy
2016-01-30 22:10       ` Max Kirillov
2016-02-01  5:15         ` Duy Nguyen
2016-01-26 11:44     ` [PATCH v3 6/6] worktree add: switch to worktree version 1 Nguyễn Thái Ngọc Duy
2016-02-01  5:33       ` Max Kirillov
2016-02-01  6:05         ` Duy Nguyen
2016-02-02  5:35           ` Max Kirillov
2016-01-27 22:23     ` [PATCH v3 0/6] Split .git/config in multiple worktree setup Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1449083626-20075-3-git-send-email-pclouds@gmail.com \
    --to=pclouds@gmail.com \
    --cc=Jens.Lehmann@web.de \
    --cc=git@drmicha.warpmail.net \
    --cc=git@vger.kernel.org \
    --cc=max@max630.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).