git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v3 3/6] worktree: add 'init-worktree-config' subcommand

From
Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
Date
Dec 28, 2021, 21:32 UTC
Message-ID
<e2a0a458115a26cfb855f7040f15e5198072b3a5.1640727143.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.1101.v3.git.1640727143.gitgitgadget@gmail.com>
From: Derrick Stolee <dstolee@microsoft.com>

Some features, such as the sparse-checkout builtin, currently use the worktree config extension. It might seem simple to upgrade the repository format and add extensions.worktreeConfig, which is what happens in the sparse-checkout builtin. However, this is overly simplistic and can cause issues in some cases. We will transition away from making this upgrade automatically, but first we will make an easy way for users to upgrade their repositories correctly.

Transitioning from one config file to multiple has some strange side-effects. In particular, if the base repository is bare and the worktree is not, Git knows to treat the worktree as non-bare as a special case when not using worktree config. Once worktree config is enabled, Git stops that special case since the core.bare setting could apply at the worktree config level.

Similarly, the core.worktree config setting is a precursor to the 'git worktree' feature, allowing config to point to a different worktree, presumably temporarily. This is special-cased to be ignored in a worktree, but that case is dropped when worktree config is enabled.

To help resolve this transition, create the 'git worktree init-worktree-config' helper. This new subcommand does the following:

 1. Set core.repositoryFormatVersion to 1 in the common config file.
 2. Set extensions.worktreeConfig to true in the common config file.
 3. If core.bare is true in the common config file, then move that
    setting to the main worktree's config file.
 4. Move the core.worktree config value to the main worktree's config
    file.

If the repository is already configured to use worktree config, then none of these steps happen. This preserves any state that the user might have created on purpose.

Update the documentation to mention this subcommand as the proper way to upgrade to worktree config files.

To gain access to the core repository's config and config.worktree file, we reference a repository struct's 'commondir' member. If the repository was a submodule instead of a worktree, then this still applies correctly.

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
---
 Documentation/git-worktree.txt           | 22 +++++--
 builtin/worktree.c                       | 82 ++++++++++++++++++++++++
 t/t2407-worktree-init-worktree-config.sh | 68 ++++++++++++++++++++
 3 files changed, 167 insertions(+), 5 deletions(-)
 create mode 100755 t/t2407-worktree-init-worktree-config.sh
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 9e862fbcf79..0f0642ac039 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]
+'git worktree init-worktree-config'
 'git worktree list' [-v | --porcelain]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree move' <worktree> <new-path>
@@ -92,6 +93,14 @@ checked out in the new working tree, if it's not checked out anywhere
 else, otherwise the command will refuse to create the working tree (unless
 `--force` is used).
 
+init-worktree-config::
+
+Initialize config settings to enable worktree-specific config settings.
+This will set `core.repositoryFormatversion=1` and enable
+`extensions.worktreeConfig`, which might cause some third-party tools from
+being able to operate on your repository. See CONFIGURATION FILE for more
+details.
+
 list::
 
 List details of each working tree.  The main working tree is listed first,
@@ -290,10 +299,10 @@ already present in the config file, they will be applied to the main
 working trees only.
 
 In order to have configuration specific to working trees, you can turn
-on the `worktreeConfig` extension, e.g.:
+on the `worktreeConfig` extension, using this command:
 
 ------------
-$ git config extensions.worktreeConfig true
+$ git worktree init-worktree-config
 ------------
 
 In this mode, specific configuration stays in the path pointed by `git
@@ -303,9 +312,12 @@ 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 they exist in `$GIT_DIR/config`, 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:
+them to the `config.worktree` of the main working tree. These keys are
+moved automatically when you use the `git worktree init-worktree-config`
+command.
+
+You may also take this opportunity to review and move other configuration
+that you do not want to share to all working trees:
 
  - `core.worktree` and `core.bare` should never be shared
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index a57fcd0f3c5..937ee6fc38b 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -17,6 +17,7 @@
 
 static const char * const worktree_usage[] = {
 	N_("git worktree add [<options>] <path> [<commit-ish>]"),
+	N_("git worktree init-worktree-config"),
 	N_("git worktree list [<options>]"),
 	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree move <worktree> <new-path>"),
@@ -1031,6 +1032,85 @@ static int repair(int ac, const char **av, const char *prefix)
 	return rc;
 }
 
+static int move_config_setting(const char *key, const char *value,
+			       const char *from_file, const char *to_file)
+{
+	if (git_config_set_in_file_gently(to_file, key, value))
+		return error(_("unable to set %s in '%s'"), key, to_file);
+	if (git_config_set_in_file_gently(from_file, key, NULL))
+		return error(_("unable to unset %s in '%s'"), key, from_file);
+	return 0;
+}
+
+static int init_worktree_config(int ac, const char **av, const char *prefix)
+{
+	struct repository *r = the_repository;
+	struct option options[] = {
+		OPT_END()
+	};
+	int res = 0;
+	int bare = 0;
+	struct config_set cs = { 0 };
+	const char *core_worktree;
+	char *common_config_file = xstrfmt("%s/config", r->commondir);
+	char *main_worktree_file = xstrfmt("%s/config.worktree", r->commondir);
+
+	/* Report error on any arguments */
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac)
+		usage_with_options(worktree_usage, options);
+
+	git_configset_init(&cs);
+	git_configset_add_file(&cs, common_config_file);
+
+	/*
+	 * If the format and extension are already enabled, then we can
+	 * skip the upgrade process.
+	 */
+	if (repository_format_worktree_config)
+		return 0;
+
+	if (upgrade_repository_format(r, 1) < 0) {
+		res = error(_("unable to upgrade repository format to enable worktreeConfig"));
+		goto cleanup;
+	}
+	if ((res = git_config_set_gently("extensions.worktreeConfig", "true"))) {
+		error(_("failed to set extensions.worktreeConfig setting"));
+		goto cleanup;
+	}
+
+	/*
+	 * If core.bare is true in the common config file, then we need to
+	 * move it to the base worktree's config file or it will break all
+	 * worktrees. If it is false, then leave it in place because it
+	 * _could_ be negating a global core.bare=true.
+	 */
+	if (!git_configset_get_bool(&cs, "core.bare", &bare) && bare) {
+		if ((res = move_config_setting("core.bare", "true",
+					       common_config_file,
+					       main_worktree_file)))
+			goto cleanup;
+	}
+	/*
+	 * If core.worktree is set, then the base worktree is located
+	 * somewhere different than the parent of the common Git dir.
+	 * Relocate that value to avoid breaking all worktrees with this
+	 * upgrade to worktree config.
+	 */
+	if (!git_configset_get_string_tmp(&cs, "core.worktree", &core_worktree)) {
+		if ((res = move_config_setting("core.worktree", core_worktree,
+					       common_config_file,
+					       main_worktree_file)))
+			goto cleanup;
+	}
+
+cleanup:
+	git_configset_clear(&cs);
+	free(common_config_file);
+	free(main_worktree_file);
+	return res;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -1045,6 +1125,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		prefix = "";
 	if (!strcmp(av[1], "add"))
 		return add(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "init-worktree-config"))
+		return init_worktree_config(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "prune"))
 		return prune(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "list"))
diff --git a/t/t2407-worktree-init-worktree-config.sh b/t/t2407-worktree-init-worktree-config.sh
new file mode 100755
index 00000000000..b3bd0fa1322
--- /dev/null
+++ b/t/t2407-worktree-init-worktree-config.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+test_description='test git worktree init-worktree-config'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	git init base &&
+	test_commit -C base commit &&
+	git -C base worktree add --detach worktree
+'
+
+reset_config_when_finished () {
+	test_when_finished git -C base config --unset core.repositoryFormatVersion &&
+	test_when_finished git -C base config --unset extensions.worktreeConfig &&
+	rm -rf base/.git/config.worktree &&
+	rm -rf base/.git/worktrees/worktree/config.worktree
+}
+
+test_expect_success 'upgrades repo format and adds extension' '
+	reset_config_when_finished &&
+	git -C base worktree init-worktree-config >out 2>err &&
+	test_must_be_empty out &&
+	test_must_be_empty err &&
+	test_cmp_config -C base 1 core.repositoryFormatVersion &&
+	test_cmp_config -C base true extensions.worktreeConfig
+'
+
+test_expect_success 'relocates core.worktree' '
+	reset_config_when_finished &&
+	mkdir dir &&
+	git -C base config core.worktree ../../dir &&
+	git -C base worktree init-worktree-config >out 2>err &&
+	test_must_be_empty out &&
+	test_must_be_empty err &&
+	test_cmp_config -C base 1 core.repositoryFormatVersion &&
+	test_cmp_config -C base true extensions.worktreeConfig &&
+	test_cmp_config -C base ../../dir core.worktree &&
+	test_must_fail git -C worktree core.worktree
+'
+
+test_expect_success 'relocates core.bare' '
+	reset_config_when_finished &&
+	git -C base config core.bare true &&
+	git -C base worktree init-worktree-config >out 2>err &&
+	test_must_be_empty out &&
+	test_must_be_empty err &&
+	test_cmp_config -C base 1 core.repositoryFormatVersion &&
+	test_cmp_config -C base true extensions.worktreeConfig &&
+	test_cmp_config -C base true core.bare &&
+	test_must_fail git -C worktree core.bare
+'
+
+test_expect_success 'skips upgrade is already upgraded' '
+	reset_config_when_finished &&
+	git -C base worktree init-worktree-config &&
+	git -C base config core.bare true &&
+
+	# this should be a no-op, even though core.bare
+	# makes the worktree be broken.
+	git -C base worktree init-worktree-config >out 2>err &&
+	test_must_be_empty out &&
+	test_must_be_empty err &&
+	test_must_fail git -C base config --worktree core.bare &&
+	git -C base config core.bare
+'
+
+test_done
-- 
gitgitgadget
Previous: Derrick Stolee via GitGitGadgetNext: Eric Sunshine
Message 47 of 138 in “Sparse checkout: fix bug with worktree of bare repo”
  1. 0/4 Sparse checkout: fix bug with worktree of bare repoDerrick Stolee via GitGitGadget, Dec 20, 2021
  2. 1/4 setup: use a repository when upgrading formatDerrick Stolee via GitGitGadget, Dec 20, 2021
  3. 2/4 config: make some helpers repo-awareDerrick Stolee via GitGitGadget, Dec 20, 2021
  4. 3/4 config: add repo_config_set_worktree_gently()Derrick Stolee via GitGitGadget, Dec 20, 2021
  5. Derrick StoleeDec 20, 2021
  6. Eric SunshineDec 21, 2021
  7. Eric SunshineDec 21, 2021
  8. Derrick StoleeDec 21, 2021
  9. Eric SunshineDec 21, 2021
  10. Derrick StoleeDec 21, 2021
  11. Eric SunshineDec 21, 2021
  12. 4/4 sparse-checkout: use repo_config_set_worktree_gently()Derrick Stolee via GitGitGadget, Dec 20, 2021
  13. Eric SunshineDec 20, 2021
  14. Derrick StoleeDec 20, 2021
  15. Eric SunshineDec 21, 2021
  16. 0/5 Sparse checkout: fix bug with worktree of bare repoDerrick Stolee via GitGitGadget, Dec 21, 2021
  17. 1/5 setup: use a repository when upgrading formatDerrick Stolee via GitGitGadget, Dec 21, 2021
  18. 2/5 config: make some helpers repo-awareDerrick Stolee via GitGitGadget, Dec 21, 2021
  19. 3/5 worktree: add upgrade_to_worktree_config()Derrick Stolee via GitGitGadget, Dec 21, 2021
  20. Eric SunshineDec 22, 2021
  21. Derrick StoleeDec 28, 2021
  22. Eric SunshineDec 28, 2021
  23. Derrick StoleeDec 28, 2021
  24. Junio C HamanoDec 22, 2021
  25. Derrick StoleeDec 28, 2021
  26. 4/5 config: add repo_config_set_worktree_gently()Derrick Stolee via GitGitGadget, Dec 21, 2021
  27. Eric SunshineDec 22, 2021
  28. 5/5 sparse-checkout: use repo_config_set_worktree_gently()Derrick Stolee via GitGitGadget, Dec 21, 2021
  29. Eric SunshineDec 22, 2021
  30. Eric SunshineDec 22, 2021
  31. Elijah NewrenDec 22, 2021
  32. Eric SunshineDec 27, 2021
  33. Eric SunshineDec 27, 2021
  34. Elijah NewrenDec 27, 2021
  35. Eric SunshineDec 28, 2021
  36. Eric SunshineDec 30, 2021
  37. Elijah NewrenDec 30, 2021
  38. Elijah NewrenDec 27, 2021
  39. Eric SunshineDec 28, 2021
  40. Elijah NewrenDec 28, 2021
  41. Eric SunshineDec 30, 2021
  42. Elijah NewrenDec 30, 2021
  43. Eric SunshineJan 3, 2022
  44. 0/6 Sparse checkout: fix bug with worktree of bare repoDerrick Stolee via GitGitGadget, Dec 28, 2021
  45. 1/6 setup: use a repository when upgrading formatDerrick Stolee via GitGitGadget, Dec 28, 2021
  46. 2/6 config: make some helpers repo-awareDerrick Stolee via GitGitGadget, Dec 28, 2021
  47. 3/6 worktree: add 'init-worktree-config' subcommandDerrick Stolee via GitGitGadget, Dec 28, 2021
  48. Eric SunshineDec 29, 2021
  49. Eric SunshineDec 30, 2021
  50. Derrick StoleeDec 30, 2021
  51. Eric SunshineJan 3, 2022
  52. 4/6 config: add repo_config_set_worktree_gently()Derrick Stolee via GitGitGadget, Dec 28, 2021
  53. 5/6 sparse-checkout: use repo_config_set_worktree_gently()Derrick Stolee via GitGitGadget, Dec 28, 2021
  54. Eric SunshineDec 30, 2021
  55. 6/6 worktree: copy sparse-checkout patterns and config on addDerrick Stolee via GitGitGadget, Dec 28, 2021
  56. Eric SunshineDec 29, 2021
  57. Derrick StoleeDec 29, 2021
  58. Elijah NewrenDec 29, 2021
  59. Derrick StoleeDec 29, 2021
  60. Elijah NewrenDec 29, 2021
  61. Eric SunshineDec 30, 2021
  62. Eric SunshineDec 30, 2021
  63. Elijah NewrenDec 29, 2021
  64. Derrick StoleeDec 29, 2021
  65. Eric SunshineDec 30, 2021
  66. Eric SunshineDec 30, 2021
  67. Derrick StoleeDec 30, 2021
  68. Elijah NewrenDec 30, 2021
  69. Eric SunshineJan 3, 2022
  70. Elijah NewrenDec 30, 2021
  71. 0/5 Sparse checkout: fix bug with worktree of bare repoDerrick Stolee via GitGitGadget, Jan 25, 2022
  72. 2/5 worktree: create init_worktree_config()Derrick Stolee via GitGitGadget, Jan 25, 2022
  73. Elijah NewrenJan 27, 2022
  74. 3/5 config: add repo_config_set_worktree_gently()Derrick Stolee via GitGitGadget, Jan 25, 2022
  75. 1/5 Documentation: add extensions.worktreeConfig detailsDerrick Stolee via GitGitGadget, Jan 25, 2022
  76. Bagas SanjayaJan 26, 2022
  77. Derrick StoleeJan 27, 2022
  78. Elijah NewrenJan 27, 2022
  79. Derrick StoleeJan 27, 2022
  80. 4/5 sparse-checkout: set worktree-config correctlyDerrick Stolee via GitGitGadget, Jan 25, 2022
  81. Elijah NewrenJan 27, 2022
  82. Derrick StoleeJan 27, 2022
  83. 5/5 worktree: copy sparse-checkout patterns and config on addDerrick Stolee via GitGitGadget, Jan 25, 2022
  84. Elijah NewrenJan 27, 2022
  85. Elijah NewrenJan 27, 2022
  86. Derrick StoleeJan 27, 2022
  87. 0/5 Sparse checkout: fix bug with worktree of bare repoDerrick Stolee via GitGitGadget, Jan 31, 2022
  88. 1/5 Documentation: add extensions.worktreeConfig detailsDerrick Stolee via GitGitGadget, Jan 31, 2022
  89. Eric SunshineFeb 6, 2022
  90. 2/5 worktree: create init_worktree_config()Derrick Stolee via GitGitGadget, Jan 31, 2022
  91. Eric SunshineFeb 6, 2022
  92. 3/5 config: add repo_config_set_worktree_gently()Derrick Stolee via GitGitGadget, Jan 31, 2022
  93. 4/5 sparse-checkout: set worktree-config correctlyDerrick Stolee via GitGitGadget, Jan 31, 2022
  94. Eric SunshineFeb 6, 2022
  95. 5/5 worktree: copy sparse-checkout patterns and config on addDerrick Stolee via GitGitGadget, Jan 31, 2022
  96. Jean-Noël AVILAFeb 6, 2022
  97. Derrick StoleeFeb 7, 2022
  98. Jean-Noël AvilaFeb 9, 2022
  99. Derrick StoleeFeb 9, 2022
  100. Eric SunshineFeb 6, 2022
  101. Eric SunshineFeb 6, 2022
  102. Derrick StoleeFeb 7, 2022
  103. Eric SunshineFeb 15, 2022
  104. Derrick StoleeFeb 16, 2022
  105. Elijah NewrenJan 31, 2022
  106. 0/6 Sparse checkout: fix bug with worktree of bare repoDerrick Stolee via GitGitGadget, Feb 7, 2022
  107. 1/6 Documentation: add extensions.worktreeConfig detailsDerrick Stolee via GitGitGadget, Feb 7, 2022
  108. Junio C HamanoFeb 8, 2022
  109. Derrick StoleeFeb 9, 2022
  110. Junio C HamanoFeb 9, 2022
  111. Derrick StoleeFeb 9, 2022
  112. Elijah NewrenFeb 9, 2022
  113. Junio C HamanoFeb 9, 2022
  114. Eric SunshineFeb 15, 2022
  115. Junio C HamanoFeb 16, 2022
  116. Elijah NewrenFeb 9, 2022
  117. Junio C HamanoFeb 9, 2022
  118. 2/6 worktree: create init_worktree_config()Derrick Stolee via GitGitGadget, Feb 7, 2022
  119. Junio C HamanoFeb 8, 2022
  120. Derrick StoleeFeb 9, 2022
  121. Junio C HamanoFeb 9, 2022
  122. Elijah NewrenFeb 9, 2022
  123. 3/6 config: add repo_config_set_worktree_gently()Derrick Stolee via GitGitGadget, Feb 7, 2022
  124. Junio C HamanoFeb 8, 2022
  125. Derrick StoleeFeb 9, 2022
  126. Junio C HamanoFeb 9, 2022
  127. Derrick StoleeFeb 10, 2022
  128. Junio C HamanoFeb 10, 2022
  129. 4/6 sparse-checkout: set worktree-config correctlyDerrick Stolee via GitGitGadget, Feb 7, 2022
  130. 5/6 worktree: copy sparse-checkout patterns and config on addDerrick Stolee via GitGitGadget, Feb 7, 2022
  131. Eric SunshineFeb 15, 2022
  132. 6/6 config: make git_configset_get_string_tmp() privateDerrick Stolee via GitGitGadget, Feb 7, 2022
  133. Elijah NewrenFeb 8, 2022
  134. Eric SunshineFeb 8, 2022
  135. Elijah NewrenFeb 8, 2022
  136. Eric SunshineFeb 8, 2022
  137. Eric SunshineFeb 16, 2022
  138. Derrick StoleeFeb 8, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.