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: "Junio C Hamano" <gitster@pobox.com>,
	max@max630.net, git@drmicha.warpmail.net, Jens.Lehmann@web.de,
	"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH v3 6/6] worktree add: switch to worktree version 1
Date: Tue, 26 Jan 2016 18:44:45 +0700	[thread overview]
Message-ID: <1453808685-21235-7-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1453808685-21235-1-git-send-email-pclouds@gmail.com>

The most obvious use case is, "git worktree add" creates the first
linked worktree. In this case, we should be able to move to latest
worktree version. The following happens:

 - common/config is created with extensions.worktree 1 and
   core.repositoryformatversion 1

 - all config keys except a few per-worktree are moved to
   common/config

 - per-worktree keys stay with the main worktree's config file

 - the main worktree config file also has worktree version explicitly
   set to 1. This is to prevent older Git binaries from reading it.

What if the repo already has another linked worktree and the user
wants to stay at version 0, maybe because multiple git binaries can
access this repo? "worktree add --version=0" can be used, but it's
really not recommended to stay at lower (and buggy) version.

A note about core.bare staying per-worktree. On the surface it does
not make sense for core.bare to be worktree specific. It's made so in
order to "grow" new worktrees from a bare repo. In these new linked
worktrees, core.bare will be hidden away and worktree-related commands
won't complain about bare repository.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt |  9 +++-
 builtin/worktree.c             | 95 ++++++++++++++++++++++++++++++++++++++++++
 t/t2028-worktree-config.sh     | 47 ++++++++++++++++++++-
 3 files changed, 148 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 6082d4d..0d7d523 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -9,7 +9,7 @@ git-worktree - Manage multiple working trees
 SYNOPSIS
 --------
 [verse]
-'git worktree add' [-f] [--detach] [-b <new-branch>] <path> [<branch>]
+'git worktree add' [-f] [--detach] [-b <new-branch>] [--version=<N>] <path> [<branch>]
 'git worktree prune' [-n] [-v] [--expire <expire>]
 'git worktree list' [--porcelain]
 
@@ -89,6 +89,13 @@ OPTIONS
 	With `add`, detach HEAD in the new working tree. See "DETACHED HEAD"
 	in linkgit:git-checkout[1].
 
+--version=<N>::
+	By default when a new working directory is added, worktree
+	layout is automatically migrated to latest version. This
+	option can be used to specify only migrate to the specified
+	version, or no migrate at all if it's already current worktree
+	version.
+
 -n::
 --dry-run::
 	With `prune`, do not remove anything; just report what it would
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 475b958..551fe37 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -140,6 +140,7 @@ static char *junk_work_tree;
 static char *junk_git_dir;
 static int is_junk;
 static pid_t junk_pid;
+static int target_version = 1;
 
 static void remove_junk(void)
 {
@@ -184,6 +185,93 @@ static const char *worktree_basename(const char *path, int *olen)
 	return name;
 }
 
+struct key_data {
+	const char *key;
+	char *value;
+};
+
+static int get_one_key(const char *key, const char *value, void *cb)
+{
+	struct key_data *kd = cb;
+
+	if (!strcmp(key, kd->key))
+		kd->value = xstrdup(value);
+
+	return 0;
+}
+
+static char *get_key(const char *file, const char *key)
+{
+	struct key_data kd;
+
+	kd.key = key;
+	kd.value = NULL;
+	if (git_config_from_file(get_one_key, file, &kd))
+		return NULL;
+	return kd.value;
+}
+
+static void migrate_worktree_layout(void)
+{
+	const char *per_wortree_keys[] = {
+		"core.bare",
+		"core.ignorestat",
+		"core.sparsecheckout",
+		"core.worktree",
+		NULL
+	};
+	struct strbuf sb = STRBUF_INIT;
+	const char **key_p;
+
+	switch (repository_format_worktree_version) {
+	case 0:
+		strbuf_addf(&sb, "%s/common", get_git_common_dir());
+		if (mkdir_in_gitdir(sb.buf))
+			die_errno(_("failed to create directory %s"), sb.buf);
+		if (repository_format_version < 1 &&
+		    git_config_set("core.repositoryformatversion", "1"))
+			die(_("failed to set core.repositoryformatversion to one"));
+		if (git_config_set("extensions.worktree", "1"))
+			die(_("failed to set extensions.worktree to one"));
+		strbuf_addstr(&sb, "/config");
+		if (rename(git_path("config"), sb.buf))
+			die_errno(_("failed to set move config file to %s"),
+				  sb.buf);
+		for (key_p = per_wortree_keys; *key_p; key_p++) {
+			const char *key = *key_p;
+			char *value = get_key(sb.buf, key);
+
+			if (value) {
+				if (git_config_set(key, value))
+					die(_("failed to keep %s in main worktree's config file"), key);
+				if (git_config_set_in_file(sb.buf, key, NULL))
+					die(_("failed to delete %s in shared config file"), key);
+				free(value);
+			}
+		}
+
+		/*
+		 * we're still in version 0 in this process, this will
+		 * create a new file $GIT_COMMON_DIR/config with only
+		 * one key, extensions.worktree. This will force old
+		 * git binaries that do not understand v1 to bail out.
+		 */
+		if (repository_format_version < 1 &&
+		    git_config_set("core.repositoryformatversion", "1"))
+			die(_("failed to set core.repositoryformatversion to one"));
+		if (git_config_set("extensions.worktree", "1"))
+			die(_("failed to set extensions.worktree to one"));
+
+		repository_format_worktree_version = 1;
+		break;
+	case 1:
+		break;
+	default:
+		die(_("unsupported worktree format version %d"),
+		    repository_format_worktree_version);
+	}
+}
+
 static int add_worktree(const char *path, const char *refname,
 			const struct add_opts *opts)
 {
@@ -297,6 +385,9 @@ static int add_worktree(const char *path, const char *refname,
 		free(junk_git_dir);
 		junk_work_tree = NULL;
 		junk_git_dir = NULL;
+
+		while (repository_format_worktree_version < target_version)
+			migrate_worktree_layout();
 	}
 done:
 	strbuf_reset(&sb);
@@ -322,6 +413,8 @@ static int add(int ac, const char **av, const char *prefix)
 		OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
 			   N_("create or reset a branch")),
 		OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
+		OPT_INTEGER(0, "version", &target_version,
+			   N_("stay at this worktree version")),
 		OPT_END()
 	};
 
@@ -331,6 +424,8 @@ static int add(int ac, const char **av, const char *prefix)
 		die(_("-b, -B, and --detach are mutually exclusive"));
 	if (ac < 1 || ac > 2)
 		usage_with_options(worktree_usage, options);
+	if (target_version < 0 || target_version > 1)
+		die(_("invalid worktree version %d"), target_version);
 
 	path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];
 	branch = ac < 2 ? "HEAD" : av[1];
diff --git a/t/t2028-worktree-config.sh b/t/t2028-worktree-config.sh
index d11b2ce..0d4cb8e 100755
--- a/t/t2028-worktree-config.sh
+++ b/t/t2028-worktree-config.sh
@@ -6,8 +6,8 @@ test_description="config file in multi worktree"
 
 test_expect_success 'setup' '
 	test_commit start &&
-	git worktree add wt1 &&
-	git worktree add wt2
+	git worktree add --version=0 wt1 &&
+	git worktree add --version=0 wt2
 '
 
 test_expect_success 'main config is shared in version 0' '
@@ -58,4 +58,47 @@ test_expect_success 'config --repo on v1' '
 	test_cmp expected actual
 '
 
+test_expect_success 'prepare worktree v0' '
+	test_create_repo repo-v0 &&
+	(
+		cd repo-v0 &&
+		test_commit v0 &&
+		git config core.sparsecheckout true &&
+		git config core.ignorestat true &&
+		git config core.worktree "$TEST_DIRECTORY" &&
+		git config share.key value
+	)
+'
+
+test_expect_success 'migrate v0 to v1' '
+	git -C repo-v0 worktree add --version=1 wt
+'
+
+test_expect_success 'after migration: main wortree has extensions.worktree' '
+	test "`git -C repo-v0 config core.repositoryformatversion`" = 1 &&
+	test "`git -C repo-v0 config extensions.worktree`" = 1
+'
+
+test_expect_success 'after migration: linked wortree has extensions.worktree' '
+	test "`git -C repo-v0/wt config core.repositoryformatversion`" = 1 &&
+	test "`git -C repo-v0/wt config extensions.worktree`" = 1
+'
+
+test_expect_success 'after migration: main wortree keeps per-worktree vars' '
+	test "`git -C repo-v0 config core.sparsecheckout`" = true &&
+	test "`git -C repo-v0 config core.ignorestat`" = true &&
+	test "`git -C repo-v0 config core.worktree`" = "$TEST_DIRECTORY"
+'
+
+test_expect_success 'after migration: linked wortree has no per-worktree vars' '
+	test_must_fail git -C repo-v0/wt config core.sparsecheckout &&
+	test_must_fail git -C repo-v0/wt config core.ignorestat &&
+	test_must_fail git -C repo-v0/wt config core.worktree
+'
+
+test_expect_success 'after migration: shared vars are shared' '
+	test "`git -C repo-v0 config share.key`" = value &&
+	test "`git -C repo-v0/wt config share.key`" = value
+'
+
 test_done
-- 
2.7.0.288.g1d8ad15

  parent reply	other threads:[~2016-01-26 11:45 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 ` [PATCH 2/5] config.c: move worktree-specific variables to .git/worktrees/ Nguyễn Thái Ngọc Duy
2015-12-06  7:47   ` 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     ` Nguyễn Thái Ngọc Duy [this message]
2016-02-01  5:33       ` [PATCH v3 6/6] worktree add: switch to worktree version 1 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=1453808685-21235-7-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=gitster@pobox.com \
    --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).