{"thread":{"id":"57125","subject":"[PATCH 0/4] Sparse checkout: fix bug with worktree of bare repo","startedAt":"2021-12-20T15:57:29Z","lastAt":"2022-02-16T13:59:04Z","messageCount":138,"participants":["Derrick Stolee via GitGitGadget","Eric Sunshine","Derrick Stolee","Junio C Hamano","Elijah Newren","Bagas Sanjaya","Jean-Noël AVILA","Jean-Noël Avila"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"444490","messageId":"pull.1101.git.1640015844.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":null,"subject":"[PATCH 0/4] Sparse checkout: fix bug with worktree of bare repo","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-20T15:57:20Z","receivedAt":"2021-12-20T15:57:29Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"This patch series includes a fix to the bug reported by Sean Allred [1] and\ndiagnosed by Eric Sunshine [2].\n\nThe root cause is that 'git sparse-checkout init' writes to the worktree\nconfig without checking that core.bare might need to be set. This only\nmatters when the base repository is bare, since creating the config.worktree\nfile and enabling extensions.worktreeConfig will cause Git to treat the base\nrepo's core.bare=false as important for this worktree.\n\nThis series fixes this, but also puts in place some helpers to prevent this\nfrom happening in the future. While here, some of the config paths are\nmodified to take a repository struct.\n\nThe critical bits are in Patches 3 and 4 which introduce the helper and then\nconsume it in builtin/sparse-checkout.c and sparse-index.c.\n\n[1]\nhttps://lore.kernel.org/git/CABceR4bZmtC4rCwgxZ1BBYZP69VOUca1f_moJoP989vTUZWu9Q@mail.gmail.com/\n[2]\nhttps://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n\nThanks, -Stolee\n\nDerrick Stolee (4):\n  setup: use a repository when upgrading format\n  config: make some helpers repo-aware\n  config: add repo_config_set_worktree_gently()\n  sparse-checkout: use repo_config_set_worktree_gently()\n\n builtin/sparse-checkout.c          | 25 +++++--------\n config.c                           | 56 ++++++++++++++++++++++++++++--\n config.h                           | 13 +++++++\n list-objects-filter-options.c      |  2 +-\n repository.h                       |  2 +-\n setup.c                            |  6 ++--\n sparse-index.c                     | 10 ++----\n t/t1091-sparse-checkout-builtin.sh | 14 +++++++-\n 8 files changed, 95 insertions(+), 33 deletions(-)\n\n\nbase-commit: 69a9c10c95e28df457e33b3c7400b16caf2e2962\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1101%2Fderrickstolee%2Fsparse-checkout%2Fbare-worktree-bug-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1101/derrickstolee/sparse-checkout/bare-worktree-bug-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/1101\n-- \ngitgitgadget\n"},{"id":"444491","messageId":"28813703ff612cd346a49549ce11ba79a47eb223.1640015844.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.git.1640015844.gitgitgadget@gmail.com","subject":"[PATCH 1/4] setup: use a repository when upgrading format","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-20T15:57:21Z","receivedAt":"2021-12-20T15:57:29Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe upgrade_repository_format() helper previously was not aware of the\npossibility of multiple repositories. Add a 'struct repository *'\nparameter so it is possible to call it from a specific repository.\n\nThe implementation already referred to the_repository in one place, so\nthat is an easy replacement. The use of git_config_set() is replaced\nwith a call to repo_config_set().\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/sparse-checkout.c     | 2 +-\n list-objects-filter-options.c | 2 +-\n repository.h                  | 2 +-\n setup.c                       | 6 +++---\n 4 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex d0f5c4702be..34447f87cd8 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -358,7 +358,7 @@ static int set_config(enum sparse_checkout_mode mode)\n {\n \tconst char *config_path;\n \n-\tif (upgrade_repository_format(1) < 0)\n+\tif (upgrade_repository_format(the_repository, 1) < 0)\n \t\tdie(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n \tif (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n \t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\ndiff --git a/list-objects-filter-options.c b/list-objects-filter-options.c\nindex fd8d59f653a..6e21d12045e 100644\n--- a/list-objects-filter-options.c\n+++ b/list-objects-filter-options.c\n@@ -372,7 +372,7 @@ void partial_clone_register(\n \t\t\t */\n \t\t\treturn;\n \t} else {\n-\t\tif (upgrade_repository_format(1) < 0)\n+\t\tif (upgrade_repository_format(the_repository, 1) < 0)\n \t\t\tdie(_(\"unable to upgrade repository format to support partial clone\"));\n \n \t\t/* Add promisor config for the remote */\ndiff --git a/repository.h b/repository.h\nindex 98f95834706..d3fc1f7689d 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -215,6 +215,6 @@ void prepare_repo_settings(struct repository *r);\n  * Return 1 if upgrade repository format to target_version succeeded,\n  * 0 if no upgrade is necessary, and -1 when upgrade is not possible.\n  */\n-int upgrade_repository_format(int target_version);\n+int upgrade_repository_format(struct repository *, int target_version);\n \n #endif /* REPOSITORY_H */\ndiff --git a/setup.c b/setup.c\nindex 347d7181ae9..90516664ce5 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -595,14 +595,14 @@ static int check_repository_format_gently(const char *gitdir, struct repository_\n \treturn 0;\n }\n \n-int upgrade_repository_format(int target_version)\n+int upgrade_repository_format(struct repository *r, int target_version)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct strbuf repo_version = STRBUF_INIT;\n \tstruct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;\n \n-\tstrbuf_git_common_path(&sb, the_repository, \"config\");\n+\tstrbuf_git_common_path(&sb, r, \"config\");\n \tread_repository_format(&repo_fmt, sb.buf);\n \tstrbuf_release(&sb);\n \n@@ -621,7 +621,7 @@ int upgrade_repository_format(int target_version)\n \t\t\t     repo_fmt.unknown_extensions.items[0].string);\n \n \tstrbuf_addf(&repo_version, \"%d\", target_version);\n-\tgit_config_set(\"core.repositoryformatversion\", repo_version.buf);\n+\trepo_config_set(r, \"core.repositoryformatversion\", repo_version.buf);\n \tstrbuf_release(&repo_version);\n \treturn 1;\n }\n-- \ngitgitgadget\n\n"},{"id":"444492","messageId":"3b549770eb9133fc78739ecc4eaba274e138076f.1640015844.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.git.1640015844.gitgitgadget@gmail.com","subject":"[PATCH 2/4] config: make some helpers repo-aware","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-20T15:57:22Z","receivedAt":"2021-12-20T15:57:30Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nAs we prepare to add new config helpers to write into a config.worktree,\nlet's make some existing methods be available for writing to a config\nfile relative to a repository.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n config.c | 29 ++++++++++++++++++++++++++---\n config.h |  7 +++++++\n 2 files changed, 33 insertions(+), 3 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex c5873f3a706..9c9eef16018 100644\n--- a/config.c\n+++ b/config.c\n@@ -2882,7 +2882,12 @@ int git_config_set_gently(const char *key, const char *value)\n \n void git_config_set(const char *key, const char *value)\n {\n-\tgit_config_set_multivar(key, value, NULL, 0);\n+\trepo_config_set(the_repository, key, value);\n+}\n+\n+void repo_config_set(struct repository *r, const char *key, const char *value)\n+{\n+\trepo_config_set_multivar(r, key, value, NULL, 0);\n \n \ttrace2_cmd_set_config(key, value);\n }\n@@ -3177,14 +3182,32 @@ void git_config_set_multivar_in_file(const char *config_filename,\n int git_config_set_multivar_gently(const char *key, const char *value,\n \t\t\t\t   const char *value_pattern, unsigned flags)\n {\n-\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n+\treturn repo_config_set_multivar_gently(the_repository, key, value,\n+\t\t\t\t\t       value_pattern, flags);\n+}\n+\n+int repo_config_set_multivar_gently(struct repository *r, const char *key,\n+\t\t\t\t    const char *value,\n+\t\t\t\t    const char *value_pattern, unsigned flags)\n+{\n+\treturn git_config_set_multivar_in_file_gently(repo_git_path(r, \"config\"),\n+\t\t\t\t\t\t      key, value, value_pattern,\n \t\t\t\t\t\t      flags);\n }\n \n void git_config_set_multivar(const char *key, const char *value,\n \t\t\t     const char *value_pattern, unsigned flags)\n {\n-\tgit_config_set_multivar_in_file(NULL, key, value, value_pattern,\n+\trepo_config_set_multivar(the_repository, key, value,\n+\t\t\t\t value_pattern, flags);\n+}\n+\n+void repo_config_set_multivar(struct repository *r, const char *key,\n+\t\t\t      const char *value, const char *value_pattern,\n+\t\t\t      unsigned flags)\n+{\n+\tgit_config_set_multivar_in_file(repo_git_path(r, \"config\"),\n+\t\t\t\t\tkey, value, value_pattern,\n \t\t\t\t\tflags);\n }\n \ndiff --git a/config.h b/config.h\nindex f119de01309..5531fc018e3 100644\n--- a/config.h\n+++ b/config.h\n@@ -258,6 +258,11 @@ int git_config_set_gently(const char *, const char *);\n  */\n void git_config_set(const char *, const char *);\n \n+/**\n+ * write config values to `.git/config`, takes a key/value pair as parameter.\n+ */\n+void repo_config_set(struct repository *, const char *, const char *);\n+\n int git_config_parse_key(const char *, char **, size_t *);\n \n /*\n@@ -281,6 +286,8 @@ int git_config_parse_key(const char *, char **, size_t *);\n \n int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n+int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n+void repo_config_set_multivar(struct repository *, const char *, const char *, const char *, unsigned);\n int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n \n /**\n-- \ngitgitgadget\n\n"},{"id":"444493","messageId":"67993f6cff254d341ba4ad7fe7709b57eb3e74d4.1640015844.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.git.1640015844.gitgitgadget@gmail.com","subject":"[PATCH 3/4] config: add repo_config_set_worktree_gently()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-20T15:57:23Z","receivedAt":"2021-12-20T15:57:31Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWhen adding config values to the worktree config, we might enable the\nextensions.worktreeConfig setting and create the config.worktree file\nfor the first time. When the base repository is bare, this creates a\nchange of behavior for determining if the worktree is bare or not. A\nworktree off of a bare repository is assumed to be non-bare when\nextensions.worktreeConfig is disabled. When extensions.worktreeConfig is\nenabled but config.worktree is empty, the worktree is considered bare\nbecause the base repo's core.bare=true setting is used.\n\nTo avoid issues like this, create a helper that initializes all the\nright settings in the correct order. A caller will be added in the next\nchange.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n config.c | 27 +++++++++++++++++++++++++++\n config.h |  6 ++++++\n 2 files changed, 33 insertions(+)\n\ndiff --git a/config.c b/config.c\nindex 9c9eef16018..67f3d5015ef 100644\n--- a/config.c\n+++ b/config.c\n@@ -2880,6 +2880,33 @@ int git_config_set_gently(const char *key, const char *value)\n \treturn git_config_set_multivar_gently(key, value, NULL, 0);\n }\n \n+int repo_config_set_worktree_gently(struct repository *r,\n+\t\t\t\t    const char *key, const char *value)\n+{\n+\tint res;\n+\tconst char *config_filename = repo_git_path(r, \"config.worktree\");\n+\n+\t/*\n+\t * Ensure that core.bare reflects the current worktree, since the\n+\t * logic for is_bare_repository() changes if extensions.worktreeConfig\n+\t * is disabled.\n+\t */\n+\tif ((res = git_config_set_multivar_in_file_gently(config_filename, \"core.bare\",\n+\t\t\t\t\t\t\t  r->worktree ? \"false\" : \"true\",\n+\t\t\t\t\t\t\t  NULL, 0))) {\n+\t\terror(_(\"unable to set core.bare setting in worktree config\"));\n+\t\treturn res;\n+\t}\n+\tif (upgrade_repository_format(r, 1) < 0)\n+\t\treturn error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n+\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n+\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n+\t\treturn res;\n+\t}\n+\n+\treturn git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0);\n+}\n+\n void git_config_set(const char *key, const char *value)\n {\n \trepo_config_set(the_repository, key, value);\ndiff --git a/config.h b/config.h\nindex 5531fc018e3..d8db420849e 100644\n--- a/config.h\n+++ b/config.h\n@@ -253,6 +253,12 @@ void git_config_set_in_file(const char *, const char *, const char *);\n \n int git_config_set_gently(const char *, const char *);\n \n+/**\n+ * Write a config value into the config.worktree file for the current\n+ * worktree. This will initialize extensions.worktreeConfig if necessary.\n+ */\n+int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n+\n /**\n  * write config values to `.git/config`, takes a key/value pair as parameter.\n  */\n-- \ngitgitgadget\n\n"},{"id":"444494","messageId":"6202f767f4ae8012ce8cdf78d3234eba478d6681.1640015845.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.git.1640015844.gitgitgadget@gmail.com","subject":"[PATCH 4/4] sparse-checkout: use repo_config_set_worktree_gently()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-20T15:57:24Z","receivedAt":"2021-12-20T15:57:32Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe previous change added repo_config_set_worktree_gently() to assist\nwriting config values into the worktree.config file, especially when\nthat may not have been initialized.\n\nWhen the base repo is bare, running 'git sparse-checkout init' in a\nworktree will create the config.worktree file for the worktree, but that\nwill start causing the worktree to parse the bare repo's core.bare=true\nvalue and start treating the worktree as bare. This causes more problems\nas other commands are run in that worktree.\n\nThe fix is to have this assignment into config.worktree be handled by\nthe repo_config_set_worktree_gently() helper.\n\nReported-by: Sean Allred <allred.sean@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/sparse-checkout.c          | 25 ++++++++-----------------\n sparse-index.c                     | 10 +++-------\n t/t1091-sparse-checkout-builtin.sh | 14 +++++++++++++-\n 3 files changed, 24 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 34447f87cd8..ec2c9a146cc 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -356,26 +356,17 @@ enum sparse_checkout_mode {\n \n static int set_config(enum sparse_checkout_mode mode)\n {\n-\tconst char *config_path;\n-\n-\tif (upgrade_repository_format(the_repository, 1) < 0)\n-\t\tdie(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n-\tif (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n-\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n+\tif (repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckout\",\n+\t\t\t\t\t    mode ? \"true\" : \"false\") ||\n+\t    repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckoutCone\",\n+\t\t\t\t\t    mode == MODE_CONE_PATTERNS ?\n+\t\t\t\t\t\t\"true\" : \"false\"))\n \t\treturn 1;\n-\t}\n-\n-\tconfig_path = git_path(\"config.worktree\");\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckout\",\n-\t\t\t\t      mode ? \"true\" : NULL);\n-\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckoutCone\",\n-\t\t\t\t      mode == MODE_CONE_PATTERNS ? \"true\" : NULL);\n \n \tif (mode == MODE_NO_PATTERNS)\n-\t\tset_sparse_index_config(the_repository, 0);\n+\t\treturn set_sparse_index_config(the_repository, 0);\n \n \treturn 0;\n }\ndiff --git a/sparse-index.c b/sparse-index.c\nindex a1d505d50e9..e93609999e0 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -99,13 +99,9 @@ static int convert_to_sparse_rec(struct index_state *istate,\n \n int set_sparse_index_config(struct repository *repo, int enable)\n {\n-\tint res;\n-\tchar *config_path = repo_git_path(repo, \"config.worktree\");\n-\tres = git_config_set_in_file_gently(config_path,\n-\t\t\t\t\t    \"index.sparse\",\n-\t\t\t\t\t    enable ? \"true\" : NULL);\n-\tfree(config_path);\n-\n+\tint res = repo_config_set_worktree_gently(repo,\n+\t\t\t\t\t\t  \"index.sparse\",\n+\t\t\t\t\t\t  enable ? \"true\" : \"false\");\n \tprepare_repo_settings(repo);\n \trepo->settings.sparse_index = enable;\n \treturn res;\ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex 272ba1b566b..3ff125c5fd6 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -71,6 +71,18 @@ test_expect_success 'git sparse-checkout init' '\n \tcheck_files repo a\n '\n \n+test_expect_success 'init in a worktree of a bare repo' '\n+\ttest_when_finished rm -rf bare worktree &&\n+\tgit clone --bare repo bare &&\n+\tgit -C bare worktree add ../worktree &&\n+\t(\n+\t\tcd worktree &&\n+\t\tgit sparse-checkout init &&\n+\t\ttest_cmp_config false core.bare &&\n+\t\tgit sparse-checkout set /*\n+\t)\n+'\n+\n test_expect_success 'git sparse-checkout list after init' '\n \tgit -C repo sparse-checkout list >actual &&\n \tcat >expect <<-\\EOF &&\n@@ -219,7 +231,7 @@ test_expect_success 'sparse-index enabled and disabled' '\n \t\ttest-tool -C repo read-cache --table >cache &&\n \t\t! grep \" tree \" cache &&\n \t\tgit -C repo config --list >config &&\n-\t\t! grep index.sparse config\n+\t\ttest_cmp_config -C repo false index.sparse\n \t)\n '\n \n-- \ngitgitgadget\n"},{"id":"444499","messageId":"CAPig+cTVzMtiHzkJq7VRg4Xa3xhrq7KKCdK5OSDY6bvwKu_ynA@mail.gmail.com","threadId":"57125","inReplyTo":"pull.1101.git.1640015844.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/4] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-20T16:21:00Z","receivedAt":"2021-12-20T16:21:12Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Dec 20, 2021 at 10:57 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> This patch series includes a fix to the bug reported by Sean Allred [1] and\n> diagnosed by Eric Sunshine [2].\n>\n> The root cause is that 'git sparse-checkout init' writes to the worktree\n> config without checking that core.bare might need to be set. This only\n> matters when the base repository is bare, since creating the config.worktree\n> file and enabling extensions.worktreeConfig will cause Git to treat the base\n> repo's core.bare=false as important for this worktree.\n\nThanks for jumping on this so quickly. Unfortunately, however, as\nmentioned in [1] and [2], I think the approach implemented here of\nsetting `core.bare=false` in the worktree-specific config is\nfundamentally flawed since it only addresses the problem for worktrees\nin which `git sparse-checkout init` has been run, but leaves all other\nworktrees potentially broken (both existing and new worktrees). As far\nas I can see, the _only_ correct solution is for the new helper\nfunction to enable `extensions.worktreeConfig` _and_ relocate\n`core.bare` and `core.worktree` from .git/config to\n.git/worktree.config, thus implementing the requirements documented in\ngit-worktree.txt.\n\nI also raised a separate question in [2] about whether `git\nsparse-checkout init` or the new helper function should be warning the\nuser that upgrading the repository format and setting\n`extensions.worktreeConfig` might break third-party tools. However,\nthat question is tangential to the fix being addressed here and\ndoesn't need to be addressed by this series.\n\n[1]: https://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n[2]: https://lore.kernel.org/git/CAPig+cQPUe9REf+wgVNjyak_nk3V361h-48rTFgk6TGC7vJgOA@mail.gmail.com/\n"},{"id":"444508","messageId":"6d72a020-ded7-6ef2-825c-ce6421194b26@gmail.com","threadId":"57125","inReplyTo":"67993f6cff254d341ba4ad7fe7709b57eb3e74d4.1640015844.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 3/4] config: add repo_config_set_worktree_gently()","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-20T17:32:04Z","receivedAt":"2021-12-20T17:32:11Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/20/2021 10:57 AM, Derrick Stolee via GitGitGadget wrote:\n> From: Derrick Stolee <dstolee@microsoft.com>\n\n...\n\n> +\t/*\n> +\t * Ensure that core.bare reflects the current worktree, since the\n> +\t * logic for is_bare_repository() changes if extensions.worktreeConfig\n> +\t * is disabled.\n> +\t */\n> +\tif ((res = git_config_set_multivar_in_file_gently(config_filename, \"core.bare\",\n> +\t\t\t\t\t\t\t  r->worktree ? \"false\" : \"true\",\n> +\t\t\t\t\t\t\t  NULL, 0))) {\n> +\t\terror(_(\"unable to set core.bare setting in worktree config\"));\n> +\t\treturn res;\n> +\t}\n\nAs mentioned by Eric, this portion isn't correct. It fixes _this_ worktree, but\nany other existing worktrees would become broken.\n\nThe fix would be to detect if the core config file has core.bare=false and then\nto move that setting into the base repo's config.worktree file.\n\nI believe that if we do that change, then the rest of this patch series is valid.\n\nWhat do others think?\n\nThanks,\n-Stolee\n"},{"id":"444509","messageId":"cedea0bf-5def-86a4-5d3b-5377949c1f12@gmail.com","threadId":"57125","inReplyTo":"CAPig+cTVzMtiHzkJq7VRg4Xa3xhrq7KKCdK5OSDY6bvwKu_ynA@mail.gmail.com","subject":"Re: [PATCH 0/4] Sparse checkout: fix bug with worktree of bare repo","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-20T17:34:50Z","receivedAt":"2021-12-20T17:34:58Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/20/2021 11:21 AM, Eric Sunshine wrote:\n> On Mon, Dec 20, 2021 at 10:57 AM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>> This patch series includes a fix to the bug reported by Sean Allred [1] and\n>> diagnosed by Eric Sunshine [2].\n>>\n>> The root cause is that 'git sparse-checkout init' writes to the worktree\n>> config without checking that core.bare might need to be set. This only\n>> matters when the base repository is bare, since creating the config.worktree\n>> file and enabling extensions.worktreeConfig will cause Git to treat the base\n>> repo's core.bare=false as important for this worktree.\n> \n> Thanks for jumping on this so quickly. Unfortunately, however, as\n> mentioned in [1] and [2], I think the approach implemented here of\n> setting `core.bare=false` in the worktree-specific config is\n> fundamentally flawed since it only addresses the problem for worktrees\n> in which `git sparse-checkout init` has been run, but leaves all other\n> worktrees potentially broken (both existing and new worktrees). As far\n> as I can see, the _only_ correct solution is for the new helper\n> function to enable `extensions.worktreeConfig` _and_ relocate\n> `core.bare` and `core.worktree` from .git/config to\n> .git/worktree.config, thus implementing the requirements documented in\n> git-worktree.txt.\n\nThanks for clarifying what I had misread. I commented on Patch 3 at the\nplace that should be changed to relocate the setting. The test in patch 4\ncould have multiple worktrees to verify that it works.\n\nI'll plan on providing a v2 with that change tomorrow, leaving time to\nfind any other glaring errors.\n \n> I also raised a separate question in [2] about whether `git\n> sparse-checkout init` or the new helper function should be warning the\n> user that upgrading the repository format and setting\n> `extensions.worktreeConfig` might break third-party tools. However,\n> that question is tangential to the fix being addressed here and\n> doesn't need to be addressed by this series.\n\nLet's continue to simmer on this one. If there is a clear direction for\ndoing this (should it just be an advice message?) then we can do that\nwhenever.\n \n> [1]: https://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n> [2]: https://lore.kernel.org/git/CAPig+cQPUe9REf+wgVNjyak_nk3V361h-48rTFgk6TGC7vJgOA@mail.gmail.com/\n\nThanks,\n-Stolee\n"},{"id":"444564","messageId":"CAPig+cTuLYFc9fpAe8Uq9fvBYuSGcc9SA1O-q1BRw0DYxDF4Eg@mail.gmail.com","threadId":"57125","inReplyTo":"6d72a020-ded7-6ef2-825c-ce6421194b26@gmail.com","subject":"Re: [PATCH 3/4] config: add repo_config_set_worktree_gently()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-21T00:01:34Z","receivedAt":"2021-12-21T00:01:47Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Dec 20, 2021 at 12:32 PM Derrick Stolee <stolee@gmail.com> wrote:\n> On 12/20/2021 10:57 AM, Derrick Stolee via GitGitGadget wrote:\n> > +     /*\n> > +      * Ensure that core.bare reflects the current worktree, since the\n> > +      * logic for is_bare_repository() changes if extensions.worktreeConfig\n> > +      * is disabled.\n> > +      */\n> > +     if ((res = git_config_set_multivar_in_file_gently(config_filename, \"core.bare\",\n> > +                                                       r->worktree ? \"false\" : \"true\",\n> > +                                                       NULL, 0))) {\n>\n> As mentioned by Eric, this portion isn't correct. It fixes _this_ worktree, but\n> any other existing worktrees would become broken.\n>\n> The fix would be to detect if the core config file has core.bare=false and then\n> to move that setting into the base repo's config.worktree file.\n>\n> I believe that if we do that change, then the rest of this patch series is valid.\n\nSorry, but I'm not following what you're suggesting, and I'm not sure\nwhat you mean by \"core config file\" and \"base repo's config.worktree\nfile\". Also, we aren't specifically concerned that `core.bare=false`.\n\nConceptually the proper fix is quite simple. (Whether the actual\nimplementation is simple is a different question; I haven't looked\nclosely at the code yet to be able to answer that.) First, though,\nlet's make clear what different config files are involved:\n\n.git/config -- config shared by the repository and all worktrees\n(including the main worktree)\n\n.git/config.worktree - config specific to the main worktree (or to the\nrepository itself if bare)\n\n.git/worktrees/<id>/config.worktree -- config specific to worktree <id>\n\nIn the above list, I'm using \".git/\" loosely to mean either a bare\nrepository (i.e. \"bare.git\") or the \".git/\" directory within the main\nworktree; the difference is immaterial to this discussion. When\n`extensions.worktreeConfig` is false or unset, only the first item in\nthe above list is consulted; when `extensions.worktreeConfig` is true,\nthen the `config.worktree` files are consulted, as well (depending\nupon which worktree you're in).\n\nRegarding the actual \"fix\": we want a new utility function which\nenables per-worktree configuration and handles all the required\nbookkeeping actions described in git-worktree.txt. Specifically, if\nper-worktree configuration is not already enabled, the function will\nneed to:\n\n(1) set `extensions.worktreeConfig=true` in .git/config\n\n(1) relocate `core.bare` from .git/config to .git/config.worktree if\nthat key exists\n\n(2) relocate `core.worktree` from .git/config to .git/config.worktree\nif that key exists\n\nThat's it. It doesn't need to create or touch any\n.git/worktrees/<id>/config.worktree file(s); it should _not_ add a\n`core.bare=false` to .git/worktrees/<id>/config.worktree, as this v1\npatch series does.\n"},{"id":"444581","messageId":"CAPig+cT=evew0iePP-TZ+DTJ=oY6hrjiOiDtYqEr6KLRvkqC9Q@mail.gmail.com","threadId":"57125","inReplyTo":"67993f6cff254d341ba4ad7fe7709b57eb3e74d4.1640015844.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 3/4] config: add repo_config_set_worktree_gently()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-21T05:53:19Z","receivedAt":"2021-12-21T05:53:32Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Dec 20, 2021 at 10:57 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> When adding config values to the worktree config, we might enable the\n> extensions.worktreeConfig setting and create the config.worktree file\n> for the first time. When the base repository is bare, this creates a\n> change of behavior for determining if the worktree is bare or not. A\n> worktree off of a bare repository is assumed to be non-bare when\n> extensions.worktreeConfig is disabled. When extensions.worktreeConfig is\n> enabled but config.worktree is empty, the worktree is considered bare\n> because the base repo's core.bare=true setting is used.\n>\n> To avoid issues like this, create a helper that initializes all the\n> right settings in the correct order. A caller will be added in the next\n> change.\n\nAs discussed already in [1], [2], and [3], the solution implemented by\nthis patch is undesirable, and I gave an outline in [4] about how I\nthink the new utility function ought to be implemented instead, so I\nwon't say anything further about that here. However, I do still have\none or two review comments to make about the general approach taken by\npatch. See below...\n\n[1]: https://lore.kernel.org/git/CAPig+cQPUe9REf+wgVNjyak_nk3V361h-48rTFgk6TGC7vJgOA@mail.gmail.com/\n[2]: https://lore.kernel.org/git/CAPig+cTVzMtiHzkJq7VRg4Xa3xhrq7KKCdK5OSDY6bvwKu_ynA@mail.gmail.com/\n[3]: https://lore.kernel.org/git/6d72a020-ded7-6ef2-825c-ce6421194b26@gmail.com/\n[4]: https://lore.kernel.org/git/CAPig+cTuLYFc9fpAe8Uq9fvBYuSGcc9SA1O-q1BRw0DYxDF4Eg@mail.gmail.com/\n\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/config.c b/config.c\n> @@ -2880,6 +2880,33 @@ int git_config_set_gently(const char *key, const char *value)\n> +int repo_config_set_worktree_gently(struct repository *r,\n> +                                   const char *key, const char *value)\n> +{\n> +       int res;\n> +       const char *config_filename = repo_git_path(r, \"config.worktree\");\n> +\n> +       /*\n> +        * Ensure that core.bare reflects the current worktree, since the\n> +        * logic for is_bare_repository() changes if extensions.worktreeConfig\n> +        * is disabled.\n> +        */\n> +       if ((res = git_config_set_multivar_in_file_gently(config_filename, \"core.bare\",\n> +                                                         r->worktree ? \"false\" : \"true\",\n> +                                                         NULL, 0))) {\n> +               error(_(\"unable to set core.bare setting in worktree config\"));\n> +               return res;\n> +       }\n> +       if (upgrade_repository_format(r, 1) < 0)\n> +               return error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n> +       if ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n> +               error(_(\"failed to set extensions.worktreeConfig setting\"));\n> +               return res;\n> +       }\n> +\n> +       return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0);\n> +}\n> diff --git a/config.h b/config.h\n> @@ -253,6 +253,12 @@ void git_config_set_in_file(const char *, const char *, const char *);\n> +/**\n> + * Write a config value into the config.worktree file for the current\n> + * worktree. This will initialize extensions.worktreeConfig if necessary.\n> + */\n> +int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n\nI understand your desire to make this \"setter\" function as transparent\nand simple for clients as possible, however, I think it does too much\nby conflating two very distinct operations (one which changes the\nnature of the repository itself, and one which simply sets a config\nvariable), and is far too magical. It doesn't help that the function\nname gives no indication of just how magical it is, and it is easy to\nimagine people calling this function thinking that it's just a simple\n\"config setter\" like all the other similarly-named functions, without\nrealizing the impact it may have on the repository overall (i.e.\nupgrading to version 1 and changing to per-worktree config).\n\nI would feel much more comfortable for the new utility function to\nhave a single-purpose: namely, to upgrade the repository to a\nper-worktree configuration mode (if not already upgraded) as outlined\nin [4]. That's it. It shouldn't do anything other than that. This way,\ncallers which need per-worktree configuration must call the new\nfunction explicitly to obtain the desired behavior, rather than\ngetting per-worktree configuration as a magical and implicit\nside-effect of calling what looks like a plain old \"config setter\".\nThis should make it easier to reason about. Taking this approach also\nmeans that you don't need to introduce a special-purpose \"config\nsetter\" just for worktrees; instead, you'd use the normal existing\n\"config setter\" functions. For instance, if the new utility function\nis named enable_per_worktree_config(), then `git sparse-checkout init`\nmight do something like this:\n\n    enable_per_worktree_config(r);\n    config_path = repo_git_path(r, \"config.worktree\")\n    git_config_set_in_file_gently(config_path, \"core.sparseCheckout\", ...);\n    git_config_set_in_file_gently(config_path, \"core.sparseCheckoutCone\", ...);\n\n(This, of course, assumes that repo_git_path() latches the changes\nmade by enable_per_worktree_config() so that it \"does the right\nthing\", but it seems that existing code in `git sparse-checkout init`\nis already expecting it to do so, so perhaps it does work that way.)\n"},{"id":"444582","messageId":"CAPig+cT7bf7yyO-BpobSgHiXk703sPEa3n-toNMUG=ixjUqCsw@mail.gmail.com","threadId":"57125","inReplyTo":"CAPig+cTuLYFc9fpAe8Uq9fvBYuSGcc9SA1O-q1BRw0DYxDF4Eg@mail.gmail.com","subject":"Re: [PATCH 3/4] config: add repo_config_set_worktree_gently()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-21T05:59:42Z","receivedAt":"2021-12-21T05:59:54Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Dec 20, 2021 at 7:01 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> Regarding the actual \"fix\": we want a new utility function which\n> enables per-worktree configuration and handles all the required\n> bookkeeping actions described in git-worktree.txt. Specifically, if\n> per-worktree configuration is not already enabled, the function will\n> need to:\n>\n> (1) set `extensions.worktreeConfig=true` in .git/config\n>\n> (1) relocate `core.bare` from .git/config to .git/config.worktree if\n> that key exists\n>\n> (2) relocate `core.worktree` from .git/config to .git/config.worktree\n> if that key exists\n\nA couple additional notes:\n\nFirst, I can't count to three.\n\nSecond, item (0) in the above list would be to upgrade the repository\nto version 1 since that's a prerequisite of using `extensions` (which\nyou know already, but I want to be clear for any other readers that\nthe new utility function should perform this step, as well).\n"},{"id":"444583","messageId":"CAPig+cSW5wmKS1+KG2SWFO86uX24usP4dv3qX4fhKpgeZ8+Cww@mail.gmail.com","threadId":"57125","inReplyTo":"cedea0bf-5def-86a4-5d3b-5377949c1f12@gmail.com","subject":"Re: [PATCH 0/4] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-21T06:10:39Z","receivedAt":"2021-12-21T06:10:52Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Dec 20, 2021 at 12:34 PM Derrick Stolee <stolee@gmail.com> wrote:\n> On 12/20/2021 11:21 AM, Eric Sunshine wrote:\n> > Thanks for jumping on this so quickly. Unfortunately, however, as\n> > mentioned in [1] and [2], I think the approach implemented here of\n> > setting `core.bare=false` in the worktree-specific config is\n> > fundamentally flawed since it only addresses the problem for worktrees\n> > in which `git sparse-checkout init` has been run, but leaves all other\n> > worktrees potentially broken (both existing and new worktrees). As far\n> > as I can see, the _only_ correct solution is for the new helper\n> > function to enable `extensions.worktreeConfig` _and_ relocate\n> > `core.bare` and `core.worktree` from .git/config to\n> > .git/worktree.config, thus implementing the requirements documented in\n> > git-worktree.txt.\n>\n> Thanks for clarifying what I had misread. I commented on Patch 3 at the\n> place that should be changed to relocate the setting. The test in patch 4\n> could have multiple worktrees to verify that it works.\n\nI sent several pages worth of response to patch [3/4] because\n(apparently) I don't know how to be laconic.\n\n> I'll plan on providing a v2 with that change tomorrow, leaving time to\n> find any other glaring errors.\n\nLet's make sure we agree on the proper approach and solution before\nfiring off v2.\n\n> > I also raised a separate question in [2] about whether `git\n> > sparse-checkout init` or the new helper function should be warning the\n> > user that upgrading the repository format and setting\n> > `extensions.worktreeConfig` might break third-party tools. However,\n> > that question is tangential to the fix being addressed here and\n> > doesn't need to be addressed by this series.\n>\n> Let's continue to simmer on this one. If there is a clear direction for\n> doing this (should it just be an advice message?) then we can do that\n> whenever.\n\nIndeed, no hurry on this one. It's entirely tangential to the present\npatch series, and requires discussion and thought; it can be tackled\nlater (if at all).\n"},{"id":"444608","messageId":"73744786-4248-f04f-5d56-11e938d174e7@gmail.com","threadId":"57125","inReplyTo":"CAPig+cTuLYFc9fpAe8Uq9fvBYuSGcc9SA1O-q1BRw0DYxDF4Eg@mail.gmail.com","subject":"Re: [PATCH 3/4] config: add repo_config_set_worktree_gently()","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-21T13:41:11Z","receivedAt":"2021-12-21T13:41:16Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/20/2021 7:01 PM, Eric Sunshine wrote:\n> On Mon, Dec 20, 2021 at 12:32 PM Derrick Stolee <stolee@gmail.com> wrote:\n>> On 12/20/2021 10:57 AM, Derrick Stolee via GitGitGadget wrote:\n>>> +     /*\n>>> +      * Ensure that core.bare reflects the current worktree, since the\n>>> +      * logic for is_bare_repository() changes if extensions.worktreeConfig\n>>> +      * is disabled.\n>>> +      */\n>>> +     if ((res = git_config_set_multivar_in_file_gently(config_filename, \"core.bare\",\n>>> +                                                       r->worktree ? \"false\" : \"true\",\n>>> +                                                       NULL, 0))) {\n>>\n>> As mentioned by Eric, this portion isn't correct. It fixes _this_ worktree, but\n>> any other existing worktrees would become broken.\n>>\n>> The fix would be to detect if the core config file has core.bare=false and then\n>> to move that setting into the base repo's config.worktree file.\n>>\n>> I believe that if we do that change, then the rest of this patch series is valid.\n> \n> Sorry, but I'm not following what you're suggesting, and I'm not sure\n> what you mean by \"core config file\" and \"base repo's config.worktree\n> file\". Also, we aren't specifically concerned that `core.bare=false`.\n> \n> Conceptually the proper fix is quite simple. (Whether the actual\n> implementation is simple is a different question; I haven't looked\n> closely at the code yet to be able to answer that.) First, though,\n> let's make clear what different config files are involved:\n> \n> .git/config -- config shared by the repository and all worktrees\n> (including the main worktree)\n> \n> .git/config.worktree - config specific to the main worktree (or to the\n> repository itself if bare)\n> \n> .git/worktrees/<id>/config.worktree -- config specific to worktree <id>\n> \n> In the above list, I'm using \".git/\" loosely to mean either a bare\n> repository (i.e. \"bare.git\") or the \".git/\" directory within the main\n> worktree; the difference is immaterial to this discussion. When\n> `extensions.worktreeConfig` is false or unset, only the first item in\n> the above list is consulted; when `extensions.worktreeConfig` is true,\n> then the `config.worktree` files are consulted, as well (depending\n> upon which worktree you're in).\n> \n> Regarding the actual \"fix\": we want a new utility function which\n> enables per-worktree configuration and handles all the required\n> bookkeeping actions described in git-worktree.txt. Specifically, if\n> per-worktree configuration is not already enabled, the function will\n> need to:\n> \n> (1) set `extensions.worktreeConfig=true` in .git/config\n> \n> (1) relocate `core.bare` from .git/config to .git/config.worktree if\n> that key exists\n> \n> (2) relocate `core.worktree` from .git/config to .git/config.worktree\n> if that key exists\n\nYou are describing (in better detail) what I meant in my message about\nwhat needs to change in this patch.\n \n> That's it. It doesn't need to create or touch any\n> .git/worktrees/<id>/config.worktree file(s); it should _not_ add a\n> `core.bare=false` to .git/worktrees/<id>/config.worktree, as this v1\n> patch series does.\n\nYes, the current patch is incorrect. However, changing just that one\naspect of this patch in the current method (in config.c) should make\nit behave the way you are advocating.\n\nI should have a v2 up later today and we can talk in more specifics\nabout that if you want to wait until then.\n\nThanks,\n-Stolee\n"},{"id":"444609","messageId":"c8c995d3-c73c-6b3f-8d5c-f1411abd56e9@gmail.com","threadId":"57125","inReplyTo":"CAPig+cT=evew0iePP-TZ+DTJ=oY6hrjiOiDtYqEr6KLRvkqC9Q@mail.gmail.com","subject":"Re: [PATCH 3/4] config: add repo_config_set_worktree_gently()","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-21T13:45:58Z","receivedAt":"2021-12-21T13:46:02Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/21/2021 12:53 AM, Eric Sunshine wrote:\n> On Mon, Dec 20, 2021 at 10:57 AM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>> When adding config values to the worktree config, we might enable the\n>> extensions.worktreeConfig setting and create the config.worktree file\n>> for the first time. When the base repository is bare, this creates a\n>> change of behavior for determining if the worktree is bare or not. A\n>> worktree off of a bare repository is assumed to be non-bare when\n>> extensions.worktreeConfig is disabled. When extensions.worktreeConfig is\n>> enabled but config.worktree is empty, the worktree is considered bare\n>> because the base repo's core.bare=true setting is used.\n>>\n>> To avoid issues like this, create a helper that initializes all the\n>> right settings in the correct order. A caller will be added in the next\n>> change.\n> \n> As discussed already in [1], [2], and [3], the solution implemented by\n> this patch is undesirable, and I gave an outline in [4] about how I\n> think the new utility function ought to be implemented instead, so I\n> won't say anything further about that here. However, I do still have\n> one or two review comments to make about the general approach taken by\n> patch. See below...\n> \n> [1]: https://lore.kernel.org/git/CAPig+cQPUe9REf+wgVNjyak_nk3V361h-48rTFgk6TGC7vJgOA@mail.gmail.com/\n> [2]: https://lore.kernel.org/git/CAPig+cTVzMtiHzkJq7VRg4Xa3xhrq7KKCdK5OSDY6bvwKu_ynA@mail.gmail.com/\n> [3]: https://lore.kernel.org/git/6d72a020-ded7-6ef2-825c-ce6421194b26@gmail.com/\n> [4]: https://lore.kernel.org/git/CAPig+cTuLYFc9fpAe8Uq9fvBYuSGcc9SA1O-q1BRw0DYxDF4Eg@mail.gmail.com/\n> \n>> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n>> ---\n>> diff --git a/config.c b/config.c\n>> @@ -2880,6 +2880,33 @@ int git_config_set_gently(const char *key, const char *value)\n>> +int repo_config_set_worktree_gently(struct repository *r,\n>> +                                   const char *key, const char *value)\n>> +{\n>> +       int res;\n>> +       const char *config_filename = repo_git_path(r, \"config.worktree\");\n>> +\n>> +       /*\n>> +        * Ensure that core.bare reflects the current worktree, since the\n>> +        * logic for is_bare_repository() changes if extensions.worktreeConfig\n>> +        * is disabled.\n>> +        */\n>> +       if ((res = git_config_set_multivar_in_file_gently(config_filename, \"core.bare\",\n>> +                                                         r->worktree ? \"false\" : \"true\",\n>> +                                                         NULL, 0))) {\n>> +               error(_(\"unable to set core.bare setting in worktree config\"));\n>> +               return res;\n>> +       }\n>> +       if (upgrade_repository_format(r, 1) < 0)\n>> +               return error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n>> +       if ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n>> +               error(_(\"failed to set extensions.worktreeConfig setting\"));\n>> +               return res;\n>> +       }\n>> +\n>> +       return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0);\n>> +}\n>> diff --git a/config.h b/config.h\n>> @@ -253,6 +253,12 @@ void git_config_set_in_file(const char *, const char *, const char *);\n>> +/**\n>> + * Write a config value into the config.worktree file for the current\n>> + * worktree. This will initialize extensions.worktreeConfig if necessary.\n>> + */\n>> +int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n> \n> I understand your desire to make this \"setter\" function as transparent\n> and simple for clients as possible, however, I think it does too much\n> by conflating two very distinct operations (one which changes the\n> nature of the repository itself, and one which simply sets a config\n> variable), and is far too magical. It doesn't help that the function\n> name gives no indication of just how magical it is, and it is easy to\n> imagine people calling this function thinking that it's just a simple\n> \"config setter\" like all the other similarly-named functions, without\n> realizing the impact it may have on the repository overall (i.e.\n> upgrading to version 1 and changing to per-worktree config).\n> \n> I would feel much more comfortable for the new utility function to\n> have a single-purpose: namely, to upgrade the repository to a\n> per-worktree configuration mode (if not already upgraded) as outlined\n> in [4]. That's it. It shouldn't do anything other than that. This way,\n> callers which need per-worktree configuration must call the new\n> function explicitly to obtain the desired behavior, rather than\n> getting per-worktree configuration as a magical and implicit\n> side-effect of calling what looks like a plain old \"config setter\".\n> This should make it easier to reason about. Taking this approach also\n> means that you don't need to introduce a special-purpose \"config\n> setter\" just for worktrees; instead, you'd use the normal existing\n> \"config setter\" functions. For instance, if the new utility function\n> is named enable_per_worktree_config(), then `git sparse-checkout init`\n> might do something like this:\n\nI understand your desire to separate these concerns, and maybe there\nis value in having another method that _just_ does the \"upgrade to\nworktree config\". However, if we don't also create this helper method\nfor setting worktree-specific config, then we are going to hit this\nissue again.\n \n>     enable_per_worktree_config(r);\n>     config_path = repo_git_path(r, \"config.worktree\")\n>     git_config_set_in_file_gently(config_path, \"core.sparseCheckout\", ...);\n>     git_config_set_in_file_gently(config_path, \"core.sparseCheckoutCone\", ...);\n> \n> (This, of course, assumes that repo_git_path() latches the changes\n> made by enable_per_worktree_config() so that it \"does the right\n> thing\", but it seems that existing code in `git sparse-checkout init`\n> is already expecting it to do so, so perhaps it does work that way.)\n\nIf we expect every caller that assigns config to the worktree to follow\nthis sequence of events, then we should encapsulate that in a method so\ndevelopers can discover it and call it instead of needing to write these\nlines over again. Just repeating the literal \"config.worktree\" in\nmultiple places is enough justification for making a helper, let alone\nthese more subtle issues around bare repos and non-bare worktrees.\n\nThe helper method will need clear documentation to say \"this will upgrade\nthe repository format and add extensions.worktreeConfig\" so those new\nconsumers are aware of the full functionality.\n\nThanks,\n-Stolee\n"},{"id":"444653","messageId":"pull.1101.v2.git.1640114048.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.git.1640015844.gitgitgadget@gmail.com","subject":"[PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-21T19:14:03Z","receivedAt":"2021-12-21T19:14:12Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"This patch series includes a fix to the bug reported by Sean Allred [1] and\ndiagnosed by Eric Sunshine [2].\n\nThe root cause is that 'git sparse-checkout init' writes to the worktree\nconfig without checking that core.bare might need to be set. This only\nmatters when the base repository is bare, since creating the config.worktree\nfile and enabling extensions.worktreeConfig will cause Git to treat the base\nrepo's core.bare=false as important for this worktree.\n\nThis series fixes this, but also puts in place some helpers to prevent this\nfrom happening in the future. While here, some of the config paths are\nmodified to take a repository struct.\n\nThe critical bits are in Patches 3, 4, and 5 which introduce a helper for\nupgrading to worktree config, a helper to write to worktree config, and then\nconsume that config helper in builtin/sparse-checkout.c and sparse-index.c.\n\n[1]\nhttps://lore.kernel.org/git/CABceR4bZmtC4rCwgxZ1BBYZP69VOUca1f_moJoP989vTUZWu9Q@mail.gmail.com/\n[2]\nhttps://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n\n\nUpdate in v2\n============\n\n * Eric correctly pointed out that I was writing core.bare incorrectly. It\n   should move out of the core config and into the core repository's\n   worktree config.\n * Patch 3 is new, separating the \"upgrade\" logic out of config.c, but it is\n   still called by the config helper to make it painless to write worktree\n   config.\n\nThanks, -Stolee\n\nDerrick Stolee (5):\n  setup: use a repository when upgrading format\n  config: make some helpers repo-aware\n  worktree: add upgrade_to_worktree_config()\n  config: add repo_config_set_worktree_gently()\n  sparse-checkout: use repo_config_set_worktree_gently()\n\n builtin/sparse-checkout.c          | 25 +++++-----------\n config.c                           | 39 +++++++++++++++++++++++--\n config.h                           | 14 +++++++++\n list-objects-filter-options.c      |  2 +-\n repository.h                       |  2 +-\n setup.c                            |  6 ++--\n sparse-index.c                     | 10 ++-----\n t/t1091-sparse-checkout-builtin.sh | 16 +++++++++-\n worktree.c                         | 47 ++++++++++++++++++++++++++++++\n worktree.h                         | 12 ++++++++\n 10 files changed, 140 insertions(+), 33 deletions(-)\n\n\nbase-commit: 69a9c10c95e28df457e33b3c7400b16caf2e2962\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1101%2Fderrickstolee%2Fsparse-checkout%2Fbare-worktree-bug-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1101/derrickstolee/sparse-checkout/bare-worktree-bug-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/1101\n\nRange-diff vs v1:\n\n 1:  28813703ff6 = 1:  889e69dc45d setup: use a repository when upgrading format\n 2:  3b549770eb9 = 2:  3e01356815a config: make some helpers repo-aware\n -:  ----------- > 3:  ed8e2a7b19d worktree: add upgrade_to_worktree_config()\n 3:  67993f6cff2 ! 4:  22896e9bb04 config: add repo_config_set_worktree_gently()\n     @@ Metadata\n       ## Commit message ##\n          config: add repo_config_set_worktree_gently()\n      \n     -    When adding config values to the worktree config, we might enable the\n     -    extensions.worktreeConfig setting and create the config.worktree file\n     -    for the first time. When the base repository is bare, this creates a\n     -    change of behavior for determining if the worktree is bare or not. A\n     -    worktree off of a bare repository is assumed to be non-bare when\n     -    extensions.worktreeConfig is disabled. When extensions.worktreeConfig is\n     -    enabled but config.worktree is empty, the worktree is considered bare\n     -    because the base repo's core.bare=true setting is used.\n     +    The previous change added upgrade_to_worktree_config() to assist\n     +    creating a worktree-specific config for the first time. However, this\n     +    requires every config writer to care about that upgrade before writing\n     +    to the worktree-specific config. In addition, callers need to know how\n     +    to generate the name of the config.worktree file and pass it to the\n     +    config API.\n      \n     -    To avoid issues like this, create a helper that initializes all the\n     -    right settings in the correct order. A caller will be added in the next\n     -    change.\n     +    To assist, create a new repo_config_set_worktree_gently() method in the\n     +    config API that handles the upgrade_to_worktree_config() method in\n     +    addition to assigning the value in the worktree-specific config. This\n     +    will be consumed by an upcoming change.\n      \n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## config.c ##\n     +@@\n     + #include \"dir.h\"\n     + #include \"color.h\"\n     + #include \"refs.h\"\n     ++#include \"worktree.h\"\n     + \n     + struct config_source {\n     + \tstruct config_source *prev;\n      @@ config.c: int git_config_set_gently(const char *key, const char *value)\n       \treturn git_config_set_multivar_gently(key, value, NULL, 0);\n       }\n     @@ config.c: int git_config_set_gently(const char *key, const char *value)\n      +int repo_config_set_worktree_gently(struct repository *r,\n      +\t\t\t\t    const char *key, const char *value)\n      +{\n     -+\tint res;\n     -+\tconst char *config_filename = repo_git_path(r, \"config.worktree\");\n     -+\n     -+\t/*\n     -+\t * Ensure that core.bare reflects the current worktree, since the\n     -+\t * logic for is_bare_repository() changes if extensions.worktreeConfig\n     -+\t * is disabled.\n     -+\t */\n     -+\tif ((res = git_config_set_multivar_in_file_gently(config_filename, \"core.bare\",\n     -+\t\t\t\t\t\t\t  r->worktree ? \"false\" : \"true\",\n     -+\t\t\t\t\t\t\t  NULL, 0))) {\n     -+\t\terror(_(\"unable to set core.bare setting in worktree config\"));\n     -+\t\treturn res;\n     -+\t}\n     -+\tif (upgrade_repository_format(r, 1) < 0)\n     -+\t\treturn error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n     -+\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n     -+\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n     -+\t\treturn res;\n     -+\t}\n     -+\n     -+\treturn git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0);\n     ++\treturn upgrade_to_worktree_config(r) ||\n     ++\t       git_config_set_multivar_in_file_gently(\n     ++\t\t\t repo_git_path(r, \"config.worktree\"),\n     ++\t\t\t key, value, NULL, 0);\n      +}\n      +\n       void git_config_set(const char *key, const char *value)\n     @@ config.h: void git_config_set_in_file(const char *, const char *, const char *);\n       \n      +/**\n      + * Write a config value into the config.worktree file for the current\n     -+ * worktree. This will initialize extensions.worktreeConfig if necessary.\n     ++ * worktree. This will initialize extensions.worktreeConfig if necessary,\n     ++ * which might trigger some changes to the root repository's config file.\n      + */\n      +int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n      +\n 4:  6202f767f4a ! 5:  06457fafa78 sparse-checkout: use repo_config_set_worktree_gently()\n     @@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'git sparse-checkout ini\n      +\t(\n      +\t\tcd worktree &&\n      +\t\tgit sparse-checkout init &&\n     -+\t\ttest_cmp_config false core.bare &&\n     ++\t\ttest_must_fail git config core.bare &&\n      +\t\tgit sparse-checkout set /*\n     -+\t)\n     ++\t) &&\n     ++\tgit -C bare config --list --show-origin >actual &&\n     ++\tgrep \"file:config.worktree\tcore.bare=true\" actual\n      +'\n      +\n       test_expect_success 'git sparse-checkout list after init' '\n\n-- \ngitgitgadget\n"},{"id":"444654","messageId":"889e69dc45d0b02e8207a39e321c7a752bdb2532.1640114048.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v2.git.1640114048.gitgitgadget@gmail.com","subject":"[PATCH v2 1/5] setup: use a repository when upgrading format","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-21T19:14:04Z","receivedAt":"2021-12-21T19:14:13Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe upgrade_repository_format() helper previously was not aware of the\npossibility of multiple repositories. Add a 'struct repository *'\nparameter so it is possible to call it from a specific repository.\n\nThe implementation already referred to the_repository in one place, so\nthat is an easy replacement. The use of git_config_set() is replaced\nwith a call to repo_config_set().\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/sparse-checkout.c     | 2 +-\n list-objects-filter-options.c | 2 +-\n repository.h                  | 2 +-\n setup.c                       | 6 +++---\n 4 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex d0f5c4702be..34447f87cd8 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -358,7 +358,7 @@ static int set_config(enum sparse_checkout_mode mode)\n {\n \tconst char *config_path;\n \n-\tif (upgrade_repository_format(1) < 0)\n+\tif (upgrade_repository_format(the_repository, 1) < 0)\n \t\tdie(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n \tif (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n \t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\ndiff --git a/list-objects-filter-options.c b/list-objects-filter-options.c\nindex fd8d59f653a..6e21d12045e 100644\n--- a/list-objects-filter-options.c\n+++ b/list-objects-filter-options.c\n@@ -372,7 +372,7 @@ void partial_clone_register(\n \t\t\t */\n \t\t\treturn;\n \t} else {\n-\t\tif (upgrade_repository_format(1) < 0)\n+\t\tif (upgrade_repository_format(the_repository, 1) < 0)\n \t\t\tdie(_(\"unable to upgrade repository format to support partial clone\"));\n \n \t\t/* Add promisor config for the remote */\ndiff --git a/repository.h b/repository.h\nindex 98f95834706..d3fc1f7689d 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -215,6 +215,6 @@ void prepare_repo_settings(struct repository *r);\n  * Return 1 if upgrade repository format to target_version succeeded,\n  * 0 if no upgrade is necessary, and -1 when upgrade is not possible.\n  */\n-int upgrade_repository_format(int target_version);\n+int upgrade_repository_format(struct repository *, int target_version);\n \n #endif /* REPOSITORY_H */\ndiff --git a/setup.c b/setup.c\nindex 347d7181ae9..90516664ce5 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -595,14 +595,14 @@ static int check_repository_format_gently(const char *gitdir, struct repository_\n \treturn 0;\n }\n \n-int upgrade_repository_format(int target_version)\n+int upgrade_repository_format(struct repository *r, int target_version)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct strbuf repo_version = STRBUF_INIT;\n \tstruct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;\n \n-\tstrbuf_git_common_path(&sb, the_repository, \"config\");\n+\tstrbuf_git_common_path(&sb, r, \"config\");\n \tread_repository_format(&repo_fmt, sb.buf);\n \tstrbuf_release(&sb);\n \n@@ -621,7 +621,7 @@ int upgrade_repository_format(int target_version)\n \t\t\t     repo_fmt.unknown_extensions.items[0].string);\n \n \tstrbuf_addf(&repo_version, \"%d\", target_version);\n-\tgit_config_set(\"core.repositoryformatversion\", repo_version.buf);\n+\trepo_config_set(r, \"core.repositoryformatversion\", repo_version.buf);\n \tstrbuf_release(&repo_version);\n \treturn 1;\n }\n-- \ngitgitgadget\n\n"},{"id":"444655","messageId":"3e01356815a84c9356f645735adde04906690f02.1640114048.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v2.git.1640114048.gitgitgadget@gmail.com","subject":"[PATCH v2 2/5] config: make some helpers repo-aware","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-21T19:14:05Z","receivedAt":"2021-12-21T19:14:14Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nAs we prepare to add new config helpers to write into a config.worktree,\nlet's make some existing methods be available for writing to a config\nfile relative to a repository.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n config.c | 29 ++++++++++++++++++++++++++---\n config.h |  7 +++++++\n 2 files changed, 33 insertions(+), 3 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex c5873f3a706..9c9eef16018 100644\n--- a/config.c\n+++ b/config.c\n@@ -2882,7 +2882,12 @@ int git_config_set_gently(const char *key, const char *value)\n \n void git_config_set(const char *key, const char *value)\n {\n-\tgit_config_set_multivar(key, value, NULL, 0);\n+\trepo_config_set(the_repository, key, value);\n+}\n+\n+void repo_config_set(struct repository *r, const char *key, const char *value)\n+{\n+\trepo_config_set_multivar(r, key, value, NULL, 0);\n \n \ttrace2_cmd_set_config(key, value);\n }\n@@ -3177,14 +3182,32 @@ void git_config_set_multivar_in_file(const char *config_filename,\n int git_config_set_multivar_gently(const char *key, const char *value,\n \t\t\t\t   const char *value_pattern, unsigned flags)\n {\n-\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n+\treturn repo_config_set_multivar_gently(the_repository, key, value,\n+\t\t\t\t\t       value_pattern, flags);\n+}\n+\n+int repo_config_set_multivar_gently(struct repository *r, const char *key,\n+\t\t\t\t    const char *value,\n+\t\t\t\t    const char *value_pattern, unsigned flags)\n+{\n+\treturn git_config_set_multivar_in_file_gently(repo_git_path(r, \"config\"),\n+\t\t\t\t\t\t      key, value, value_pattern,\n \t\t\t\t\t\t      flags);\n }\n \n void git_config_set_multivar(const char *key, const char *value,\n \t\t\t     const char *value_pattern, unsigned flags)\n {\n-\tgit_config_set_multivar_in_file(NULL, key, value, value_pattern,\n+\trepo_config_set_multivar(the_repository, key, value,\n+\t\t\t\t value_pattern, flags);\n+}\n+\n+void repo_config_set_multivar(struct repository *r, const char *key,\n+\t\t\t      const char *value, const char *value_pattern,\n+\t\t\t      unsigned flags)\n+{\n+\tgit_config_set_multivar_in_file(repo_git_path(r, \"config\"),\n+\t\t\t\t\tkey, value, value_pattern,\n \t\t\t\t\tflags);\n }\n \ndiff --git a/config.h b/config.h\nindex f119de01309..5531fc018e3 100644\n--- a/config.h\n+++ b/config.h\n@@ -258,6 +258,11 @@ int git_config_set_gently(const char *, const char *);\n  */\n void git_config_set(const char *, const char *);\n \n+/**\n+ * write config values to `.git/config`, takes a key/value pair as parameter.\n+ */\n+void repo_config_set(struct repository *, const char *, const char *);\n+\n int git_config_parse_key(const char *, char **, size_t *);\n \n /*\n@@ -281,6 +286,8 @@ int git_config_parse_key(const char *, char **, size_t *);\n \n int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n+int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n+void repo_config_set_multivar(struct repository *, const char *, const char *, const char *, unsigned);\n int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n \n /**\n-- \ngitgitgadget\n\n"},{"id":"444656","messageId":"ed8e2a7b19d236642b3b8d3a49d017d29753db56.1640114048.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v2.git.1640114048.gitgitgadget@gmail.com","subject":"[PATCH v2 3/5] worktree: add upgrade_to_worktree_config()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-21T19:14:06Z","receivedAt":"2021-12-21T19:14:15Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nSome features, such as the sparse-checkout builtin, require using the\nworktree config extension. It might seem simple to upgrade the\nrepository format and add extensions.worktreeConfig, and that is what\nhappens in the sparse-checkout builtin.\n\nTransitioning from one config file to multiple has some strange\nside-effects. In particular, if the base repository is bare and the\nworktree is not, Git knows to treat the worktree as non-bare as a\nspecial case when not using worktree config. Once worktree config is\nenabled, Git stops that special case since the core.bare setting could\napply at the worktree config level. This opens the door for bare\nworktrees.\n\nTo help resolve this transition, create upgrade_to_worktree_config() to\nnavigate the intricacies of this operation. In particular, we need to\nlook for core.bare=true within the base config file and move that\nsetting into the core repository's config.worktree file.\n\nTo gain access to the core repository's config and config.worktree file,\nwe reference a repository struct's 'commondir' member. If the repository\nwas a submodule instead of a worktree, then this still applies\ncorrectly.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n worktree.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++\n worktree.h | 12 ++++++++++++\n 2 files changed, 59 insertions(+)\n\ndiff --git a/worktree.c b/worktree.c\nindex 2c155b10150..e8ddbe2bfcc 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -5,6 +5,7 @@\n #include \"worktree.h\"\n #include \"dir.h\"\n #include \"wt-status.h\"\n+#include \"config.h\"\n \n void free_worktrees(struct worktree **worktrees)\n {\n@@ -830,3 +831,49 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,\n \t*wtpath = path;\n \treturn 0;\n }\n+\n+int upgrade_to_worktree_config(struct repository *r)\n+{\n+\tint res;\n+\tint bare = 0;\n+\tstruct config_set cs = { 0 };\n+\tchar *base_config_file = xstrfmt(\"%s/config\", r->commondir);\n+\tchar *base_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n+\n+\tgit_configset_init(&cs);\n+\tgit_configset_add_file(&cs, base_config_file);\n+\n+\t/*\n+\t * If the base repository is bare, then we need to move core.bare=true\n+\t * out of the base config file and into the base repository's\n+\t * config.worktree file.\n+\t */\n+\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n+\t\tif ((res = git_config_set_in_file_gently(base_worktree_file,\n+\t\t\t\t\t\t\t\"core.bare\", \"true\"))) {\n+\t\t\terror(_(\"unable to set core.bare=true in '%s'\"), base_worktree_file);\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tif ((res = git_config_set_in_file_gently(base_config_file,\n+\t\t\t\t\t\t\t\"core.bare\", NULL))) {\n+\t\t\terror(_(\"unable to unset core.bare=true in '%s'\"), base_config_file);\n+\t\t\tgoto cleanup;\n+\t\t}\n+\t}\n+\tif (upgrade_repository_format(r, 1) < 0) {\n+\t\tres = error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n+\t\tgoto cleanup;\n+\t}\n+\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n+\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n+\t\tgoto cleanup;\n+\t}\n+\n+cleanup:\n+\tgit_configset_clear(&cs);\n+\tfree(base_config_file);\n+\tfree(base_worktree_file);\n+\ttrace2_printf(\"returning %d\", res);\n+\treturn res;\n+}\ndiff --git a/worktree.h b/worktree.h\nindex 8b7c408132d..170b6b1e1f5 100644\n--- a/worktree.h\n+++ b/worktree.h\n@@ -182,4 +182,16 @@ void strbuf_worktree_ref(const struct worktree *wt,\n \t\t\t struct strbuf *sb,\n \t\t\t const char *refname);\n \n+/**\n+ * Upgrade the config of the current repository and its base (if different\n+ * from this repository) to use worktree-config. This might adjust config\n+ * in both repositories, including:\n+ *\n+ * 1. Upgrading the repository format version to 1.\n+ * 2. Adding extensions.worktreeConfig to the base config file.\n+ * 3. Moving core.bare=true from the base config file to the base\n+ *    repository's config.worktree file.\n+ */\n+int upgrade_to_worktree_config(struct repository *r);\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"444657","messageId":"22896e9bb04cdf022cc13468d60808df69a6854f.1640114048.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v2.git.1640114048.gitgitgadget@gmail.com","subject":"[PATCH v2 4/5] config: add repo_config_set_worktree_gently()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-21T19:14:07Z","receivedAt":"2021-12-21T19:14:16Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe previous change added upgrade_to_worktree_config() to assist\ncreating a worktree-specific config for the first time. However, this\nrequires every config writer to care about that upgrade before writing\nto the worktree-specific config. In addition, callers need to know how\nto generate the name of the config.worktree file and pass it to the\nconfig API.\n\nTo assist, create a new repo_config_set_worktree_gently() method in the\nconfig API that handles the upgrade_to_worktree_config() method in\naddition to assigning the value in the worktree-specific config. This\nwill be consumed by an upcoming change.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n config.c | 10 ++++++++++\n config.h |  7 +++++++\n 2 files changed, 17 insertions(+)\n\ndiff --git a/config.c b/config.c\nindex 9c9eef16018..81f3a689c11 100644\n--- a/config.c\n+++ b/config.c\n@@ -21,6 +21,7 @@\n #include \"dir.h\"\n #include \"color.h\"\n #include \"refs.h\"\n+#include \"worktree.h\"\n \n struct config_source {\n \tstruct config_source *prev;\n@@ -2880,6 +2881,15 @@ int git_config_set_gently(const char *key, const char *value)\n \treturn git_config_set_multivar_gently(key, value, NULL, 0);\n }\n \n+int repo_config_set_worktree_gently(struct repository *r,\n+\t\t\t\t    const char *key, const char *value)\n+{\n+\treturn upgrade_to_worktree_config(r) ||\n+\t       git_config_set_multivar_in_file_gently(\n+\t\t\t repo_git_path(r, \"config.worktree\"),\n+\t\t\t key, value, NULL, 0);\n+}\n+\n void git_config_set(const char *key, const char *value)\n {\n \trepo_config_set(the_repository, key, value);\ndiff --git a/config.h b/config.h\nindex 5531fc018e3..b05c51b3528 100644\n--- a/config.h\n+++ b/config.h\n@@ -253,6 +253,13 @@ void git_config_set_in_file(const char *, const char *, const char *);\n \n int git_config_set_gently(const char *, const char *);\n \n+/**\n+ * Write a config value into the config.worktree file for the current\n+ * worktree. This will initialize extensions.worktreeConfig if necessary,\n+ * which might trigger some changes to the root repository's config file.\n+ */\n+int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n+\n /**\n  * write config values to `.git/config`, takes a key/value pair as parameter.\n  */\n-- \ngitgitgadget\n\n"},{"id":"444658","messageId":"06457fafa78e18b7bb7f6d87408b0759d98344d8.1640114048.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v2.git.1640114048.gitgitgadget@gmail.com","subject":"[PATCH v2 5/5] sparse-checkout: use repo_config_set_worktree_gently()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-21T19:14:08Z","receivedAt":"2021-12-21T19:14:17Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe previous change added repo_config_set_worktree_gently() to assist\nwriting config values into the worktree.config file, especially when\nthat may not have been initialized.\n\nWhen the base repo is bare, running 'git sparse-checkout init' in a\nworktree will create the config.worktree file for the worktree, but that\nwill start causing the worktree to parse the bare repo's core.bare=true\nvalue and start treating the worktree as bare. This causes more problems\nas other commands are run in that worktree.\n\nThe fix is to have this assignment into config.worktree be handled by\nthe repo_config_set_worktree_gently() helper.\n\nReported-by: Sean Allred <allred.sean@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/sparse-checkout.c          | 25 ++++++++-----------------\n sparse-index.c                     | 10 +++-------\n t/t1091-sparse-checkout-builtin.sh | 16 +++++++++++++++-\n 3 files changed, 26 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 34447f87cd8..ec2c9a146cc 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -356,26 +356,17 @@ enum sparse_checkout_mode {\n \n static int set_config(enum sparse_checkout_mode mode)\n {\n-\tconst char *config_path;\n-\n-\tif (upgrade_repository_format(the_repository, 1) < 0)\n-\t\tdie(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n-\tif (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n-\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n+\tif (repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckout\",\n+\t\t\t\t\t    mode ? \"true\" : \"false\") ||\n+\t    repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckoutCone\",\n+\t\t\t\t\t    mode == MODE_CONE_PATTERNS ?\n+\t\t\t\t\t\t\"true\" : \"false\"))\n \t\treturn 1;\n-\t}\n-\n-\tconfig_path = git_path(\"config.worktree\");\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckout\",\n-\t\t\t\t      mode ? \"true\" : NULL);\n-\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckoutCone\",\n-\t\t\t\t      mode == MODE_CONE_PATTERNS ? \"true\" : NULL);\n \n \tif (mode == MODE_NO_PATTERNS)\n-\t\tset_sparse_index_config(the_repository, 0);\n+\t\treturn set_sparse_index_config(the_repository, 0);\n \n \treturn 0;\n }\ndiff --git a/sparse-index.c b/sparse-index.c\nindex a1d505d50e9..e93609999e0 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -99,13 +99,9 @@ static int convert_to_sparse_rec(struct index_state *istate,\n \n int set_sparse_index_config(struct repository *repo, int enable)\n {\n-\tint res;\n-\tchar *config_path = repo_git_path(repo, \"config.worktree\");\n-\tres = git_config_set_in_file_gently(config_path,\n-\t\t\t\t\t    \"index.sparse\",\n-\t\t\t\t\t    enable ? \"true\" : NULL);\n-\tfree(config_path);\n-\n+\tint res = repo_config_set_worktree_gently(repo,\n+\t\t\t\t\t\t  \"index.sparse\",\n+\t\t\t\t\t\t  enable ? \"true\" : \"false\");\n \tprepare_repo_settings(repo);\n \trepo->settings.sparse_index = enable;\n \treturn res;\ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex 272ba1b566b..b563ccfeb36 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -71,6 +71,20 @@ test_expect_success 'git sparse-checkout init' '\n \tcheck_files repo a\n '\n \n+test_expect_success 'init in a worktree of a bare repo' '\n+\ttest_when_finished rm -rf bare worktree &&\n+\tgit clone --bare repo bare &&\n+\tgit -C bare worktree add ../worktree &&\n+\t(\n+\t\tcd worktree &&\n+\t\tgit sparse-checkout init &&\n+\t\ttest_must_fail git config core.bare &&\n+\t\tgit sparse-checkout set /*\n+\t) &&\n+\tgit -C bare config --list --show-origin >actual &&\n+\tgrep \"file:config.worktree\tcore.bare=true\" actual\n+'\n+\n test_expect_success 'git sparse-checkout list after init' '\n \tgit -C repo sparse-checkout list >actual &&\n \tcat >expect <<-\\EOF &&\n@@ -219,7 +233,7 @@ test_expect_success 'sparse-index enabled and disabled' '\n \t\ttest-tool -C repo read-cache --table >cache &&\n \t\t! grep \" tree \" cache &&\n \t\tgit -C repo config --list >config &&\n-\t\t! grep index.sparse config\n+\t\ttest_cmp_config -C repo false index.sparse\n \t)\n '\n \n-- \ngitgitgadget\n"},{"id":"444676","messageId":"CAPig+cRombN-8g0t7Hs9qQypJoY41gK3+kvypH4D0G6EB4JgbQ@mail.gmail.com","threadId":"57125","inReplyTo":"c8c995d3-c73c-6b3f-8d5c-f1411abd56e9@gmail.com","subject":"Re: [PATCH 3/4] config: add repo_config_set_worktree_gently()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-21T23:29:35Z","receivedAt":"2021-12-21T23:29:48Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 21, 2021 at 8:46 AM Derrick Stolee <stolee@gmail.com> wrote:\n> On 12/21/2021 12:53 AM, Eric Sunshine wrote:\n> > On Mon, Dec 20, 2021 at 10:57 AM Derrick Stolee via GitGitGadget\n> > <gitgitgadget@gmail.com> wrote:\n> >> +/**\n> >> + * Write a config value into the config.worktree file for the current\n> >> + * worktree. This will initialize extensions.worktreeConfig if necessary.\n> >> + */\n> >> +int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n> >\n> > I understand your desire to make this \"setter\" function as transparent\n> > and simple for clients as possible, however, I think it does too much\n> > by conflating two very distinct operations (one which changes the\n> > nature of the repository itself, and one which simply sets a config\n> > variable), and is far too magical. It doesn't help that the function\n> > name gives no indication of just how magical it is, and it is easy to\n> > imagine people calling this function thinking that it's just a simple\n> > \"config setter\" like all the other similarly-named functions, without\n> > realizing the impact it may have on the repository overall (i.e.\n> > upgrading to version 1 and changing to per-worktree config).\n> >\n> > I would feel much more comfortable for the new utility function to\n> > have a single-purpose: namely, to upgrade the repository to a\n> > per-worktree configuration mode (if not already upgraded) as outlined\n> > in [4]. That's it. It shouldn't do anything other than that. This way,\n> > callers which need per-worktree configuration must call the new\n> > function explicitly to obtain the desired behavior, rather than\n> > getting per-worktree configuration as a magical and implicit\n> > side-effect of calling what looks like a plain old \"config setter\".\n> > This should make it easier to reason about. Taking this approach also\n> > means that you don't need to introduce a special-purpose \"config\n> > setter\" just for worktrees; instead, you'd use the normal existing\n> > \"config setter\" functions. For instance, if the new utility function\n> > is named enable_per_worktree_config(), then `git sparse-checkout init`\n> > might do something like this:\n>\n> I understand your desire to separate these concerns, and maybe there\n> is value in having another method that _just_ does the \"upgrade to\n> worktree config\". However, if we don't also create this helper method\n> for setting worktree-specific config, then we are going to hit this\n> issue again.\n\nThere are several good reasons for having a single-purpose function\nwhich upgrades to per-worktree config. Not only is it easier to\ndiscover such a function, but it is also easier to reason about the\nbehavior when it does just this one thing. Moreover, aside from\nproviding a common implementation for modules which may want or\nrequire the functionality (such as `git sparse-checkout init`), it\nwould form a solid basis for a git-worktree command for enabling\nper-worktree configuration. And, such a command could be valuable for\npeople who want to utilize per-worktree configuration for their own\nreasons (not related to `git-sparse-checkout`).\n\nWith only `git sparse-checkout init` wanting to write per-worktree\nconfig, thus far, it does not feel like a convincing argument that an\nautomagical helper function which conflates upgrading the repository\nto per-worktree config _and_ writing a per-worktree config key is a\ngood idea or that it will be needed again. But more on that below...\n\n> >     enable_per_worktree_config(r);\n> >     config_path = repo_git_path(r, \"config.worktree\")\n> >     git_config_set_in_file_gently(config_path, \"core.sparseCheckout\", ...);\n> >     git_config_set_in_file_gently(config_path, \"core.sparseCheckoutCone\", ...);\n>\n> If we expect every caller that assigns config to the worktree to follow\n> this sequence of events, then we should encapsulate that in a method so\n> developers can discover it and call it instead of needing to write these\n> lines over again. Just repeating the literal \"config.worktree\" in\n> multiple places is enough justification for making a helper, let alone\n> these more subtle issues around bare repos and non-bare worktrees.\n\nOn the contrary, because it is such an unusual and potentially\ndangerous step to take (i.e. it changes the repository in a way which\nthird-party tools may not understand), any module which absolutely\n_requires_ per-worktree config support should be enabling it\nexplicitly rather than expecting it to happen implicitly and\nmagically. By keeping these concerns separate, it is not only easier\nfor people working on this code in the future to reason about the\nbehavior, but it also provides a cleaner path for electively aborting\nthe operation should that ever become desirable. For instance:\n\n    % git sparse-checkout init\n    WARNING: This operation will upgrade the repository format to\n    WARNING: version 1 and enable per-worktree configuration, thus\n    WARNING: potentially making the repository incompatible with\n    WARNING: third-party tools.\n\n    Are you sure you want to continue [y/N]?\n\nYour response is also conflating the slight pain of repeated\n`repo_git_path(r, \"config.worktree\")` with the need to upgrade to\nper-worktree configuration, which highlights another issue...\n\nThe big question is: why does git-sparse-checkout mandate per-worktree\nconfiguration? I haven't followed sparse checkout development closely,\nso I may be missing some obvious reasons. I can see why you would want\nto _recommend_ and even nudge people to use per-worktree\nconfiguration, which you could do both in the documentation and even\nas a \"HINT\" from the `git sparse-checkout init` command, but\nabsolutely forcing them into per-worktree configuration seems far too\nopinionated for a general-purpose Git command and closes the door\nunnecessarily on people who may have quite valid reasons for using\nsparse checkout _without_ per-worktree configuration (i.e. perhaps\nthey only ever use a single worktree -- the main one -- or perhaps\nthey really do want the sparse checkout to apply to every worktree).\nThis unconditional enforcement of per-worktree config seems better\nsuited for `scalar` which is intentionally opinionated.\n\nWith the view that `git sparse-checkout init` is too opinionated and\ncloses doors unnecessarily, then `git sparse-checkout init` should not\nbe upgrading the repository to per-worktree configuration\nunconditionally. Instead, either the documentation should recommend\nthis step to users; for example:\n\n    It is recommended that sparse checkout be used with per-worktree\n    configuration so that each worktree can have its own sparse\n    settings. Per-worktree configuration can be enabled with the\n    (fictitious) `git worktree config --enable-per-worktree` command:\n\n    % git worktree config --enable-per-worktree\n    % git sparse-checkout init\n\nOr, enabling per-worktree configuration could be enabled _on-demand_\nby `git sparse-checkout init`; for instance:\n\n    % git sparse-checkout init --per-worktree\n\nAlthough the immediate discussion is about git-sparse-checkout, this\nidea that a command adapts to the repository rather than demanding a\nspecific repository arrangement, or indeed forcibly changing the\nrepository arrangement, is far friendlier and leaves doors open which\nwould otherwise be closed.\n\nIt also makes your proposed repo_config_set_worktree_gently() which\n\"does the right thing\" much more palatable since \"does the right\nthing\" no longer means forcibly changing the repository arrangement.\nInstead, this convenience function would simply pick the correct\nconfiguration file on behalf of the caller; namely, if\n`extensions.worktreeConfig` is disabled, then it writes to\n.git/config, whereas if `extensions.worktreeConfig` is enabled, then\nit writes to .git/config.worktree or\n.git/worktrees/<id>/config.worktree, depending upon the worktree\nyou're in. That behavior would satisfy your desire to have a\nconvenience function to modify the correct config file regardless of\nwhether the repository has per-worktree configuration or not, and\nleaves git-sparse-checkout flexible enough to work with or without\nper-worktree configuration. I would have no problem with a\nrepo_config_set_worktree_gently() function which works as described\nhere, whereas I feel plenty uncomfortable with the\nrepo_config_set_worktree_gently() implemented by this series.\n\nReferring back to what you said earlier:\n\n    However, if we don't also create this helper method for setting\n    worktree-specific config, then we are going to hit this issue\n    again. (Stolee)\n\nYes, we might hit this issue in the future if some command absolutely\nrequires per-worktree config, however, as outlined above, I think we\nshould strive as much as possible to make commands work without\nrequiring special repository arrangement, instead allowing people to\nopt-in to repository-wide changes. By avoiding unconditionally\nrequiring the repository be configured in a particular way, we're less\nlikely to break third-party tools.\n"},{"id":"444689","messageId":"CAPig+cQrJ9yWjkc8VMu=uyx_qtrXdL3cNnxLVafoxOo6e-r9kw@mail.gmail.com","threadId":"57125","inReplyTo":"ed8e2a7b19d236642b3b8d3a49d017d29753db56.1640114048.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 3/5] worktree: add upgrade_to_worktree_config()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-22T00:45:03Z","receivedAt":"2021-12-22T00:45:16Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 21, 2021 at 2:14 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> Some features, such as the sparse-checkout builtin, require using the\n> worktree config extension. It might seem simple to upgrade the\n> repository format and add extensions.worktreeConfig, and that is what\n> happens in the sparse-checkout builtin.\n>\n> Transitioning from one config file to multiple has some strange\n> side-effects. In particular, if the base repository is bare and the\n> worktree is not, Git knows to treat the worktree as non-bare as a\n> special case when not using worktree config. Once worktree config is\n> enabled, Git stops that special case since the core.bare setting could\n> apply at the worktree config level. This opens the door for bare\n> worktrees.\n\nIt would be a good idea to drop the final sentence since there is no\nsuch thing as a bare worktree (either conceptually or practically),\nand end the first sentence at \"case\": i.e. \"... stops that special\ncase.\"\n\n> To help resolve this transition, create upgrade_to_worktree_config() to\n> navigate the intricacies of this operation. In particular, we need to\n> look for core.bare=true within the base config file and move that\n> setting into the core repository's config.worktree file.\n>\n> To gain access to the core repository's config and config.worktree file,\n> we reference a repository struct's 'commondir' member. If the repository\n> was a submodule instead of a worktree, then this still applies\n> correctly.\n\nI'm not sure how much this commit message is going to help someone who\ndid not participate in the discussion which led to this patch series.\nI think the entire commit message could be written more concisely like\nthis:\n\n    worktree: add helper to upgrade repository to per-worktree config\n\n    Changing a repository to use per-worktree configuration is a\n    somewhat involved manual process, as described in the\n    `git-worktree` documentation, but it's easy to get wrong if the\n    steps are not followed precisely, which could lead to odd\n    anomalies such as Git thinking that a worktree is \"bare\" (which\n    conceptually makes no sense). Therefore, add a utility function to\n    automate the process of switching to per-worktree configuration\n    for modules which require such configuration. (In the future, it\n    may make sense to also expose this convenience as a `git-worktree`\n    command to automate the process for end-users, as well.)\n\n    To upgrade the repository to per-worktree configuration, it performs\n    these steps:\n\n    * enable `extensions.worktreeConfig` in .git/config\n\n    * relocate `core.bare` from .git/config to .git/config.worktree\n      (if key exists)\n\n    * relocate `core.worktree` from .git/config to\n      .git/config.worktree (if key exists)\n\n    It also upgrades the repository format to 1 if necessary since\n    that is a prerequisite of using `extensions`.\n\n> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/worktree.c b/worktree.c\n> @@ -830,3 +831,49 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,\n> +int upgrade_to_worktree_config(struct repository *r)\n> +{\n> +       int res;\n> +       int bare = 0;\n> +       struct config_set cs = { 0 };\n\nThis function is doing a lot of unnecessary work if per-worktree\nconfiguration is already enabled. The very first thing it should be\ndoing is checking whether or not it actually needs to do that work,\nand short-circuit if it doesn't. I would think that simply checking\nwhether `extensions.worktreeConfig` is true and returning early if it\nis would be sufficient.\n\n> +       char *base_config_file = xstrfmt(\"%s/config\", r->commondir);\n\nIf we look at path.c:strbuf_worktree_gitdir(), we see that this should\nbe using `r->gitdir`, not `r->commondir`.\n\n> +       char *base_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n\nPer path.c:strbuf_worktree_gitdir(), this use of `r->commondir` is\ncorrect. Good.\n\nCan we use more meaningful variable names? It's not at all clear what\n\"base\" means in this context (I don't think it has any analog in Git\nterminology). Perhaps name these `shared_config` and `repo_config`,\nrespectively.\n\n> +       git_configset_init(&cs);\n> +       git_configset_add_file(&cs, base_config_file);\n> +\n> +       /*\n> +        * If the base repository is bare, then we need to move core.bare=true\n> +        * out of the base config file and into the base repository's\n> +        * config.worktree file.\n> +        */\n\nHere, too, it's not clear what \"base\" means. I think you want to say\nthat it needs to \"move `core.bare` from the shared config to the\nrepo-specific config\".\n\n> +       if (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n> +               if ((res = git_config_set_in_file_gently(base_worktree_file,\n> +                                                       \"core.bare\", \"true\"))) {\n> +                       error(_(\"unable to set core.bare=true in '%s'\"), base_worktree_file);\n> +                       goto cleanup;\n> +               }\n> +\n> +               if ((res = git_config_set_in_file_gently(base_config_file,\n> +                                                       \"core.bare\", NULL))) {\n> +                       error(_(\"unable to unset core.bare=true in '%s'\"), base_config_file);\n> +                       goto cleanup;\n> +               }\n> +       }\n\nThis seems unnecessarily complicated and overly specific, thus\npotentially confusing. The requirements laid out in git-worktree.txt\nsay only to move the configuration key from .git/config to\n.git/config.worktree; it doesn't add any qualifiers about the value\nbeing \"true\". And, indeed, we should not care about the value; it's\nimmaterial to this operation. Instead, we should just treat it\nopaquely and relocate the key and value from .git/config (if it\nexists) to .git/config.worktree without interpretation.\n\nThe error messages are too specific, as well, by mentioning \"true\".\nThey could instead be:\n\n    unable to set `core.bare` in '%s'\n\nand\n\n    unable to remove `core.bare` from '%s'\n\nHowever, there is a much more _severe_ problem with this\nimplementation: it is incomplete. As documented in git-worktree.txt\n(and mentioned several times during this discussion), this utility\nfunction _must_ relocate both `core.bare` _and_ `core.worktree` (if\nthey exist) from .git/config to .git/config.worktree. This\nimplementation neglects to relocate `core.worktree`, which can leave\nthings in quite a broken state (just as neglecting to relocate\n`core.bare` can).\n\n> +       if (upgrade_repository_format(r, 1) < 0) {\n> +               res = error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n> +               goto cleanup;\n> +       }\n> +       if ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n> +               error(_(\"failed to set extensions.worktreeConfig setting\"));\n> +               goto cleanup;\n> +       }\n\nThe order in which this function performs its operations can leave the\nrepository in a broken state if any of the steps fails. For instance,\nif setting `extensions.worktreeConfig=true` fails _after_ you've\nrelocated `core.bare` (and `core.worktree`) to .git/config.worktree,\nthen those configuration values will no longer be \"active\" since the\nconfig system won't consult .git/config.worktree without\n`extensions.worktreeConfig` enabled.\n\nTo be resilient against this sort of problem, you should perform the\noperations in this order:\n\n(1) upgrade repository format to 1\n(2) enable `extensions.worktreeConfig`\n(3) relocate `core.bare` and `core.worktree`\n\n> +cleanup:\n> +       git_configset_clear(&cs);\n> +       free(base_config_file);\n> +       free(base_worktree_file);\n> +       trace2_printf(\"returning %d\", res);\n\nIs this leftover debugging or intentional?\n\n> +       return res;\n> +}\n> diff --git a/worktree.h b/worktree.h\n> @@ -182,4 +182,16 @@ void strbuf_worktree_ref(const struct worktree *wt,\n> +/**\n> + * Upgrade the config of the current repository and its base (if different\n> + * from this repository) to use worktree-config. This might adjust config\n> + * in both repositories, including:\n\nHere, too, it's not clear what \"base\" means. Moreover, this seems to\nbe talking about multiple repositories, but we're only dealing with a\nsingle repository and zero or more worktrees, so it's not clear what\nthis is trying to say.\n\n> + * 1. Upgrading the repository format version to 1.\n> + * 2. Adding extensions.worktreeConfig to the base config file.\n> + * 3. Moving core.bare=true from the base config file to the base\n> + *    repository's config.worktree file.\n\nAs mentioned above, it's unnecessary and perhaps confusing to focus\nonly on \"true\" here; we should be treating the value opaquely.\n\nAlso, if you're talking about the specific config settings which this\nrelocates, then `core.worktree` should be mentioned too, not just\n`core.bare`.\n\n> + */\n> +int upgrade_to_worktree_config(struct repository *r);\n"},{"id":"444691","messageId":"CAPig+cRi2SA6+poaemY8XR5ZoMweuztfiENpcRVOCnukg3Qa7w@mail.gmail.com","threadId":"57125","inReplyTo":"22896e9bb04cdf022cc13468d60808df69a6854f.1640114048.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 4/5] config: add repo_config_set_worktree_gently()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-22T01:11:40Z","receivedAt":"2021-12-22T01:11:53Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 21, 2021 at 2:14 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> The previous change added upgrade_to_worktree_config() to assist\n> creating a worktree-specific config for the first time. However, this\n> requires every config writer to care about that upgrade before writing\n> to the worktree-specific config. In addition, callers need to know how\n> to generate the name of the config.worktree file and pass it to the\n> config API.\n>\n> To assist, create a new repo_config_set_worktree_gently() method in the\n> config API that handles the upgrade_to_worktree_config() method in\n> addition to assigning the value in the worktree-specific config. This\n> will be consumed by an upcoming change.\n\nI still feel very uncomfortable about this function conflating two\nvery different concerns (upgrading the repository to per-worktree\nconfig, and the simple setting of a config variable). Since I've\nalready explained my discomfort and suggested alternatives several\ntimes during this discussion (most recently at [1]), I don't have all\nthat much more to say. However, I do have a few comments...\n\nFirst, I would have no problem if this function \"did the right thing\"\nwhere \"the right thing\" means correctly choosing between .git/config\nand .git/config.worktree depending upon whether or not\n`extensions.worktreeConfig` is set, and only that. It should not\nperform any sort of repository upgrade on its own. Doing it this way\nshould satisfy your major concern of relieving callers from having to\nfigure out the correct configuration file name.\n\nSecond, if you absolutely must have this function, as implemented, as\npart of the public API (despite my protests), please give it a name\nwhich is sufficiently different from the other \"config setter\"\nfunctions so that people won't be confused into thinking it's just\nanother \"setter\" without realizing that calling it has repository-wide\nconsequences.\n\nThird, I don't have an objection if you want to make this function\nprivate (static) to builtin/sparse-checkout.c, thus omitting it from\nthe public API. This way you can have its convenience where you want\nit, and we can delay finishing this discussion until such time that it\nbecomes apparent that other modules want its convenience, as well, if\nthat ever comes about.\n\n[1]: https://lore.kernel.org/git/CAPig+cRombN-8g0t7Hs9qQypJoY41gK3+kvypH4D0G6EB4JgbQ@mail.gmail.com/\n\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/config.c b/config.c\n> index 9c9eef16018..81f3a689c11 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -21,6 +21,7 @@\n>  #include \"dir.h\"\n>  #include \"color.h\"\n>  #include \"refs.h\"\n> +#include \"worktree.h\"\n>\n>  struct config_source {\n>         struct config_source *prev;\n> @@ -2880,6 +2881,15 @@ int git_config_set_gently(const char *key, const char *value)\n>         return git_config_set_multivar_gently(key, value, NULL, 0);\n>  }\n>\n> +int repo_config_set_worktree_gently(struct repository *r,\n> +                                   const char *key, const char *value)\n> +{\n> +       return upgrade_to_worktree_config(r) ||\n> +              git_config_set_multivar_in_file_gently(\n> +                        repo_git_path(r, \"config.worktree\"),\n> +                        key, value, NULL, 0);\n> +}\n> +\n>  void git_config_set(const char *key, const char *value)\n>  {\n>         repo_config_set(the_repository, key, value);\n> diff --git a/config.h b/config.h\n> index 5531fc018e3..b05c51b3528 100644\n> --- a/config.h\n> +++ b/config.h\n> @@ -253,6 +253,13 @@ void git_config_set_in_file(const char *, const char *, const char *);\n>\n>  int git_config_set_gently(const char *, const char *);\n>\n> +/**\n> + * Write a config value into the config.worktree file for the current\n> + * worktree. This will initialize extensions.worktreeConfig if necessary,\n> + * which might trigger some changes to the root repository's config file.\n> + */\n> +int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n> +\n>  /**\n>   * write config values to `.git/config`, takes a key/value pair as parameter.\n>   */\n> --\n"},{"id":"444744","messageId":"xmqq4k71mc75.fsf@gitster.g","threadId":"57125","inReplyTo":"ed8e2a7b19d236642b3b8d3a49d017d29753db56.1640114048.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 3/5] worktree: add upgrade_to_worktree_config()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-22T05:38:06Z","receivedAt":"2021-12-22T05:43:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +int upgrade_to_worktree_config(struct repository *r)\n\nNot a suggestion to rename the function, but does it mean \"we have\nbeen using a single configuration for all worktrees attached to the\nrepository, but we are switching to use per-worktree configuration\nfile\"?  I am wondering if the phrase \"worktree_config\" is clear\nenough for our future developers.\n\n> +{\n> +\tint res;\n> +\tint bare = 0;\n> +\tstruct config_set cs = { 0 };\n> +\tchar *base_config_file = xstrfmt(\"%s/config\", r->commondir);\n> +\tchar *base_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n> +\n> +\tgit_configset_init(&cs);\n> +\tgit_configset_add_file(&cs, base_config_file);\n> +\n> +\t/*\n> +\t * If the base repository is bare, then we need to move core.bare=true\n> +\t * out of the base config file and into the base repository's\n> +\t * config.worktree file.\n> +\t */\n> +\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n> +\t\tif ((res = git_config_set_in_file_gently(base_worktree_file,\n> +\t\t\t\t\t\t\t\"core.bare\", \"true\"))) {\n> +\t\t\terror(_(\"unable to set core.bare=true in '%s'\"), base_worktree_file);\n> +\t\t\tgoto cleanup;\n> +\t\t}\n\nHmph, I would have expected to see\n\n\t\tif (git_config_set_in_file_gently(...)) {\n\t\t\tres = error(_(\"...\"));\n                        goto cleanup;\n\t\t}\n\ninstead (obviously with \"res\" initialized to \"0\" to assume all will\nbe well at the beginning).\n\nMore importantly, are we prepared to see the repository 'r' that\nalready uses per-worktree config layout and refrain from causing any\ndamage to it, or are we all perfect developers that any caller that\ncalls this on repository 'r' that does not need to be upgraded is a\nBUG()?\n\nIs \"core.bare\" the only thing that needs special treatment?  Will it\nstay to be, or will we see more configuration variables that need\nspecial casing like this?\n\n> +\tif (upgrade_repository_format(r, 1) < 0) {\n> +\t\tres = error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n> +\t\tgoto cleanup;\n> +\t}\n> +\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n> +\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n> +\t\tgoto cleanup;\n> +\t}\n> +\n> +cleanup:\n> +\tgit_configset_clear(&cs);\n> +\tfree(base_config_file);\n> +\tfree(base_worktree_file);\n> +\ttrace2_printf(\"returning %d\", res);\n> +\treturn res;\n> +}\n> diff --git a/worktree.h b/worktree.h\n> index 8b7c408132d..170b6b1e1f5 100644\n> --- a/worktree.h\n> +++ b/worktree.h\n> @@ -182,4 +182,16 @@ void strbuf_worktree_ref(const struct worktree *wt,\n>  \t\t\t struct strbuf *sb,\n>  \t\t\t const char *refname);\n>  \n> +/**\n> + * Upgrade the config of the current repository and its base (if different\n> + * from this repository) to use worktree-config. This might adjust config\n> + * in both repositories, including:\n> + *\n> + * 1. Upgrading the repository format version to 1.\n> + * 2. Adding extensions.worktreeConfig to the base config file.\n> + * 3. Moving core.bare=true from the base config file to the base\n> + *    repository's config.worktree file.\n> + */\n> +int upgrade_to_worktree_config(struct repository *r);\n> +\n>  #endif\n"},{"id":"444745","messageId":"CAPig+cRuY40RNi4bC3CBfghLLqz74VUPRbaYJYEhmF78b0GfPQ@mail.gmail.com","threadId":"57125","inReplyTo":"06457fafa78e18b7bb7f6d87408b0759d98344d8.1640114048.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 5/5] sparse-checkout: use repo_config_set_worktree_gently()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-22T05:48:21Z","receivedAt":"2021-12-22T05:48:34Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 21, 2021 at 2:14 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> The previous change added repo_config_set_worktree_gently() to assist\n> writing config values into the worktree.config file, especially when\n> that may not have been initialized.\n>\n> When the base repo is bare, running 'git sparse-checkout init' in a\n> worktree will create the config.worktree file for the worktree, but that\n> will start causing the worktree to parse the bare repo's core.bare=true\n> value and start treating the worktree as bare. This causes more problems\n> as other commands are run in that worktree.\n>\n> The fix is to have this assignment into config.worktree be handled by\n> the repo_config_set_worktree_gently() helper.\n>\n> Reported-by: Sean Allred <allred.sean@gmail.com>\n> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\n> @@ -71,6 +71,20 @@ test_expect_success 'git sparse-checkout init' '\n> +test_expect_success 'init in a worktree of a bare repo' '\n\nNit: Although `init` doing an incomplete job of enabling per-worktree\nconfig was indeed the source of the problem, it actually manifested\nwhen invoking other commands, such as `set`. Consequently, it may be\nslightly misleading to talk about `init` in the test title. A title\nsuch as \"worktree of a bare repo\" might be good enough. Anyhow, just a\nnit.\n\n> +       test_when_finished rm -rf bare worktree &&\n> +       git clone --bare repo bare &&\n> +       git -C bare worktree add ../worktree &&\n> +       (\n> +               cd worktree &&\n> +               git sparse-checkout init &&\n> +               test_must_fail git config core.bare &&\n\nNit: I'm rather \"meh\" on explicitly checking `core.bare` here since\nit's not particularly relevant to the test: just testing `init + set`\nalone is enough to trigger the bug which begat this patch series.\nFuture readers of this test might even be confused by the presence of\nthis `core.bare` check.\n\n> +               git sparse-checkout set /*\n\nThe `/*` is expanding to all entries in the root of the filesystem,\nwhich probably isn't what you intended. I suspect you want literal\n\"/*\", in which case you need to quote it:\n\n    git sparse-checkout set \"/*\"\n\n> +       ) &&\n> +       git -C bare config --list --show-origin >actual &&\n> +       grep \"file:config.worktree      core.bare=true\" actual\n\nAs mentioned above, I'm fairly meh on this part (and perhaps leaning\ntoward the negative) since it places too much emphasis on a low-level\ndetail. I _could_ see this as a test of the new function which\nupgrades the repo to per-worktree config, but that's not what _this_\ntest is about.\n\n> +'\n"},{"id":"444747","messageId":"CAPig+cTxS54QyAjHB3JhqNndKiBdxyt_ROtOnxKt0VGswJj=8A@mail.gmail.com","threadId":"57125","inReplyTo":"pull.1101.v2.git.1640114048.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-22T06:05:00Z","receivedAt":"2021-12-22T06:05:12Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 21, 2021 at 2:14 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> The root cause is that 'git sparse-checkout init' writes to the worktree\n> config without checking that core.bare might need to be set. This only\n> matters when the base repository is bare, since creating the config.worktree\n> file and enabling extensions.worktreeConfig will cause Git to treat the base\n> repo's core.bare=false as important for this worktree.\n\nI'm having trouble understanding what this is trying to say. Did you\nmean \"true\" rather than \"false\" in the final sentence? Anyhow, I think\nthis description is somewhat stale. A more succinct way to describe\nthe issue is that `git sparse-checkout init` wasn't correctly\nupgrading the repo to per-worktree configuration, with the result that\nthe `core.bare=true` setting of a bare repo bled into\nworktree-specific configuration, which caused a bit of havoc. This\npatch series fixes `init` to upgrade the repo properly.\n\n> The critical bits are in Patches 3, 4, and 5 which introduce a helper for\n> upgrading to worktree config, a helper to write to worktree config, and then\n> consume that config helper in builtin/sparse-checkout.c and sparse-index.c.\n>\n> Update in v2\n> ============\n>\n>  * Eric correctly pointed out that I was writing core.bare incorrectly. It\n>    should move out of the core config and into the core repository's\n>    worktree config.\n>  * Patch 3 is new, separating the \"upgrade\" logic out of config.c, but it is\n>    still called by the config helper to make it painless to write worktree\n>    config.\n\nIt's good to see the \"upgrade to per-worktree config\" split out to a\nstandalone single-purpose utility function. I left several review\ncomments in that patch, the most important of which is that the\nimplementation is incomplete (because it doesn't relocate\n`core.worktree`), thus it can leave the repo in an inconsistent and\nbroken state. Several of the other review comments are actionable, as\nwell.\n\nI'm still concerned about and uncomfortable with the implementation of\nthe new repo_config_set_worktree_gently(), but I've left ample\ncomments about that elsewhere in this discussion, so I needn't go into\nit here.\n\nThanks for working on this.\n"},{"id":"444839","messageId":"CABPp-BG7nwsdEYrnfqhAbWU4ndJHcqGf6RS_6DzJittuNVLvoA@mail.gmail.com","threadId":"57125","inReplyTo":"pull.1101.v2.git.1640114048.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-22T22:54:19Z","receivedAt":"2021-12-22T22:54:34Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Dec 22, 2021 at 8:00 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> This patch series includes a fix to the bug reported by Sean Allred [1] and\n> diagnosed by Eric Sunshine [2].\n>\n> The root cause is that 'git sparse-checkout init' writes to the worktree\n> config without checking that core.bare might need to be set. This only\n> matters when the base repository is bare, since creating the config.worktree\n> file and enabling extensions.worktreeConfig will cause Git to treat the base\n> repo's core.bare=false as important for this worktree.\n>\n> This series fixes this, but also puts in place some helpers to prevent this\n> from happening in the future. While here, some of the config paths are\n> modified to take a repository struct.\n>\n> The critical bits are in Patches 3, 4, and 5 which introduce a helper for\n> upgrading to worktree config, a helper to write to worktree config, and then\n> consume that config helper in builtin/sparse-checkout.c and sparse-index.c.\n\nBased on the description I went and fetched the patch series and tried it out.\n\nThis feels like a bandaid to me.  In addition to fixating on core.bare\n(thus overlooking core.worktree), it also overlooks that people can\nuse worktrees without using sparse-checkout.  What if they do\nsomething like:\n\n  git clone --bare $URL myrepo\n  cd myrepo\n  git worktree add foo\n  git worktree add bar\n  git worktree add baz\n  ... days/weeks later ...\n  cd foo\n  git config extensions.worktreeConfig true\n  git config status.showUntrackedFiles no  # Or other config options\n  ... hours/days later ..\n  cd ../bar\n  git status\n\nAt this point the user gets \"fatal: this operation must be run in a\nwork tree\".  And it's much, much worse if the original clone was not\nbare, but had core.worktree pointing somewhere else (because then the\n`git status` invocation will show the differences between the *other*\nworktree with the HEAD of *this* worktree).  I think that \"git\nworktree add\" should check if either core.bare is false or\ncore.worktree is set, and if so then set extensions.worktreeConfig and\nmigrate the relevant config.\n\nWhile there may be some concern about non-git clients not\nunderstanding extensions.worktreeConfig, I'd say that this type of\nsituation is one where we are just flat broken without it.  Without\nit, we're stuck in a situation that will: (a) confuse users (\"Why does\ncore.bare/core.worktree seem to get ignored or have incorrect\nvalues?\"), (b) confuse non-git clients (are they really going to have\nthe tricky logic to overrule core.bare/core.worktree when in another\nworktree?), (c) confuse git itself after subsequent configuration\ntweaks, and (d) (related to item c) lead to ever more complicated\nlogic in core git to attempt to know when and how to overrule\ncore.bare and core.worktree as additional concerns enter the picture\n(e.g. will someone contribute code to override core.bare/core.worktree\nwhen run from a worktree with extensions.worktreeConfig=true, just as\nsomeone originally wrote code to override core.bare/core.worktree when\nthe extensions.worktreeConfig setting wasn't present?)\n\n\nI also think `git worktree add` should handle additional configuration\nitems related to sparse checkouts (as we've discussed elsewhere in the\npast), but that's going a bit outside the scope of this series; I only\nmention it so that we're aware the functionality added to `git\nworktree add` will be getting some additions in the future.\n"},{"id":"444995","messageId":"CAPig+cRDDGU=9BB6kd1tMJR8DmWKSSJwpTD8JeszrY685Fc3-Q@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BG7nwsdEYrnfqhAbWU4ndJHcqGf6RS_6DzJittuNVLvoA@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-27T07:15:22Z","receivedAt":"2021-12-27T07:15:35Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Dec 22, 2021 at 5:54 PM Elijah Newren <newren@gmail.com> wrote:\n> On Wed, Dec 22, 2021 at 8:00 AM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> > This patch series includes a fix to the bug reported by Sean Allred [1] and\n> > diagnosed by Eric Sunshine [2].\n>\n> This feels like a bandaid to me.  In addition to fixating on core.bare\n> (thus overlooking core.worktree), it also overlooks that people can\n> use worktrees without using sparse-checkout.  What if they do\n> something like:\n>\n>   git clone --bare $URL myrepo\n>   cd myrepo\n>   git worktree add foo\n>   git worktree add bar\n>   git worktree add baz\n>   ... days/weeks later ...\n>   cd foo\n>   git config extensions.worktreeConfig true\n>   git config status.showUntrackedFiles no  # Or other config options\n>   ... hours/days later ..\n>   cd ../bar\n>   git status\n>\n> At this point the user gets \"fatal: this operation must be run in a\n> work tree\".\n\nYour example indeed leads to a broken state because it doesn't follow\nthe instructions given by git-worktree.txt for enabling\n`extensions.worktreeConfig`, which involves additional bookkeeping\noperations beyond merely setting that config variable. It is exactly\nthis sort of situation which prompted me to suggest several\ntimes[1,2,3] in the conversation following my diagnosis of the\nproblem, as well as in my reviews of this series, that we may want to\nadd a git-worktree subcommand which does all the necessary bookkeeping\nto enable `extensions.worktreeConfig` rather than expecting users to\nhandle it all manually. In [1], I called this hypothetical command\n`git worktree manage --enable-worktree-config ` and in [4], I called\nit `git worktree config --enable-per-worktree` (not because I like\neither name, but because I couldn't think of anything better).\n\n> I think that \"git\n> worktree add\" should check if either core.bare is false or\n> core.worktree is set, and if so then set extensions.worktreeConfig and\n> migrate the relevant config.\n\n(I think you meant \"...if either core.bare is _true_ or...\".)\n\nSimilar to my response to Sean in [1] and to Stolee in [2], while this\nmay help the situation for worktrees created _after_\n`extensions.worktreeConfig` is enabled, it does _not_ help existing\nworktrees at all. For this reason, in my opinion, `git worktree add`\nis simply not the correct place to be addressing this problem, and\nit's why I suggested a separate command for enabling the feature and\ndoing all the necessary bookkeeping. It's also why I suggested[2] that\nin the long run, we may want per-worktree config to be the default\n(and only) behavior rather than the current (legacy) behavior of all\nconfig being shared between worktrees.\n\nAside from that, I'm uncomfortable with the suggestion that `git\nworktree add` should be responsible for making these sort of dramatic\nchanges (upgrading to version=1 and enabling\n`extensions.worktreeConfig`) to the repository automatically. That\nseems very much out of scope for what this command should be doing. On\nthe other hand, I would have no problem with `git worktree add`\nprotecting users by detecting whether `core.bare=true` or\n`core.worktree` is set in the shared .git/config file and aborting\nwith an error if so, and giving a \"HINT\" telling the user to enable\nper-worktree config via the (hypothetical) `git worktree config\n--enable-per-worktree` command.\n\nRegarding your feeling that this patch series is a \"band-aid\", while I\nagree with you that we ultimately need a better approach, such as the\nhypothetical `git worktree config --enable-per-worktree` (or\neventually making per-worktree config be the default), that better\nsolution does not need to be implemented today, and certainly\nshouldn't derail _this_ patch series which is aimed at fixing a very\nreal bug which exists presently in `git sparse-checkout init`. This\npatch series does need a good number of improvements and fixes before\nit is ready -- as indicated by my v2 review comments[4,5,6], the most\nobvious of which is its missing handling of `core.worktree` -- but I\ndo think this series is headed in the correct direction by focusing on\nfixing the immediate problem with `git sparse-checkout init` (and\npaving the way for an eventual more complete solution, such as `git\nworktree config --enable-per-worktree `).\n\n[1]: https://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n[2]: https://lore.kernel.org/git/CAPig+cQPUe9REf+wgVNjyak_nk3V361h-48rTFgk6TGC7vJgOA@mail.gmail.com/\n[3]: https://lore.kernel.org/git/CAPig+cRombN-8g0t7Hs9qQypJoY41gK3+kvypH4D0G6EB4JgbQ@mail.gmail.com/\n[4]: https://lore.kernel.org/git/CAPig+cQrJ9yWjkc8VMu=uyx_qtrXdL3cNnxLVafoxOo6e-r9kw@mail.gmail.com/\n[5]: https://lore.kernel.org/git/CAPig+cRi2SA6+poaemY8XR5ZoMweuztfiENpcRVOCnukg3Qa7w@mail.gmail.com/\n[6]: https://lore.kernel.org/git/CAPig+cRuY40RNi4bC3CBfghLLqz74VUPRbaYJYEhmF78b0GfPQ@mail.gmail.com/#t\n\n> I also think `git worktree add` should handle additional configuration\n> items related to sparse checkouts (as we've discussed elsewhere in the\n> past), but that's going a bit outside the scope of this series; I only\n> mention it so that we're aware the functionality added to `git\n> worktree add` will be getting some additions in the future.\n\nI vaguely recall some mention of this not long ago on the list but\ndidn't follow the discussion at all. Do you have pointers or a\nsummary?\n"},{"id":"444996","messageId":"CAPig+cQjYiARBwSZTsswk442TvDC2UiyOx5wNdRwF6C7i8ENxA@mail.gmail.com","threadId":"57125","inReplyTo":"CAPig+cRDDGU=9BB6kd1tMJR8DmWKSSJwpTD8JeszrY685Fc3-Q@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-27T07:34:17Z","receivedAt":"2021-12-27T07:34:30Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Dec 27, 2021 at 2:15 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Wed, Dec 22, 2021 at 5:54 PM Elijah Newren <newren@gmail.com> wrote:\n> > I think that \"git\n> > worktree add\" should check if either core.bare is false or\n> > core.worktree is set, and if so then set extensions.worktreeConfig and\n> > migrate the relevant config.\n>\n> Similar to my response to Sean in [1] and to Stolee in [2], while this\n> may help the situation for worktrees created _after_\n> `extensions.worktreeConfig` is enabled, it does _not_ help existing\n> worktrees at all. For this reason, in my opinion, `git worktree add`\n> is simply not the correct place to be addressing this problem, and\n> it's why I suggested a separate command for enabling the feature and\n> doing all the necessary bookkeeping. It's also why I suggested[2] that\n> in the long run, we may want per-worktree config to be the default\n> (and only) behavior rather than the current (legacy) behavior of all\n> config being shared between worktrees.\n\nThinking upon it further, I take back what I said about it not helping\nexisting worktrees.\n\nYour proposal is _almost_ the same as my suggestion of eventually\nmaking per-worktree config the default. The difference is that you're\nonly making it the default if `core.bare=true` or `core.worktree` is\nset. But do we need that distinction? If people are comfortable with\nthat, then are they comfortable with simply flipping the switch and\nmaking per-worktree config the default today regardless of `core.bare`\nand `core.worktree`?\n\nI'm not sure that I'm comfortable with it due to the potential of\nbreaking older versions of git which don't understand\n`extensions.worktreeConfig`, as well as breaking third-party tools,\nbut maybe other people feel differently?\n"},{"id":"445012","messageId":"CABPp-BFxz5B_wUubzaYeGEaztALqDMxxVTrcT4d1kKjpX8pRDQ@mail.gmail.com","threadId":"57125","inReplyTo":"CAPig+cRDDGU=9BB6kd1tMJR8DmWKSSJwpTD8JeszrY685Fc3-Q@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-27T19:35:03Z","receivedAt":"2021-12-27T19:35:18Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Dec 26, 2021 at 11:15 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Wed, Dec 22, 2021 at 5:54 PM Elijah Newren <newren@gmail.com> wrote:\n> > On Wed, Dec 22, 2021 at 8:00 AM Derrick Stolee via GitGitGadget\n> > <gitgitgadget@gmail.com> wrote:\n> > > This patch series includes a fix to the bug reported by Sean Allred [1] and\n> > > diagnosed by Eric Sunshine [2].\n> >\n> > This feels like a bandaid to me.  In addition to fixating on core.bare\n> > (thus overlooking core.worktree), it also overlooks that people can\n> > use worktrees without using sparse-checkout.  What if they do\n> > something like:\n> >\n> >   git clone --bare $URL myrepo\n> >   cd myrepo\n> >   git worktree add foo\n> >   git worktree add bar\n> >   git worktree add baz\n> >   ... days/weeks later ...\n> >   cd foo\n> >   git config extensions.worktreeConfig true\n> >   git config status.showUntrackedFiles no  # Or other config options\n> >   ... hours/days later ..\n> >   cd ../bar\n> >   git status\n> >\n> > At this point the user gets \"fatal: this operation must be run in a\n> > work tree\".\n>\n> Your example indeed leads to a broken state because it doesn't follow\n> the instructions given by git-worktree.txt for enabling\n> `extensions.worktreeConfig`, which involves additional bookkeeping\n> operations beyond merely setting that config variable.\n\nThese are instructions which neither Stolee nor I was aware of prior\nto your pointing it out.  Not only had we often flipped that variable,\nwe did so for many of our users (I know I did for some time prior to\nStolee introducing sparse-checkout).  With `git sparse-checkout` we\npropagated that to many more users, and now have many repositories out\nin the wild that have been set up for _years_ in violation of these\ninstructions.  So, even if Stolee and I are independently particularly\nbad about noticing the relevant documentation, we now have a situation\nwhere people can discover this misconfiguration just by looking around\nin their config.  Once they notice it, they may well copy it\nelsewhere.\n\nI'd suspect that Stolee and I are actually _more_ likely to be aware\nof relevant documentation than the average Git user, so if we missed\nit, I suspect many of them will.  Especially now that we've amplified\ntheir opportunities for discovering repositories set up in\ncontravention to that documentation.\n\nSo, I don't think relying on folks to read this particular piece of\ndocumentation is a reliable course of action...at least not without\nsome changes to make it much more likely to be noticed.\n\n> It is exactly\n> this sort of situation which prompted me to suggest several\n> times[1,2,3] in the conversation following my diagnosis of the\n> problem, as well as in my reviews of this series, that we may want to\n> add a git-worktree subcommand which does all the necessary bookkeeping\n> to enable `extensions.worktreeConfig` rather than expecting users to\n> handle it all manually. In [1], I called this hypothetical command\n> `git worktree manage --enable-worktree-config ` and in [4], I called\n> it `git worktree config --enable-per-worktree` (not because I like\n> either name, but because I couldn't think of anything better).\n\nHow would users discover this new command and use it?  Is it any more\nreliably discoverable than the above documentation?\n\nYour suggestion sounds to me like \"We know this command will break\nthings, so we'll provide another command they can use to avoid the\nbreakage, and hope they notice this new command and use it.\"  I'm sure\nthat's not your intent, and perhaps there's a way of making this\nsuggestion robust, but to me it just sounds like it leads to\ninevitable breakage.  I'd rather just fix the command that can break\nthings.\n\n> > I think that \"git\n> > worktree add\" should check if either core.bare is false or\n> > core.worktree is set, and if so then set extensions.worktreeConfig and\n> > migrate the relevant config.\n>\n> (I think you meant \"...if either core.bare is _true_ or...\".)\n\nYes, indeed.\n\n> Similar to my response to Sean in [1] and to Stolee in [2], while this\n> may help the situation for worktrees created _after_\n> `extensions.worktreeConfig` is enabled, it does _not_ help existing\n> worktrees at all. For this reason, in my opinion, `git worktree add`\n> is simply not the correct place to be addressing this problem, and\n> it's why I suggested a separate command for enabling the feature and\n> doing all the necessary bookkeeping. It's also why I suggested[2] that\n> in the long run, we may want per-worktree config to be the default\n> (and only) behavior rather than the current (legacy) behavior of all\n> config being shared between worktrees.\n>\n> Aside from that, I'm uncomfortable with the suggestion that `git\n> worktree add` should be responsible for making these sort of dramatic\n> changes (upgrading to version=1 and enabling\n> `extensions.worktreeConfig`) to the repository automatically. That\n> seems very much out of scope for what this command should be doing. On\n> the other hand, I would have no problem with `git worktree add`\n> protecting users by detecting whether `core.bare=true` or\n> `core.worktree` is set in the shared .git/config file and aborting\n> with an error if so, and giving a \"HINT\" telling the user to enable\n> per-worktree config via the (hypothetical) `git worktree config\n> --enable-per-worktree` command.\n>\n> Regarding your feeling that this patch series is a \"band-aid\", while I\n> agree with you that we ultimately need a better approach, such as the\n> hypothetical `git worktree config --enable-per-worktree` (or\n> eventually making per-worktree config be the default), that better\n> solution does not need to be implemented today, and certainly\n> shouldn't derail _this_ patch series which is aimed at fixing a very\n> real bug which exists presently in `git sparse-checkout init`. This\n> patch series does need a good number of improvements and fixes before\n> it is ready -- as indicated by my v2 review comments[4,5,6], the most\n> obvious of which is its missing handling of `core.worktree` -- but I\n> do think this series is headed in the correct direction by focusing on\n> fixing the immediate problem with `git sparse-checkout init` (and\n> paving the way for an eventual more complete solution, such as `git\n> worktree config --enable-per-worktree `).\n\nLooks like you've changed your opinion a bit and it'd be better for me\nto respond to these parts in your follow-up email.\n\n> [1]: https://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n> [2]: https://lore.kernel.org/git/CAPig+cQPUe9REf+wgVNjyak_nk3V361h-48rTFgk6TGC7vJgOA@mail.gmail.com/\n> [3]: https://lore.kernel.org/git/CAPig+cRombN-8g0t7Hs9qQypJoY41gK3+kvypH4D0G6EB4JgbQ@mail.gmail.com/\n> [4]: https://lore.kernel.org/git/CAPig+cQrJ9yWjkc8VMu=uyx_qtrXdL3cNnxLVafoxOo6e-r9kw@mail.gmail.com/\n> [5]: https://lore.kernel.org/git/CAPig+cRi2SA6+poaemY8XR5ZoMweuztfiENpcRVOCnukg3Qa7w@mail.gmail.com/\n> [6]: https://lore.kernel.org/git/CAPig+cRuY40RNi4bC3CBfghLLqz74VUPRbaYJYEhmF78b0GfPQ@mail.gmail.com/#t\n>\n> > I also think `git worktree add` should handle additional configuration\n> > items related to sparse checkouts (as we've discussed elsewhere in the\n> > past), but that's going a bit outside the scope of this series; I only\n> > mention it so that we're aware the functionality added to `git\n> > worktree add` will be getting some additions in the future.\n>\n> I vaguely recall some mention of this not long ago on the list but\n> didn't follow the discussion at all. Do you have pointers or a\n> summary?\n\nFor the microsoft repositories, sparse-checkouts are needed because a\nfull checkout is unmanageable (millions of files to check out\notherwise).  For other repositories, full checkouts might technically\nbe manageable, but are annoyingly slow and users may only want to work\nwith sparse checkouts (and for some of these, due to various\nmono-repoization efforts, the repository is growing towards a size\nwhere manageability of full checkouts is decreasing).\n\nThe fact that `git worktree add` does a full checkout is quite\npainful...possibility to the point of making worktrees useless for\nsome users.  I think `git worktree add` should copy the sparsity of\nthe worktree from which it was invoked.\n\nAddressing potential questions/objections to this proposal:\n  * just requiring users to do a full checkout first is very\nunfriendly: the checkout might not even fit in available disk space,\nand even if it does fit, this has the performance penalty of inflating\nand writing all files to disk only to delete a (vast?) majority of\nthem immediately after.  Users have shown a willingness to swallow a\nlot of pain trying to figure out how to avoid that performance\npenalty.\n  * full-checkout: If users do want a full checkout of the new\nworktree despite running from a sparse-checkout, it's a single command\naway (`git sparse-checkout disable` or `<sparsity-wrapper-script>\n--undo`).  And in that case, the invoked commands don't do huge\namounts of unnecessary work.\n  * using --no-checkout as a proxy: This means no files checked out\nand no index file.  The lack of an index file makes it appear that\neverything was manually deleted (with the deletion staged).  Also, if\nthe project is using some kind of <sparsity-wrapper-script> (e.g. for\ndetermining dependencies between directories so that appropriate\n'modules' can be specified and transformed into a list of directories\npassed to sparse-checkout), then the sparsity-wrapper-script isn't\navailable to them to invoke.  If users try to check out just the\nwrapper file, then an index will be created and have just one entry\nand we kind of cement the fact that all other files look like they\nwere intended to be deleted.  Also, even if the user runs `git\nsparse-checkout init --cone`, you don't actually don't transform this\nno-checkout into a sparse checkout because sparse-checkout doesn't\nwant to undo your staged deletions.  Despite the fact that I'm very\nfamiliar with all the implementation internals, it was not obvious to\nme all the necessary additional commands needed for users to get a\nsparse checkout while making use of --no-checkout.  Users stand little\nchance of figuring the necessary command invocations out without a\nhuge amount of effort (and they've given up and come to me before\nasking for help, and my first response turned out to be incomplete in\nvarious cases...).\n"},{"id":"445015","messageId":"CABPp-BHMr32MuisNzt-R3O4=HPY63HQT0=ykrbtiASyS1K_JjQ@mail.gmail.com","threadId":"57125","inReplyTo":"CAPig+cQjYiARBwSZTsswk442TvDC2UiyOx5wNdRwF6C7i8ENxA@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-27T20:16:42Z","receivedAt":"2021-12-27T20:16:59Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Dec 26, 2021 at 11:34 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Mon, Dec 27, 2021 at 2:15 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > On Wed, Dec 22, 2021 at 5:54 PM Elijah Newren <newren@gmail.com> wrote:\n> > > I think that \"git\n> > > worktree add\" should check if either core.bare is false or\n> > > core.worktree is set, and if so then set extensions.worktreeConfig and\n> > > migrate the relevant config.\n> >\n> > Similar to my response to Sean in [1] and to Stolee in [2], while this\n> > may help the situation for worktrees created _after_\n> > `extensions.worktreeConfig` is enabled, it does _not_ help existing\n> > worktrees at all. For this reason, in my opinion, `git worktree add`\n> > is simply not the correct place to be addressing this problem, and\n> > it's why I suggested a separate command for enabling the feature and\n> > doing all the necessary bookkeeping. It's also why I suggested[2] that\n> > in the long run, we may want per-worktree config to be the default\n> > (and only) behavior rather than the current (legacy) behavior of all\n> > config being shared between worktrees.\n>\n> Thinking upon it further, I take back what I said about it not helping\n> existing worktrees.\n>\n> Your proposal is _almost_ the same as my suggestion of eventually\n> making per-worktree config the default. The difference is that you're\n> only making it the default if `core.bare=true` or `core.worktree` is\n> set.\n\nIndeed.  :-)\n\n> But do we need that distinction? If people are comfortable with\n> that, then are they comfortable with simply flipping the switch and\n> making per-worktree config the default today regardless of `core.bare`\n> and `core.worktree`?\n\nThis is tempting, at least if we leave core.repositoryFormatVersion as\n0 (see 11664196ac (\"Revert \"check_repository_format_gently(): refuse\nextensions for old repositories\"\", 2020-07-15)) when core.bare is\nfalse and core.worktree was unset.  However, for that case:\n\n* This is a case where operating on the primary worktree was not\npreviously problematic for older git versions or third party tools.\n* Interestingly, git <= 2.6.2 can continue to operate on the primary\nworktree (because it didn't know to error out on unknown extensions)\n* git >= 2.19.0 could continue to operate on the primary worktree\n(because it understands the extension)\n* git versions between that range would suddenly break, erroring out\non the unknown extension (though those versions would start working\nagain if we migrated core.bare and core.worktree but just didn't set\nextensions.worktreeConfig).\n\n> I'm not sure that I'm comfortable with it due to the potential of\n> breaking older versions of git which don't understand\n> `extensions.worktreeConfig`, as well as breaking third-party tools,\n> but maybe other people feel differently?\n\nThe distinction I made was particularly written with third party tools\nand older versions of git in mind, to allow them to continue to safely\noperate when possible.  But let's flesh it out a little:\n\n* core.bare = false AND core.worktree is unset (i.e. a typical\nnon-bare clone), AND we try to add a worktree: we have _years_ of\nin-the-wild usage showing that old git versions and third party tools\noperate fine without migrating the config.  Leave it in place and do\nnot set extensions.worktreeConfig and do not upgrade\ncore.repositoryFormatVersion.\n\n* (core.bare = true OR core.worktree is set) AND we try to add a\nworktree: all third party tools and all git versions (old and new) are\nbroken anyway.  Flip the switch (upgrade core.repositoryFormatVersion\nto 1, set extensions.worktreeConfig=true, and migrate\ncore.bare/core.worktree for main repo and newly created worktree),\nwhich at least allows new git versions and new tools to work\ncorrectly, and will hopefully cause old tools to error out since this\nis a configuration they won't handle correctly.\n\n\nFurther:\n  * Toggling extensions.worktreeConfig=true for the first time is\nrather trivial for users to try; for years they have been able to do\nso without making _any_ other changes and expect things to continue to\nwork (assuming new enough git versions and third-party tools).  They\nhave likely disseminated this information to other users.  I certainly\ndid.  If we are going to expect more of anyone toggling this option,\nwe need lots of documentation and perhaps code changes to help shore\nup the path.  I'd rather just allow folks to continue to do such\ntoggling.\n  * Toggling either core.bare or core.worktree in an existing clone\nrequires significant additional manual work to make things consistent.\nBecause it requires a lot more knowledge and work, I think the burden\nshould be on these users to know about the ramifications with existing\nworktrees.  (I've never heard of a user other than myself attempt to\ntoggle these; I'm sure there are some, it just seems quite rare.)\n"},{"id":"445054","messageId":"CAPig+cRYKKGA1af4hV0fz_nZWNG=zMgAziuAimDxWTz6L8u3Tg@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BFxz5B_wUubzaYeGEaztALqDMxxVTrcT4d1kKjpX8pRDQ@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-28T07:33:02Z","receivedAt":"2021-12-28T07:33:15Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Dec 27, 2021 at 2:35 PM Elijah Newren <newren@gmail.com> wrote:\n> On Sun, Dec 26, 2021 at 11:15 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > Your example indeed leads to a broken state because it doesn't follow\n> > the instructions given by git-worktree.txt for enabling\n> > `extensions.worktreeConfig`, which involves additional bookkeeping\n> > operations beyond merely setting that config variable.\n>\n> These are instructions which neither Stolee nor I was aware of prior\n> to your pointing it out.  [...]\n> I'd suspect that Stolee and I are actually _more_ likely to be aware\n> of relevant documentation than the average Git user, so if we missed\n> it, I suspect many of them will. [...]\n\nI wasn't originally aware of the bookkeeping instructions either. In\nfact, for a good while, I wasn't even aware that Duy had implemented\nper-worktree configuration or that there was such a thing. I either\nmust have been entirely off-list during the implementation or was not\nin a position to follow changes to the project at the time. I only\nbecame aware of per-worktree configuration at some point in the last\ntwo or three years when someone asked some question on the list\nrelated to the feature and I ended up diving into the documentation,\nthe source code, and the patches themselves in order to understand it\nfully -- because I think I didn't understand it merely from reading\nthe documentation which is rather sparse (no pun intended). And I had\nforgotten enough about it since then that I had to re-research it when\nSean raised the issue on the list a few days back in relation to\nsparse-checkout.\n\n> So, I don't think relying on folks to read this particular piece of\n> documentation is a reliable course of action...at least not without\n> some changes to make it much more likely to be noticed.\n\nThe sparsity of documentation about per-worktree configuration\ncertainly doesn't help, nor the fact that it's fairly near the end of\ngit-worktree.txt, at which point people may have given up reading. We\ncould make it a bit more prominent by mentioning it early in the\ncommand description, but it still involves enough fiddly bookkeeping\nthat it likely will continue to be problematic.\n\nMaking per-worktree configuration the default does seem like the best\nlong-term solution. Doing so should make all these problems go away. I\ndon't know what Duy's plans were, nor whether he had some migration\nstrategy planned.\n\n> > I vaguely recall some mention of this not long ago on the list but\n> > didn't follow the discussion at all. Do you have pointers or a\n> > summary?\n>\n> For the microsoft repositories, sparse-checkouts are needed because a\n> full checkout is unmanageable (millions of files to check out\n> otherwise).  For other repositories, full checkouts might technically\n> be manageable, but are annoyingly slow and users may only want to work\n> with sparse checkouts (and for some of these, due to various\n> mono-repoization efforts, the repository is growing towards a size\n> where manageability of full checkouts is decreasing).\n>\n> The fact that `git worktree add` does a full checkout is quite\n> painful...possibility to the point of making worktrees useless for\n> some users.  I think `git worktree add` should copy the sparsity of\n> the worktree from which it was invoked.\n\nOkay, I do recall reading a message in which you proposed this, though\nI didn't understand the reasoning for the suggestion since I wasn't\nfollowing the discussion. The explanation you provide here makes the\nproposal understandable.\n\n>   * using --no-checkout as a proxy: This means no files checked out\n> and no index file.  The lack of an index file makes it appear that\n> everything was manually deleted (with the deletion staged).  Also, if\n> the project is using some kind of <sparsity-wrapper-script> (e.g. for\n> determining dependencies between directories so that appropriate\n> 'modules' can be specified and transformed into a list of directories\n> passed to sparse-checkout), then the sparsity-wrapper-script isn't\n> available to them to invoke.  If users try to check out just the\n> wrapper file, then an index will be created and have just one entry\n> and we kind of cement the fact that all other files look like they\n> were intended to be deleted.  Also, even if the user runs `git\n> sparse-checkout init --cone`, you don't actually don't transform this\n> no-checkout into a sparse checkout because sparse-checkout doesn't\n> want to undo your staged deletions.  Despite the fact that I'm very\n> familiar with all the implementation internals, it was not obvious to\n> me all the necessary additional commands needed for users to get a\n> sparse checkout while making use of --no-checkout.  Users stand little\n> chance of figuring the necessary command invocations out without a\n> huge amount of effort (and they've given up and come to me before\n> asking for help, and my first response turned out to be incomplete in\n> various cases...).\n\nYou've clearly put much more thought into this than I have (since I\nonly just read this), so I'm not likely to have any meaningful input,\nbut I'll write down a few thoughts/questions which popped into my head\nwhile reading what you wrote. Perhaps they've already been discussed\nelsewhere, so feel free to ignore (and they may not be worth\nresponding to anyhow).\n\nWhen you say \"copy the sparsity of the worktree from which it was\ninvoked\", do you mean that literally, such that it special-cases it\nand only copies sparse-checkout information? An alternative would be\nto allow the user to specify -- via the shared configuration\n(.git/config) -- exactly which config keys get inherited/copied over\nby `git worktree add`. Such a solution would avoid special-casing\nsparse-checkout and could be useful in the future for other commands\nwhich might need such functionality, though this approach may be\noverengineered.\n\nA more general approach might be for the new worktree to copy all the\nper-worktree configuration from the worktree in which the command was\ninvoked, thus sparsity would be inherited \"for free\" along with other\nsettings. This has the benefits of not requiring sparse-checkout\nspecial-cases in the code and it's easy to document (\"the new worktree\ninherits/copies configuration settings from the worktree in which `git\nworktree add` was invoked\") and easy to understand.\n\nI also wondered if adding some sort of `--sparse-checkout=...` option\nto `git worktree add` would solve this particular dilemma, thus\nallowing the user to configure custom sparse-checkout for the worktree\nas it is being created. I also very briefly wondered if this should\ninstead be a feature of the `git sparse-checkout` command itself, such\nas `git sparse-checkout add-worktree`, but I think that's probably a\ndead-end in terms of user discoverability, whereas `git worktree add\n--sparse-checkout=...` is more easily discoverable for people wanting\nto work with worktrees.\n"},{"id":"445056","messageId":"CAPig+cTFSDw-9Aq+=+r4sHSzTmG7s2T93Z0uqWTxHbKwGFaiYQ@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BHMr32MuisNzt-R3O4=HPY63HQT0=ykrbtiASyS1K_JjQ@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-28T09:11:55Z","receivedAt":"2021-12-28T09:12:08Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Dec 27, 2021 at 3:16 PM Elijah Newren <newren@gmail.com> wrote:\n> On Sun, Dec 26, 2021 at 11:34 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > Your proposal is _almost_ the same as my suggestion of eventually\n> > making per-worktree config the default. The difference is that you're\n> > only making it the default if `core.bare=true` or `core.worktree` is\n> > set.\n>\n> Indeed.  :-)\n>\n> > But do we need that distinction? If people are comfortable with\n> > that, then are they comfortable with simply flipping the switch and\n> > making per-worktree config the default today regardless of `core.bare`\n> > and `core.worktree`?\n>\n> This is tempting, at least if we leave core.repositoryFormatVersion as\n> 0 (see 11664196ac (\"Revert \"check_repository_format_gently(): refuse\n> extensions for old repositories\"\", 2020-07-15)) when core.bare is\n> false and core.worktree was unset.  However, for that case:\n\nI'll try to respond to this email when I can find a quiet block of\ntime to really concentrate on what you're saying and reason through it\nmore thoroughly; it will probably require several read-throughs.\n"},{"id":"445087","messageId":"53f3531d-29ee-f16e-1d60-21ca7b4a3fbd@gmail.com","threadId":"57125","inReplyTo":"CAPig+cQrJ9yWjkc8VMu=uyx_qtrXdL3cNnxLVafoxOo6e-r9kw@mail.gmail.com","subject":"Re: [PATCH v2 3/5] worktree: add upgrade_to_worktree_config()","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-28T15:03:03Z","receivedAt":"2021-12-28T15:03:11Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/21/2021 7:45 PM, Eric Sunshine wrote:\n> On Tue, Dec 21, 2021 at 2:14 PM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>> Some features, such as the sparse-checkout builtin, require using the\n>> worktree config extension. It might seem simple to upgrade the\n>> repository format and add extensions.worktreeConfig, and that is what\n>> happens in the sparse-checkout builtin.\n>>\n>> Transitioning from one config file to multiple has some strange\n>> side-effects. In particular, if the base repository is bare and the\n>> worktree is not, Git knows to treat the worktree as non-bare as a\n>> special case when not using worktree config. Once worktree config is\n>> enabled, Git stops that special case since the core.bare setting could\n>> apply at the worktree config level. This opens the door for bare\n>> worktrees.\n> \n> It would be a good idea to drop the final sentence since there is no\n> such thing as a bare worktree (either conceptually or practically),\n> and end the first sentence at \"case\": i.e. \"... stops that special\n> case.\"\n\nBare worktrees don't exist, that is correct. But if one existed it\nwould be a directory where you could operate as if it is a bare repo,\nbut it has its own HEAD different from the base repo's HEAD. Not sure\nwhy one would want it.\n\nI guess the question is: what happens if someone writes core.bare=true\ninto their worktree config? A question to resolve another day, perhaps.\n \n>> To help resolve this transition, create upgrade_to_worktree_config() to\n>> navigate the intricacies of this operation. In particular, we need to\n>> look for core.bare=true within the base config file and move that\n>> setting into the core repository's config.worktree file.\n>>\n>> To gain access to the core repository's config and config.worktree file,\n>> we reference a repository struct's 'commondir' member. If the repository\n>> was a submodule instead of a worktree, then this still applies\n>> correctly.\n> \n> I'm not sure how much this commit message is going to help someone who\n> did not participate in the discussion which led to this patch series.\n> I think the entire commit message could be written more concisely like\n> this:\n\nGood suggestions to add the necessary context here. Thanks.\n\n>     worktree: add helper to upgrade repository to per-worktree config\n> \n>     Changing a repository to use per-worktree configuration is a\n>     somewhat involved manual process, as described in the\n>     `git-worktree` documentation, but it's easy to get wrong if the\n>     steps are not followed precisely, which could lead to odd\n>     anomalies such as Git thinking that a worktree is \"bare\" (which\n>     conceptually makes no sense). Therefore, add a utility function to\n>     automate the process of switching to per-worktree configuration\n>     for modules which require such configuration. (In the future, it\n>     may make sense to also expose this convenience as a `git-worktree`\n>     command to automate the process for end-users, as well.)\n> \n>     To upgrade the repository to per-worktree configuration, it performs\n>     these steps:\n> \n>     * enable `extensions.worktreeConfig` in .git/config\n> \n>     * relocate `core.bare` from .git/config to .git/config.worktree\n>       (if key exists)\n> \n>     * relocate `core.worktree` from .git/config to\n>       .git/config.worktree (if key exists)\n> \n>     It also upgrades the repository format to 1 if necessary since\n>     that is a prerequisite of using `extensions`.\n> \n>> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n>> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n>> ---\n>> diff --git a/worktree.c b/worktree.c\n>> @@ -830,3 +831,49 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,\n>> +int upgrade_to_worktree_config(struct repository *r)\n>> +{\n>> +       int res;\n>> +       int bare = 0;\n>> +       struct config_set cs = { 0 };\n> \n> This function is doing a lot of unnecessary work if per-worktree\n> configuration is already enabled. The very first thing it should be\n> doing is checking whether or not it actually needs to do that work,\n> and short-circuit if it doesn't. I would think that simply checking\n> whether `extensions.worktreeConfig` is true and returning early if it\n> is would be sufficient.\n\nThat would be good. I originally erred on the side of least complicated\nbut slower because this is not run very often.\n\n>> +       char *base_config_file = xstrfmt(\"%s/config\", r->commondir);\n> \n> If we look at path.c:strbuf_worktree_gitdir(), we see that this should\n> be using `r->gitdir`, not `r->commondir`.\n> \n>> +       char *base_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n> \n> Per path.c:strbuf_worktree_gitdir(), this use of `r->commondir` is\n> correct. Good.\n> \n> Can we use more meaningful variable names? It's not at all clear what\n> \"base\" means in this context (I don't think it has any analog in Git\n> terminology). Perhaps name these `shared_config` and `repo_config`,\n> respectively.\n\n'repo_config' is too generic, because I want the worktree config for\nthe \"original\" repo. I chose to call that the \"base\" repo and its\nworktree config. Shared_config is a good name, though.\n\n>> +       git_configset_init(&cs);\n>> +       git_configset_add_file(&cs, base_config_file);\n>> +\n>> +       /*\n>> +        * If the base repository is bare, then we need to move core.bare=true\n>> +        * out of the base config file and into the base repository's\n>> +        * config.worktree file.\n>> +        */\n> \n> Here, too, it's not clear what \"base\" means. I think you want to say\n> that it needs to \"move `core.bare` from the shared config to the\n> repo-specific config\".\n\nYes, but specific to the original/root/bare repo. I'm open to preferences\nhere, but \"repo\" isn't specific enough.\n\n>> +       if (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n>> +               if ((res = git_config_set_in_file_gently(base_worktree_file,\n>> +                                                       \"core.bare\", \"true\"))) {\n>> +                       error(_(\"unable to set core.bare=true in '%s'\"), base_worktree_file);\n>> +                       goto cleanup;\n>> +               }\n>> +\n>> +               if ((res = git_config_set_in_file_gently(base_config_file,\n>> +                                                       \"core.bare\", NULL))) {\n>> +                       error(_(\"unable to unset core.bare=true in '%s'\"), base_config_file);\n>> +                       goto cleanup;\n>> +               }\n>> +       }\n> \n> This seems unnecessarily complicated and overly specific, thus\n> potentially confusing. The requirements laid out in git-worktree.txt\n> say only to move the configuration key from .git/config to\n> .git/config.worktree; it doesn't add any qualifiers about the value\n> being \"true\". And, indeed, we should not care about the value; it's\n> immaterial to this operation. Instead, we should just treat it\n> opaquely and relocate the key and value from .git/config (if it\n> exists) to .git/config.worktree without interpretation.\n> \n> The error messages are too specific, as well, by mentioning \"true\".\n> They could instead be:\n> \n>     unable to set `core.bare` in '%s'\n> \n> and\n> \n>     unable to remove `core.bare` from '%s'\n> \n> However, there is a much more _severe_ problem with this\n> implementation: it is incomplete. As documented in git-worktree.txt\n> (and mentioned several times during this discussion), this utility\n> function _must_ relocate both `core.bare` _and_ `core.worktree` (if\n> they exist) from .git/config to .git/config.worktree. This\n> implementation neglects to relocate `core.worktree`, which can leave\n> things in quite a broken state (just as neglecting to relocate\n> `core.bare` can).\n\nIt took me a long time to actually understand the purpose of\ncore.worktree, since it seems in conflict with the 'git worktree'\nfeature. Indeed, it is special-cased the same way core.bare is, so\nthis relocation is required.\n\n>> +       if (upgrade_repository_format(r, 1) < 0) {\n>> +               res = error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n>> +               goto cleanup;\n>> +       }\n>> +       if ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n>> +               error(_(\"failed to set extensions.worktreeConfig setting\"));\n>> +               goto cleanup;\n>> +       }\n> \n> The order in which this function performs its operations can leave the\n> repository in a broken state if any of the steps fails. For instance,\n> if setting `extensions.worktreeConfig=true` fails _after_ you've\n> relocated `core.bare` (and `core.worktree`) to .git/config.worktree,\n> then those configuration values will no longer be \"active\" since the\n> config system won't consult .git/config.worktree without\n> `extensions.worktreeConfig` enabled.\n> \n> To be resilient against this sort of problem, you should perform the\n> operations in this order:\n> \n> (1) upgrade repository format to 1\n> (2) enable `extensions.worktreeConfig`\n> (3) relocate `core.bare` and `core.worktree`\n\nThis order can still cause some issues, specifically the worktree will\nstill think it is bare or the core.worktree value is incorrect, but that\nis less serious than a broken base repo.\n\n>> +cleanup:\n>> +       git_configset_clear(&cs);\n>> +       free(base_config_file);\n>> +       free(base_worktree_file);\n>> +       trace2_printf(\"returning %d\", res);\n> \n> Is this leftover debugging or intentional?\n\nLeftover debugging. Thanks for catching.\n\nThanks,\n-Stolee\n"},{"id":"445088","messageId":"aa73d9bb-1d34-4991-a242-991251ec02be@gmail.com","threadId":"57125","inReplyTo":"xmqq4k71mc75.fsf@gitster.g","subject":"Re: [PATCH v2 3/5] worktree: add upgrade_to_worktree_config()","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-28T15:13:18Z","receivedAt":"2021-12-28T15:13:29Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/22/2021 12:38 AM, Junio C Hamano wrote:\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n>> +int upgrade_to_worktree_config(struct repository *r)\n> \n> Not a suggestion to rename the function, but does it mean \"we have\n> been using a single configuration for all worktrees attached to the\n> repository, but we are switching to use per-worktree configuration\n> file\"?  I am wondering if the phrase \"worktree_config\" is clear\n> enough for our future developers.\n> \n>> +{\n>> +\tint res;\n>> +\tint bare = 0;\n>> +\tstruct config_set cs = { 0 };\n>> +\tchar *base_config_file = xstrfmt(\"%s/config\", r->commondir);\n>> +\tchar *base_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n>> +\n>> +\tgit_configset_init(&cs);\n>> +\tgit_configset_add_file(&cs, base_config_file);\n>> +\n>> +\t/*\n>> +\t * If the base repository is bare, then we need to move core.bare=true\n>> +\t * out of the base config file and into the base repository's\n>> +\t * config.worktree file.\n>> +\t */\n>> +\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n>> +\t\tif ((res = git_config_set_in_file_gently(base_worktree_file,\n>> +\t\t\t\t\t\t\t\"core.bare\", \"true\"))) {\n>> +\t\t\terror(_(\"unable to set core.bare=true in '%s'\"), base_worktree_file);\n>> +\t\t\tgoto cleanup;\n>> +\t\t}\n> \n> Hmph, I would have expected to see\n> \n> \t\tif (git_config_set_in_file_gently(...)) {\n> \t\t\tres = error(_(\"...\"));\n>                         goto cleanup;\n> \t\t}\n> \n> instead (obviously with \"res\" initialized to \"0\" to assume all will\n> be well at the beginning).\n\nDeep down in the git_config_set_... stack, it returns helpful error\ncodes that I thought would be good to communicate upwards. cmd_config()\nuses these error codes, too, but that's a more obvious place where the\nuser is expecting the error code to be related to the config operation.\n \nIf this communication is not helpful, then I will use the pattern you\nsuggest. I will assume this is the case unless told otherwise.\n\n> More importantly, are we prepared to see the repository 'r' that\n> already uses per-worktree config layout and refrain from causing any\n> damage to it, or are we all perfect developers that any caller that\n> calls this on repository 'r' that does not need to be upgraded is a\n> BUG()?\n\nI don't think we should add burden to the callers to check that the\nrepo is not using worktree config. Thinking back to your rename idea\nthis could be \"ensure_using_worktree_config()\" to make it clear that\nwe will use the worktree config after the method, whether or not we\nwere using it beforehand.\n\nI think this current implementation is non-damaging if someone calls\nit after already using worktree config. The change being that a user\ncan go and add core.bare=false to the common config file and break all\nworktrees again, and the current implementation will help heal that.\nIt's probably better to let the user have that ability to mess things\nup and not try to fix something so broken.\n\nThanks,\n-Stolee\n"},{"id":"445114","messageId":"CAPig+cT76vv08wjM7ryopXxFsg15Cy9Ax3tYNsERM9D_gJCYbw@mail.gmail.com","threadId":"57125","inReplyTo":"53f3531d-29ee-f16e-1d60-21ca7b4a3fbd@gmail.com","subject":"Re: [PATCH v2 3/5] worktree: add upgrade_to_worktree_config()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-28T16:58:25Z","receivedAt":"2021-12-28T16:58:37Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 28, 2021 at 10:03 AM Derrick Stolee <stolee@gmail.com> wrote:\n> On 12/21/2021 7:45 PM, Eric Sunshine wrote:\n> > It would be a good idea to drop the final sentence since there is no\n> > such thing as a bare worktree (either conceptually or practically),\n> > and end the first sentence at \"case\": i.e. \"... stops that special\n> > case.\"\n>\n> Bare worktrees don't exist, that is correct. But if one existed it\n> would be a directory where you could operate as if it is a bare repo,\n> but it has its own HEAD different from the base repo's HEAD. Not sure\n> why one would want it.\n\nI'm not following. I also still don't know what \"base repo\" is or\nwhere two HEADs would arise.\n\n> >> +       char *base_config_file = xstrfmt(\"%s/config\", r->commondir);\n> >> +       char *base_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n> >\n> > Per path.c:strbuf_worktree_gitdir(), this use of `r->commondir` is\n> > correct. Good.\n> >\n> > Can we use more meaningful variable names? It's not at all clear what\n> > \"base\" means in this context (I don't think it has any analog in Git\n> > terminology). Perhaps name these `shared_config` and `repo_config`,\n> > respectively.\n>\n> 'repo_config' is too generic, because I want the worktree config for\n> the \"original\" repo. I chose to call that the \"base\" repo and its\n> worktree config. Shared_config is a good name, though.\n\nThere seems to be some terminology confusion or conflict at play here.\nWe're dealing with only a single repository and zero or more\nworktrees, so I'm still having trouble understanding your references\nto \"original repo\" and \"base repo\", which seem to indicate multiple\nrepositories.\n\n> >> +       /*\n> >> +        * If the base repository is bare, then we need to move core.bare=true\n> >> +        * out of the base config file and into the base repository's\n> >> +        * config.worktree file.\n> >> +        */\n> >\n> > Here, too, it's not clear what \"base\" means. I think you want to say\n> > that it needs to \"move `core.bare` from the shared config to the\n> > repo-specific config\".\n>\n> Yes, but specific to the original/root/bare repo. I'm open to preferences\n> here, but \"repo\" isn't specific enough.\n\nThere's only a single repository, so this should be clear, however,\nthere appears to be some terminology mismatch. I'll enumerate a few\nitems in an effort to clarify how I'm using the terminology...\n\n    .git/ -- the repository residing within the main worktree\n\n    bare.git/ -- a bare repository\n\n    .git/config -- configuration shared by the repository and all worktrees\n\n    bare.git/config -- configuration shared by the repository and all worktrees\n\n    .git/config.worktree -- configuration specific to the main worktree\n\n    bare.git/config.worktree -- configuration specific to the bare repository\n\n    .git/worktrees/<id>/config -- configuration specific to worktree <id>\n\n    bare.git/worktrees/<id>/config -- configuration specific to worktree <id>\n\n> > However, there is a much more _severe_ problem with this\n> > implementation: it is incomplete. As documented in git-worktree.txt\n> > (and mentioned several times during this discussion), this utility\n> > function _must_ relocate both `core.bare` _and_ `core.worktree` (if\n> > they exist) from .git/config to .git/config.worktree. This\n> > implementation neglects to relocate `core.worktree`, which can leave\n> > things in quite a broken state (just as neglecting to relocate\n> > `core.bare` can).\n>\n> It took me a long time to actually understand the purpose of\n> core.worktree, since it seems in conflict with the 'git worktree'\n> feature. Indeed, it is special-cased the same way core.bare is, so\n> this relocation is required.\n\nIndeed, the situation is unfortunately confusing in this area.\n`core.worktree` predates multiple-worktree support (i.e.\n`git-worktree`) by quite a long time and is a mechanism for allowing\nthe repository (.git/) to exist at a distinct location from the\nworktree (by which I mean \"main worktree\" since there was no such\nthing as a \"linked worktree\" at a time). `git-worktree` generalized\nthe concept by making multiple worktrees first-class citizens, but\n`core.worktree` and GIT_WORKTREE still need to be supported for\nbackward compatibility even though they conflict (or can conflict)\nrather badly with multiple-worktrees.\n"},{"id":"445115","messageId":"67cf358c-3a64-0c2f-4696-d444d36cfd97@gmail.com","threadId":"57125","inReplyTo":"CAPig+cT76vv08wjM7ryopXxFsg15Cy9Ax3tYNsERM9D_gJCYbw@mail.gmail.com","subject":"Re: [PATCH v2 3/5] worktree: add upgrade_to_worktree_config()","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-28T17:03:57Z","receivedAt":"2021-12-28T17:04:05Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/28/2021 11:58 AM, Eric Sunshine wrote:\n> On Tue, Dec 28, 2021 at 10:03 AM Derrick Stolee <stolee@gmail.com> wrote:\n>> On 12/21/2021 7:45 PM, Eric Sunshine wrote:\n>>> It would be a good idea to drop the final sentence since there is no\n>>> such thing as a bare worktree (either conceptually or practically),\n>>> and end the first sentence at \"case\": i.e. \"... stops that special\n>>> case.\"\n>>\n>> Bare worktrees don't exist, that is correct. But if one existed it\n>> would be a directory where you could operate as if it is a bare repo,\n>> but it has its own HEAD different from the base repo's HEAD. Not sure\n>> why one would want it.\n> \n> I'm not following. I also still don't know what \"base repo\" is or\n> where two HEADs would arise.\n> \n>>>> +       char *base_config_file = xstrfmt(\"%s/config\", r->commondir);\n>>>> +       char *base_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n>>>\n>>> Per path.c:strbuf_worktree_gitdir(), this use of `r->commondir` is\n>>> correct. Good.\n>>>\n>>> Can we use more meaningful variable names? It's not at all clear what\n>>> \"base\" means in this context (I don't think it has any analog in Git\n>>> terminology). Perhaps name these `shared_config` and `repo_config`,\n>>> respectively.\n>>\n>> 'repo_config' is too generic, because I want the worktree config for\n>> the \"original\" repo. I chose to call that the \"base\" repo and its\n>> worktree config. Shared_config is a good name, though.\n> \n> There seems to be some terminology confusion or conflict at play here.\n> We're dealing with only a single repository and zero or more\n> worktrees, so I'm still having trouble understanding your references\n> to \"original repo\" and \"base repo\", which seem to indicate multiple\n> repositories.\n\nYour use of \"main worktree\" is what I am meaning. I will adopt your\nterminology.\n\nThanks,\n-Stolee\n"},{"id":"445120","messageId":"CABPp-BGKyDJV9DP+igmCC_Ad0jgvb4aOAYpXWCbx9hW8ShhDQg@mail.gmail.com","threadId":"57125","inReplyTo":"CAPig+cRYKKGA1af4hV0fz_nZWNG=zMgAziuAimDxWTz6L8u3Tg@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-28T18:16:37Z","receivedAt":"2021-12-28T18:16:51Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Dec 27, 2021 at 11:33 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Mon, Dec 27, 2021 at 2:35 PM Elijah Newren <newren@gmail.com> wrote:\n> > On Sun, Dec 26, 2021 at 11:15 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > > Your example indeed leads to a broken state because it doesn't follow\n> > > the instructions given by git-worktree.txt for enabling\n> > > `extensions.worktreeConfig`, which involves additional bookkeeping\n> > > operations beyond merely setting that config variable.\n> >\n> > These are instructions which neither Stolee nor I was aware of prior\n> > to your pointing it out.  [...]\n> > I'd suspect that Stolee and I are actually _more_ likely to be aware\n> > of relevant documentation than the average Git user, so if we missed\n> > it, I suspect many of them will. [...]\n>\n> I wasn't originally aware of the bookkeeping instructions either. In\n> fact, for a good while, I wasn't even aware that Duy had implemented\n> per-worktree configuration or that there was such a thing. I either\n> must have been entirely off-list during the implementation or was not\n> in a position to follow changes to the project at the time. I only\n> became aware of per-worktree configuration at some point in the last\n> two or three years when someone asked some question on the list\n> related to the feature and I ended up diving into the documentation,\n> the source code, and the patches themselves in order to understand it\n> fully -- because I think I didn't understand it merely from reading\n> the documentation which is rather sparse (no pun intended). And I had\n> forgotten enough about it since then that I had to re-research it when\n> Sean raised the issue on the list a few days back in relation to\n> sparse-checkout.\n>\n> > So, I don't think relying on folks to read this particular piece of\n> > documentation is a reliable course of action...at least not without\n> > some changes to make it much more likely to be noticed.\n>\n> The sparsity of documentation about per-worktree configuration\n> certainly doesn't help, nor the fact that it's fairly near the end of\n> git-worktree.txt, at which point people may have given up reading. We\n> could make it a bit more prominent by mentioning it early in the\n> command description, but it still involves enough fiddly bookkeeping\n> that it likely will continue to be problematic.\n\nFurther, it's not clear people even looked at git-worktree.txt at the\ntime they learned about extensions.worktreeConfig.  I believe I\ndiscovered and started using extensions.worktreeConfig from `git\nconfig --help`, which makes no mention of or even reference to the\nneed for any extra steps.  (I didn't see the original mailing list\ndiscussions around that setting either.)  It never occurred to me in\nthe ~3 years since to even look in `git worktree --help` for\nadditional guidance around that config setting until this particular\nmailing list thread.\n\n> Making per-worktree configuration the default does seem like the best\n> long-term solution. Doing so should make all these problems go away. I\n> don't know what Duy's plans were, nor whether he had some migration\n> strategy planned.\n>\n> > > I vaguely recall some mention of this not long ago on the list but\n> > > didn't follow the discussion at all. Do you have pointers or a\n> > > summary?\n> >\n> > For the microsoft repositories, sparse-checkouts are needed because a\n> > full checkout is unmanageable (millions of files to check out\n> > otherwise).  For other repositories, full checkouts might technically\n> > be manageable, but are annoyingly slow and users may only want to work\n> > with sparse checkouts (and for some of these, due to various\n> > mono-repoization efforts, the repository is growing towards a size\n> > where manageability of full checkouts is decreasing).\n> >\n> > The fact that `git worktree add` does a full checkout is quite\n> > painful...possibility to the point of making worktrees useless for\n> > some users.  I think `git worktree add` should copy the sparsity of\n> > the worktree from which it was invoked.\n>\n> Okay, I do recall reading a message in which you proposed this, though\n> I didn't understand the reasoning for the suggestion since I wasn't\n> following the discussion. The explanation you provide here makes the\n> proposal understandable.\n>\n> >   * using --no-checkout as a proxy: This means no files checked out\n> > and no index file.  The lack of an index file makes it appear that\n> > everything was manually deleted (with the deletion staged).  Also, if\n> > the project is using some kind of <sparsity-wrapper-script> (e.g. for\n> > determining dependencies between directories so that appropriate\n> > 'modules' can be specified and transformed into a list of directories\n> > passed to sparse-checkout), then the sparsity-wrapper-script isn't\n> > available to them to invoke.  If users try to check out just the\n> > wrapper file, then an index will be created and have just one entry\n> > and we kind of cement the fact that all other files look like they\n> > were intended to be deleted.  Also, even if the user runs `git\n> > sparse-checkout init --cone`, you don't actually don't transform this\n> > no-checkout into a sparse checkout because sparse-checkout doesn't\n> > want to undo your staged deletions.  Despite the fact that I'm very\n> > familiar with all the implementation internals, it was not obvious to\n> > me all the necessary additional commands needed for users to get a\n> > sparse checkout while making use of --no-checkout.  Users stand little\n> > chance of figuring the necessary command invocations out without a\n> > huge amount of effort (and they've given up and come to me before\n> > asking for help, and my first response turned out to be incomplete in\n> > various cases...).\n>\n> You've clearly put much more thought into this than I have (since I\n> only just read this), so I'm not likely to have any meaningful input,\n> but I'll write down a few thoughts/questions which popped into my head\n> while reading what you wrote. Perhaps they've already been discussed\n> elsewhere, so feel free to ignore (and they may not be worth\n> responding to anyhow).\n>\n> When you say \"copy the sparsity of the worktree from which it was\n> invoked\", do you mean that literally, such that it special-cases it\n> and only copies sparse-checkout information? An alternative would be\n> to allow the user to specify -- via the shared configuration\n> (.git/config) -- exactly which config keys get inherited/copied over\n> by `git worktree add`. Such a solution would avoid special-casing\n> sparse-checkout and could be useful in the future for other commands\n> which might need such functionality, though this approach may be\n> overengineered.\n>\n> A more general approach might be for the new worktree to copy all the\n> per-worktree configuration from the worktree in which the command was\n> invoked, thus sparsity would be inherited \"for free\" along with other\n> settings. This has the benefits of not requiring sparse-checkout\n> special-cases in the code and it's easy to document (\"the new worktree\n> inherits/copies configuration settings from the worktree in which `git\n> worktree add` was invoked\") and easy to understand.\n\nOoh, this is a good point and I *really* like this simple solution.\nThanks for pointing it out.\n\nDo note, though, that it's more than just config.worktree -- I also\nwant the ${GITDIR}/info/sparse-checkout file copied.\n\n> I also wondered if adding some sort of `--sparse-checkout=...` option\n> to `git worktree add` would solve this particular dilemma, thus\n> allowing the user to configure custom sparse-checkout for the worktree\n> as it is being created. I also very briefly wondered if this should\n> instead be a feature of the `git sparse-checkout` command itself, such\n> as `git sparse-checkout add-worktree`, but I think that's probably a\n> dead-end in terms of user discoverability, whereas `git worktree add\n> --sparse-checkout=...` is more easily discoverable for people wanting\n> to work with worktrees.\n\nThis might be a useful extra capability (we'd probably want to keep\nthis flag in sync with git-clone's --sparse flag and whatever\ncapabilities grow there), but I don't see it as a solution to this\nproblem.  I think the default needs to be copying the existing\nsparsity.  Making users specify cone/non-cone mode and\nsparse-index/non-sparse-index and and several dozen directories by\nhand just doesn't sound reasonable to me.  (We have a case with\nseveral hundred directories/modules, with various dependencies between\nthem.  Users can use a wrapper, `./sparsify --modules $MODULE_A\n$MODULE_B` which figures out the several dozen relevant directories\nand calls sparse-checkout set with those, but of course that wrapper\nwon't yet be available in the new worktree until after the new\nworktree has been added.)\n\nAn alternative that would be workable, though annoying, is giving the\nuser a super-sparse checkout with only files in the toplevel directory\npresent (i.e. what you'd get after `git sparse-checkout init --cone`\nor `git clone --sparse ...`), and then making them use the normal\ntools to manually specify the wanted sparsity (which probably requires\nswitching back to the previous worktree to run some info commands to\ndetermine exactly what the sparsity was).\n\nAn increasingly unworkable alternative is the current behavior of\ndefaulting to a full checkout in all cases (and forcing users to\nsparsify afterwards).  A full checkout is fine if the user came from\none (and probably preferable in such a case), but it's increasingly\nproblematic for us even with our repo being nowhere near the size of\nthe microsoft repos.\n"},{"id":"445137","messageId":"pull.1101.v3.git.1640727143.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v2.git.1640114048.gitgitgadget@gmail.com","subject":"[PATCH v3 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-28T21:32:17Z","receivedAt":"2021-12-28T21:32:29Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"This series is based on a merge of en/sparse-checkout-set,\nen/worktree-chatty-to-stderr, and ds/sparse-checkout-malformed-pattern-fix.\n\nThis patch series includes a fix to the bug reported by Sean Allred [1] and\ndiagnosed by Eric Sunshine [2].\n\nThe root cause is that 'git sparse-checkout init' writes to the worktree\nconfig without checking that core.bare might need to be set. This only\nmatters when the base repository is bare, since creating the config.worktree\nfile and enabling extensions.worktreeConfig will cause Git to treat the base\nrepo's core.bare=false as important for this worktree.\n\nThis series fixes this, but also puts in place some helpers to prevent this\nfrom happening in the future. While here, some of the config paths are\nmodified to take a repository struct.\n\n * 'git sparse-checkout' will now modify the worktree config, if enabled. It\n   will no longer auto-upgrade users into using worktree config.\n\n * The new 'git worktree init-worktree-config' will upgrade users to using\n   worktree config. It will relocate core.bare and core.worktree if\n   necessary.\n\n * 'git worktree add' will copy the sparse-checkout patterns from the\n   current worktree to the new one. If worktree config is enabled, then the\n   config settings from the current worktree are copied to the new\n   worktree's config file.\n\n\nUpdates in v3\n=============\n\n * This is now rebased onto a merge of:\n   \n   * en/sparse-checkout-set (for dependence on 'git sparse-checkout set\n     --cone'),\n   * en/worktree-chatty-to-stderr (for an adjacent change in\n     Documentation/git-worktree.txt), and\n   * ds/sparse-checkout-malformed-pattern-fix (since it needed a fixup! with\n     the --worktree option in a test)\n\n * The strategy is changed significantly: we simultaneously stop\n   auto-upgrading to worktree config in 'git sparse-checkout set' while also\n   providing a clear way for users to self-upgrade in 'git worktree\n   init-worktree-config'. This upgrade moves core.bare and core.worktree, if\n   they exist, and does not do anything if worktree config is already\n   enabled.\n\n * The sparse-checkout builtin will write to the worktree config, if it is\n   enabled. The helper it uses now will fallback to the common config file\n   if worktree config is not enabled.\n\n * The 'git worktree add' command is updated to copy the sparse-checkout\n   patterns and config from the current worktree into the new one.\n\n[1]\nhttps://lore.kernel.org/git/CABceR4bZmtC4rCwgxZ1BBYZP69VOUca1f_moJoP989vTUZWu9Q@mail.gmail.com/\n[2]\nhttps://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n\n\nUpdate in v2\n============\n\n * Eric correctly pointed out that I was writing core.bare incorrectly. It\n   should move out of the core config and into the core repository's\n   worktree config.\n * Patch 3 is new, separating the \"upgrade\" logic out of config.c, but it is\n   still called by the config helper to make it painless to write worktree\n   config.\n\nThanks, -Stolee\n\nDerrick Stolee (6):\n  setup: use a repository when upgrading format\n  config: make some helpers repo-aware\n  worktree: add 'init-worktree-config' subcommand\n  config: add repo_config_set_worktree_gently()\n  sparse-checkout: use repo_config_set_worktree_gently()\n  worktree: copy sparse-checkout patterns and config on add\n\n Documentation/git-worktree.txt           |  22 +++-\n builtin/sparse-checkout.c                |  25 ++---\n builtin/worktree.c                       | 123 +++++++++++++++++++++++\n config.c                                 |  50 ++++++++-\n config.h                                 |  15 +++\n list-objects-filter-options.c            |   2 +-\n repository.h                             |   2 +-\n setup.c                                  |   6 +-\n sparse-index.c                           |  10 +-\n t/t1091-sparse-checkout-builtin.sh       | 110 ++++++++++++++++++--\n t/t2407-worktree-init-worktree-config.sh |  68 +++++++++++++\n 11 files changed, 385 insertions(+), 48 deletions(-)\n create mode 100755 t/t2407-worktree-init-worktree-config.sh\n\n\nbase-commit: 998dc12e841b4b17dd5a4700bb443fa215505e3d\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1101%2Fderrickstolee%2Fsparse-checkout%2Fbare-worktree-bug-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1101/derrickstolee/sparse-checkout/bare-worktree-bug-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/1101\n\nRange-diff vs v2:\n\n 1:  889e69dc45d = 1:  749ba67d21e setup: use a repository when upgrading format\n 2:  3e01356815a = 2:  61b96937016 config: make some helpers repo-aware\n 3:  ed8e2a7b19d ! 3:  e2a0a458115 worktree: add upgrade_to_worktree_config()\n     @@ Metadata\n      Author: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## Commit message ##\n     -    worktree: add upgrade_to_worktree_config()\n     +    worktree: add 'init-worktree-config' subcommand\n      \n     -    Some features, such as the sparse-checkout builtin, require using the\n     +    Some features, such as the sparse-checkout builtin, currently use the\n          worktree config extension. It might seem simple to upgrade the\n     -    repository format and add extensions.worktreeConfig, and that is what\n     -    happens in the sparse-checkout builtin.\n     +    repository format and add extensions.worktreeConfig, which is what\n     +    happens in the sparse-checkout builtin. However, this is overly\n     +    simplistic and can cause issues in some cases. We will transition away\n     +    from making this upgrade automatically, but first we will make an easy\n     +    way for users to upgrade their repositories correctly.\n      \n          Transitioning from one config file to multiple has some strange\n          side-effects. In particular, if the base repository is bare and the\n          worktree is not, Git knows to treat the worktree as non-bare as a\n          special case when not using worktree config. Once worktree config is\n          enabled, Git stops that special case since the core.bare setting could\n     -    apply at the worktree config level. This opens the door for bare\n     -    worktrees.\n     +    apply at the worktree config level.\n      \n     -    To help resolve this transition, create upgrade_to_worktree_config() to\n     -    navigate the intricacies of this operation. In particular, we need to\n     -    look for core.bare=true within the base config file and move that\n     -    setting into the core repository's config.worktree file.\n     +    Similarly, the core.worktree config setting is a precursor to the 'git\n     +    worktree' feature, allowing config to point to a different worktree,\n     +    presumably temporarily. This is special-cased to be ignored in a\n     +    worktree, but that case is dropped when worktree config is enabled.\n     +\n     +    To help resolve this transition, create the 'git worktree\n     +    init-worktree-config' helper. This new subcommand does the following:\n     +\n     +     1. Set core.repositoryFormatVersion to 1 in the common config file.\n     +     2. Set extensions.worktreeConfig to true in the common config file.\n     +     3. If core.bare is true in the common config file, then move that\n     +        setting to the main worktree's config file.\n     +     4. Move the core.worktree config value to the main worktree's config\n     +        file.\n     +\n     +    If the repository is already configured to use worktree config, then\n     +    none of these steps happen. This preserves any state that the user might\n     +    have created on purpose.\n     +\n     +    Update the documentation to mention this subcommand as the proper way to\n     +    upgrade to worktree config files.\n      \n          To gain access to the core repository's config and config.worktree file,\n          we reference a repository struct's 'commondir' member. If the repository\n     @@ Commit message\n          Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n     - ## worktree.c ##\n     + ## Documentation/git-worktree.txt ##\n     +@@ Documentation/git-worktree.txt: SYNOPSIS\n     + --------\n     + [verse]\n     + 'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]\n     ++'git worktree init-worktree-config'\n     + 'git worktree list' [-v | --porcelain]\n     + 'git worktree lock' [--reason <string>] <worktree>\n     + 'git worktree move' <worktree> <new-path>\n     +@@ Documentation/git-worktree.txt: checked out in the new working tree, if it's not checked out anywhere\n     + else, otherwise the command will refuse to create the working tree (unless\n     + `--force` is used).\n     + \n     ++init-worktree-config::\n     ++\n     ++Initialize config settings to enable worktree-specific config settings.\n     ++This will set `core.repositoryFormatversion=1` and enable\n     ++`extensions.worktreeConfig`, which might cause some third-party tools from\n     ++being able to operate on your repository. See CONFIGURATION FILE for more\n     ++details.\n     ++\n     + list::\n     + \n     + List details of each working tree.  The main working tree is listed first,\n     +@@ Documentation/git-worktree.txt: already present in the config file, they will be applied to the main\n     + working trees only.\n     + \n     + In order to have configuration specific to working trees, you can turn\n     +-on the `worktreeConfig` extension, e.g.:\n     ++on the `worktreeConfig` extension, using this command:\n     + \n     + ------------\n     +-$ git config extensions.worktreeConfig true\n     ++$ git worktree init-worktree-config\n     + ------------\n     + \n     + In this mode, specific configuration stays in the path pointed by `git\n     +@@ Documentation/git-worktree.txt: versions will refuse to access repositories with this extension.\n     + \n     + Note that in this file, the exception for `core.bare` and `core.worktree`\n     + is gone. If they exist in `$GIT_DIR/config`, you must move\n     +-them to the `config.worktree` of the main working tree. You may also\n     +-take this opportunity to review and move other configuration that you\n     +-do not want to share to all working trees:\n     ++them to the `config.worktree` of the main working tree. These keys are\n     ++moved automatically when you use the `git worktree init-worktree-config`\n     ++command.\n     ++\n     ++You may also take this opportunity to review and move other configuration\n     ++that you do not want to share to all working trees:\n     + \n     +  - `core.worktree` and `core.bare` should never be shared\n     + \n     +\n     + ## builtin/worktree.c ##\n      @@\n     - #include \"worktree.h\"\n     - #include \"dir.h\"\n     - #include \"wt-status.h\"\n     -+#include \"config.h\"\n       \n     - void free_worktrees(struct worktree **worktrees)\n     - {\n     -@@ worktree.c: int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,\n     - \t*wtpath = path;\n     - \treturn 0;\n     + static const char * const worktree_usage[] = {\n     + \tN_(\"git worktree add [<options>] <path> [<commit-ish>]\"),\n     ++\tN_(\"git worktree init-worktree-config\"),\n     + \tN_(\"git worktree list [<options>]\"),\n     + \tN_(\"git worktree lock [<options>] <path>\"),\n     + \tN_(\"git worktree move <worktree> <new-path>\"),\n     +@@ builtin/worktree.c: static int repair(int ac, const char **av, const char *prefix)\n     + \treturn rc;\n       }\n     + \n     ++static int move_config_setting(const char *key, const char *value,\n     ++\t\t\t       const char *from_file, const char *to_file)\n     ++{\n     ++\tif (git_config_set_in_file_gently(to_file, key, value))\n     ++\t\treturn error(_(\"unable to set %s in '%s'\"), key, to_file);\n     ++\tif (git_config_set_in_file_gently(from_file, key, NULL))\n     ++\t\treturn error(_(\"unable to unset %s in '%s'\"), key, from_file);\n     ++\treturn 0;\n     ++}\n      +\n     -+int upgrade_to_worktree_config(struct repository *r)\n     ++static int init_worktree_config(int ac, const char **av, const char *prefix)\n      +{\n     -+\tint res;\n     ++\tstruct repository *r = the_repository;\n     ++\tstruct option options[] = {\n     ++\t\tOPT_END()\n     ++\t};\n     ++\tint res = 0;\n      +\tint bare = 0;\n      +\tstruct config_set cs = { 0 };\n     -+\tchar *base_config_file = xstrfmt(\"%s/config\", r->commondir);\n     -+\tchar *base_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n     ++\tconst char *core_worktree;\n     ++\tchar *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n     ++\tchar *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n     ++\n     ++\t/* Report error on any arguments */\n     ++\tac = parse_options(ac, av, prefix, options, worktree_usage, 0);\n     ++\tif (ac)\n     ++\t\tusage_with_options(worktree_usage, options);\n      +\n      +\tgit_configset_init(&cs);\n     -+\tgit_configset_add_file(&cs, base_config_file);\n     ++\tgit_configset_add_file(&cs, common_config_file);\n      +\n      +\t/*\n     -+\t * If the base repository is bare, then we need to move core.bare=true\n     -+\t * out of the base config file and into the base repository's\n     -+\t * config.worktree file.\n     ++\t * If the format and extension are already enabled, then we can\n     ++\t * skip the upgrade process.\n      +\t */\n     -+\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n     -+\t\tif ((res = git_config_set_in_file_gently(base_worktree_file,\n     -+\t\t\t\t\t\t\t\"core.bare\", \"true\"))) {\n     -+\t\t\terror(_(\"unable to set core.bare=true in '%s'\"), base_worktree_file);\n     -+\t\t\tgoto cleanup;\n     -+\t\t}\n     ++\tif (repository_format_worktree_config)\n     ++\t\treturn 0;\n      +\n     -+\t\tif ((res = git_config_set_in_file_gently(base_config_file,\n     -+\t\t\t\t\t\t\t\"core.bare\", NULL))) {\n     -+\t\t\terror(_(\"unable to unset core.bare=true in '%s'\"), base_config_file);\n     -+\t\t\tgoto cleanup;\n     -+\t\t}\n     -+\t}\n      +\tif (upgrade_repository_format(r, 1) < 0) {\n      +\t\tres = error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n      +\t\tgoto cleanup;\n     @@ worktree.c: int should_prune_worktree(const char *id, struct strbuf *reason, cha\n      +\t\tgoto cleanup;\n      +\t}\n      +\n     ++\t/*\n     ++\t * If core.bare is true in the common config file, then we need to\n     ++\t * move it to the base worktree's config file or it will break all\n     ++\t * worktrees. If it is false, then leave it in place because it\n     ++\t * _could_ be negating a global core.bare=true.\n     ++\t */\n     ++\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n     ++\t\tif ((res = move_config_setting(\"core.bare\", \"true\",\n     ++\t\t\t\t\t       common_config_file,\n     ++\t\t\t\t\t       main_worktree_file)))\n     ++\t\t\tgoto cleanup;\n     ++\t}\n     ++\t/*\n     ++\t * If core.worktree is set, then the base worktree is located\n     ++\t * somewhere different than the parent of the common Git dir.\n     ++\t * Relocate that value to avoid breaking all worktrees with this\n     ++\t * upgrade to worktree config.\n     ++\t */\n     ++\tif (!git_configset_get_string_tmp(&cs, \"core.worktree\", &core_worktree)) {\n     ++\t\tif ((res = move_config_setting(\"core.worktree\", core_worktree,\n     ++\t\t\t\t\t       common_config_file,\n     ++\t\t\t\t\t       main_worktree_file)))\n     ++\t\t\tgoto cleanup;\n     ++\t}\n     ++\n      +cleanup:\n      +\tgit_configset_clear(&cs);\n     -+\tfree(base_config_file);\n     -+\tfree(base_worktree_file);\n     -+\ttrace2_printf(\"returning %d\", res);\n     ++\tfree(common_config_file);\n     ++\tfree(main_worktree_file);\n      +\treturn res;\n      +}\n     ++\n     + int cmd_worktree(int ac, const char **av, const char *prefix)\n     + {\n     + \tstruct option options[] = {\n     +@@ builtin/worktree.c: int cmd_worktree(int ac, const char **av, const char *prefix)\n     + \t\tprefix = \"\";\n     + \tif (!strcmp(av[1], \"add\"))\n     + \t\treturn add(ac - 1, av + 1, prefix);\n     ++\tif (!strcmp(av[1], \"init-worktree-config\"))\n     ++\t\treturn init_worktree_config(ac - 1, av + 1, prefix);\n     + \tif (!strcmp(av[1], \"prune\"))\n     + \t\treturn prune(ac - 1, av + 1, prefix);\n     + \tif (!strcmp(av[1], \"list\"))\n      \n     - ## worktree.h ##\n     -@@ worktree.h: void strbuf_worktree_ref(const struct worktree *wt,\n     - \t\t\t struct strbuf *sb,\n     - \t\t\t const char *refname);\n     - \n     -+/**\n     -+ * Upgrade the config of the current repository and its base (if different\n     -+ * from this repository) to use worktree-config. This might adjust config\n     -+ * in both repositories, including:\n     -+ *\n     -+ * 1. Upgrading the repository format version to 1.\n     -+ * 2. Adding extensions.worktreeConfig to the base config file.\n     -+ * 3. Moving core.bare=true from the base config file to the base\n     -+ *    repository's config.worktree file.\n     -+ */\n     -+int upgrade_to_worktree_config(struct repository *r);\n     -+\n     - #endif\n     + ## t/t2407-worktree-init-worktree-config.sh (new) ##\n     +@@\n     ++#!/bin/sh\n     ++\n     ++test_description='test git worktree init-worktree-config'\n     ++\n     ++. ./test-lib.sh\n     ++\n     ++test_expect_success setup '\n     ++\tgit init base &&\n     ++\ttest_commit -C base commit &&\n     ++\tgit -C base worktree add --detach worktree\n     ++'\n     ++\n     ++reset_config_when_finished () {\n     ++\ttest_when_finished git -C base config --unset core.repositoryFormatVersion &&\n     ++\ttest_when_finished git -C base config --unset extensions.worktreeConfig &&\n     ++\trm -rf base/.git/config.worktree &&\n     ++\trm -rf base/.git/worktrees/worktree/config.worktree\n     ++}\n     ++\n     ++test_expect_success 'upgrades repo format and adds extension' '\n     ++\treset_config_when_finished &&\n     ++\tgit -C base worktree init-worktree-config >out 2>err &&\n     ++\ttest_must_be_empty out &&\n     ++\ttest_must_be_empty err &&\n     ++\ttest_cmp_config -C base 1 core.repositoryFormatVersion &&\n     ++\ttest_cmp_config -C base true extensions.worktreeConfig\n     ++'\n     ++\n     ++test_expect_success 'relocates core.worktree' '\n     ++\treset_config_when_finished &&\n     ++\tmkdir dir &&\n     ++\tgit -C base config core.worktree ../../dir &&\n     ++\tgit -C base worktree init-worktree-config >out 2>err &&\n     ++\ttest_must_be_empty out &&\n     ++\ttest_must_be_empty err &&\n     ++\ttest_cmp_config -C base 1 core.repositoryFormatVersion &&\n     ++\ttest_cmp_config -C base true extensions.worktreeConfig &&\n     ++\ttest_cmp_config -C base ../../dir core.worktree &&\n     ++\ttest_must_fail git -C worktree core.worktree\n     ++'\n     ++\n     ++test_expect_success 'relocates core.bare' '\n     ++\treset_config_when_finished &&\n     ++\tgit -C base config core.bare true &&\n     ++\tgit -C base worktree init-worktree-config >out 2>err &&\n     ++\ttest_must_be_empty out &&\n     ++\ttest_must_be_empty err &&\n     ++\ttest_cmp_config -C base 1 core.repositoryFormatVersion &&\n     ++\ttest_cmp_config -C base true extensions.worktreeConfig &&\n     ++\ttest_cmp_config -C base true core.bare &&\n     ++\ttest_must_fail git -C worktree core.bare\n     ++'\n     ++\n     ++test_expect_success 'skips upgrade is already upgraded' '\n     ++\treset_config_when_finished &&\n     ++\tgit -C base worktree init-worktree-config &&\n     ++\tgit -C base config core.bare true &&\n     ++\n     ++\t# this should be a no-op, even though core.bare\n     ++\t# makes the worktree be broken.\n     ++\tgit -C base worktree init-worktree-config >out 2>err &&\n     ++\ttest_must_be_empty out &&\n     ++\ttest_must_be_empty err &&\n     ++\ttest_must_fail git -C base config --worktree core.bare &&\n     ++\tgit -C base config core.bare\n     ++'\n     ++\n     ++test_done\n 4:  22896e9bb04 ! 4:  45316cd01c9 config: add repo_config_set_worktree_gently()\n     @@ Metadata\n       ## Commit message ##\n          config: add repo_config_set_worktree_gently()\n      \n     -    The previous change added upgrade_to_worktree_config() to assist\n     -    creating a worktree-specific config for the first time. However, this\n     -    requires every config writer to care about that upgrade before writing\n     -    to the worktree-specific config. In addition, callers need to know how\n     -    to generate the name of the config.worktree file and pass it to the\n     -    config API.\n     +    Some config settings, such as those for sparse-checkout, are likely\n     +    intended to only apply to one worktree at a time. To make this write\n     +    easier, add a new config API method, repo_config_set_worktree_gently().\n      \n     -    To assist, create a new repo_config_set_worktree_gently() method in the\n     -    config API that handles the upgrade_to_worktree_config() method in\n     -    addition to assigning the value in the worktree-specific config. This\n     -    will be consumed by an upcoming change.\n     +    This method will attempt to write to the worktree-specific config, but\n     +    will instead write to the common config file if worktree config is not\n     +    enabled.  The next change will introduce a consumer of this method.\n      \n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n     @@ config.c: int git_config_set_gently(const char *key, const char *value)\n      +int repo_config_set_worktree_gently(struct repository *r,\n      +\t\t\t\t    const char *key, const char *value)\n      +{\n     -+\treturn upgrade_to_worktree_config(r) ||\n     -+\t       git_config_set_multivar_in_file_gently(\n     -+\t\t\t repo_git_path(r, \"config.worktree\"),\n     -+\t\t\t key, value, NULL, 0);\n     ++\t/* Only use worktree-specific config if it is is already enabled. */\n     ++\tif (repository_format_worktree_config) {\n     ++\t\tchar *file = repo_git_path(r, \"config.worktree\");\n     ++\t\tint ret = git_config_set_multivar_in_file_gently(\n     ++\t\t\t\t\tfile, key, value, NULL, 0);\n     ++\t\tfree(file);\n     ++\t\treturn ret;\n     ++\t}\n     ++\treturn repo_config_set_gently(r, key, value);\n      +}\n      +\n       void git_config_set(const char *key, const char *value)\n       {\n       \trepo_config_set(the_repository, key, value);\n     +@@ config.c: int repo_config_set_multivar_gently(struct repository *r, const char *key,\n     + \t\t\t\t\t\t      flags);\n     + }\n     + \n     ++int repo_config_set_gently(struct repository *r,\n     ++\t\t\t   const char *key, const char *value)\n     ++{\n     ++\treturn repo_config_set_multivar_gently(r, key, value, NULL, 0);\n     ++}\n     ++\n     + void git_config_set_multivar(const char *key, const char *value,\n     + \t\t\t     const char *value_pattern, unsigned flags)\n     + {\n      \n       ## config.h ##\n      @@ config.h: void git_config_set_in_file(const char *, const char *, const char *);\n     @@ config.h: void git_config_set_in_file(const char *, const char *, const char *);\n       int git_config_set_gently(const char *, const char *);\n       \n      +/**\n     -+ * Write a config value into the config.worktree file for the current\n     -+ * worktree. This will initialize extensions.worktreeConfig if necessary,\n     -+ * which might trigger some changes to the root repository's config file.\n     ++ * Write a config value that should apply to the current worktree. If\n     ++ * extensions.worktreeConfig is enabled, then the write will happen in the\n     ++ * current worktree's config. Otherwise, write to the common config file.\n      + */\n      +int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n      +\n       /**\n        * write config values to `.git/config`, takes a key/value pair as parameter.\n        */\n     +@@ config.h: int git_config_set_multivar_gently(const char *, const char *, const char *, uns\n     + void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n     + int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n     + void repo_config_set_multivar(struct repository *, const char *, const char *, const char *, unsigned);\n     ++int repo_config_set_gently(struct repository *, const char *, const char *);\n     + int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n     + \n     + /**\n 5:  06457fafa78 ! 5:  b200819c1bb sparse-checkout: use repo_config_set_worktree_gently()\n     @@ Commit message\n          sparse-checkout: use repo_config_set_worktree_gently()\n      \n          The previous change added repo_config_set_worktree_gently() to assist\n     -    writing config values into the worktree.config file, especially when\n     -    that may not have been initialized.\n     +    writing config values into the worktree.config file, if enabled.\n      \n     -    When the base repo is bare, running 'git sparse-checkout init' in a\n     -    worktree will create the config.worktree file for the worktree, but that\n     -    will start causing the worktree to parse the bare repo's core.bare=true\n     -    value and start treating the worktree as bare. This causes more problems\n     -    as other commands are run in that worktree.\n     +    Let the sparse-checkout builtin use this helper instead of attempting to\n     +    initialize the worktree config on its own. This changes behavior of 'git\n     +    sparse-checkout set' in a few important ways:\n      \n     -    The fix is to have this assignment into config.worktree be handled by\n     -    the repo_config_set_worktree_gently() helper.\n     +     1. Git will no longer upgrade the repository format and add the\n     +        worktree config extension. The user should run 'git worktree\n     +        init-worktree-config' to enable this feature.\n     +\n     +     2. If worktree config is disabled, then this command will set the\n     +        core.sparseCheckout (and possibly core.sparseCheckoutCone and\n     +        index.sparse) values in the common config file.\n     +\n     +     3. If the main worktree is bare, then this command will not put the\n     +        worktree in a broken state.\n     +\n     +    The main reason to use worktree-specific config for the sparse-checkout\n     +    builtin was to avoid enabling sparse-checkout patterns in one and\n     +    causing a loss of files in another. If a worktree does not have a\n     +    sparse-checkout patterns file, then the sparse-checkout logic will not\n     +    kick in on that worktree.\n     +\n     +    This new logic introduces a new user pattern that could lead to some\n     +    confusion. Suppose a user has not upgraded to worktree config and\n     +    follows these steps in order:\n     +\n     +     1. Enable sparse-checkout in a worktree.\n     +\n     +     2. Disable sparse-checkout in that worktree without deleting that\n     +        worktree's sparse-checkout file.\n     +\n     +     3. Enable sparse-checkout in another worktree.\n     +\n     +    After these steps, the first worktree will have sparse-checkout enabled\n     +    with whatever patterns exist. The worktree does not immediately have\n     +    those patterns applied, but a variety of Git commands would apply the\n     +    sparse-checkout patterns and update the worktree state to reflect those\n     +    patterns. This situation is likely very rare and the workaround is to\n     +    upgrade to worktree specific config on purpose. Users already in this\n     +    state used the sparse-checkout builtin with a version that upgraded to\n     +    worktree config, anyway.\n      \n          Reported-by: Sean Allred <allred.sean@gmail.com>\n          Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n     @@ sparse-index.c: static int convert_to_sparse_rec(struct index_state *istate,\n       \treturn res;\n      \n       ## t/t1091-sparse-checkout-builtin.sh ##\n     -@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'git sparse-checkout init' '\n     - \tcheck_files repo a\n     +@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'interaction with clone --no-checkout (unborn index)' '\n       '\n       \n     -+test_expect_success 'init in a worktree of a bare repo' '\n     -+\ttest_when_finished rm -rf bare worktree &&\n     -+\tgit clone --bare repo bare &&\n     -+\tgit -C bare worktree add ../worktree &&\n     -+\t(\n     -+\t\tcd worktree &&\n     -+\t\tgit sparse-checkout init &&\n     -+\t\ttest_must_fail git config core.bare &&\n     -+\t\tgit sparse-checkout set /*\n     + test_expect_success 'set enables config' '\n     +-\tgit init empty-config &&\n     ++\tgit init initial-config &&\n     + \t(\n     +-\t\tcd empty-config &&\n     ++\t\tcd initial-config &&\n     ++\t\ttest_commit file file &&\n     ++\t\tmkdir dir &&\n     ++\t\ttest_commit dir dir/file &&\n     ++\t\tgit worktree add --detach ../initial-worktree &&\n     ++\t\tgit sparse-checkout set --cone\n      +\t) &&\n     -+\tgit -C bare config --list --show-origin >actual &&\n     -+\tgrep \"file:config.worktree\tcore.bare=true\" actual\n     ++\ttest_cmp_config -C initial-config true core.sparseCheckout &&\n     ++\ttest_cmp_config -C initial-worktree true core.sparseCheckout &&\n     ++\ttest_cmp_config -C initial-config true core.sparseCheckoutCone &&\n     ++\ttest_cmp_config -C initial-worktree true core.sparseCheckoutCone &&\n     ++\n     ++\t# initial-config has a sparse-checkout file\n     ++\t# that only contains files at root.\n     ++\tls initial-config >only-file &&\n     ++\tcat >expect <<-EOF &&\n     ++\tfile\n     ++\tEOF\n     ++\ttest_cmp expect only-file &&\n     ++\n     ++\t# initial-worktree does not have its own sparse-checkout\n     ++\t# file, so the repply does not modify the worktree at all.\n     ++\tgit -C initial-worktree sparse-checkout reapply &&\n     ++\tls initial-worktree >all &&\n     ++\tcat >expect <<-EOF &&\n     ++\tdir\n     ++\tfile\n     ++\tEOF\n     ++\ttest_cmp expect all\n      +'\n      +\n     - test_expect_success 'git sparse-checkout list after init' '\n     - \tgit -C repo sparse-checkout list >actual &&\n     - \tcat >expect <<-\\EOF &&\n     ++test_expect_success 'set enables worktree config, if enabled' '\n     ++\tgit init worktree-config &&\n     ++\t(\n     ++\t\tcd worktree-config &&\n     + \t\ttest_commit test file &&\n     +-\t\ttest_path_is_missing .git/config.worktree &&\n     +-\t\tgit sparse-checkout set nothing &&\n     +-\t\ttest_path_is_file .git/config.worktree &&\n     +-\t\ttest_cmp_config true core.sparseCheckout\n     +-\t)\n     ++\t\tgit worktree add --detach ../worktree-config2 &&\n     ++\t\tgit worktree init-worktree-config &&\n     ++\t\tgit sparse-checkout set --cone &&\n     ++\t\tgit config --worktree core.sparseCheckout &&\n     ++\t\tgit config --worktree core.sparseCheckoutCone\n     ++\t) &&\n     ++\ttest_cmp_config -C worktree-config true core.sparseCheckout &&\n     ++\ttest_must_fail git -C worktree-config2 core.sparseCheckout &&\n     ++\ttest_cmp_config -C worktree-config true core.sparseCheckoutCone &&\n     ++\ttest_must_fail git -C worktree-config2 core.sparseCheckoutCone\n     + '\n     + \n     + test_expect_success 'set sparse-checkout using builtin' '\n     +@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'add to sparse-checkout' '\n     + '\n     + \n     + test_expect_success 'cone mode: match patterns' '\n     ++\tgit -C repo worktree init-worktree-config &&\n     + \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n     + \trm -rf repo/a repo/folder1 repo/folder2 &&\n     + \tgit -C repo read-tree -mu HEAD 2>err &&\n      @@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'sparse-index enabled and disabled' '\n       \t\ttest-tool -C repo read-cache --table >cache &&\n       \t\t! grep \" tree \" cache &&\n     @@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'sparse-index enabled an\n       \t)\n       '\n       \n     +@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'fail when lock is taken' '\n     + '\n     + \n     + test_expect_success '.gitignore should not warn about cone mode' '\n     ++\tgit -C repo worktree init-worktree-config &&\n     + \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n     + \techo \"**/bin/*\" >repo/.gitignore &&\n     + \tgit -C repo reset --hard 2>err &&\n -:  ----------- > 6:  fcece09546c worktree: copy sparse-checkout patterns and config on add\n\n-- \ngitgitgadget\n"},{"id":"445138","messageId":"749ba67d21e0dacfaadc0fabdbab5e46e3d5bfcc.1640727143.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v3.git.1640727143.gitgitgadget@gmail.com","subject":"[PATCH v3 1/6] setup: use a repository when upgrading format","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-28T21:32:18Z","receivedAt":"2021-12-28T21:32:31Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe upgrade_repository_format() helper previously was not aware of the\npossibility of multiple repositories. Add a 'struct repository *'\nparameter so it is possible to call it from a specific repository.\n\nThe implementation already referred to the_repository in one place, so\nthat is an easy replacement. The use of git_config_set() is replaced\nwith a call to repo_config_set().\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/sparse-checkout.c     | 2 +-\n list-objects-filter-options.c | 2 +-\n repository.h                  | 2 +-\n setup.c                       | 6 +++---\n 4 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 679c1070368..08f8df2648c 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -361,7 +361,7 @@ static int set_config(enum sparse_checkout_mode mode)\n {\n \tconst char *config_path;\n \n-\tif (upgrade_repository_format(1) < 0)\n+\tif (upgrade_repository_format(the_repository, 1) < 0)\n \t\tdie(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n \tif (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n \t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\ndiff --git a/list-objects-filter-options.c b/list-objects-filter-options.c\nindex fd8d59f653a..6e21d12045e 100644\n--- a/list-objects-filter-options.c\n+++ b/list-objects-filter-options.c\n@@ -372,7 +372,7 @@ void partial_clone_register(\n \t\t\t */\n \t\t\treturn;\n \t} else {\n-\t\tif (upgrade_repository_format(1) < 0)\n+\t\tif (upgrade_repository_format(the_repository, 1) < 0)\n \t\t\tdie(_(\"unable to upgrade repository format to support partial clone\"));\n \n \t\t/* Add promisor config for the remote */\ndiff --git a/repository.h b/repository.h\nindex 98f95834706..d3fc1f7689d 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -215,6 +215,6 @@ void prepare_repo_settings(struct repository *r);\n  * Return 1 if upgrade repository format to target_version succeeded,\n  * 0 if no upgrade is necessary, and -1 when upgrade is not possible.\n  */\n-int upgrade_repository_format(int target_version);\n+int upgrade_repository_format(struct repository *, int target_version);\n \n #endif /* REPOSITORY_H */\ndiff --git a/setup.c b/setup.c\nindex 347d7181ae9..90516664ce5 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -595,14 +595,14 @@ static int check_repository_format_gently(const char *gitdir, struct repository_\n \treturn 0;\n }\n \n-int upgrade_repository_format(int target_version)\n+int upgrade_repository_format(struct repository *r, int target_version)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct strbuf repo_version = STRBUF_INIT;\n \tstruct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;\n \n-\tstrbuf_git_common_path(&sb, the_repository, \"config\");\n+\tstrbuf_git_common_path(&sb, r, \"config\");\n \tread_repository_format(&repo_fmt, sb.buf);\n \tstrbuf_release(&sb);\n \n@@ -621,7 +621,7 @@ int upgrade_repository_format(int target_version)\n \t\t\t     repo_fmt.unknown_extensions.items[0].string);\n \n \tstrbuf_addf(&repo_version, \"%d\", target_version);\n-\tgit_config_set(\"core.repositoryformatversion\", repo_version.buf);\n+\trepo_config_set(r, \"core.repositoryformatversion\", repo_version.buf);\n \tstrbuf_release(&repo_version);\n \treturn 1;\n }\n-- \ngitgitgadget\n\n"},{"id":"445139","messageId":"61b96937016acd61da66deb0d14e93f795eaca51.1640727143.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v3.git.1640727143.gitgitgadget@gmail.com","subject":"[PATCH v3 2/6] config: make some helpers repo-aware","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-28T21:32:19Z","receivedAt":"2021-12-28T21:32:36Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nAs we prepare to add new config helpers to write into a config.worktree,\nlet's make some existing methods be available for writing to a config\nfile relative to a repository.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n config.c | 29 ++++++++++++++++++++++++++---\n config.h |  7 +++++++\n 2 files changed, 33 insertions(+), 3 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex c5873f3a706..9c9eef16018 100644\n--- a/config.c\n+++ b/config.c\n@@ -2882,7 +2882,12 @@ int git_config_set_gently(const char *key, const char *value)\n \n void git_config_set(const char *key, const char *value)\n {\n-\tgit_config_set_multivar(key, value, NULL, 0);\n+\trepo_config_set(the_repository, key, value);\n+}\n+\n+void repo_config_set(struct repository *r, const char *key, const char *value)\n+{\n+\trepo_config_set_multivar(r, key, value, NULL, 0);\n \n \ttrace2_cmd_set_config(key, value);\n }\n@@ -3177,14 +3182,32 @@ void git_config_set_multivar_in_file(const char *config_filename,\n int git_config_set_multivar_gently(const char *key, const char *value,\n \t\t\t\t   const char *value_pattern, unsigned flags)\n {\n-\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n+\treturn repo_config_set_multivar_gently(the_repository, key, value,\n+\t\t\t\t\t       value_pattern, flags);\n+}\n+\n+int repo_config_set_multivar_gently(struct repository *r, const char *key,\n+\t\t\t\t    const char *value,\n+\t\t\t\t    const char *value_pattern, unsigned flags)\n+{\n+\treturn git_config_set_multivar_in_file_gently(repo_git_path(r, \"config\"),\n+\t\t\t\t\t\t      key, value, value_pattern,\n \t\t\t\t\t\t      flags);\n }\n \n void git_config_set_multivar(const char *key, const char *value,\n \t\t\t     const char *value_pattern, unsigned flags)\n {\n-\tgit_config_set_multivar_in_file(NULL, key, value, value_pattern,\n+\trepo_config_set_multivar(the_repository, key, value,\n+\t\t\t\t value_pattern, flags);\n+}\n+\n+void repo_config_set_multivar(struct repository *r, const char *key,\n+\t\t\t      const char *value, const char *value_pattern,\n+\t\t\t      unsigned flags)\n+{\n+\tgit_config_set_multivar_in_file(repo_git_path(r, \"config\"),\n+\t\t\t\t\tkey, value, value_pattern,\n \t\t\t\t\tflags);\n }\n \ndiff --git a/config.h b/config.h\nindex f119de01309..5531fc018e3 100644\n--- a/config.h\n+++ b/config.h\n@@ -258,6 +258,11 @@ int git_config_set_gently(const char *, const char *);\n  */\n void git_config_set(const char *, const char *);\n \n+/**\n+ * write config values to `.git/config`, takes a key/value pair as parameter.\n+ */\n+void repo_config_set(struct repository *, const char *, const char *);\n+\n int git_config_parse_key(const char *, char **, size_t *);\n \n /*\n@@ -281,6 +286,8 @@ int git_config_parse_key(const char *, char **, size_t *);\n \n int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n+int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n+void repo_config_set_multivar(struct repository *, const char *, const char *, const char *, unsigned);\n int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n \n /**\n-- \ngitgitgadget\n\n"},{"id":"445140","messageId":"e2a0a458115a26cfb855f7040f15e5198072b3a5.1640727143.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v3.git.1640727143.gitgitgadget@gmail.com","subject":"[PATCH v3 3/6] worktree: add 'init-worktree-config' subcommand","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-28T21:32:20Z","receivedAt":"2021-12-28T21:32:37Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nSome features, such as the sparse-checkout builtin, currently use the\nworktree config extension. It might seem simple to upgrade the\nrepository format and add extensions.worktreeConfig, which is what\nhappens in the sparse-checkout builtin. However, this is overly\nsimplistic and can cause issues in some cases. We will transition away\nfrom making this upgrade automatically, but first we will make an easy\nway for users to upgrade their repositories correctly.\n\nTransitioning from one config file to multiple has some strange\nside-effects. In particular, if the base repository is bare and the\nworktree is not, Git knows to treat the worktree as non-bare as a\nspecial case when not using worktree config. Once worktree config is\nenabled, Git stops that special case since the core.bare setting could\napply at the worktree config level.\n\nSimilarly, the core.worktree config setting is a precursor to the 'git\nworktree' feature, allowing config to point to a different worktree,\npresumably temporarily. This is special-cased to be ignored in a\nworktree, but that case is dropped when worktree config is enabled.\n\nTo help resolve this transition, create the 'git worktree\ninit-worktree-config' helper. This new subcommand does the following:\n\n 1. Set core.repositoryFormatVersion to 1 in the common config file.\n 2. Set extensions.worktreeConfig to true in the common config file.\n 3. If core.bare is true in the common config file, then move that\n    setting to the main worktree's config file.\n 4. Move the core.worktree config value to the main worktree's config\n    file.\n\nIf the repository is already configured to use worktree config, then\nnone of these steps happen. This preserves any state that the user might\nhave created on purpose.\n\nUpdate the documentation to mention this subcommand as the proper way to\nupgrade to worktree config files.\n\nTo gain access to the core repository's config and config.worktree file,\nwe reference a repository struct's 'commondir' member. If the repository\nwas a submodule instead of a worktree, then this still applies\ncorrectly.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/git-worktree.txt           | 22 +++++--\n builtin/worktree.c                       | 82 ++++++++++++++++++++++++\n t/t2407-worktree-init-worktree-config.sh | 68 ++++++++++++++++++++\n 3 files changed, 167 insertions(+), 5 deletions(-)\n create mode 100755 t/t2407-worktree-init-worktree-config.sh\n\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex 9e862fbcf79..0f0642ac039 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -10,6 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]\n+'git worktree init-worktree-config'\n 'git worktree list' [-v | --porcelain]\n 'git worktree lock' [--reason <string>] <worktree>\n 'git worktree move' <worktree> <new-path>\n@@ -92,6 +93,14 @@ checked out in the new working tree, if it's not checked out anywhere\n else, otherwise the command will refuse to create the working tree (unless\n `--force` is used).\n \n+init-worktree-config::\n+\n+Initialize config settings to enable worktree-specific config settings.\n+This will set `core.repositoryFormatversion=1` and enable\n+`extensions.worktreeConfig`, which might cause some third-party tools from\n+being able to operate on your repository. See CONFIGURATION FILE for more\n+details.\n+\n list::\n \n List details of each working tree.  The main working tree is listed first,\n@@ -290,10 +299,10 @@ already present in the config file, they will be applied to the main\n working trees only.\n \n In order to have configuration specific to working trees, you can turn\n-on the `worktreeConfig` extension, e.g.:\n+on the `worktreeConfig` extension, using this command:\n \n ------------\n-$ git config extensions.worktreeConfig true\n+$ git worktree init-worktree-config\n ------------\n \n In this mode, specific configuration stays in the path pointed by `git\n@@ -303,9 +312,12 @@ versions will refuse to access repositories with this extension.\n \n Note that in this file, the exception for `core.bare` and `core.worktree`\n is gone. If they exist in `$GIT_DIR/config`, you must move\n-them to the `config.worktree` of the main working tree. You may also\n-take this opportunity to review and move other configuration that you\n-do not want to share to all working trees:\n+them to the `config.worktree` of the main working tree. These keys are\n+moved automatically when you use the `git worktree init-worktree-config`\n+command.\n+\n+You may also take this opportunity to review and move other configuration\n+that you do not want to share to all working trees:\n \n  - `core.worktree` and `core.bare` should never be shared\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex a57fcd0f3c5..937ee6fc38b 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -17,6 +17,7 @@\n \n static const char * const worktree_usage[] = {\n \tN_(\"git worktree add [<options>] <path> [<commit-ish>]\"),\n+\tN_(\"git worktree init-worktree-config\"),\n \tN_(\"git worktree list [<options>]\"),\n \tN_(\"git worktree lock [<options>] <path>\"),\n \tN_(\"git worktree move <worktree> <new-path>\"),\n@@ -1031,6 +1032,85 @@ static int repair(int ac, const char **av, const char *prefix)\n \treturn rc;\n }\n \n+static int move_config_setting(const char *key, const char *value,\n+\t\t\t       const char *from_file, const char *to_file)\n+{\n+\tif (git_config_set_in_file_gently(to_file, key, value))\n+\t\treturn error(_(\"unable to set %s in '%s'\"), key, to_file);\n+\tif (git_config_set_in_file_gently(from_file, key, NULL))\n+\t\treturn error(_(\"unable to unset %s in '%s'\"), key, from_file);\n+\treturn 0;\n+}\n+\n+static int init_worktree_config(int ac, const char **av, const char *prefix)\n+{\n+\tstruct repository *r = the_repository;\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\tint res = 0;\n+\tint bare = 0;\n+\tstruct config_set cs = { 0 };\n+\tconst char *core_worktree;\n+\tchar *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n+\tchar *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n+\n+\t/* Report error on any arguments */\n+\tac = parse_options(ac, av, prefix, options, worktree_usage, 0);\n+\tif (ac)\n+\t\tusage_with_options(worktree_usage, options);\n+\n+\tgit_configset_init(&cs);\n+\tgit_configset_add_file(&cs, common_config_file);\n+\n+\t/*\n+\t * If the format and extension are already enabled, then we can\n+\t * skip the upgrade process.\n+\t */\n+\tif (repository_format_worktree_config)\n+\t\treturn 0;\n+\n+\tif (upgrade_repository_format(r, 1) < 0) {\n+\t\tres = error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n+\t\tgoto cleanup;\n+\t}\n+\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n+\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n+\t\tgoto cleanup;\n+\t}\n+\n+\t/*\n+\t * If core.bare is true in the common config file, then we need to\n+\t * move it to the base worktree's config file or it will break all\n+\t * worktrees. If it is false, then leave it in place because it\n+\t * _could_ be negating a global core.bare=true.\n+\t */\n+\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n+\t\tif ((res = move_config_setting(\"core.bare\", \"true\",\n+\t\t\t\t\t       common_config_file,\n+\t\t\t\t\t       main_worktree_file)))\n+\t\t\tgoto cleanup;\n+\t}\n+\t/*\n+\t * If core.worktree is set, then the base worktree is located\n+\t * somewhere different than the parent of the common Git dir.\n+\t * Relocate that value to avoid breaking all worktrees with this\n+\t * upgrade to worktree config.\n+\t */\n+\tif (!git_configset_get_string_tmp(&cs, \"core.worktree\", &core_worktree)) {\n+\t\tif ((res = move_config_setting(\"core.worktree\", core_worktree,\n+\t\t\t\t\t       common_config_file,\n+\t\t\t\t\t       main_worktree_file)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+cleanup:\n+\tgit_configset_clear(&cs);\n+\tfree(common_config_file);\n+\tfree(main_worktree_file);\n+\treturn res;\n+}\n+\n int cmd_worktree(int ac, const char **av, const char *prefix)\n {\n \tstruct option options[] = {\n@@ -1045,6 +1125,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)\n \t\tprefix = \"\";\n \tif (!strcmp(av[1], \"add\"))\n \t\treturn add(ac - 1, av + 1, prefix);\n+\tif (!strcmp(av[1], \"init-worktree-config\"))\n+\t\treturn init_worktree_config(ac - 1, av + 1, prefix);\n \tif (!strcmp(av[1], \"prune\"))\n \t\treturn prune(ac - 1, av + 1, prefix);\n \tif (!strcmp(av[1], \"list\"))\ndiff --git a/t/t2407-worktree-init-worktree-config.sh b/t/t2407-worktree-init-worktree-config.sh\nnew file mode 100755\nindex 00000000000..b3bd0fa1322\n--- /dev/null\n+++ b/t/t2407-worktree-init-worktree-config.sh\n@@ -0,0 +1,68 @@\n+#!/bin/sh\n+\n+test_description='test git worktree init-worktree-config'\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\tgit init base &&\n+\ttest_commit -C base commit &&\n+\tgit -C base worktree add --detach worktree\n+'\n+\n+reset_config_when_finished () {\n+\ttest_when_finished git -C base config --unset core.repositoryFormatVersion &&\n+\ttest_when_finished git -C base config --unset extensions.worktreeConfig &&\n+\trm -rf base/.git/config.worktree &&\n+\trm -rf base/.git/worktrees/worktree/config.worktree\n+}\n+\n+test_expect_success 'upgrades repo format and adds extension' '\n+\treset_config_when_finished &&\n+\tgit -C base worktree init-worktree-config >out 2>err &&\n+\ttest_must_be_empty out &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp_config -C base 1 core.repositoryFormatVersion &&\n+\ttest_cmp_config -C base true extensions.worktreeConfig\n+'\n+\n+test_expect_success 'relocates core.worktree' '\n+\treset_config_when_finished &&\n+\tmkdir dir &&\n+\tgit -C base config core.worktree ../../dir &&\n+\tgit -C base worktree init-worktree-config >out 2>err &&\n+\ttest_must_be_empty out &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp_config -C base 1 core.repositoryFormatVersion &&\n+\ttest_cmp_config -C base true extensions.worktreeConfig &&\n+\ttest_cmp_config -C base ../../dir core.worktree &&\n+\ttest_must_fail git -C worktree core.worktree\n+'\n+\n+test_expect_success 'relocates core.bare' '\n+\treset_config_when_finished &&\n+\tgit -C base config core.bare true &&\n+\tgit -C base worktree init-worktree-config >out 2>err &&\n+\ttest_must_be_empty out &&\n+\ttest_must_be_empty err &&\n+\ttest_cmp_config -C base 1 core.repositoryFormatVersion &&\n+\ttest_cmp_config -C base true extensions.worktreeConfig &&\n+\ttest_cmp_config -C base true core.bare &&\n+\ttest_must_fail git -C worktree core.bare\n+'\n+\n+test_expect_success 'skips upgrade is already upgraded' '\n+\treset_config_when_finished &&\n+\tgit -C base worktree init-worktree-config &&\n+\tgit -C base config core.bare true &&\n+\n+\t# this should be a no-op, even though core.bare\n+\t# makes the worktree be broken.\n+\tgit -C base worktree init-worktree-config >out 2>err &&\n+\ttest_must_be_empty out &&\n+\ttest_must_be_empty err &&\n+\ttest_must_fail git -C base config --worktree core.bare &&\n+\tgit -C base config core.bare\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"445141","messageId":"45316cd01c97285d2b8b22d117c4975209f8c3a5.1640727143.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v3.git.1640727143.gitgitgadget@gmail.com","subject":"[PATCH v3 4/6] config: add repo_config_set_worktree_gently()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-28T21:32:21Z","receivedAt":"2021-12-28T21:32:38Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nSome config settings, such as those for sparse-checkout, are likely\nintended to only apply to one worktree at a time. To make this write\neasier, add a new config API method, repo_config_set_worktree_gently().\n\nThis method will attempt to write to the worktree-specific config, but\nwill instead write to the common config file if worktree config is not\nenabled.  The next change will introduce a consumer of this method.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n config.c | 21 +++++++++++++++++++++\n config.h |  8 ++++++++\n 2 files changed, 29 insertions(+)\n\ndiff --git a/config.c b/config.c\nindex 9c9eef16018..f849aef253c 100644\n--- a/config.c\n+++ b/config.c\n@@ -21,6 +21,7 @@\n #include \"dir.h\"\n #include \"color.h\"\n #include \"refs.h\"\n+#include \"worktree.h\"\n \n struct config_source {\n \tstruct config_source *prev;\n@@ -2880,6 +2881,20 @@ int git_config_set_gently(const char *key, const char *value)\n \treturn git_config_set_multivar_gently(key, value, NULL, 0);\n }\n \n+int repo_config_set_worktree_gently(struct repository *r,\n+\t\t\t\t    const char *key, const char *value)\n+{\n+\t/* Only use worktree-specific config if it is is already enabled. */\n+\tif (repository_format_worktree_config) {\n+\t\tchar *file = repo_git_path(r, \"config.worktree\");\n+\t\tint ret = git_config_set_multivar_in_file_gently(\n+\t\t\t\t\tfile, key, value, NULL, 0);\n+\t\tfree(file);\n+\t\treturn ret;\n+\t}\n+\treturn repo_config_set_gently(r, key, value);\n+}\n+\n void git_config_set(const char *key, const char *value)\n {\n \trepo_config_set(the_repository, key, value);\n@@ -3195,6 +3210,12 @@ int repo_config_set_multivar_gently(struct repository *r, const char *key,\n \t\t\t\t\t\t      flags);\n }\n \n+int repo_config_set_gently(struct repository *r,\n+\t\t\t   const char *key, const char *value)\n+{\n+\treturn repo_config_set_multivar_gently(r, key, value, NULL, 0);\n+}\n+\n void git_config_set_multivar(const char *key, const char *value,\n \t\t\t     const char *value_pattern, unsigned flags)\n {\ndiff --git a/config.h b/config.h\nindex 5531fc018e3..1386009fac8 100644\n--- a/config.h\n+++ b/config.h\n@@ -253,6 +253,13 @@ void git_config_set_in_file(const char *, const char *, const char *);\n \n int git_config_set_gently(const char *, const char *);\n \n+/**\n+ * Write a config value that should apply to the current worktree. If\n+ * extensions.worktreeConfig is enabled, then the write will happen in the\n+ * current worktree's config. Otherwise, write to the common config file.\n+ */\n+int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n+\n /**\n  * write config values to `.git/config`, takes a key/value pair as parameter.\n  */\n@@ -288,6 +295,7 @@ int git_config_set_multivar_gently(const char *, const char *, const char *, uns\n void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n void repo_config_set_multivar(struct repository *, const char *, const char *, const char *, unsigned);\n+int repo_config_set_gently(struct repository *, const char *, const char *);\n int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n \n /**\n-- \ngitgitgadget\n\n"},{"id":"445142","messageId":"b200819c1bb564695b68d8ed2caebfca0302f93f.1640727143.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v3.git.1640727143.gitgitgadget@gmail.com","subject":"[PATCH v3 5/6] sparse-checkout: use repo_config_set_worktree_gently()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-28T21:32:22Z","receivedAt":"2021-12-28T21:32:42Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe previous change added repo_config_set_worktree_gently() to assist\nwriting config values into the worktree.config file, if enabled.\n\nLet the sparse-checkout builtin use this helper instead of attempting to\ninitialize the worktree config on its own. This changes behavior of 'git\nsparse-checkout set' in a few important ways:\n\n 1. Git will no longer upgrade the repository format and add the\n    worktree config extension. The user should run 'git worktree\n    init-worktree-config' to enable this feature.\n\n 2. If worktree config is disabled, then this command will set the\n    core.sparseCheckout (and possibly core.sparseCheckoutCone and\n    index.sparse) values in the common config file.\n\n 3. If the main worktree is bare, then this command will not put the\n    worktree in a broken state.\n\nThe main reason to use worktree-specific config for the sparse-checkout\nbuiltin was to avoid enabling sparse-checkout patterns in one and\ncausing a loss of files in another. If a worktree does not have a\nsparse-checkout patterns file, then the sparse-checkout logic will not\nkick in on that worktree.\n\nThis new logic introduces a new user pattern that could lead to some\nconfusion. Suppose a user has not upgraded to worktree config and\nfollows these steps in order:\n\n 1. Enable sparse-checkout in a worktree.\n\n 2. Disable sparse-checkout in that worktree without deleting that\n    worktree's sparse-checkout file.\n\n 3. Enable sparse-checkout in another worktree.\n\nAfter these steps, the first worktree will have sparse-checkout enabled\nwith whatever patterns exist. The worktree does not immediately have\nthose patterns applied, but a variety of Git commands would apply the\nsparse-checkout patterns and update the worktree state to reflect those\npatterns. This situation is likely very rare and the workaround is to\nupgrade to worktree specific config on purpose. Users already in this\nstate used the sparse-checkout builtin with a version that upgraded to\nworktree config, anyway.\n\nReported-by: Sean Allred <allred.sean@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/sparse-checkout.c          | 25 +++++--------\n sparse-index.c                     | 10 ++----\n t/t1091-sparse-checkout-builtin.sh | 57 +++++++++++++++++++++++++-----\n 3 files changed, 60 insertions(+), 32 deletions(-)\n\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 08f8df2648c..d0d6749593e 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -359,26 +359,17 @@ enum sparse_checkout_mode {\n \n static int set_config(enum sparse_checkout_mode mode)\n {\n-\tconst char *config_path;\n-\n-\tif (upgrade_repository_format(the_repository, 1) < 0)\n-\t\tdie(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n-\tif (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n-\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n+\tif (repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckout\",\n+\t\t\t\t\t    mode ? \"true\" : \"false\") ||\n+\t    repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckoutCone\",\n+\t\t\t\t\t    mode == MODE_CONE_PATTERNS ?\n+\t\t\t\t\t\t\"true\" : \"false\"))\n \t\treturn 1;\n-\t}\n-\n-\tconfig_path = git_path(\"config.worktree\");\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckout\",\n-\t\t\t\t      mode ? \"true\" : NULL);\n-\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckoutCone\",\n-\t\t\t\t      mode == MODE_CONE_PATTERNS ? \"true\" : NULL);\n \n \tif (mode == MODE_NO_PATTERNS)\n-\t\tset_sparse_index_config(the_repository, 0);\n+\t\treturn set_sparse_index_config(the_repository, 0);\n \n \treturn 0;\n }\ndiff --git a/sparse-index.c b/sparse-index.c\nindex a1d505d50e9..e93609999e0 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -99,13 +99,9 @@ static int convert_to_sparse_rec(struct index_state *istate,\n \n int set_sparse_index_config(struct repository *repo, int enable)\n {\n-\tint res;\n-\tchar *config_path = repo_git_path(repo, \"config.worktree\");\n-\tres = git_config_set_in_file_gently(config_path,\n-\t\t\t\t\t    \"index.sparse\",\n-\t\t\t\t\t    enable ? \"true\" : NULL);\n-\tfree(config_path);\n-\n+\tint res = repo_config_set_worktree_gently(repo,\n+\t\t\t\t\t\t  \"index.sparse\",\n+\t\t\t\t\t\t  enable ? \"true\" : \"false\");\n \tprepare_repo_settings(repo);\n \trepo->settings.sparse_index = enable;\n \treturn res;\ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex 447a8669e02..15403158c49 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -146,15 +146,54 @@ test_expect_success 'interaction with clone --no-checkout (unborn index)' '\n '\n \n test_expect_success 'set enables config' '\n-\tgit init empty-config &&\n+\tgit init initial-config &&\n \t(\n-\t\tcd empty-config &&\n+\t\tcd initial-config &&\n+\t\ttest_commit file file &&\n+\t\tmkdir dir &&\n+\t\ttest_commit dir dir/file &&\n+\t\tgit worktree add --detach ../initial-worktree &&\n+\t\tgit sparse-checkout set --cone\n+\t) &&\n+\ttest_cmp_config -C initial-config true core.sparseCheckout &&\n+\ttest_cmp_config -C initial-worktree true core.sparseCheckout &&\n+\ttest_cmp_config -C initial-config true core.sparseCheckoutCone &&\n+\ttest_cmp_config -C initial-worktree true core.sparseCheckoutCone &&\n+\n+\t# initial-config has a sparse-checkout file\n+\t# that only contains files at root.\n+\tls initial-config >only-file &&\n+\tcat >expect <<-EOF &&\n+\tfile\n+\tEOF\n+\ttest_cmp expect only-file &&\n+\n+\t# initial-worktree does not have its own sparse-checkout\n+\t# file, so the repply does not modify the worktree at all.\n+\tgit -C initial-worktree sparse-checkout reapply &&\n+\tls initial-worktree >all &&\n+\tcat >expect <<-EOF &&\n+\tdir\n+\tfile\n+\tEOF\n+\ttest_cmp expect all\n+'\n+\n+test_expect_success 'set enables worktree config, if enabled' '\n+\tgit init worktree-config &&\n+\t(\n+\t\tcd worktree-config &&\n \t\ttest_commit test file &&\n-\t\ttest_path_is_missing .git/config.worktree &&\n-\t\tgit sparse-checkout set nothing &&\n-\t\ttest_path_is_file .git/config.worktree &&\n-\t\ttest_cmp_config true core.sparseCheckout\n-\t)\n+\t\tgit worktree add --detach ../worktree-config2 &&\n+\t\tgit worktree init-worktree-config &&\n+\t\tgit sparse-checkout set --cone &&\n+\t\tgit config --worktree core.sparseCheckout &&\n+\t\tgit config --worktree core.sparseCheckoutCone\n+\t) &&\n+\ttest_cmp_config -C worktree-config true core.sparseCheckout &&\n+\ttest_must_fail git -C worktree-config2 core.sparseCheckout &&\n+\ttest_cmp_config -C worktree-config true core.sparseCheckoutCone &&\n+\ttest_must_fail git -C worktree-config2 core.sparseCheckoutCone\n '\n \n test_expect_success 'set sparse-checkout using builtin' '\n@@ -202,6 +241,7 @@ test_expect_success 'add to sparse-checkout' '\n '\n \n test_expect_success 'cone mode: match patterns' '\n+\tgit -C repo worktree init-worktree-config &&\n \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n \trm -rf repo/a repo/folder1 repo/folder2 &&\n \tgit -C repo read-tree -mu HEAD 2>err &&\n@@ -241,7 +281,7 @@ test_expect_success 'sparse-index enabled and disabled' '\n \t\ttest-tool -C repo read-cache --table >cache &&\n \t\t! grep \" tree \" cache &&\n \t\tgit -C repo config --list >config &&\n-\t\t! grep index.sparse config\n+\t\ttest_cmp_config -C repo false index.sparse\n \t)\n '\n \n@@ -380,6 +420,7 @@ test_expect_success 'fail when lock is taken' '\n '\n \n test_expect_success '.gitignore should not warn about cone mode' '\n+\tgit -C repo worktree init-worktree-config &&\n \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n \techo \"**/bin/*\" >repo/.gitignore &&\n \tgit -C repo reset --hard 2>err &&\n-- \ngitgitgadget\n\n"},{"id":"445143","messageId":"fcece09546cbdb5f1bcd0d0c5aaa3a54e9d3b852.1640727143.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v3.git.1640727143.gitgitgadget@gmail.com","subject":"[PATCH v3 6/6] worktree: copy sparse-checkout patterns and config on add","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-28T21:32:23Z","receivedAt":"2021-12-28T21:32:44Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWhen adding a new worktree, it is reasonable to expect that we want to\nuse the current set of sparse-checkout settings for that new worktree.\nThis is particularly important for repositories where the worktree would\nbecome too large to be useful. This is even more important when using\npartial clone as well, since we want to avoid downloading the missing\nblobs for files that should not be written to the new worktree.\n\nThe only way to create such a worktree without this intermediate step of\nexpanding the full worktree is to copy the sparse-checkout patterns and\nconfig settings during 'git worktree add'. Each worktree has its own\nsparse-checkout patterns, and the default behavior when the\nsparse-checkout file is missing is to include all paths at HEAD. Thus,\nwe need to have patterns from somewhere, they might as well be the\ncurrent worktree's patterns. These are then modified independently in\nthe future.\n\nIn addition to the sparse-checkout file, copy the worktree config file\nif worktree config is enabled and the file exists. This will copy over\nany important settings to ensure the new worktree behaves the same as\nthe current one.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/worktree.c                 | 41 +++++++++++++++++++++++\n t/t1091-sparse-checkout-builtin.sh | 53 ++++++++++++++++++++++++++++--\n 2 files changed, 91 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 937ee6fc38b..bca49a55f13 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -336,6 +336,47 @@ static int add_worktree(const char *path, const char *refname,\n \tstrbuf_addf(&sb, \"%s/commondir\", sb_repo.buf);\n \twrite_file(sb.buf, \"../..\");\n \n+\t/*\n+\t * If the current worktree has sparse-checkout enabled, then copy\n+\t * the sparse-checkout patterns from the current worktree.\n+\t */\n+\tif (core_apply_sparse_checkout) {\n+\t\tchar *from_file = git_pathdup(\"info/sparse-checkout\");\n+\t\tchar *to_file = xstrfmt(\"%s/worktrees/%s/info/sparse-checkout\",\n+\t\t\t\t\trealpath.buf, name);\n+\n+\t\tif (file_exists(from_file)) {\n+\t\t\tif (safe_create_leading_directories(to_file) ||\n+\t\t\t    copy_file(to_file, from_file, 0666))\n+\t\t\t\terror(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n+\t\t\t\t      from_file, to_file);\n+\t\t}\n+\n+\t\tfree(from_file);\n+\t\tfree(to_file);\n+\t}\n+\n+\t/*\n+\t * If we are using worktree config, then copy all currenct config\n+\t * values from the current worktree into the new one, that way the\n+\t * new worktree behaves the same as this one.\n+\t */\n+\tif (repository_format_worktree_config) {\n+\t\tchar *from_file = git_pathdup(\"config.worktree\");\n+\t\tchar *to_file = xstrfmt(\"%s/worktrees/%s/config.worktree\",\n+\t\t\t\t\trealpath.buf, name);\n+\n+\t\tif (file_exists(from_file)) {\n+\t\t\tif (safe_create_leading_directories(to_file) ||\n+\t\t\t    copy_file(to_file, from_file, 0666))\n+\t\t\t\terror(_(\"failed to copy worktree config from '%s' to '%s'\"),\n+\t\t\t\t      from_file, to_file);\n+\t\t}\n+\n+\t\tfree(from_file);\n+\t\tfree(to_file);\n+\t}\n+\n \tstrvec_pushf(&child_env, \"%s=%s\", GIT_DIR_ENVIRONMENT, sb_git.buf);\n \tstrvec_pushf(&child_env, \"%s=%s\", GIT_WORK_TREE_ENVIRONMENT, path);\n \tcp.git_cmd = 1;\ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex 15403158c49..ce84819e1f5 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -180,6 +180,53 @@ test_expect_success 'set enables config' '\n '\n \n test_expect_success 'set enables worktree config, if enabled' '\n+\tgit init worktree-patterns &&\n+\t(\n+\t\tcd worktree-patterns &&\n+\t\ttest_commit test file &&\n+\t\tmkdir dir dir2 &&\n+\t\ttest_commit dir dir/file &&\n+\t\ttest_commit dir2 dir2/file &&\n+\n+\t\t# By initializing the worktree config here...\n+\t\tgit worktree init-worktree-config &&\n+\n+\t\t# This set command places config values in worktree-\n+\t\t# specific config...\n+\t\tgit sparse-checkout set --cone dir &&\n+\n+\t\t# Which must be copied, along with the sparse-checkout\n+\t\t# patterns, here.\n+\t\tgit worktree add --detach ../worktree-patterns2\n+\t) &&\n+\ttest_cmp_config -C worktree-patterns true core.sparseCheckout &&\n+\ttest_cmp_config -C worktree-patterns2 true core.sparseCheckout &&\n+\ttest_cmp_config -C worktree-patterns true core.sparseCheckoutCone &&\n+\ttest_cmp_config -C worktree-patterns2 true core.sparseCheckoutCone &&\n+\ttest_cmp worktree-patterns/.git/info/sparse-checkout \\\n+\t\t worktree-patterns/.git/worktrees/worktree-patterns2/info/sparse-checkout &&\n+\n+\tls worktree-patterns >expect &&\n+\tls worktree-patterns2 >actual &&\n+\ttest_cmp expect actual &&\n+\n+\t# Double check that the copy works from a non-main worktree.\n+\t(\n+\t\tcd worktree-patterns2 &&\n+\t\tgit sparse-checkout set dir2 &&\n+\t\tgit worktree add --detach ../worktree-patterns3\n+\t) &&\n+\ttest_cmp_config -C worktree-patterns3 true core.sparseCheckout &&\n+\ttest_cmp_config -C worktree-patterns3 true core.sparseCheckoutCone &&\n+\ttest_cmp worktree-patterns/.git/worktrees/worktree-patterns2/info/sparse-checkout \\\n+\t\t worktree-patterns/.git/worktrees/worktree-patterns3/info/sparse-checkout &&\n+\n+\tls worktree-patterns2 >expect &&\n+\tls worktree-patterns3 >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'worktree add copies sparse-checkout patterns' '\n \tgit init worktree-config &&\n \t(\n \t\tcd worktree-config &&\n@@ -547,11 +594,11 @@ test_expect_success 'interaction with submodules' '\n \n test_expect_success 'different sparse-checkouts with worktrees' '\n \tgit -C repo worktree add --detach ../worktree &&\n-\tcheck_files worktree \"a deep folder1 folder2\" &&\n+\tcheck_files worktree \"a folder1\" &&\n \tgit -C worktree sparse-checkout init --cone &&\n-\tgit -C repo sparse-checkout set folder1 &&\n+\tgit -C repo sparse-checkout set folder1 folder2 &&\n \tgit -C worktree sparse-checkout set deep/deeper1 &&\n-\tcheck_files repo a folder1 &&\n+\tcheck_files repo a folder1 folder2 &&\n \tcheck_files worktree a deep\n '\n \n-- \ngitgitgadget\n"},{"id":"445168","messageId":"CAPig+cSUOknNC9GMyPvAqdBU0r1MVgvSpvgpSpXUmBm67HO7PQ@mail.gmail.com","threadId":"57125","inReplyTo":"fcece09546cbdb5f1bcd0d0c5aaa3a54e9d3b852.1640727143.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 6/6] worktree: copy sparse-checkout patterns and config on add","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-29T06:37:30Z","receivedAt":"2021-12-29T06:37:44Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 28, 2021 at 4:32 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> When adding a new worktree, it is reasonable to expect that we want to\n> use the current set of sparse-checkout settings for that new worktree.\n> This is particularly important for repositories where the worktree would\n> become too large to be useful. This is even more important when using\n> partial clone as well, since we want to avoid downloading the missing\n> blobs for files that should not be written to the new worktree.\n>\n> The only way to create such a worktree without this intermediate step of\n> expanding the full worktree is to copy the sparse-checkout patterns and\n> config settings during 'git worktree add'. Each worktree has its own\n> sparse-checkout patterns, and the default behavior when the\n> sparse-checkout file is missing is to include all paths at HEAD. Thus,\n> we need to have patterns from somewhere, they might as well be the\n> current worktree's patterns. These are then modified independently in\n> the future.\n>\n> In addition to the sparse-checkout file, copy the worktree config file\n> if worktree config is enabled and the file exists. This will copy over\n> any important settings to ensure the new worktree behaves the same as\n> the current one.\n\nThis is not a proper review. I just happened to very quickly scan my\neyes over this patch without even having looked at any of the others,\nnor have I read the v3 cover letter closely yet. Nevertheless, while\nskimming this patch, an issue jumped out at me...\n\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/builtin/worktree.c b/builtin/worktree.c\n> @@ -336,6 +336,47 @@ static int add_worktree(const char *path, const char *refname,\n> +       /*\n> +        * If we are using worktree config, then copy all currenct config\n> +        * values from the current worktree into the new one, that way the\n> +        * new worktree behaves the same as this one.\n> +        */\n\ns/currenct/current/\n\n> +       if (repository_format_worktree_config) {\n> +               char *from_file = git_pathdup(\"config.worktree\");\n> +               char *to_file = xstrfmt(\"%s/worktrees/%s/config.worktree\",\n> +                                       realpath.buf, name);\n> +\n> +               if (file_exists(from_file)) {\n> +                       if (safe_create_leading_directories(to_file) ||\n> +                           copy_file(to_file, from_file, 0666))\n> +                               error(_(\"failed to copy worktree config from '%s' to '%s'\"),\n> +                                     from_file, to_file);\n> +               }\n\nI presume that you lifted this idea from [1] in which I offhandedly\nmentioned that one possible way to implement Elijah's desire to copy\nsparse-checkout configuration when a new worktree is created would be\nto simply copy the existing worktree-specific configuration to the new\nworktree. Unfortunately, a direct implementation of that idea suffers\nthe same problem which started this entire thread. Namely:\n\n    % git clone --bare <url>/bare.git\n    % cd bare.git/\n    % git worktree init-worktree-config\n    % git worktree add -d ../foo\n    Preparing worktree (detached HEAD a0df8ce)\n    HEAD is now at a0df8ce gobbledygook\n    % cd ../foo/\n    % git sparse-checkout init\n    fatal: this operation must be run in a work tree\n    % git config --get --show-origin --show-scope core.bare\n    worktree file:.../bare.git/worktrees/foo/config.worktree true\n\nThe problem is that for a bare repository, after `git worktree\ninit-worktree-config`, \"bare.git/config.worktree\" contains the\nrepo-specific `core.bare=true` setting, so copying\n\"bare.git/config.worktree\" to\n\"bare.git/worktrees/<id>/config.worktree\" verbatim has undesired\nconsequences.\n\nThe obvious way to work around this problem is to (again) special-case\n`core.bare` and `core.worktree` to remove them when copying the\nworktree-specific configuration. Whether or not that is the best\nsolution or even desirable is a different question. (I haven't thought\nit through enough to have an opinion.)\n\n[1]: https://lore.kernel.org/git/CAPig+cRYKKGA1af4hV0fz_nZWNG=zMgAziuAimDxWTz6L8u3Tg@mail.gmail.com/\n"},{"id":"445169","messageId":"CAPig+cRO4CZj4VQSwsNqDEeUFxnCyER31VRdU9B8CQw_hayEgg@mail.gmail.com","threadId":"57125","inReplyTo":"e2a0a458115a26cfb855f7040f15e5198072b3a5.1640727143.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 3/6] worktree: add 'init-worktree-config' subcommand","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-29T06:48:45Z","receivedAt":"2021-12-29T06:48:57Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 28, 2021 at 4:32 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> [...]\n> To help resolve this transition, create the 'git worktree\n> init-worktree-config' helper. This new subcommand does the following:\n> [...]\n\nLike my not-a-proper-review of [6/6], this also is not a proper review...\n\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/builtin/worktree.c b/builtin/worktree.c\n> @@ -1031,6 +1032,85 @@ static int repair(int ac, const char **av, const char *prefix)\n> +static int init_worktree_config(int ac, const char **av, const char *prefix)\n> +{\n> +       struct config_set cs = { 0 };\n\nOn macOS with \"Apple LLVM version 10.0.0 (clang-1000.10.44.4)\" and\nDEVELOPER=1, the above code breaks the build:\n\n    builtin/worktree.c:1093:27: error: suggest braces around\n    initialization of subobject [-Werror,-Wmissing-braces]\n    struct config_set cs = { 0 };\n\nIt wants extra braces in the initializer. This fixes it:\n\n    struct config_set cs = { { 0 } };\n"},{"id":"445170","messageId":"CABPp-BHuO3B366uJuODMQo-y449p8cAMVn0g2MTcO5di3Xa7Zg@mail.gmail.com","threadId":"57125","inReplyTo":"pull.1101.v3.git.1640727143.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-29T09:39:48Z","receivedAt":"2021-12-29T09:40:05Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Dec 28, 2021 at 1:32 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> This series is based on a merge of en/sparse-checkout-set,\n> en/worktree-chatty-to-stderr, and ds/sparse-checkout-malformed-pattern-fix.\n\nI think you mean es/worktree-chatty-to-stderr (not 'en/')\n\n> This patch series includes a fix to the bug reported by Sean Allred [1] and\n> diagnosed by Eric Sunshine [2].\n>\n> The root cause is that 'git sparse-checkout init' writes to the worktree\n> config without checking that core.bare might need to be set. This only\n> matters when the base repository is bare, since creating the config.worktree\n> file and enabling extensions.worktreeConfig will cause Git to treat the base\n> repo's core.bare=false as important for this worktree.\n>\n> This series fixes this, but also puts in place some helpers to prevent this\n> from happening in the future. While here, some of the config paths are\n> modified to take a repository struct.\n>\n>  * 'git sparse-checkout' will now modify the worktree config, if enabled. It\n>    will no longer auto-upgrade users into using worktree config.\n\nThis sounds dangerous to me.\n\n>  * The new 'git worktree init-worktree-config' will upgrade users to using\n>    worktree config. It will relocate core.bare and core.worktree if\n>    necessary.\n\nThis sounds like giving users an extra step to unbreak themselves,\ninstead of just having the commands do it.  (And might risk also\nbreaking things in a different direction?  I'll have to read over the\npatches to see...)\n\n>  * 'git worktree add' will copy the sparse-checkout patterns from the\n>    current worktree to the new one. If worktree config is enabled, then the\n>    config settings from the current worktree are copied to the new\n>    worktree's config file.\n\nThis sounds awesome.  Wahoo!  (core.worktree should be cleared,\nthough, and core.bare should either be cleared or set to false if\nfound in the worktree config.)\n\n...\n> Range-diff vs v2:\n>\n>  1:  889e69dc45d = 1:  749ba67d21e setup: use a repository when upgrading format\n>  2:  3e01356815a = 2:  61b96937016 config: make some helpers repo-aware\n>  3:  ed8e2a7b19d ! 3:  e2a0a458115 worktree: add upgrade_to_worktree_config()\n...\n>      @@ Commit message\n>           Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n>           Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n>\n>      - ## worktree.c ##\n>      + ## Documentation/git-worktree.txt ##\n>      +@@ Documentation/git-worktree.txt: SYNOPSIS\n>      + --------\n>      + [verse]\n>      + 'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]\n>      ++'git worktree init-worktree-config'\n>      + 'git worktree list' [-v | --porcelain]\n>      + 'git worktree lock' [--reason <string>] <worktree>\n>      + 'git worktree move' <worktree> <new-path>\n>      +@@ Documentation/git-worktree.txt: checked out in the new working tree, if it's not checked out anywhere\n>      + else, otherwise the command will refuse to create the working tree (unless\n>      + `--force` is used).\n>      +\n>      ++init-worktree-config::\n>      ++\n>      ++Initialize config settings to enable worktree-specific config settings.\n>      ++This will set `core.repositoryFormatversion=1` and enable\n>      ++`extensions.worktreeConfig`, which might cause some third-party tools from\n\ns/cause/prevent/ ?\n\n>      ++being able to operate on your repository. See CONFIGURATION FILE for more\n>      ++details.\n\nSo, if users attempt to use `git worktree add` or `git sparse-checkout\n{init,set}` without first running this, they can break other\nworktrees.  And if they do run this new command, they potentially\nbreak third-party tools or older git versions.\n\nYet, with the very common case (in fact, I'd go so far as to say the\nnearly universal case) of having both core.bare=false and no\ncore.worktree set, we never had any such problems with the former\nlogic.  This seems like a serious regression to me.  I'll keep\nreading.\n\n...\n>      +@@ Documentation/git-worktree.txt: versions will refuse to access repositories with this extension.\n>      +\n>      + Note that in this file, the exception for `core.bare` and `core.worktree`\n>      + is gone. If they exist in `$GIT_DIR/config`, you must move\n>      +-them to the `config.worktree` of the main working tree. You may also\n>      +-take this opportunity to review and move other configuration that you\n>      +-do not want to share to all working trees:\n>      ++them to the `config.worktree` of the main working tree. These keys are\n>      ++moved automatically when you use the `git worktree init-worktree-config`\n>      ++command.\n>      ++\n>      ++You may also take this opportunity to review and move other configuration\n>      ++that you do not want to share to all working trees:\n>      +\n>      +  - `core.worktree` and `core.bare` should never be shared\n\nI'm fine with the wording, but technically core.bare=false is fine to\nshare, it's just core.bare=true that is not.  (And yes, I agree that\ncore.worktree is never safe to share)\n\n...\n>  5:  06457fafa78 ! 5:  b200819c1bb sparse-checkout: use repo_config_set_worktree_gently()\n>      @@ Commit message\n>           sparse-checkout: use repo_config_set_worktree_gently()\n>\n...\n>      +    Let the sparse-checkout builtin use this helper instead of attempting to\n>      +    initialize the worktree config on its own. This changes behavior of 'git\n>      +    sparse-checkout set' in a few important ways:\n>\n>      -    The fix is to have this assignment into config.worktree be handled by\n>      -    the repo_config_set_worktree_gently() helper.\n>      +     1. Git will no longer upgrade the repository format and add the\n>      +        worktree config extension. The user should run 'git worktree\n>      +        init-worktree-config' to enable this feature.\n>      +\n>      +     2. If worktree config is disabled, then this command will set the\n>      +        core.sparseCheckout (and possibly core.sparseCheckoutCone and\n>      +        index.sparse) values in the common config file.\n\nYikes.\n\n>      +     3. If the main worktree is bare, then this command will not put the\n>      +        worktree in a broken state.\n>      +\n>      +    The main reason to use worktree-specific config for the sparse-checkout\n>      +    builtin was to avoid enabling sparse-checkout patterns in one and\n>      +    causing a loss of files in another. If a worktree does not have a\n>      +    sparse-checkout patterns file, then the sparse-checkout logic will not\n>      +    kick in on that worktree.\n>      +\n>      +    This new logic introduces a new user pattern that could lead to some\n>      +    confusion. Suppose a user has not upgraded to worktree config and\n>      +    follows these steps in order:\n>      +\n>      +     1. Enable sparse-checkout in a worktree.\n>      +\n>      +     2. Disable sparse-checkout in that worktree without deleting that\n>      +        worktree's sparse-checkout file.\n>      +\n>      +     3. Enable sparse-checkout in another worktree.\n>      +\n>      +    After these steps, the first worktree will have sparse-checkout enabled\n>      +    with whatever patterns exist. The worktree does not immediately have\n>      +    those patterns applied, but a variety of Git commands would apply the\n>      +    sparse-checkout patterns and update the worktree state to reflect those\n>      +    patterns. This situation is likely very rare and the workaround is to\n\nNo, it's not even rare, let alone very rare.  I'd actually call it\ncommon.  Since 'sparse-checkout disable' does not delete the\nsparse-checkout file, and we've encouraged folks to use the\nsparse-checkout command (or a wrapper thereof) instead of direct\nediting of the sparse-checkout file (and indeed, the sparse-checkout\ncommand will overwrite the sparse-checkout file which further\ndiscourages users from feeling they own it), having the file left\naround after disabling is the common case.  So, the only question is,\nhow often do users disable and re-enable sparse-checkout, and\npotentially only do so in some of their worktrees?  At my $DAYJOB,\nthat's actually quite common.  I got multiple reports quite soon after\nintroducing our `sparsify` tool about users doing something like this;\nthis is what led me to learn of the extensions.worktreeConfig, and why\nI pointed it out to you on your first submission of\ngit-sparse-checkout[1]\n(https://lore.kernel.org/git/CABPp-BFcH5hQqujjmc88L3qGx3QAYZ_chH6PXQXyp13ipfV6hQ@mail.gmail.com/)\n\nSome more details about sparse-checkouts at $DAYJOB:\n  * We have numerous users who used to have their own small little\nrepos, and want to still have that experience.  sparse-checkouts were\na way to provide a small-repo feel for them, and in particular to\nspeed up IDEs that otherwise insist on indexing everything no matter\nhow much we try to tell them to only pay attention to part of the\nfiles (and similar speed issues with the build system since gradle\nconfiguration is so slow).  They want every worktree to be sparse and\nnever face the full repo.  I talk about these users the most, because\nwe've mostly satisfied the other users.\n  * We have numerous users who work mostly on specific modules, but\noccasionally do cross-cutting work.  They will have most worktrees be\nsparse, but keep at least one that is full.  They may also undo\nsparsity briefly and redo it in various worktrees.\n  * We also have a few users who work primarily on cross-cutting\nfeatures and just keep the full checkout and ignore the\nsparse-checkout stuff.\n  * There is also a special tool, write-locks.sh or some name like\nthat, which must unsparsify, run something like `./gradle\n--write-locks` and other stuff, and then re-sparsify.  The tool does\nnot work without the full worktree.  Now, not all users need to run\nthis, and in fact most users who do probably are the same ones that\n\"occasionally do cross-cutting work\", but it's a little bit wider\ngroup than that.  So we have additional unsparsify/resparsify steps,\nand it's further complicated by the fact that users may not even know\nthat we do the unsparsification and resparsification behind the scenes\nfor them (our `sparsify` wrapper has a stash-like feature, though I\nthink only allowing for one on the stash, which is called behind the\nscenes.)  The write-locks logic is super slow, which kind of hides the\notherwise slow unsparsify/resparsify steps.\n\n(For each of the above type of users, we have many folks who don't use\nworktrees, but they're not relevant to this discussion.)\n\nWe also have third party git tools, whether jgit (usually via gradle\nplugins), whatever IDEs tend to use (eclipse used egit, not sure what\nintellij or visual studio use), probably various special one-off\nscripts that read a bit more git configuration than they should and do\nvarious tasks, and a wide range of git versions in use (though any\ngiven user will likely only use one git version, and we are perfectly\nhappy to specify a minimum version for sparse-checkout usage).\n\n\nSo that's the range of users for this discussion, but I think we also\nneed to flesh out the caveats of your change since you missed a few:\n  * Even if users haven't sparsified/unsparsified/re-sparsified in\nanother worktree (i.e. they have a worktree that has always been\nfull), the sparse-checkout init/set in another worktree _still_ causes\nproblems because when users switch to the 'full' worktree,\ncontrib/completion/git-prompt.sh will report it as sparse.  Users are\ngoing to get confused and report bugs just based on their prompt even\nif it's technically a \"dense\" worktree.\n  * The cone-mode and sparse-index are also shared between worktrees,\nwhich may not present many problems for me right now at $DAYJOB, but\ncould be problematic for other users out there.\n\nSo, here's the experience I expect from these patches at $DAYJOB:\n  (1) Several users per week hit the case of one worktree being\nsparsified when it wasn't supposed to be.\n  (2) These users have no idea how to figure out what they need to do\nto fix it.  The init-worktree-config is no more discoverable than the\ndocumentation on the official steps for enabling\nextensions.worktreeConfig (See\nhttps://lore.kernel.org/git/CABPp-BGKyDJV9DP+igmCC_Ad0jgvb4aOAYpXWCbx9hW8ShhDQg@mail.gmail.com/\nup through the paragraph, \"Further, it's not even clear people would\nlook at git-worktree.txt.)\n  (3) Even if they do discover it, and run it, it's an extra step they\nnever needed to take before.  Why are we adding a \"unbreak these other\ncommands we want to run\" step?\n  (4) Also, even if they do discover it, and run it, suddenly we are\nsetting core.repositoryFormatVersion=1.  That scares me.  I have years\nof experience at $DAYJOB saying that the tooling we have works fine\nwith extensions.worktreeConfig=true.  I have none with setting\ncore.repositoryFormatVersion=1, but now we're effectively requiring it\nby your documentation.  I have no idea how\njgit/egit/other-random-stuff interacts with that.  I'd be willing to\ndo some tests with targetted users to try to learn more, but suddenly\nturning it on for everyone in cases that we know worked fine without\nit previously feels unsafe to me.  Maybe I'm over-worrying here, but\nsee also commit 11664196ac (\"Revert \"check_repository_format_gently():\nrefuse\nextensions for old repositories\"\", 2020-07-15) -- it just feels a bit\nlate to recommend for users, especially when they'll see it as \"oh, if\nyou don't want this other bug we recently introduced you need to run\nthis....\".\n\n\n\nSo, I'd like to reiterate my earlier suggestion which would avoid\nthese regressions while also fixing the reported bug:\n  * If core.bare=true or core.worktree is set, then at `git worktree\nadd` time, automatically run the logic you have here for\ninit-worktree-config.  Having either of those config settings with\nmultiple worktrees is currently broken in all git versions and likely\nin most all external tools.  As such, being aggressive in the new\nconfig settings to allow new versions of git to work seems totally\nsafe to me -- we can't be any more broken than we already were.\n  * If core.bare=false and core.worktree is not set, then:\n    * `git sparse-checkout {init,set}` should set\nextensions.worktreeConfig if not already set, and always set the\ncore.sparse* and index.sparse settings in worktree-specific files.\n    * `git worktree add`, if extensions.worktreeConfig is already set,\nwill copy both the info/sparse-checkout file and the config.worktree\nsettings (module core.bare and core.worktree, if present) to the new\nworktree\n"},{"id":"445184","messageId":"e2ef25b5-9802-1dd9-b96c-ea85d2082b48@gmail.com","threadId":"57125","inReplyTo":"CAPig+cSUOknNC9GMyPvAqdBU0r1MVgvSpvgpSpXUmBm67HO7PQ@mail.gmail.com","subject":"Re: [PATCH v3 6/6] worktree: copy sparse-checkout patterns and config on add","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-29T17:31:45Z","receivedAt":"2021-12-29T17:31:53Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/29/2021 1:37 AM, Eric Sunshine wrote:\n> On Tue, Dec 28, 2021 at 4:32 PM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>> When adding a new worktree, it is reasonable to expect that we want to\n>> use the current set of sparse-checkout settings for that new worktree.\n>> This is particularly important for repositories where the worktree would\n>> become too large to be useful. This is even more important when using\n>> partial clone as well, since we want to avoid downloading the missing\n>> blobs for files that should not be written to the new worktree.\n>>\n>> The only way to create such a worktree without this intermediate step of\n>> expanding the full worktree is to copy the sparse-checkout patterns and\n>> config settings during 'git worktree add'. Each worktree has its own\n>> sparse-checkout patterns, and the default behavior when the\n>> sparse-checkout file is missing is to include all paths at HEAD. Thus,\n>> we need to have patterns from somewhere, they might as well be the\n>> current worktree's patterns. These are then modified independently in\n>> the future.\n>>\n>> In addition to the sparse-checkout file, copy the worktree config file\n>> if worktree config is enabled and the file exists. This will copy over\n>> any important settings to ensure the new worktree behaves the same as\n>> the current one.\n> \n> This is not a proper review. I just happened to very quickly scan my\n> eyes over this patch without even having looked at any of the others,\n> nor have I read the v3 cover letter closely yet. Nevertheless, while\n> skimming this patch, an issue jumped out at me...\n> \n>> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n>> ---\n>> diff --git a/builtin/worktree.c b/builtin/worktree.c\n>> @@ -336,6 +336,47 @@ static int add_worktree(const char *path, const char *refname,\n>> +       /*\n>> +        * If we are using worktree config, then copy all currenct config\n>> +        * values from the current worktree into the new one, that way the\n>> +        * new worktree behaves the same as this one.\n>> +        */\n> \n> s/currenct/current/\n> \n>> +       if (repository_format_worktree_config) {\n>> +               char *from_file = git_pathdup(\"config.worktree\");\n>> +               char *to_file = xstrfmt(\"%s/worktrees/%s/config.worktree\",\n>> +                                       realpath.buf, name);\n>> +\n>> +               if (file_exists(from_file)) {\n>> +                       if (safe_create_leading_directories(to_file) ||\n>> +                           copy_file(to_file, from_file, 0666))\n>> +                               error(_(\"failed to copy worktree config from '%s' to '%s'\"),\n>> +                                     from_file, to_file);\n>> +               }\n> \n> I presume that you lifted this idea from [1] in which I offhandedly\n> mentioned that one possible way to implement Elijah's desire to copy\n> sparse-checkout configuration when a new worktree is created would be\n> to simply copy the existing worktree-specific configuration to the new\n> worktree. Unfortunately, a direct implementation of that idea suffers\n> the same problem which started this entire thread. Namely:\n> \n>     % git clone --bare <url>/bare.git\n>     % cd bare.git/\n>     % git worktree init-worktree-config\n>     % git worktree add -d ../foo\n>     Preparing worktree (detached HEAD a0df8ce)\n>     HEAD is now at a0df8ce gobbledygook\n>     % cd ../foo/\n>     % git sparse-checkout init\n>     fatal: this operation must be run in a work tree\n>     % git config --get --show-origin --show-scope core.bare\n>     worktree file:.../bare.git/worktrees/foo/config.worktree true\n> \n> The problem is that for a bare repository, after `git worktree\n> init-worktree-config`, \"bare.git/config.worktree\" contains the\n> repo-specific `core.bare=true` setting, so copying\n> \"bare.git/config.worktree\" to\n> \"bare.git/worktrees/<id>/config.worktree\" verbatim has undesired\n> consequences.\n\nIt is certainly unfortunate that this can happen when core.bare or\ncore.worktree are set in the config.worktree of the bare repo.\n\nThe thing we are doing here is trying to create a worktree that exactly\nmatches the current worktree (even if it is bare or redirected to a\ndifferent working directory), but since we don't actually support a\n\"bare\" worktree this does not work.\n\n> The obvious way to work around this problem is to (again) special-case\n> `core.bare` and `core.worktree` to remove them when copying the\n> worktree-specific configuration. Whether or not that is the best\n> solution or even desirable is a different question. (I haven't thought\n> it through enough to have an opinion.)\n\nIt makes sense to special case these two settings since we want to\nallow creating a working worktree from a bare repo, even if it has\nworktree config stating that it is bare.\n\nAs far as the implementation goes, we could do the copy and then\nunset those two values in the new file. That's an easy enough change.\nI'll wait for more feedback on the overall ideas (and names of things\nlike the init-worktree-config subcommand).\n\nThanks,\n-Stolee\n"},{"id":"445185","messageId":"1db0f601-4769-15c0-cd58-ecddfa1fc9d5@gmail.com","threadId":"57125","inReplyTo":"CABPp-BHuO3B366uJuODMQo-y449p8cAMVn0g2MTcO5di3Xa7Zg@mail.gmail.com","subject":"Re: [PATCH v3 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-29T17:38:58Z","receivedAt":"2021-12-29T17:39:04Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/29/2021 4:39 AM, Elijah Newren wrote:\n> On Tue, Dec 28, 2021 at 1:32 PM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>>\n>> This series is based on a merge of en/sparse-checkout-set,\n>> en/worktree-chatty-to-stderr, and ds/sparse-checkout-malformed-pattern-fix.\n> \n> I think you mean es/worktree-chatty-to-stderr (not 'en/')\n\nYes. Thanks.\n \n>> This patch series includes a fix to the bug reported by Sean Allred [1] and\n>> diagnosed by Eric Sunshine [2].\n>>\n>> The root cause is that 'git sparse-checkout init' writes to the worktree\n>> config without checking that core.bare might need to be set. This only\n>> matters when the base repository is bare, since creating the config.worktree\n>> file and enabling extensions.worktreeConfig will cause Git to treat the base\n>> repo's core.bare=false as important for this worktree.\n>>\n>> This series fixes this, but also puts in place some helpers to prevent this\n>> from happening in the future. While here, some of the config paths are\n>> modified to take a repository struct.\n>>\n>>  * 'git sparse-checkout' will now modify the worktree config, if enabled. It\n>>    will no longer auto-upgrade users into using worktree config.\n> \n> This sounds dangerous to me.\n\n> ...\n>>      +    Let the sparse-checkout builtin use this helper instead of attempting to\n>>      +    initialize the worktree config on its own. This changes behavior of 'git\n>>      +    sparse-checkout set' in a few important ways:\n>>\n>>      -    The fix is to have this assignment into config.worktree be handled by\n>>      -    the repo_config_set_worktree_gently() helper.\n>>      +     1. Git will no longer upgrade the repository format and add the\n>>      +        worktree config extension. The user should run 'git worktree\n>>      +        init-worktree-config' to enable this feature.\n>>      +\n>>      +     2. If worktree config is disabled, then this command will set the\n>>      +        core.sparseCheckout (and possibly core.sparseCheckoutCone and\n>>      +        index.sparse) values in the common config file.\n> \n> Yikes.\n> \n>>      +     3. If the main worktree is bare, then this command will not put the\n>>      +        worktree in a broken state.\n>>      +\n>>      +    The main reason to use worktree-specific config for the sparse-checkout\n>>      +    builtin was to avoid enabling sparse-checkout patterns in one and\n>>      +    causing a loss of files in another. If a worktree does not have a\n>>      +    sparse-checkout patterns file, then the sparse-checkout logic will not\n>>      +    kick in on that worktree.\n>>      +\n>>      +    This new logic introduces a new user pattern that could lead to some\n>>      +    confusion. Suppose a user has not upgraded to worktree config and\n>>      +    follows these steps in order:\n>>      +\n>>      +     1. Enable sparse-checkout in a worktree.\n>>      +\n>>      +     2. Disable sparse-checkout in that worktree without deleting that\n>>      +        worktree's sparse-checkout file.\n>>      +\n>>      +     3. Enable sparse-checkout in another worktree.\n>>      +\n>>      +    After these steps, the first worktree will have sparse-checkout enabled\n>>      +    with whatever patterns exist. The worktree does not immediately have\n>>      +    those patterns applied, but a variety of Git commands would apply the\n>>      +    sparse-checkout patterns and update the worktree state to reflect those\n>>      +    patterns. This situation is likely very rare and the workaround is to\n> \n> No, it's not even rare, let alone very rare.  I'd actually call it\n> common.  Since 'sparse-checkout disable' does not delete the\n> sparse-checkout file, and we've encouraged folks to use the\n> sparse-checkout command (or a wrapper thereof) instead of direct\n> editing of the sparse-checkout file (and indeed, the sparse-checkout\n> command will overwrite the sparse-checkout file which further\n> discourages users from feeling they own it), having the file left\n> around after disabling is the common case.  So, the only question is,\n> how often do users disable and re-enable sparse-checkout, and\n> potentially only do so in some of their worktrees?  At my $DAYJOB,\n> that's actually quite common.  I got multiple reports quite soon after\n> introducing our `sparsify` tool about users doing something like this;\n> this is what led me to learn of the extensions.worktreeConfig, and why\n> I pointed it out to you on your first submission of\n> git-sparse-checkout[1]\n> (https://lore.kernel.org/git/CABPp-BFcH5hQqujjmc88L3qGx3QAYZ_chH6PXQXyp13ipfV6hQ@mail.gmail.com/)\n\nThank you for these comments and the detailed descriptions of things\nfrom your $DAYJOB. That's helpful context and I'm happy to switch back\nto enabling the extension in the sparse-checkout builtin. I might need\nto rearrange the code so there is an API in worktree.c instead of just\nthe subcommand in builtin/worktree.c, but that's pretty minor. I'll\nkeep Eric's earlier suggestion to have the upgrade be a separate call\nfrom the repo_config_set_worktree_gently().\n\nThanks,\n-Stolee\n"},{"id":"445199","messageId":"CABPp-BE9umOTkjfnB0X+9nTZaBmeXd4z5DmiZ1Upzds=m1spPw@mail.gmail.com","threadId":"57125","inReplyTo":"e2ef25b5-9802-1dd9-b96c-ea85d2082b48@gmail.com","subject":"Re: [PATCH v3 6/6] worktree: copy sparse-checkout patterns and config on add","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-29T19:51:51Z","receivedAt":"2021-12-29T19:52:05Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Dec 29, 2021 at 9:31 AM Derrick Stolee <stolee@gmail.com> wrote:\n>\n> On 12/29/2021 1:37 AM, Eric Sunshine wrote:\n> > On Tue, Dec 28, 2021 at 4:32 PM Derrick Stolee via GitGitGadget\n> > <gitgitgadget@gmail.com> wrote:\n\n> > The obvious way to work around this problem is to (again) special-case\n> > `core.bare` and `core.worktree` to remove them when copying the\n> > worktree-specific configuration. Whether or not that is the best\n> > solution or even desirable is a different question. (I haven't thought\n> > it through enough to have an opinion.)\n>\n> It makes sense to special case these two settings since we want to\n> allow creating a working worktree from a bare repo, even if it has\n> worktree config stating that it is bare.\n\nAgreed.\n\n> As far as the implementation goes, we could do the copy and then\n> unset those two values in the new file. That's an easy enough change.\n\n> I'll wait for more feedback on the overall ideas (and names of things\n> like the init-worktree-config subcommand).\n\nWhat value does the init-worktree-config subcommand provide; why\nshouldn't we just get rid of it?\n\nI know Eric was strongly suggesting it, but he was thinking in terms\nof always doing that full switchover step, or never doing it.  Both\nextremes had the potential to cause user-visible bugs, and thus he\nsuggested providing a command to allow users to pick their poison.  I\nprovided a suggestion avoiding both extremes that doesn't have that\npick-your-poison approach, so I don't see why forcing users into this\nextra step makes any sense.\n\nBut perhaps I missed something.  Is there a usecase for users to\nexplicitly use this?\n"},{"id":"445200","messageId":"0aa32f4d-9101-16ff-58b8-29a8651a69f8@gmail.com","threadId":"57125","inReplyTo":"CABPp-BE9umOTkjfnB0X+9nTZaBmeXd4z5DmiZ1Upzds=m1spPw@mail.gmail.com","subject":"Re: [PATCH v3 6/6] worktree: copy sparse-checkout patterns and config on add","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-29T21:39:08Z","receivedAt":"2021-12-29T21:39:14Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/29/2021 2:51 PM, Elijah Newren wrote:\n> On Wed, Dec 29, 2021 at 9:31 AM Derrick Stolee <stolee@gmail.com> wrote:\n>>\n>> On 12/29/2021 1:37 AM, Eric Sunshine wrote:\n>>> On Tue, Dec 28, 2021 at 4:32 PM Derrick Stolee via GitGitGadget\n>>> <gitgitgadget@gmail.com> wrote:\n> \n>>> The obvious way to work around this problem is to (again) special-case\n>>> `core.bare` and `core.worktree` to remove them when copying the\n>>> worktree-specific configuration. Whether or not that is the best\n>>> solution or even desirable is a different question. (I haven't thought\n>>> it through enough to have an opinion.)\n>>\n>> It makes sense to special case these two settings since we want to\n>> allow creating a working worktree from a bare repo, even if it has\n>> worktree config stating that it is bare.\n> \n> Agreed.\n> \n>> As far as the implementation goes, we could do the copy and then\n>> unset those two values in the new file. That's an easy enough change.\n> \n>> I'll wait for more feedback on the overall ideas (and names of things\n>> like the init-worktree-config subcommand).\n> \n> What value does the init-worktree-config subcommand provide; why\n> shouldn't we just get rid of it?\n> \n> I know Eric was strongly suggesting it, but he was thinking in terms\n> of always doing that full switchover step, or never doing it.  Both\n> extremes had the potential to cause user-visible bugs, and thus he\n> suggested providing a command to allow users to pick their poison.  I\n> provided a suggestion avoiding both extremes that doesn't have that\n> pick-your-poison approach, so I don't see why forcing users into this\n> extra step makes any sense.\n> \n> But perhaps I missed something.  Is there a usecase for users to\n> explicitly use this?\n\nI think the motivation is that worktree config is something that is\nharder to set up than to just run a 'git config' command, and we\nshould guide users into a best practice for using it. The\ndocumentation becomes \"run this command to enable it\".\n\nIt also provides a place to update the steps if we were to change\nsomething in the future around worktree config, but I'm guessing\nthe ship has sailed and backwards compatibility will keep us from\nintroducing a new setting that would need to be added here.\n\nThanks,\n-Stolee\n"},{"id":"445201","messageId":"CABPp-BFdVPRgFy8DLFEB13Trh5J4S+xma-U5FUnUvQLNMfNNjA@mail.gmail.com","threadId":"57125","inReplyTo":"0aa32f4d-9101-16ff-58b8-29a8651a69f8@gmail.com","subject":"Re: [PATCH v3 6/6] worktree: copy sparse-checkout patterns and config on add","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-29T22:45:04Z","receivedAt":"2021-12-29T22:45:19Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Dec 29, 2021 at 1:39 PM Derrick Stolee <stolee@gmail.com> wrote:\n>\n> On 12/29/2021 2:51 PM, Elijah Newren wrote:\n> > On Wed, Dec 29, 2021 at 9:31 AM Derrick Stolee <stolee@gmail.com> wrote:\n> >>\n> >> On 12/29/2021 1:37 AM, Eric Sunshine wrote:\n> >>> On Tue, Dec 28, 2021 at 4:32 PM Derrick Stolee via GitGitGadget\n> >>> <gitgitgadget@gmail.com> wrote:\n> >\n> >>> The obvious way to work around this problem is to (again) special-case\n> >>> `core.bare` and `core.worktree` to remove them when copying the\n> >>> worktree-specific configuration. Whether or not that is the best\n> >>> solution or even desirable is a different question. (I haven't thought\n> >>> it through enough to have an opinion.)\n> >>\n> >> It makes sense to special case these two settings since we want to\n> >> allow creating a working worktree from a bare repo, even if it has\n> >> worktree config stating that it is bare.\n> >\n> > Agreed.\n> >\n> >> As far as the implementation goes, we could do the copy and then\n> >> unset those two values in the new file. That's an easy enough change.\n> >\n> >> I'll wait for more feedback on the overall ideas (and names of things\n> >> like the init-worktree-config subcommand).\n> >\n> > What value does the init-worktree-config subcommand provide; why\n> > shouldn't we just get rid of it?\n> >\n> > I know Eric was strongly suggesting it, but he was thinking in terms\n> > of always doing that full switchover step, or never doing it.  Both\n> > extremes had the potential to cause user-visible bugs, and thus he\n> > suggested providing a command to allow users to pick their poison.  I\n> > provided a suggestion avoiding both extremes that doesn't have that\n> > pick-your-poison approach, so I don't see why forcing users into this\n> > extra step makes any sense.\n> >\n> > But perhaps I missed something.  Is there a usecase for users to\n> > explicitly use this?\n>\n> I think the motivation is that worktree config is something that is\n> harder to set up than to just run a 'git config' command, and we\n> should guide users into a best practice for using it. The\n> documentation becomes \"run this command to enable it\".\n\nOkay, but that's an answer to a different question -- namely, \"if\nusers want/need to explicitly set it up, why should we have a\ncommand?\"  Your answer here is a very good answer to that question,\nbut you've assumed the \"if\".  My question was on the \"if\": (Why) Do\nusers need or want to explicitly set it up?\n\nSecondarily, if users want to set it up explicitly, is the work here\nreally sufficient to help guide them?  In particular, I discovered and\nstarted using extensions.worktreeConfig without ever looking at the\nrelevant portions of git-worktree.txt (the references in\ngit-config.txt never mentioned them).  I also pushed this usage to\nothers, including even to you with `git-sparse-checkout`, and no\nreviewer on this list ever caught it or informed me of the `proper`\nadditional guidelines found in git-worktree.txt until this thread\nstarted.  So, relying on folks to read git-worktree.txt for this\nconfig item feels a bit weak to me.  Granted, your new command will be\nmuch more likely to be read since it appears near the top of\ngit-worktree.txt, but I just don't think that's enough.  The\nreferences to extensions.worktreeConfig in git-config.txt should\nreference any special command or extended steps if we expect users to\nmanually configure it (whether via explicit new subcommand or via also\nplaying with other config settings).\n\n\nAnyway, if we think users want to set it up explicitly, and we address\nthe discoverability problem above, then I'd vote for\n\"independent-config\" or \"private-config\" (or _maybe_\n\"migrate-config\").  Because:\n  * no sense repeating the word `worktree` in `git worktree\ninit-worktree-config`.  It's redundant.\n  * The words \"independent\" or \"private\" suggest what it does and why\nusers might want to use the new subcommand.\n  * It's not an \"init\":\n    * The documentation makes no attempt to impose a temporal order of\nusing this command before `git worktree add`.  (Would we even want\nto?)\n    * As per my recommendation elsewhere, this step just isn't needed\nfor the vast majority of users (i.e. those with non-bare clones who\nleave core.worktree alone).\n    * ...and it's also not needed for other (core.bare=true or\ncore.worktree set) users since `git worktree add` will automatically\nrun this config migration for them\n\nAnd, actually, with the name \"independent-config\" or \"private-config\",\nI might be answering my own question.  It's a name that speaks to why\nusers might want it, so my objection to the new command is rapidly\ndiminishing.\n\n> It also provides a place to update the steps if we were to change\n> something in the future around worktree config, but I'm guessing\n> the ship has sailed and backwards compatibility will keep us from\n> introducing a new setting that would need to be added here.\n\nYeah, the best time to force a config change is probably when we\nintroduce a new command with it.  If we had forced\ncore.repositoryFormatVersion=1 in order to read extensions.* at the\ntime that extensions.* was introduced, that would have been fine (When\nwe tried it later, we had to revert it for backward compatibility\nreasons.).  If we had forced extensions.worktreeConfig=true when\nintroducing git-worktree, that would have been fine.  We did force\nextensions.worktreeConfig=true when we introduced git-sparse-checkout,\nwhich was good, though we localized it to sparse-checkout and avoided\nadding it to worktree usage in general at that point.\n\nThe other good time to force a config change is when we have cases\nwhere behavior is already broken anyway.  For example, the\n(core.bare=true or core.worktree is set) case that started this\nthread.  But in such cases, we have to localize the forced-config\nchange to the cases that are \"broken anyway\" unless we can verify it\nwon't break other cases.\n\nI'm not sure that this new subcommand will ever fall into either of\nthese categories, so I also agree that we'll be unlikely to ever\nchange it.\n"},{"id":"445207","messageId":"CAPig+cRs4Vvp4rQ_EhMxB_qe4YAh3+NnqETTpYyMnAjsRhAUbg@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BHMr32MuisNzt-R3O4=HPY63HQT0=ykrbtiASyS1K_JjQ@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-30T06:21:54Z","receivedAt":"2021-12-30T06:22:07Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Dec 27, 2021 at 3:16 PM Elijah Newren <newren@gmail.com> wrote:\n> On Sun, Dec 26, 2021 at 11:34 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > Your proposal is _almost_ the same as my suggestion of eventually\n> > making per-worktree config the default. The difference is that you're\n> > only making it the default if `core.bare=true` or `core.worktree` is\n> > set.\n>\n> Indeed.  :-)\n\nI mentioned previously[1] that I needed to find a block of time to\nreally think through the topic before I'd be able to respond to this\nemail. So, today I spent some time trying to reason through the\nvarious cases under discussion, and I came back and re-read this email\nwith the intention of trying to summarize my understanding of the\nsituation and my understanding of the points you were making. However,\nyou did such a good job of summarizing the various cases at the very\nend of [2] that it probably makes more sense for me to respond to that\nemail instead.\n\n[1]: https://lore.kernel.org/git/CAPig+cTFSDw-9Aq+=+r4sHSzTmG7s2T93Z0uqWTxHbKwGFaiYQ@mail.gmail.com/\n[2]: https://lore.kernel.org/git/CABPp-BHuO3B366uJuODMQo-y449p8cAMVn0g2MTcO5di3Xa7Zg@mail.gmail.com/\n\n> > But do we need that distinction? If people are comfortable with\n> > that, then are they comfortable with simply flipping the switch and\n> > making per-worktree config the default today regardless of `core.bare`\n> > and `core.worktree`?\n>\n> This is tempting, at least if we leave core.repositoryFormatVersion as\n> 0 (see 11664196ac (\"Revert \"check_repository_format_gently(): refuse\n> extensions for old repositories\"\", 2020-07-15)) when core.bare is\n> false and core.worktree was unset.  However, for that case:\n\nI had seen 11664196ac when researching one of my earlier responses,\nthough it took more than one read to (hopefully) fully understand what\nit is saying (i.e. due to an oversight, it's too late to enforce the\n`core.repositoryFormatVersion=1` requirement when extensions are used,\nas originally intended).\n\n> * This is a case where operating on the primary worktree was not\n> previously problematic for older git versions or third party tools.\n> * Interestingly, git <= 2.6.2 can continue to operate on the primary\n> worktree (because it didn't know to error out on unknown extensions)\n> * git >= 2.19.0 could continue to operate on the primary worktree\n> (because it understands the extension)\n> * git versions between that range would suddenly break, erroring out\n> on the unknown extension (though those versions would start working\n> again if we migrated core.bare and core.worktree but just didn't set\n> extensions.worktreeConfig).\n\nThe significance of versions 2.6.2 and 2.19.0 is unclear to me. What\ncontext or criteria are you using to identify those versions as\nmeaningful here?\n"},{"id":"445208","messageId":"CAPig+cRJj4Aa9Nm_yWD0=WGdn9tRmT2PpTwo5yeGZHg2Qz2rgA@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BGKyDJV9DP+igmCC_Ad0jgvb4aOAYpXWCbx9hW8ShhDQg@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-30T06:40:50Z","receivedAt":"2021-12-30T06:41:07Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 28, 2021 at 1:16 PM Elijah Newren <newren@gmail.com> wrote:\n> On Mon, Dec 27, 2021 at 11:33 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > The sparsity of documentation about per-worktree configuration\n> > certainly doesn't help, nor the fact that it's fairly near the end of\n> > git-worktree.txt, at which point people may have given up reading. We\n> > could make it a bit more prominent by mentioning it early in the\n> > command description, but it still involves enough fiddly bookkeeping\n> > that it likely will continue to be problematic.\n>\n> Further, it's not clear people even looked at git-worktree.txt at the\n> time they learned about extensions.worktreeConfig.  I believe I\n> discovered and started using extensions.worktreeConfig from `git\n> config --help`, which makes no mention of or even reference to the\n> need for any extra steps.  (I didn't see the original mailing list\n> discussions around that setting either.)  It never occurred to me in\n> the ~3 years since to even look in `git worktree --help` for\n> additional guidance around that config setting until this particular\n> mailing list thread.\n\nThat's an interesting datapoint. At the very least, we probably should\nupdate Documentation/git-config.txt to mention the extra bookkeeping\nrequired when setting `extensions.worktreeConfig=true`.\n\n> > A more general approach might be for the new worktree to copy all the\n> > per-worktree configuration from the worktree in which the command was\n> > invoked, thus sparsity would be inherited \"for free\" along with other\n> > settings. This has the benefits of not requiring sparse-checkout\n> > special-cases in the code and it's easy to document (\"the new worktree\n> > inherits/copies configuration settings from the worktree in which `git\n> > worktree add` was invoked\") and easy to understand.\n>\n> Ooh, this is a good point and I *really* like this simple solution.\n> Thanks for pointing it out.\n\nI do wonder, though, if there are traps waiting for us with this\nall-inclusive approach. I don't know what sort of worktree-specific\nconfiguration people use, so I do worry a bit that this could be\ncasting a too-wide net, and that it might in fact be better to only\ncopy the sparse-checkout settings (as ugly as it is to special-case\nthat -- but we need to special-case `core.bare` and `core.worktree`\nanyhow[1]).\n\n[1]: https://lore.kernel.org/git/CAPig+cSUOknNC9GMyPvAqdBU0r1MVgvSpvgpSpXUmBm67HO7PQ@mail.gmail.com/\n\n> Do note, though, that it's more than just config.worktree -- I also\n> want the ${GITDIR}/info/sparse-checkout file copied.\n\nThanks for pointing that out. I'm reasonably (or completely) ignorant\nof sparse-checkout since I've never used it nor read the\ndocumentation, and I didn't follow the earlier discussions.\n\n> > I also wondered if adding some sort of `--sparse-checkout=...` option\n> > to `git worktree add` would solve this particular dilemma, thus\n> > allowing the user to configure custom sparse-checkout for the worktree\n> > as it is being created. I also very briefly wondered if this should\n> > instead be a feature of the `git sparse-checkout` command itself, such\n> > as `git sparse-checkout add-worktree`, but I think that's probably a\n> > dead-end in terms of user discoverability, whereas `git worktree add\n> > --sparse-checkout=...` is more easily discoverable for people wanting\n> > to work with worktrees.\n>\n> This might be a useful extra capability (we'd probably want to keep\n> this flag in sync with git-clone's --sparse flag and whatever\n> capabilities grow there), but I don't see it as a solution to this\n> problem.  I think the default needs to be copying the existing\n> sparsity.  Making users specify cone/non-cone mode and\n> sparse-index/non-sparse-index and and several dozen directories by\n> hand just doesn't sound reasonable to me.  (We have a case with\n> several hundred directories/modules, with various dependencies between\n> them.  Users can use a wrapper, `./sparsify --modules $MODULE_A\n> $MODULE_B` which figures out the several dozen relevant directories\n> and calls sparse-checkout set with those, but of course that wrapper\n> won't yet be available in the new worktree until after the new\n> worktree has been added.)\n\nOkay.\n\n> An alternative that would be workable, though annoying, is giving the\n> user a super-sparse checkout with only files in the toplevel directory\n> present (i.e. what you'd get after `git sparse-checkout init --cone`\n> or `git clone --sparse ...`), and then making them use the normal\n> tools to manually specify the wanted sparsity (which probably requires\n> switching back to the previous worktree to run some info commands to\n> determine exactly what the sparsity was).\n\nSounds somewhat painful.\n\n> An increasingly unworkable alternative is the current behavior of\n> defaulting to a full checkout in all cases (and forcing users to\n> sparsify afterwards).  A full checkout is fine if the user came from\n> one (and probably preferable in such a case), but it's increasingly\n> problematic for us even with our repo being nowhere near the size of\n> the microsoft repos.\n\nIt feels unfortunate and a bit dirty to spread around this\nspecial-case knowledge about sparse-checkout to various parts of the\nsystem, but based upon the pain-points you describe, having a new\nworktree inherit the sparsity from the originating worktree does sound\n(given my limited knowledge of the topic) like it would ease the pain\nfor users.\n"},{"id":"445209","messageId":"CAPig+cQ8Y2FC8=d7DaZ46YwPCrzM6RzutiZ3ghhrHsg6y8KWoQ@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BHuO3B366uJuODMQo-y449p8cAMVn0g2MTcO5di3Xa7Zg@mail.gmail.com","subject":"Re: [PATCH v3 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-30T07:40:07Z","receivedAt":"2021-12-30T07:40:42Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Dec 29, 2021 at 4:40 AM Elijah Newren <newren@gmail.com> wrote:>\n> On Tue, Dec 28, 2021 at 1:32 PM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> >      ++init-worktree-config::\n> >      ++\n> >      ++Initialize config settings to enable worktree-specific config settings.\n> >      ++This will set `core.repositoryFormatversion=1` and enable\n> >      ++`extensions.worktreeConfig`, which might cause some third-party tools from\n> >      ++being able to operate on your repository. See CONFIGURATION FILE for more\n> >      ++details.\n>\n> So, if users attempt to use `git worktree add` or `git sparse-checkout\n> {init,set}` without first running this, they can break other\n> worktrees.  And if they do run this new command, they potentially\n> break third-party tools or older git versions.\n\nWhen you say \"can break other worktrees\", you don't necessarily mean\nthat in general but rather in regard to sparse-checkout -- in\nparticular, the sparse-checkout config settings and the\n`info/sparse-checkout file` -- correct? (Genuine question; I want to\nmake sure that I'm actually understanding the issues under\ndiscussion.)\n\n> >      +    After these steps, the first worktree will have sparse-checkout enabled\n> >      +    with whatever patterns exist. The worktree does not immediately have\n> >      +    those patterns applied, but a variety of Git commands would apply the\n> >      +    sparse-checkout patterns and update the worktree state to reflect those\n> >      +    patterns. This situation is likely very rare and the workaround is to\n>\n> No, it's not even rare, let alone very rare.  I'd actually call it\n> common.  Since 'sparse-checkout disable' does not delete the\n> sparse-checkout file, and we've encouraged folks to use the\n> sparse-checkout command (or a wrapper thereof) instead of direct\n> editing of the sparse-checkout file (and indeed, the sparse-checkout\n> command will overwrite the sparse-checkout file which further\n> discourages users from feeling they own it), having the file left\n> around after disabling is the common case.  So, the only question is,\n> how often do users disable and re-enable sparse-checkout, and\n> potentially only do so in some of their worktrees?  At my $DAYJOB,\n> that's actually quite common.  I got multiple reports quite soon after\n> introducing our `sparsify` tool about users doing something like this;\n> this is what led me to learn of the extensions.worktreeConfig, and why\n> I pointed it out to you on your first submission of\n> git-sparse-checkout[1]\n> (https://lore.kernel.org/git/CABPp-BFcH5hQqujjmc88L3qGx3QAYZ_chH6PXQXyp13ipfV6hQ@mail.gmail.com/)\n\nI think the link you provide here answers the genuine question I had\nasked in [1] where I didn't understand why git-sparse-checkout is\nforcing the repository to use per-worktree configuration. I also just\n(re)discovered [2] which makes it clear that per-worktree\nsparse-checkout was considered important very early on in the\ndevelopment of \"multiple checkouts\".\n\n[1]: https://lore.kernel.org/git/CAPig+cRombN-8g0t7Hs9qQypJoY41gK3+kvypH4D0G6EB4JgbQ@mail.gmail.com/\n[2]: https://lore.kernel.org/git/1404891197-18067-32-git-send-email-pclouds@gmail.com/\n\n> So, here's the experience I expect from these patches at $DAYJOB:\n>   (1) Several users per week hit the case of one worktree being\n> sparsified when it wasn't supposed to be.\n>   (2) These users have no idea how to figure out what they need to do\n> to fix it.  The init-worktree-config is no more discoverable than the\n> documentation on the official steps for enabling\n> extensions.worktreeConfig (See\n> https://lore.kernel.org/git/CABPp-BGKyDJV9DP+igmCC_Ad0jgvb4aOAYpXWCbx9hW8ShhDQg@mail.gmail.com/\n> up through the paragraph, \"Further, it's not even clear people would\n> look at git-worktree.txt.)\n>   (3) Even if they do discover it, and run it, it's an extra step they\n> never needed to take before.  Why are we adding a \"unbreak these other\n> commands we want to run\" step?\n>   (4) Also, even if they do discover it, and run it, suddenly we are\n> setting core.repositoryFormatVersion=1.  That scares me.  I have years\n> of experience at $DAYJOB saying that the tooling we have works fine\n> with extensions.worktreeConfig=true.  I have none with setting\n> core.repositoryFormatVersion=1, but now we're effectively requiring it\n> by your documentation.  I have no idea how\n> jgit/egit/other-random-stuff interacts with that.  I'd be willing to\n> do some tests with targetted users to try to learn more, but suddenly\n> turning it on for everyone in cases that we know worked fine without\n> it previously feels unsafe to me.  Maybe I'm over-worrying here, but\n> see also commit 11664196ac (\"Revert \"check_repository_format_gently():\n> refuse\n> extensions for old repositories\"\", 2020-07-15) -- it just feels a bit\n> late to recommend for users, especially when they'll see it as \"oh, if\n> you don't want this other bug we recently introduced you need to run\n> this....\".\n\nPoint #4 is pretty compelling, and the \"ship\" of enforcing\n`core.repositoryFormatVersion=1` when using `extension` configs has\n\"already sailed\" anyhow, as 11664196ac (\"Revert\n\"check_repository_format_gently(): refuse extensions for old\nrepositories\"\", 2020-07-15) clearly indicates.\n\n> So, I'd like to reiterate my earlier suggestion which would avoid\n> these regressions while also fixing the reported bug:\n>   * If core.bare=true or core.worktree is set, then at `git worktree\n> add` time, automatically run the logic you have here for\n> init-worktree-config.  Having either of those config settings with\n> multiple worktrees is currently broken in all git versions and likely\n> in most all external tools.  As such, being aggressive in the new\n> config settings to allow new versions of git to work seems totally\n> safe to me -- we can't be any more broken than we already were.\n>   * If core.bare=false and core.worktree is not set, then:\n>     * `git sparse-checkout {init,set}` should set\n> extensions.worktreeConfig if not already set, and always set the\n> core.sparse* and index.sparse settings in worktree-specific files.\n>     * `git worktree add`, if extensions.worktreeConfig is already set,\n> will copy both the info/sparse-checkout file and the config.worktree\n> settings (module core.bare and core.worktree, if present) to the new\n> worktree\n\nThanks for the clearly written enumeration of how you expect this to\nwork. This summary pretty well (or entirely) captures the conclusions\nI arrived at, as well, after devoting a chunk of time today thinking\nthrough the cases. If I'm understanding everything correctly, the\napproach outlined here solves the bare-worktree problem in the least\ninvasive and least dangerous way (for older Git versions and foreign\ntools). And we don't even need the `git worktree init-worktree-config`\nsubcommand (though we need the underlying functionality).\n"},{"id":"445210","messageId":"CAPig+cS3GcOdk_tGNb7V64syUajfO40e_4uXWmYYo9=nU-1ezw@mail.gmail.com","threadId":"57125","inReplyTo":"1db0f601-4769-15c0-cd58-ecddfa1fc9d5@gmail.com","subject":"Re: [PATCH v3 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-30T07:41:39Z","receivedAt":"2021-12-30T07:41:52Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Dec 29, 2021 at 12:39 PM Derrick Stolee <stolee@gmail.com> wrote:\n> On 12/29/2021 4:39 AM, Elijah Newren wrote:\n> > No, it's not even rare, let alone very rare.  I'd actually call it\n> > common.  Since 'sparse-checkout disable' does not delete the\n> > sparse-checkout file, and we've encouraged folks to use the\n> > sparse-checkout command (or a wrapper thereof) instead of direct\n> > editing of the sparse-checkout file (and indeed, the sparse-checkout\n> > command will overwrite the sparse-checkout file which further\n> > discourages users from feeling they own it), having the file left\n> > around after disabling is the common case.  So, the only question is,\n> > how often do users disable and re-enable sparse-checkout, and\n> > potentially only do so in some of their worktrees?  At my $DAYJOB,\n> > that's actually quite common.  I got multiple reports quite soon after\n> > introducing our `sparsify` tool about users doing something like this;\n> > this is what led me to learn of the extensions.worktreeConfig, and why\n> > I pointed it out to you on your first submission of\n> > git-sparse-checkout[1]\n> > (https://lore.kernel.org/git/CABPp-BFcH5hQqujjmc88L3qGx3QAYZ_chH6PXQXyp13ipfV6hQ@mail.gmail.com/)\n>\n> Thank you for these comments and the detailed descriptions of things\n> from your $DAYJOB. That's helpful context and I'm happy to switch back\n> to enabling the extension in the sparse-checkout builtin. I might need\n> to rearrange the code so there is an API in worktree.c instead of just\n> the subcommand in builtin/worktree.c, but that's pretty minor. I'll\n> keep Eric's earlier suggestion to have the upgrade be a separate call\n> from the repo_config_set_worktree_gently().\n\nThanks.\n"},{"id":"445211","messageId":"CAPig+cSEbiLJ1yCCv_hsoWVgpXE13B9-UwWzi7jhWq4NgYc0Nw@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BE9umOTkjfnB0X+9nTZaBmeXd4z5DmiZ1Upzds=m1spPw@mail.gmail.com","subject":"Re: [PATCH v3 6/6] worktree: copy sparse-checkout patterns and config on add","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-30T08:01:24Z","receivedAt":"2021-12-30T08:01:37Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Dec 29, 2021 at 2:52 PM Elijah Newren <newren@gmail.com> wrote:\n> On Wed, Dec 29, 2021 at 9:31 AM Derrick Stolee <stolee@gmail.com> wrote:\n> > I'll wait for more feedback on the overall ideas (and names of things\n> > like the init-worktree-config subcommand).\n>\n> What value does the init-worktree-config subcommand provide; why\n> shouldn't we just get rid of it?\n>\n> I know Eric was strongly suggesting it, but he was thinking in terms\n> of always doing that full switchover step, or never doing it.  Both\n> extremes had the potential to cause user-visible bugs, and thus he\n> suggested providing a command to allow users to pick their poison.  I\n> provided a suggestion avoiding both extremes that doesn't have that\n> pick-your-poison approach, so I don't see why forcing users into this\n> extra step makes any sense.\n\nRight. The minimally invasive, minimally dangerous approach you\noutlined at the very bottom of [1] obviates the need for\n`init-worktree-config`. We still want the underlying function for `git\nworktree add` to call, but a user-facing command providing the same\nfunctionality becomes much less meaningful since enabling per-worktree\nconfiguration involves no more than simply setting\n`extension.worktreeConfig=true` in all cases.\n\nSo, I can't think of any reason to add `init-worktree-config`\npresently (if ever).\n\n[1]: https://lore.kernel.org/git/CABPp-BHuO3B366uJuODMQo-y449p8cAMVn0g2MTcO5di3Xa7Zg@mail.gmail.com/\n"},{"id":"445212","messageId":"CAPig+cRVePM7AtoMc=czDPqZed=VQqKGUBKCBpYA0QQm18cofg@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BFdVPRgFy8DLFEB13Trh5J4S+xma-U5FUnUvQLNMfNNjA@mail.gmail.com","subject":"Re: [PATCH v3 6/6] worktree: copy sparse-checkout patterns and config on add","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-30T08:16:01Z","receivedAt":"2021-12-30T08:16:14Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Dec 29, 2021 at 5:45 PM Elijah Newren <newren@gmail.com> wrote:\n> On Wed, Dec 29, 2021 at 1:39 PM Derrick Stolee <stolee@gmail.com> wrote:\n> > I think the motivation is that worktree config is something that is\n> > harder to set up than to just run a 'git config' command, and we\n> > should guide users into a best practice for using it. The\n> > documentation becomes \"run this command to enable it\".\n>\n> Okay, but that's an answer to a different question -- namely, \"if\n> users want/need to explicitly set it up, why should we have a\n> command?\"  Your answer here is a very good answer to that question,\n> but you've assumed the \"if\".  My question was on the \"if\": (Why) Do\n> users need or want to explicitly set it up?\n>\n> Secondarily, if users want to set it up explicitly, is the work here\n> really sufficient to help guide them?  In particular, I discovered and\n> started using extensions.worktreeConfig without ever looking at the\n> relevant portions of git-worktree.txt (the references in\n> git-config.txt never mentioned them).  I also pushed this usage to\n> others, including even to you with `git-sparse-checkout`, and no\n> reviewer on this list ever caught it or informed me of the `proper`\n> additional guidelines found in git-worktree.txt until this thread\n> started.  So, relying on folks to read git-worktree.txt for this\n> config item feels a bit weak to me.  Granted, your new command will be\n> much more likely to be read since it appears near the top of\n> git-worktree.txt, but I just don't think that's enough.  The\n> references to extensions.worktreeConfig in git-config.txt should\n> reference any special command or extended steps if we expect users to\n> manually configure it (whether via explicit new subcommand or via also\n> playing with other config settings).\n\nAgreed about it being a good idea to update git-config.txt to mention\nthe extra bookkeeping related to `extensions.worktreeConfig=1` (though\nit doesn't necessarily need to be done by this series).\n\n> Anyway, if we think users want to set it up explicitly, and we address\n> the discoverability problem above, then I'd vote for\n> \"independent-config\" or \"private-config\" (or _maybe_\n> \"migrate-config\").  Because:\n>   * no sense repeating the word `worktree` in `git worktree\n> init-worktree-config`.  It's redundant.\n>   * The words \"independent\" or \"private\" suggest what it does and why\n> users might want to use the new subcommand.\n>   * It's not an \"init\":\n\nI had some similar thoughts/objections to the name\n`init-worktree-config` (not that I was able to come up with anything\nbetter). Thanks for enumerating them here. Anyhow, as you say below,\nthe new subcommand isn't really needed.\n\n>     * The documentation makes no attempt to impose a temporal order of\n> using this command before `git worktree add`.  (Would we even want\n> to?)\n\nAside from possible(?) sparse-checkout issues, I don't think there is\na reason to impose temporal order in general.\n\n>     * As per my recommendation elsewhere, this step just isn't needed\n> for the vast majority of users (i.e. those with non-bare clones who\n> leave core.worktree alone).\n>     * ...and it's also not needed for other (core.bare=true or\n> core.worktree set) users since `git worktree add` will automatically\n> run this config migration for them\n\nYour enumeration at the very end of [1] pretty well convinced me that\nwe don't need this command; certainly not at present, and perhaps\nnever.\n\n> And, actually, with the name \"independent-config\" or \"private-config\",\n> I might be answering my own question.  It's a name that speaks to why\n> users might want it, so my objection to the new command is rapidly\n> diminishing.\n\nPerhaps some day it might make sense to add such a subcommand (more\nsuitably named), but with your proposal in [1] it's hard to justify\nadding it now, and it certainly doesn't need to be part of this patch\nseries.\n\n[1]: https://lore.kernel.org/git/CABPp-BHuO3B366uJuODMQo-y449p8cAMVn0g2MTcO5di3Xa7Zg@mail.gmail.com/\n"},{"id":"445213","messageId":"CAPig+cTn1049oZQwcLk82LPoOZ9N3mYQifSDObejdcvZwo6ZJg@mail.gmail.com","threadId":"57125","inReplyTo":"e2a0a458115a26cfb855f7040f15e5198072b3a5.1640727143.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 3/6] worktree: add 'init-worktree-config' subcommand","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-30T08:41:57Z","receivedAt":"2021-12-30T08:42:10Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 28, 2021 at 4:32 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> [...]\n> To gain access to the core repository's config and config.worktree file,\n> we reference a repository struct's 'commondir' member. If the repository\n> was a submodule instead of a worktree, then this still applies\n> correctly.\n\nIn [1], I suggested that you should be using `repository->gitdir`\nrather than `repository->commondir` to access the `.git/config` file.\nIs the above paragraph saying that my suggestion was incorrect? Or is\nit incorrect only in the case of submodules? Or what is it saying?\n\n> diff --git a/builtin/worktree.c b/builtin/worktree.c\n> @@ -1031,6 +1032,85 @@ static int repair(int ac, const char **av, const char *prefix)\n> +static int init_worktree_config(int ac, const char **av, const char *prefix)\n> +{\n> +       struct repository *r = the_repository;\n> +       char *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n> +       char *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n\nSpecifically, in [1], I said that `common_config_file` should be using\n`r->gitdir` (and that the use of `r->commondir` was correct for\n`main_worktree_file`).\n\n[1]: https://lore.kernel.org/git/CAPig+cQrJ9yWjkc8VMu=uyx_qtrXdL3cNnxLVafoxOo6e-r9kw@mail.gmail.com/\n\n> +       git_configset_init(&cs);\n> +       git_configset_add_file(&cs, common_config_file);\n> +\n> +       /*\n> +        * If the format and extension are already enabled, then we can\n> +        * skip the upgrade process.\n> +        */\n> +       if (repository_format_worktree_config)\n> +               return 0;\n\nRather than `return 0`, should this be `goto cleanup`...\n\n> +       if (upgrade_repository_format(r, 1) < 0) {\n> +               res = error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n> +               goto cleanup;\n> +       }\n> +       if ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n> +               error(_(\"failed to set extensions.worktreeConfig setting\"));\n> +               goto cleanup;\n> +       }\n\n... as is done for these two cases?\n\n> +cleanup:\n> +       git_configset_clear(&cs);\n> +       free(common_config_file);\n> +       free(main_worktree_file);\n> +       return res;\n> +}\n"},{"id":"445214","messageId":"CAPig+cTGdmzfY5r3gdewdEwcRMDdnS9FtZxZNLSCNQYHeQq=wA@mail.gmail.com","threadId":"57125","inReplyTo":"b200819c1bb564695b68d8ed2caebfca0302f93f.1640727143.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 5/6] sparse-checkout: use repo_config_set_worktree_gently()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-30T09:01:28Z","receivedAt":"2021-12-30T09:01:41Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 28, 2021 at 4:32 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> The previous change added repo_config_set_worktree_gently() to assist\n> writing config values into the worktree.config file, if enabled.\n\ns/worktree.config/config.worktree/\n\n(I made the same mistake several times earlier in this discussion.)\n\n> Let the sparse-checkout builtin use this helper instead of attempting to\n> initialize the worktree config on its own. This changes behavior of 'git\n> sparse-checkout set' in a few important ways:\n\n\"worktree config\" is a bit ambiguous here. It might be clearer to say\n\"enable per-worktree configuration\" or \"enable worktree-specific\nconfiguration\".\n\n>  1. Git will no longer upgrade the repository format and add the\n>     worktree config extension. The user should run 'git worktree\n>     init-worktree-config' to enable this feature.\n>\n>  2. If worktree config is disabled, then this command will set the\n>     core.sparseCheckout (and possibly core.sparseCheckoutCone and\n>     index.sparse) values in the common config file.\n>\n>  3. If the main worktree is bare, then this command will not put the\n>     worktree in a broken state.\n\nThere are a few other cases of ambiguity in this enumerated list, as\nwell, and the need for `s/main worktree is bare/repository is bare/`,\nhowever, if you end up restructuring this series the to follow the\nrecipe Elijah set forth at the bottom of [1], then most of this\npatch's commit message will be rewritten anyhow, so my mention of\nthese minor issues will be moot.\n\n[1]: https://lore.kernel.org/git/CABPp-BHuO3B366uJuODMQo-y449p8cAMVn0g2MTcO5di3Xa7Zg@mail.gmail.com/\n\n> The main reason to use worktree-specific config for the sparse-checkout\n> builtin was to avoid enabling sparse-checkout patterns in one and\n> causing a loss of files in another. If a worktree does not have a\n> sparse-checkout patterns file, then the sparse-checkout logic will not\n> kick in on that worktree.\n>\n> This new logic introduces a new user pattern that could lead to some\n> confusion. Suppose a user has not upgraded to worktree config and\n> follows these steps in order:\n>\n>  1. Enable sparse-checkout in a worktree.\n>\n>  2. Disable sparse-checkout in that worktree without deleting that\n>     worktree's sparse-checkout file.\n>\n>  3. Enable sparse-checkout in another worktree.\n>\n> After these steps, the first worktree will have sparse-checkout enabled\n> with whatever patterns exist. The worktree does not immediately have\n> those patterns applied, but a variety of Git commands would apply the\n> sparse-checkout patterns and update the worktree state to reflect those\n> patterns. This situation is likely very rare and the workaround is to\n> upgrade to worktree specific config on purpose. Users already in this\n> state used the sparse-checkout builtin with a version that upgraded to\n> worktree config, anyway.\n>\n> Reported-by: Sean Allred <allred.sean@gmail.com>\n> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n"},{"id":"445228","messageId":"940635e7-ff92-d87d-5dfb-a61b6320c30b@gmail.com","threadId":"57125","inReplyTo":"CAPig+cTn1049oZQwcLk82LPoOZ9N3mYQifSDObejdcvZwo6ZJg@mail.gmail.com","subject":"Re: [PATCH v3 3/6] worktree: add 'init-worktree-config' subcommand","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-30T17:29:57Z","receivedAt":"2021-12-30T17:30:01Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/30/2021 3:41 AM, Eric Sunshine wrote:\n> On Tue, Dec 28, 2021 at 4:32 PM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>> [...]\n>> To gain access to the core repository's config and config.worktree file,\n>> we reference a repository struct's 'commondir' member. If the repository\n>> was a submodule instead of a worktree, then this still applies\n>> correctly.\n> \n> In [1], I suggested that you should be using `repository->gitdir`\n> rather than `repository->commondir` to access the `.git/config` file.\n> Is the above paragraph saying that my suggestion was incorrect? Or is\n> it incorrect only in the case of submodules? Or what is it saying?\n> \n>> diff --git a/builtin/worktree.c b/builtin/worktree.c\n>> @@ -1031,6 +1032,85 @@ static int repair(int ac, const char **av, const char *prefix)\n>> +static int init_worktree_config(int ac, const char **av, const char *prefix)\n>> +{\n>> +       struct repository *r = the_repository;\n>> +       char *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n>> +       char *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n> \n> Specifically, in [1], I said that `common_config_file` should be using\n> `r->gitdir` (and that the use of `r->commondir` was correct for\n> `main_worktree_file`).\n\nSince you agree that \"{r->commondir}/config.worktree\" is the main\nworktree's config file, then \"{r->commondir}/config\" should be the\nrepo-wide config file. If r->commondir and r->gitdir are different,\nthen using r->gitdir would be wrong, as far as I understand it.\n\nIndeed, tracing these two values when run in a worktree, I see:\n\n  gitdir: /home/stolee/_git/git/.git/worktrees/git-upstream\n  commondir: /home/stolee/_git/git/.git\n\nSo we definitely want commondir here.\n\n>> +       /*\n>> +        * If the format and extension are already enabled, then we can\n>> +        * skip the upgrade process.\n>> +        */\n>> +       if (repository_format_worktree_config)\n>> +               return 0;\n> \n> Rather than `return 0`, should this be `goto cleanup`...\n\nRight, or move the 'if' to before the configset is initialized. The\ngoto is simple enough.\n \n>> +       if (upgrade_repository_format(r, 1) < 0) {\n>> +               res = error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n>> +               goto cleanup;\n>> +       }\n>> +       if ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n>> +               error(_(\"failed to set extensions.worktreeConfig setting\"));\n>> +               goto cleanup;\n>> +       }\n> \n> ... as is done for these two cases?\n> \n>> +cleanup:\n>> +       git_configset_clear(&cs);\n>> +       free(common_config_file);\n>> +       free(main_worktree_file);\n>> +       return res;\n\nThanks,\n-Stolee\n"},{"id":"445229","messageId":"CABPp-BEtMLXqRUTO4TiVqMrGo51L2EOPLNz5CRf-L_rtPiqLHA@mail.gmail.com","threadId":"57125","inReplyTo":"CAPig+cRs4Vvp4rQ_EhMxB_qe4YAh3+NnqETTpYyMnAjsRhAUbg@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-30T17:40:20Z","receivedAt":"2021-12-30T17:40:38Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Dec 29, 2021 at 10:22 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Mon, Dec 27, 2021 at 3:16 PM Elijah Newren <newren@gmail.com> wrote:\n> > On Sun, Dec 26, 2021 at 11:34 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > > Your proposal is _almost_ the same as my suggestion of eventually\n> > > making per-worktree config the default. The difference is that you're\n> > > only making it the default if `core.bare=true` or `core.worktree` is\n> > > set.\n> >\n> > Indeed.  :-)\n>\n> I mentioned previously[1] that I needed to find a block of time to\n> really think through the topic before I'd be able to respond to this\n> email. So, today I spent some time trying to reason through the\n> various cases under discussion, and I came back and re-read this email\n> with the intention of trying to summarize my understanding of the\n> situation and my understanding of the points you were making. However,\n> you did such a good job of summarizing the various cases at the very\n> end of [2] that it probably makes more sense for me to respond to that\n> email instead.\n>\n> [1]: https://lore.kernel.org/git/CAPig+cTFSDw-9Aq+=+r4sHSzTmG7s2T93Z0uqWTxHbKwGFaiYQ@mail.gmail.com/\n> [2]: https://lore.kernel.org/git/CABPp-BHuO3B366uJuODMQo-y449p8cAMVn0g2MTcO5di3Xa7Zg@mail.gmail.com/\n>\n> > > But do we need that distinction? If people are comfortable with\n> > > that, then are they comfortable with simply flipping the switch and\n> > > making per-worktree config the default today regardless of `core.bare`\n> > > and `core.worktree`?\n> >\n> > This is tempting, at least if we leave core.repositoryFormatVersion as\n> > 0 (see 11664196ac (\"Revert \"check_repository_format_gently(): refuse\n> > extensions for old repositories\"\", 2020-07-15)) when core.bare is\n> > false and core.worktree was unset.  However, for that case:\n>\n> I had seen 11664196ac when researching one of my earlier responses,\n> though it took more than one read to (hopefully) fully understand what\n> it is saying (i.e. due to an oversight, it's too late to enforce the\n> `core.repositoryFormatVersion=1` requirement when extensions are used,\n> as originally intended).\n>\n> > * This is a case where operating on the primary worktree was not\n> > previously problematic for older git versions or third party tools.\n> > * Interestingly, git <= 2.6.2 can continue to operate on the primary\n> > worktree (because it didn't know to error out on unknown extensions)\n> > * git >= 2.19.0 could continue to operate on the primary worktree\n> > (because it understands the extension)\n> > * git versions between that range would suddenly break, erroring out\n> > on the unknown extension (though those versions would start working\n> > again if we migrated core.bare and core.worktree but just didn't set\n> > extensions.worktreeConfig).\n>\n> The significance of versions 2.6.2 and 2.19.0 is unclear to me. What\n> context or criteria are you using to identify those versions as\n> meaningful here?\n\nWe had been discussing how certain config settings \"might break\nexternal tools OR old git versions\", but hadn't brought up which tools\nor which git versions.  I don't know which all external tools might be\nin play (though I mentioned some in use at $DAYJOB in another thread)\nbut in this email I had just thought that I'd mention where the cutoff\npoint was in terms of git versions which understood\ncore.repositoryFormatVersion and extensions.worktreeConfig.  If other\nfolks also want to test how things behaved before or after these\npatches with \"old git versions\", those were the switchover points that\nare relevant and which I tested with.\n"},{"id":"445230","messageId":"f62709d5-a0d4-dc0c-461a-c4c75c810340@gmail.com","threadId":"57125","inReplyTo":"CAPig+cQ8Y2FC8=d7DaZ46YwPCrzM6RzutiZ3ghhrHsg6y8KWoQ@mail.gmail.com","subject":"Re: [PATCH v3 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-12-30T17:41:54Z","receivedAt":"2021-12-30T17:41:58Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 12/30/2021 2:40 AM, Eric Sunshine wrote:\n> On Wed, Dec 29, 2021 at 4:40 AM Elijah Newren <newren@gmail.com> wrote:>\n\nTaking time to focus only on this outline here:\n\n>> So, I'd like to reiterate my earlier suggestion which would avoid\n>> these regressions while also fixing the reported bug:\n\n>>   * If core.bare=true or core.worktree is set, then at `git worktree\n>> add` time, automatically run the logic you have here for\n>> init-worktree-config.  Having either of those config settings with\n>> multiple worktrees is currently broken in all git versions and likely\n>> in most all external tools.  As such, being aggressive in the new\n>> config settings to allow new versions of git to work seems totally\n>> safe to me -- we can't be any more broken than we already were.\n\nI'm not sure I agree with the \"currently broken in all git versions\"\nbecause when extensions.worktreeConfig is not enabled, the core.bare\nand core.worktree settings are ignored by the worktrees. This upgrade\nduring 'add' is the only thing I am not so sure about.\n\n>>   * If core.bare=false and core.worktree is not set, then:\n\n>>     * `git sparse-checkout {init,set}` should set\n>> extensions.worktreeConfig if not already set, and always set the\n>> core.sparse* and index.sparse settings in worktree-specific files.\n\nThis should happen no matter the case of core.bare and core.worktree\nexisting, right?\n\n>>     * `git worktree add`, if extensions.worktreeConfig is already set,\n>> will copy both the info/sparse-checkout file and the config.worktree\n>> settings (module core.bare and core.worktree, if present) to the new\n>> worktree\n\nand here, 'git worktree add' should always copy the info/sparse-checkout\nfile (if core.sparseCheckout is enabled) and copy the config.worktree\nsettings if extensions.worktreeConfig is enabled (and filter out\ncore.bare and core.worktree in the process).\n\n> Thanks for the clearly written enumeration of how you expect this to\n> work. This summary pretty well (or entirely) captures the conclusions\n> I arrived at, as well, after devoting a chunk of time today thinking\n> through the cases. If I'm understanding everything correctly, the\n> approach outlined here solves the bare-worktree problem in the least\n> invasive and least dangerous way (for older Git versions and foreign\n> tools). And we don't even need the `git worktree init-worktree-config`\n> subcommand (though we need the underlying functionality).\n\nI'm happy to drop the subcommand in favor of some documentation in\ngit-config.txt (Documentation/config/extensions.txt to be exact).\n\nThanks,\n-Stolee\n"},{"id":"445232","messageId":"CABPp-BH-oOUhTzcXMFWRfG=zjUw6C5h-oHSU=-juPTjpW92_vA@mail.gmail.com","threadId":"57125","inReplyTo":"CAPig+cRJj4Aa9Nm_yWD0=WGdn9tRmT2PpTwo5yeGZHg2Qz2rgA@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-30T18:38:06Z","receivedAt":"2021-12-30T18:38:20Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Dec 29, 2021 at 10:41 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Tue, Dec 28, 2021 at 1:16 PM Elijah Newren <newren@gmail.com> wrote:\n> > On Mon, Dec 27, 2021 at 11:33 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n\n> > > A more general approach might be for the new worktree to copy all the\n> > > per-worktree configuration from the worktree in which the command was\n> > > invoked, thus sparsity would be inherited \"for free\" along with other\n> > > settings. This has the benefits of not requiring sparse-checkout\n> > > special-cases in the code and it's easy to document (\"the new worktree\n> > > inherits/copies configuration settings from the worktree in which `git\n> > > worktree add` was invoked\") and easy to understand.\n> >\n> > Ooh, this is a good point and I *really* like this simple solution.\n> > Thanks for pointing it out.\n>\n> I do wonder, though, if there are traps waiting for us with this\n> all-inclusive approach. I don't know what sort of worktree-specific\n> configuration people use, so I do worry a bit that this could be\n> casting a too-wide net, and that it might in fact be better to only\n> copy the sparse-checkout settings (as ugly as it is to special-case\n> that -- but we need to special-case `core.bare` and `core.worktree`\n> anyhow[1]).\n>\n> [1]: https://lore.kernel.org/git/CAPig+cSUOknNC9GMyPvAqdBU0r1MVgvSpvgpSpXUmBm67HO7PQ@mail.gmail.com/\n\nI could probably be persuaded either way (do users want to copy\nsomething and tweak it, or start with a clean slate?), and it might\neven make sense to have a flag for users to specify.\n\nMy hunch, at least with the developers I work with, is that they're\nmore likely to think in terms of \"I want another worktree like this\none, except that I'm going to change...\"\n\nAlso, another reason to prefer copying all of core.worktree (minus the\nalways worktree-specific value of core.worktree, and core.bare), is\nbecause it's easy to explain in the documentation, and I think we'd be\nmuch less likely to obsolete user's knowledge over time.  (I think\nadditional sparse-checkout things, or new other features that also\nwant to be copied over, are much more likely than the addition of keys\nthat are always worktree-specific like core.worktree).\n\n...\n> > An increasingly unworkable alternative is the current behavior of\n> > defaulting to a full checkout in all cases (and forcing users to\n> > sparsify afterwards).  A full checkout is fine if the user came from\n> > one (and probably preferable in such a case), but it's increasingly\n> > problematic for us even with our repo being nowhere near the size of\n> > the microsoft repos.\n>\n> It feels unfortunate and a bit dirty to spread around this\n> special-case knowledge about sparse-checkout to various parts of the\n> system,\n\nThis might makes things worse, but it's far too late to avoid that:\n\n$ git grep -il -e sparse-checkout -e skip_worktree\n.gitignore\nDocumentation/RelNotes/2.19.0.txt\nDocumentation/RelNotes/2.25.0.txt\nDocumentation/RelNotes/2.26.0.txt\nDocumentation/RelNotes/2.27.0.txt\nDocumentation/RelNotes/2.28.0.txt\nDocumentation/RelNotes/2.34.0.txt\nDocumentation/RelNotes/2.6.0.txt\nDocumentation/config/checkout.txt\nDocumentation/config/core.txt\nDocumentation/git-add.txt\nDocumentation/git-checkout.txt\nDocumentation/git-clone.txt\nDocumentation/git-read-tree.txt\nDocumentation/git-restore.txt\nDocumentation/git-rm.txt\nDocumentation/git-sparse-checkout.txt\nDocumentation/git-worktree.txt\nDocumentation/gitrepository-layout.txt\nDocumentation/rev-list-options.txt\nDocumentation/technical/index-format.txt\nDocumentation/technical/parallel-checkout.txt\nDocumentation/technical/sparse-index.txt\nMakefile\nadvice.c\napply.c\nattr.c\nbuiltin/add.c\nbuiltin/check-ignore.c\nbuiltin/checkout.c\nbuiltin/clone.c\nbuiltin/commit.c\nbuiltin/grep.c\nbuiltin/ls-files.c\nbuiltin/mv.c\nbuiltin/read-tree.c\nbuiltin/rm.c\nbuiltin/sparse-checkout.c\nbuiltin/stash.c\nbuiltin/update-index.c\ncache-tree.c\ncache.h\ncommand-list.txt\ncontrib/completion/git-prompt.sh\ndiff-lib.c\ndiff.c\ndir.c\ndir.h\nentry.c\ngit.c\nlist-objects-filter.c\nlist-objects-filter.h\nmerge-ort.c\nmerge-recursive.c\npath.c\npathspec.c\npathspec.h\npo/bg.po\npo/ca.po\npo/de.po\npo/el.po\npo/es.po\npo/fr.po\npo/git.pot\npo/id.po\npo/it.po\npo/ko.po\npo/pl.po\npo/pt_PT.po\npo/ru.po\npo/sv.po\npo/tr.po\npo/vi.po\npo/zh_CN.po\npo/zh_TW.po\npreload-index.c\nread-cache.c\nsparse-index.c\nsparse-index.h\nt/perf/p0005-status.sh\nt/perf/p0006-read-tree-checkout.sh\nt/perf/p0007-write-cache.sh\nt/perf/p2000-sparse-operations.sh\nt/perf/repos/inflate-repo.sh\nt/perf/repos/many-files.sh\nt/t0060-path-utils.sh\nt/t1011-read-tree-sparse-checkout.sh\nt/t1090-sparse-checkout-scope.sh\nt/t1091-sparse-checkout-builtin.sh\nt/t1092-sparse-checkout-compatibility.sh\nt/t2018-checkout-branch.sh\nt/t3507-cherry-pick-conflict.sh\nt/t3602-rm-sparse-checkout.sh\nt/t3705-add-sparse-checkout.sh\nt/t5317-pack-objects-filter-objects.sh\nt/t6112-rev-list-filters-objects.sh\nt/t6428-merge-conflicts-sparse.sh\nt/t6435-merge-sparse.sh\nt/t7002-mv-sparse-checkout.sh\nt/t7012-skip-worktree-writing.sh\nt/t7063-status-untracked-cache.sh\nt/t7418-submodule-sparse-gitmodules.sh\nt/t7519-status-fsmonitor.sh\nt/t7817-grep-sparse-checkout.sh\nunpack-trees.c\nwt-status.c\n\n>  but based upon the pain-points you describe, having a new\n> worktree inherit the sparsity from the originating worktree does sound\n> (given my limited knowledge of the topic) like it would ease the pain\n> for users.\n\n:-)\n"},{"id":"445233","messageId":"CABPp-BEOgYct2nZ+6Hb+3bzEZt14pcSSa2_qLT-O==SMrh_KYA@mail.gmail.com","threadId":"57125","inReplyTo":"CAPig+cQ8Y2FC8=d7DaZ46YwPCrzM6RzutiZ3ghhrHsg6y8KWoQ@mail.gmail.com","subject":"Re: [PATCH v3 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-30T18:46:37Z","receivedAt":"2021-12-30T18:46:53Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Dec 29, 2021 at 11:40 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Wed, Dec 29, 2021 at 4:40 AM Elijah Newren <newren@gmail.com> wrote:>\n> > On Tue, Dec 28, 2021 at 1:32 PM Derrick Stolee via GitGitGadget\n> > <gitgitgadget@gmail.com> wrote:\n> > >      ++init-worktree-config::\n> > >      ++\n> > >      ++Initialize config settings to enable worktree-specific config settings.\n> > >      ++This will set `core.repositoryFormatversion=1` and enable\n> > >      ++`extensions.worktreeConfig`, which might cause some third-party tools from\n> > >      ++being able to operate on your repository. See CONFIGURATION FILE for more\n> > >      ++details.\n> >\n> > So, if users attempt to use `git worktree add` or `git sparse-checkout\n> > {init,set}` without first running this, they can break other\n> > worktrees.  And if they do run this new command, they potentially\n> > break third-party tools or older git versions.\n>\n> When you say \"can break other worktrees\", you don't necessarily mean\n> that in general but rather in regard to sparse-checkout -- in\n> particular, the sparse-checkout config settings and the\n> `info/sparse-checkout file` -- correct? (Genuine question; I want to\n> make sure that I'm actually understanding the issues under\n> discussion.)\n\nYes, thanks for the clarifications.\n\n...\n> > So, I'd like to reiterate my earlier suggestion which would avoid\n> > these regressions while also fixing the reported bug:\n> >   * If core.bare=true or core.worktree is set, then at `git worktree\n> > add` time, automatically run the logic you have here for\n> > init-worktree-config.  Having either of those config settings with\n> > multiple worktrees is currently broken in all git versions and likely\n> > in most all external tools.  As such, being aggressive in the new\n> > config settings to allow new versions of git to work seems totally\n> > safe to me -- we can't be any more broken than we already were.\n> >   * If core.bare=false and core.worktree is not set, then:\n> >     * `git sparse-checkout {init,set}` should set\n> > extensions.worktreeConfig if not already set, and always set the\n> > core.sparse* and index.sparse settings in worktree-specific files.\n> >     * `git worktree add`, if extensions.worktreeConfig is already set,\n> > will copy both the info/sparse-checkout file and the config.worktree\n> > settings (module core.bare and core.worktree, if present) to the new\n> > worktree\n>\n> Thanks for the clearly written enumeration of how you expect this to\n> work. This summary pretty well (or entirely) captures the conclusions\n> I arrived at, as well, after devoting a chunk of time today thinking\n> through the cases. If I'm understanding everything correctly, the\n> approach outlined here solves the bare-worktree problem in the least\n> invasive and least dangerous way (for older Git versions and foreign\n> tools). And we don't even need the `git worktree init-worktree-config`\n> subcommand (though we need the underlying functionality).\n\nI'm glad it helps, and that we appear to be moving towards consensus.  :-)\n"},{"id":"445237","messageId":"CABPp-BG2Ueb9rBRNZz3Z_8V7hin5SamzOJLNpjrg0k-QGUV4zA@mail.gmail.com","threadId":"57125","inReplyTo":"f62709d5-a0d4-dc0c-461a-c4c75c810340@gmail.com","subject":"Re: [PATCH v3 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-30T19:29:29Z","receivedAt":"2021-12-30T19:29:48Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Dec 30, 2021 at 9:41 AM Derrick Stolee <stolee@gmail.com> wrote:\n>\n> On 12/30/2021 2:40 AM, Eric Sunshine wrote:\n> > On Wed, Dec 29, 2021 at 4:40 AM Elijah Newren <newren@gmail.com> wrote:>\n>\n> Taking time to focus only on this outline here:\n>\n> >> So, I'd like to reiterate my earlier suggestion which would avoid\n> >> these regressions while also fixing the reported bug:\n>\n> >>   * If core.bare=true or core.worktree is set, then at `git worktree\n> >> add` time, automatically run the logic you have here for\n> >> init-worktree-config.  Having either of those config settings with\n> >> multiple worktrees is currently broken in all git versions and likely\n> >> in most all external tools.  As such, being aggressive in the new\n> >> config settings to allow new versions of git to work seems totally\n> >> safe to me -- we can't be any more broken than we already were.\n>\n> I'm not sure I agree with the \"currently broken in all git versions\"\n> because when extensions.worktreeConfig is not enabled, the core.bare\n> and core.worktree settings are ignored by the worktrees. This upgrade\n> during 'add' is the only thing I am not so sure about.\n\nOh, you're right; I mis-spoke.  If someone has core.bare=true and has\nmultiple worktrees, AND never attempts to use sparse-checkouts OR\notherwise set extensions.worktreeConfig, then git still works due to\ngit's special-case logic that will override core.bare in this\nconfiguration.  It's just setting them up for a ticking time bomb,\nwaiting for them to either use an external tool that doesn't share\nthat special case override-core.bare logic, or for the user to decide\nto set extensions.worktreeConfig directly or use sparse-checkouts.\n\n> >>   * If core.bare=false and core.worktree is not set, then:\n>\n> >>     * `git sparse-checkout {init,set}` should set\n> >> extensions.worktreeConfig if not already set, and always set the\n> >> core.sparse* and index.sparse settings in worktree-specific files.\n>\n> This should happen no matter the case of core.bare and core.worktree\n> existing, right?\n\nHmm.  I think that's safe for people who cloned and used `git worktree\nadd` with newer git versions, since `git worktree add` will have moved\ncore.bare and core.worktree to the config.worktree file when those\nhave non-default values.\n\nBut, we might want to help out the folks who have existing repos with\nwhich they have used older git versions.  So, we could have `git\nsparse-checkout {init,set}` check for non-default values of\ncore.bare/core.worktree in the shared config file, and, if found, exit\nwith an error which point users at some relevant documentation (which\nmay just suggest 'git worktree add temporary && git worktree remove\ntemporary' as a workaround for those caught in such a state.)\n\n> >>     * `git worktree add`, if extensions.worktreeConfig is already set,\n> >> will copy both the info/sparse-checkout file and the config.worktree\n> >> settings (module core.bare and core.worktree, if present) to the new\n> >> worktree\n>\n> and here, 'git worktree add' should always copy the info/sparse-checkout\n> file (if core.sparseCheckout is enabled) and copy the config.worktree\n> settings if extensions.worktreeConfig is enabled (and filter out\n> core.bare and core.worktree in the process).\n\nRight.\n\n> > Thanks for the clearly written enumeration of how you expect this to\n> > work. This summary pretty well (or entirely) captures the conclusions\n> > I arrived at, as well, after devoting a chunk of time today thinking\n> > through the cases. If I'm understanding everything correctly, the\n> > approach outlined here solves the bare-worktree problem in the least\n> > invasive and least dangerous way (for older Git versions and foreign\n> > tools). And we don't even need the `git worktree init-worktree-config`\n> > subcommand (though we need the underlying functionality).\n>\n> I'm happy to drop the subcommand in favor of some documentation in\n> git-config.txt (Documentation/config/extensions.txt to be exact).\n\nYou may also want to make the two existing references to\nextensions.worktreeConfig within Documentation/git-config.txt point\nusers at the extended documentation you add to\nDocumentation/config/extensions.txt.  (Remember, I found a reference\nto extensions.worktreeConfig, tried it, and started using and\nrecommending it without it ever occurring to me that there was a more\ndetailed explanation elsewhere, so only adding to\nDocumentation/config/extensions.txt might run into the same\ndiscoverability issues.)\n"},{"id":"445340","messageId":"CAPig+cSZ_F7KAZcCs-zOq5knGzqnTkeCvnktm3QLz6-kBSjNng@mail.gmail.com","threadId":"57125","inReplyTo":"940635e7-ff92-d87d-5dfb-a61b6320c30b@gmail.com","subject":"Re: [PATCH v3 3/6] worktree: add 'init-worktree-config' subcommand","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-01-03T06:38:17Z","receivedAt":"2022-01-03T06:38:30Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Dec 30, 2021 at 12:29 PM Derrick Stolee <stolee@gmail.com> wrote:\n> On 12/30/2021 3:41 AM, Eric Sunshine wrote:\n> > On Tue, Dec 28, 2021 at 4:32 PM Derrick Stolee via GitGitGadget\n> > <gitgitgadget@gmail.com> wrote:\n> >> +static int init_worktree_config(int ac, const char **av, const char *prefix)\n> >> +{\n> >> +       struct repository *r = the_repository;\n> >> +       char *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n> >> +       char *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n> >\n> > Specifically, in [1], I said that `common_config_file` should be using\n> > `r->gitdir` (and that the use of `r->commondir` was correct for\n> > `main_worktree_file`).\n>\n> Since you agree that \"{r->commondir}/config.worktree\" is the main\n> worktree's config file, then \"{r->commondir}/config\" should be the\n> repo-wide config file. If r->commondir and r->gitdir are different,\n> then using r->gitdir would be wrong, as far as I understand it.\n>\n> Indeed, tracing these two values when run in a worktree, I see:\n>\n>   gitdir: /home/stolee/_git/git/.git/worktrees/git-upstream\n>   commondir: /home/stolee/_git/git/.git\n>\n> So we definitely want commondir here.\n\nOkay, it looks like I was misinterpreting how\npath.c:strbuf_worktree_gitdir() worked and how it was called, and was\nperhaps a bit confused by the minimal `struct repository` comments.\n\n> >> +       if (repository_format_worktree_config)\n> >> +               return 0;\n> >\n> > Rather than `return 0`, should this be `goto cleanup`...\n>\n> Right, or move the 'if' to before the configset is initialized. The\n> goto is simple enough.\n\nMoving the `if` -- in fact, all three `if`s -- earlier is enticing,\nbut you need to be careful not to leak `common_config_file` and\n`main_worktree_file`, as well. Something like this should work, I\nthink:\n\n   if (repository_format_worktree_config)\n       return 0;\n   if (upgrade_repository_format(r, 1) < 0)\n       return error(...);\n   if (git_config_set_gently(...))\n       return error(...);\n\n   common_config_file = xstrfmt(...);\n   main_worktree_file = xstrfmt(...);\n   git_configset_init(&cs);\n   git_configset_add_file(&cs, common_config_file);\n"},{"id":"445341","messageId":"CAPig+cSayH9za9d4T6YdgjJRnS5jOoV7viWyZGw6qtA72cFEig@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BH-oOUhTzcXMFWRfG=zjUw6C5h-oHSU=-juPTjpW92_vA@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-01-03T06:51:47Z","receivedAt":"2022-01-03T06:52:00Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Dec 30, 2021 at 1:38 PM Elijah Newren <newren@gmail.com> wrote:\n> On Wed, Dec 29, 2021 at 10:41 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > On Tue, Dec 28, 2021 at 1:16 PM Elijah Newren <newren@gmail.com> wrote:\n> > > On Mon, Dec 27, 2021 at 11:33 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > > > A more general approach might be for the new worktree to copy all the\n> > > > per-worktree configuration from the worktree in which the command was\n> > > > invoked, thus sparsity would be inherited \"for free\" along with other\n> > > > settings. This has the benefits of not requiring sparse-checkout\n> > > > special-cases in the code and it's easy to document (\"the new worktree\n> > > > inherits/copies configuration settings from the worktree in which `git\n> > > > worktree add` was invoked\") and easy to understand.\n> > >\n> > > Ooh, this is a good point and I *really* like this simple solution.\n> > > Thanks for pointing it out.\n> >\n> > I do wonder, though, if there are traps waiting for us with this\n> > all-inclusive approach. I don't know what sort of worktree-specific\n> > configuration people use, so I do worry a bit that this could be\n> > casting a too-wide net, and that it might in fact be better to only\n> > copy the sparse-checkout settings (as ugly as it is to special-case\n> > that -- but we need to special-case `core.bare` and `core.worktree`\n> > anyhow[1]).\n>\n> I could probably be persuaded either way (do users want to copy\n> something and tweak it, or start with a clean slate?), and it might\n> even make sense to have a flag for users to specify.\n\nI also could probably be persuaded either way, and yes a flag is a\npossibility, though it would be nice if we could get along without it.\n\n> My hunch, at least with the developers I work with, is that they're\n> more likely to think in terms of \"I want another worktree like this\n> one, except that I'm going to change...\"\n>\n> Also, another reason to prefer copying all of core.worktree (minus the\n> always worktree-specific value of core.worktree, and core.bare), is\n> because it's easy to explain in the documentation, and I think we'd be\n> much less likely to obsolete user's knowledge over time.  (I think\n> additional sparse-checkout things, or new other features that also\n> want to be copied over, are much more likely than the addition of keys\n> that are always worktree-specific like core.worktree).\n\nAnother possible point in favor of copying all worktree-specific\nconfig to the new worktree is that if the user really does want to do\nsome configuration specific to the new worktree, then that is going to\nrequire a certain amount of manual setup after creating the new\nworktree regardless of whether we copy all worktree-specific config or\nonly a select subset (such as the sparse-checkout settings).\n"},{"id":"445342","messageId":"CAPig+cS+-L-5feyjNS5QJm-NO5Ou0EDDks_cVPGrW9_BorCv=g@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BG2Ueb9rBRNZz3Z_8V7hin5SamzOJLNpjrg0k-QGUV4zA@mail.gmail.com","subject":"Re: [PATCH v3 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-01-03T07:11:32Z","receivedAt":"2022-01-03T07:11:45Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Dec 30, 2021 at 2:29 PM Elijah Newren <newren@gmail.com> wrote:\n> On Thu, Dec 30, 2021 at 9:41 AM Derrick Stolee <stolee@gmail.com> wrote:\n> > On 12/30/2021 2:40 AM, Eric Sunshine wrote:\n> > > On Wed, Dec 29, 2021 at 4:40 AM Elijah Newren <newren@gmail.com> wrote:>\n> > >>   * If core.bare=true or core.worktree is set, then at `git worktree\n> > >> add` time, automatically run the logic you have here for\n> > >> init-worktree-config.  Having either of those config settings with\n> > >> multiple worktrees is currently broken in all git versions and likely\n> > >> in most all external tools.  As such, being aggressive in the new\n> > >> config settings to allow new versions of git to work seems totally\n> > >> safe to me -- we can't be any more broken than we already were.\n> >\n> > I'm not sure I agree with the \"currently broken in all git versions\"\n> > because when extensions.worktreeConfig is not enabled, the core.bare\n> > and core.worktree settings are ignored by the worktrees. This upgrade\n> > during 'add' is the only thing I am not so sure about.\n>\n> Oh, you're right; I mis-spoke.  If someone has core.bare=true and has\n> multiple worktrees, AND never attempts to use sparse-checkouts OR\n> otherwise set extensions.worktreeConfig, then git still works due to\n> git's special-case logic that will override core.bare in this\n> configuration.  It's just setting them up for a ticking time bomb,\n> waiting for them to either use an external tool that doesn't share\n> that special case override-core.bare logic, or for the user to decide\n> to set extensions.worktreeConfig directly or use sparse-checkouts.\n\nSo, how does this alter the proposed logic? Or does it? Does the above\ncondition get revised to:\n\n    if extensions.worktreeConfig=true and\n        (.git/config contains core.bare=true or core.worktree):\n            relocate core.bare/core.worktree to .git/config.worktree\n\nThat is, we need to relocate core.bare and core.worktree from\n.git/config to .git/config.worktree if and only if\nextensions.worktreeConfig=true (because, due to the special-case\nhandling, those two keys don't interfere with anything when\nextensions.worktreeConfig is not true).\n\nThis, of course, doesn't help the case if someone has existing\nworktrees and decides to flip extensions.worktreeConfig to true\nwithout doing the manual bookkeeping, but that case has always been\nbroken (and is documented, though not necessarily where people will\nlook). The new `git worktree add` logic, however, will fix that\nbrokenness automatically when a new worktree is added.\n\n> > >>   * If core.bare=false and core.worktree is not set, then:\n> >\n> > >>     * `git sparse-checkout {init,set}` should set\n> > >> extensions.worktreeConfig if not already set, and always set the\n> > >> core.sparse* and index.sparse settings in worktree-specific files.\n> >\n> > This should happen no matter the case of core.bare and core.worktree\n> > existing, right?\n>\n> Hmm.  I think that's safe for people who cloned and used `git worktree\n> add` with newer git versions, since `git worktree add` will have moved\n> core.bare and core.worktree to the config.worktree file when those\n> have non-default values.\n>\n> But, we might want to help out the folks who have existing repos with\n> which they have used older git versions.  So, we could have `git\n> sparse-checkout {init,set}` check for non-default values of\n> core.bare/core.worktree in the shared config file, and, if found, exit\n> with an error which point users at some relevant documentation (which\n> may just suggest 'git worktree add temporary && git worktree remove\n> temporary' as a workaround for those caught in such a state.)\n\nI'm probably missing something obvious, but rather than error out,\ncan't we just automatically relocate core.bare and core.worktree from\n.git/config to .git/config.worktree in this case?\n"},{"id":"446859","messageId":"d262a76b448a495f00f460e9ed9439a74946de49.1643136134.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v4.git.1643136134.gitgitgadget@gmail.com","subject":"[PATCH v4 2/5] worktree: create init_worktree_config()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-25T18:42:11Z","receivedAt":"2022-01-25T18:43:04Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nUpgrading a repository to use extensions.worktreeConfig is non-trivial.\nThere are several steps involved, including moving some config settings\nfrom the common config file to the main worktree's config.worktree file.\nThe previous change updated the documentation with all of these details.\n\nCommands such as 'git sparse-checkout set' upgrade the repository to use\nextensions.worktreeConfig without following these steps, causing some\nuser pain in some special cases.\n\nCreate a helper method, init_worktree_config(), that will be used in a\nlater change to fix this behavior within 'git sparse-checkout set'. The\nmethod is carefully documented in worktree.h.\n\nNote that we do _not_ upgrade the repository format version to 1 during\nthis process. The worktree config extension must be considered by Git\nand third-party tools even if core.repositoryFormatVersion is 0 for\nhistorical reasons documented in 11664196ac (\"Revert\n\"check_repository_format_gently(): refuse extensions for old\nrepositories\"\", 2020-07-15). This is a special case for this extension,\nand newer extensions (such as extensions.objectFormat) still need to\nupgrade the repository format version.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n worktree.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n worktree.h | 19 +++++++++++++++\n 2 files changed, 89 insertions(+)\n\ndiff --git a/worktree.c b/worktree.c\nindex 6f598dcfcdf..dc4ead4c8fb 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -5,6 +5,7 @@\n #include \"worktree.h\"\n #include \"dir.h\"\n #include \"wt-status.h\"\n+#include \"config.h\"\n \n void free_worktrees(struct worktree **worktrees)\n {\n@@ -826,3 +827,72 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,\n \t*wtpath = path;\n \treturn 0;\n }\n+\n+static int move_config_setting(const char *key, const char *value,\n+\t\t\t       const char *from_file, const char *to_file)\n+{\n+\tif (git_config_set_in_file_gently(to_file, key, value))\n+\t\treturn error(_(\"unable to set %s in '%s'\"), key, to_file);\n+\tif (git_config_set_in_file_gently(from_file, key, NULL))\n+\t\treturn error(_(\"unable to unset %s in '%s'\"), key, from_file);\n+\treturn 0;\n+}\n+\n+int init_worktree_config(struct repository *r)\n+{\n+\tint res = 0;\n+\tint bare = 0;\n+\tstruct config_set cs = { { 0 } };\n+\tconst char *core_worktree;\n+\tchar *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n+\tchar *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n+\n+\t/*\n+\t * If the extension is already enabled, then we can skip the\n+\t * upgrade process.\n+\t */\n+\tif (repository_format_worktree_config)\n+\t\treturn 0;\n+\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\")))\n+\t\treturn error(_(\"failed to set extensions.worktreeConfig setting\"));\n+\n+\tgit_configset_init(&cs);\n+\tgit_configset_add_file(&cs, common_config_file);\n+\n+\t/*\n+\t * If core.bare is true in the common config file, then we need to\n+\t * move it to the base worktree's config file or it will break all\n+\t * worktrees. If it is false, then leave it in place because it\n+\t * _could_ be negating a global core.bare=true.\n+\t */\n+\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n+\t\tif ((res = move_config_setting(\"core.bare\", \"true\",\n+\t\t\t\t\t       common_config_file,\n+\t\t\t\t\t       main_worktree_file)))\n+\t\t\tgoto cleanup;\n+\t}\n+\t/*\n+\t * If core.worktree is set, then the base worktree is located\n+\t * somewhere different than the parent of the common Git dir.\n+\t * Relocate that value to avoid breaking all worktrees with this\n+\t * upgrade to worktree config.\n+\t */\n+\tif (!git_configset_get_string_tmp(&cs, \"core.worktree\", &core_worktree)) {\n+\t\tif ((res = move_config_setting(\"core.worktree\", core_worktree,\n+\t\t\t\t\t       common_config_file,\n+\t\t\t\t\t       main_worktree_file)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\t/*\n+\t * Ensure that we use worktree config for the remaining lifetime\n+\t * of the current process.\n+\t */\n+\trepository_format_worktree_config = 1;\n+\n+cleanup:\n+\tgit_configset_clear(&cs);\n+\tfree(common_config_file);\n+\tfree(main_worktree_file);\n+\treturn res;\n+}\ndiff --git a/worktree.h b/worktree.h\nindex 9e06fcbdf3d..5ea5fcc3647 100644\n--- a/worktree.h\n+++ b/worktree.h\n@@ -183,4 +183,23 @@ void strbuf_worktree_ref(const struct worktree *wt,\n \t\t\t struct strbuf *sb,\n \t\t\t const char *refname);\n \n+/**\n+ * Enable worktree config for the first time. This will make the following\n+ * adjustments:\n+ *\n+ * 1. Add extensions.worktreeConfig=true in the common config file.\n+ *\n+ * 2. If the common config file has a core.worktree value or core.bare is\n+ *    set to true, then those values are moved to the main worktree's\n+ *    config.worktree file.\n+ *\n+ * If extensions.worktreeConfig is already true, then this method\n+ * terminates early without any of the above steps. The existing config\n+ * arrangement is assumed to be intentional.\n+ *\n+ * Returns 0 on success. Reports an error message and returns non-zero\n+ * if any of these steps fail.\n+ */\n+int init_worktree_config(struct repository *r);\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"446860","messageId":"pull.1101.v4.git.1643136134.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v3.git.1640727143.gitgitgadget@gmail.com","subject":"[PATCH v4 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-25T18:42:09Z","receivedAt":"2022-01-25T18:43:07Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"This series is now based on v2.35.0 since that contains all of the necessary\ntopics.\n\nThis patch series includes a fix to the bug reported by Sean Allred [1] and\ndiagnosed by Eric Sunshine [2].\n\nThe root cause is that 'git sparse-checkout init' writes to the worktree\nconfig without checking that core.bare or core.worktree are set in the\ncommon config file. This series fixes this, but also puts in place some\nhelpers to prevent this from happening in the future.\n\nATTENTION: I have significantly redesigned the series since previous\nversions, so most of this cover letter is new.\n\n * Patch 1 updates documentation around extensions.worktreeConfig in a few\n   places to improve discoverability. Several cross links are added to make\n   it easy to find the related areas. (The documentation for the changes to\n   'git sparse-checkout' are delayed to patch 4.)\n\n * Patch 2 introduces the init_worktree_config() helper which follows the\n   documented instructions to enable extensions.worktreeConfig as well as\n   move the core.bare and core.worktree config values. This update does not\n   modify core.repositoryFormatVersion, since this is not needed\n   specifically for extensions.worktreeConfig.\n\n * Patch 3 adds a new repo_config_set_worktree_gently() helper method so we\n   can internally adjust a config value within a worktree, at least if\n   extensions.worktreeConfig is enabled. (It will write to the common config\n   file if the extension is not enabled.)\n\n * Patch 4 modifies the sparse-checkout builtin to use\n   init_worktree_config() and repo_config_set_worktree_gently() in ways that\n   fix the reported bug. The behavior change here is that it will no longer\n   upgrade the repository format version, since that is not needed for\n   extensions.worktreeConfig.\n\n * Patch 5 updates 'git worktree add' to copy the worktree config from the\n   current worktree to the new one (while unsetting core.bare=true and\n   core.worktree=*) along with copying the sparse-checkout patterns file.\n\n[1]\nhttps://lore.kernel.org/git/CABceR4bZmtC4rCwgxZ1BBYZP69VOUca1f_moJoP989vTUZWu9Q@mail.gmail.com/\n[2]\nhttps://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n\n\nUpdates in v4\n=============\n\n * Rebased to v2.35.0\n * Fixed memory leak (was leaking repo_git_path() result)\n * Added additional documentation updates so curious users can discover the\n   intricacies of extensions.worktreeConfig from multiple entry points.\n * Significantly reduced the amount of changes to config.c.\n * 'git sparse-checkout' no longer upgrades the repository format.\n * Dropped the update to upgrade_repository_format(), since it is not\n   needed.\n * Dropped the 'git worktree init-worktree-config' subcommand in favor of a\n   helper method called by 'git sparse-checkout'\n * Many others because of the significant changes required by the above\n   items.\n\nThanks, -Stolee\n\nDerrick Stolee (5):\n  Documentation: add extensions.worktreeConfig details\n  worktree: create init_worktree_config()\n  config: add repo_config_set_worktree_gently()\n  sparse-checkout: set worktree-config correctly\n  worktree: copy sparse-checkout patterns and config on add\n\n Documentation/config/extensions.txt   | 31 ++++++++++++\n Documentation/git-config.txt          |  8 ++-\n Documentation/git-sparse-checkout.txt | 24 ++++++---\n Documentation/git-worktree.txt        | 11 +++--\n builtin/sparse-checkout.c             | 28 +++++------\n builtin/worktree.c                    | 60 +++++++++++++++++++++++\n config.c                              | 35 ++++++++++++--\n config.h                              |  8 +++\n sparse-index.c                        | 10 ++--\n t/t1091-sparse-checkout-builtin.sh    | 35 ++++++++++----\n t/t2400-worktree-add.sh               | 46 +++++++++++++++++-\n worktree.c                            | 70 +++++++++++++++++++++++++++\n worktree.h                            | 19 ++++++++\n 13 files changed, 336 insertions(+), 49 deletions(-)\n\n\nbase-commit: 89bece5c8c96f0b962cfc89e63f82d603fd60bed\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1101%2Fderrickstolee%2Fsparse-checkout%2Fbare-worktree-bug-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1101/derrickstolee/sparse-checkout/bare-worktree-bug-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/1101\n\nRange-diff vs v3:\n\n 1:  749ba67d21e < -:  ----------- setup: use a repository when upgrading format\n 2:  61b96937016 < -:  ----------- config: make some helpers repo-aware\n -:  ----------- > 1:  459e09dedd7 Documentation: add extensions.worktreeConfig details\n 3:  e2a0a458115 ! 2:  d262a76b448 worktree: add 'init-worktree-config' subcommand\n     @@ Metadata\n      Author: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## Commit message ##\n     -    worktree: add 'init-worktree-config' subcommand\n     +    worktree: create init_worktree_config()\n      \n     -    Some features, such as the sparse-checkout builtin, currently use the\n     -    worktree config extension. It might seem simple to upgrade the\n     -    repository format and add extensions.worktreeConfig, which is what\n     -    happens in the sparse-checkout builtin. However, this is overly\n     -    simplistic and can cause issues in some cases. We will transition away\n     -    from making this upgrade automatically, but first we will make an easy\n     -    way for users to upgrade their repositories correctly.\n     +    Upgrading a repository to use extensions.worktreeConfig is non-trivial.\n     +    There are several steps involved, including moving some config settings\n     +    from the common config file to the main worktree's config.worktree file.\n     +    The previous change updated the documentation with all of these details.\n      \n     -    Transitioning from one config file to multiple has some strange\n     -    side-effects. In particular, if the base repository is bare and the\n     -    worktree is not, Git knows to treat the worktree as non-bare as a\n     -    special case when not using worktree config. Once worktree config is\n     -    enabled, Git stops that special case since the core.bare setting could\n     -    apply at the worktree config level.\n     +    Commands such as 'git sparse-checkout set' upgrade the repository to use\n     +    extensions.worktreeConfig without following these steps, causing some\n     +    user pain in some special cases.\n      \n     -    Similarly, the core.worktree config setting is a precursor to the 'git\n     -    worktree' feature, allowing config to point to a different worktree,\n     -    presumably temporarily. This is special-cased to be ignored in a\n     -    worktree, but that case is dropped when worktree config is enabled.\n     +    Create a helper method, init_worktree_config(), that will be used in a\n     +    later change to fix this behavior within 'git sparse-checkout set'. The\n     +    method is carefully documented in worktree.h.\n      \n     -    To help resolve this transition, create the 'git worktree\n     -    init-worktree-config' helper. This new subcommand does the following:\n     +    Note that we do _not_ upgrade the repository format version to 1 during\n     +    this process. The worktree config extension must be considered by Git\n     +    and third-party tools even if core.repositoryFormatVersion is 0 for\n     +    historical reasons documented in 11664196ac (\"Revert\n     +    \"check_repository_format_gently(): refuse extensions for old\n     +    repositories\"\", 2020-07-15). This is a special case for this extension,\n     +    and newer extensions (such as extensions.objectFormat) still need to\n     +    upgrade the repository format version.\n      \n     -     1. Set core.repositoryFormatVersion to 1 in the common config file.\n     -     2. Set extensions.worktreeConfig to true in the common config file.\n     -     3. If core.bare is true in the common config file, then move that\n     -        setting to the main worktree's config file.\n     -     4. Move the core.worktree config value to the main worktree's config\n     -        file.\n     -\n     -    If the repository is already configured to use worktree config, then\n     -    none of these steps happen. This preserves any state that the user might\n     -    have created on purpose.\n     -\n     -    Update the documentation to mention this subcommand as the proper way to\n     -    upgrade to worktree config files.\n     -\n     -    To gain access to the core repository's config and config.worktree file,\n     -    we reference a repository struct's 'commondir' member. If the repository\n     -    was a submodule instead of a worktree, then this still applies\n     -    correctly.\n     -\n     -    Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n     - ## Documentation/git-worktree.txt ##\n     -@@ Documentation/git-worktree.txt: SYNOPSIS\n     - --------\n     - [verse]\n     - 'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]\n     -+'git worktree init-worktree-config'\n     - 'git worktree list' [-v | --porcelain]\n     - 'git worktree lock' [--reason <string>] <worktree>\n     - 'git worktree move' <worktree> <new-path>\n     -@@ Documentation/git-worktree.txt: checked out in the new working tree, if it's not checked out anywhere\n     - else, otherwise the command will refuse to create the working tree (unless\n     - `--force` is used).\n     - \n     -+init-worktree-config::\n     -+\n     -+Initialize config settings to enable worktree-specific config settings.\n     -+This will set `core.repositoryFormatversion=1` and enable\n     -+`extensions.worktreeConfig`, which might cause some third-party tools from\n     -+being able to operate on your repository. See CONFIGURATION FILE for more\n     -+details.\n     -+\n     - list::\n     - \n     - List details of each working tree.  The main working tree is listed first,\n     -@@ Documentation/git-worktree.txt: already present in the config file, they will be applied to the main\n     - working trees only.\n     - \n     - In order to have configuration specific to working trees, you can turn\n     --on the `worktreeConfig` extension, e.g.:\n     -+on the `worktreeConfig` extension, using this command:\n     - \n     - ------------\n     --$ git config extensions.worktreeConfig true\n     -+$ git worktree init-worktree-config\n     - ------------\n     - \n     - In this mode, specific configuration stays in the path pointed by `git\n     -@@ Documentation/git-worktree.txt: versions will refuse to access repositories with this extension.\n     - \n     - Note that in this file, the exception for `core.bare` and `core.worktree`\n     - is gone. If they exist in `$GIT_DIR/config`, you must move\n     --them to the `config.worktree` of the main working tree. You may also\n     --take this opportunity to review and move other configuration that you\n     --do not want to share to all working trees:\n     -+them to the `config.worktree` of the main working tree. These keys are\n     -+moved automatically when you use the `git worktree init-worktree-config`\n     -+command.\n     -+\n     -+You may also take this opportunity to review and move other configuration\n     -+that you do not want to share to all working trees:\n     - \n     -  - `core.worktree` and `core.bare` should never be shared\n     - \n     -\n     - ## builtin/worktree.c ##\n     + ## worktree.c ##\n      @@\n     + #include \"worktree.h\"\n     + #include \"dir.h\"\n     + #include \"wt-status.h\"\n     ++#include \"config.h\"\n       \n     - static const char * const worktree_usage[] = {\n     - \tN_(\"git worktree add [<options>] <path> [<commit-ish>]\"),\n     -+\tN_(\"git worktree init-worktree-config\"),\n     - \tN_(\"git worktree list [<options>]\"),\n     - \tN_(\"git worktree lock [<options>] <path>\"),\n     - \tN_(\"git worktree move <worktree> <new-path>\"),\n     -@@ builtin/worktree.c: static int repair(int ac, const char **av, const char *prefix)\n     - \treturn rc;\n     + void free_worktrees(struct worktree **worktrees)\n     + {\n     +@@ worktree.c: int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,\n     + \t*wtpath = path;\n     + \treturn 0;\n       }\n     - \n     ++\n      +static int move_config_setting(const char *key, const char *value,\n      +\t\t\t       const char *from_file, const char *to_file)\n      +{\n     @@ builtin/worktree.c: static int repair(int ac, const char **av, const char *prefi\n      +\treturn 0;\n      +}\n      +\n     -+static int init_worktree_config(int ac, const char **av, const char *prefix)\n     ++int init_worktree_config(struct repository *r)\n      +{\n     -+\tstruct repository *r = the_repository;\n     -+\tstruct option options[] = {\n     -+\t\tOPT_END()\n     -+\t};\n      +\tint res = 0;\n      +\tint bare = 0;\n     -+\tstruct config_set cs = { 0 };\n     ++\tstruct config_set cs = { { 0 } };\n      +\tconst char *core_worktree;\n      +\tchar *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n      +\tchar *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n      +\n     -+\t/* Report error on any arguments */\n     -+\tac = parse_options(ac, av, prefix, options, worktree_usage, 0);\n     -+\tif (ac)\n     -+\t\tusage_with_options(worktree_usage, options);\n     -+\n     -+\tgit_configset_init(&cs);\n     -+\tgit_configset_add_file(&cs, common_config_file);\n     -+\n      +\t/*\n     -+\t * If the format and extension are already enabled, then we can\n     -+\t * skip the upgrade process.\n     ++\t * If the extension is already enabled, then we can skip the\n     ++\t * upgrade process.\n      +\t */\n      +\tif (repository_format_worktree_config)\n      +\t\treturn 0;\n     ++\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\")))\n     ++\t\treturn error(_(\"failed to set extensions.worktreeConfig setting\"));\n      +\n     -+\tif (upgrade_repository_format(r, 1) < 0) {\n     -+\t\tres = error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n     -+\t\tgoto cleanup;\n     -+\t}\n     -+\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n     -+\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n     -+\t\tgoto cleanup;\n     -+\t}\n     ++\tgit_configset_init(&cs);\n     ++\tgit_configset_add_file(&cs, common_config_file);\n      +\n      +\t/*\n      +\t * If core.bare is true in the common config file, then we need to\n     @@ builtin/worktree.c: static int repair(int ac, const char **av, const char *prefi\n      +\t\t\tgoto cleanup;\n      +\t}\n      +\n     ++\t/*\n     ++\t * Ensure that we use worktree config for the remaining lifetime\n     ++\t * of the current process.\n     ++\t */\n     ++\trepository_format_worktree_config = 1;\n     ++\n      +cleanup:\n      +\tgit_configset_clear(&cs);\n      +\tfree(common_config_file);\n      +\tfree(main_worktree_file);\n      +\treturn res;\n      +}\n     -+\n     - int cmd_worktree(int ac, const char **av, const char *prefix)\n     - {\n     - \tstruct option options[] = {\n     -@@ builtin/worktree.c: int cmd_worktree(int ac, const char **av, const char *prefix)\n     - \t\tprefix = \"\";\n     - \tif (!strcmp(av[1], \"add\"))\n     - \t\treturn add(ac - 1, av + 1, prefix);\n     -+\tif (!strcmp(av[1], \"init-worktree-config\"))\n     -+\t\treturn init_worktree_config(ac - 1, av + 1, prefix);\n     - \tif (!strcmp(av[1], \"prune\"))\n     - \t\treturn prune(ac - 1, av + 1, prefix);\n     - \tif (!strcmp(av[1], \"list\"))\n      \n     - ## t/t2407-worktree-init-worktree-config.sh (new) ##\n     -@@\n     -+#!/bin/sh\n     -+\n     -+test_description='test git worktree init-worktree-config'\n     -+\n     -+. ./test-lib.sh\n     -+\n     -+test_expect_success setup '\n     -+\tgit init base &&\n     -+\ttest_commit -C base commit &&\n     -+\tgit -C base worktree add --detach worktree\n     -+'\n     -+\n     -+reset_config_when_finished () {\n     -+\ttest_when_finished git -C base config --unset core.repositoryFormatVersion &&\n     -+\ttest_when_finished git -C base config --unset extensions.worktreeConfig &&\n     -+\trm -rf base/.git/config.worktree &&\n     -+\trm -rf base/.git/worktrees/worktree/config.worktree\n     -+}\n     -+\n     -+test_expect_success 'upgrades repo format and adds extension' '\n     -+\treset_config_when_finished &&\n     -+\tgit -C base worktree init-worktree-config >out 2>err &&\n     -+\ttest_must_be_empty out &&\n     -+\ttest_must_be_empty err &&\n     -+\ttest_cmp_config -C base 1 core.repositoryFormatVersion &&\n     -+\ttest_cmp_config -C base true extensions.worktreeConfig\n     -+'\n     -+\n     -+test_expect_success 'relocates core.worktree' '\n     -+\treset_config_when_finished &&\n     -+\tmkdir dir &&\n     -+\tgit -C base config core.worktree ../../dir &&\n     -+\tgit -C base worktree init-worktree-config >out 2>err &&\n     -+\ttest_must_be_empty out &&\n     -+\ttest_must_be_empty err &&\n     -+\ttest_cmp_config -C base 1 core.repositoryFormatVersion &&\n     -+\ttest_cmp_config -C base true extensions.worktreeConfig &&\n     -+\ttest_cmp_config -C base ../../dir core.worktree &&\n     -+\ttest_must_fail git -C worktree core.worktree\n     -+'\n     -+\n     -+test_expect_success 'relocates core.bare' '\n     -+\treset_config_when_finished &&\n     -+\tgit -C base config core.bare true &&\n     -+\tgit -C base worktree init-worktree-config >out 2>err &&\n     -+\ttest_must_be_empty out &&\n     -+\ttest_must_be_empty err &&\n     -+\ttest_cmp_config -C base 1 core.repositoryFormatVersion &&\n     -+\ttest_cmp_config -C base true extensions.worktreeConfig &&\n     -+\ttest_cmp_config -C base true core.bare &&\n     -+\ttest_must_fail git -C worktree core.bare\n     -+'\n     -+\n     -+test_expect_success 'skips upgrade is already upgraded' '\n     -+\treset_config_when_finished &&\n     -+\tgit -C base worktree init-worktree-config &&\n     -+\tgit -C base config core.bare true &&\n     -+\n     -+\t# this should be a no-op, even though core.bare\n     -+\t# makes the worktree be broken.\n     -+\tgit -C base worktree init-worktree-config >out 2>err &&\n     -+\ttest_must_be_empty out &&\n     -+\ttest_must_be_empty err &&\n     -+\ttest_must_fail git -C base config --worktree core.bare &&\n     -+\tgit -C base config core.bare\n     -+'\n     -+\n     -+test_done\n     + ## worktree.h ##\n     +@@ worktree.h: void strbuf_worktree_ref(const struct worktree *wt,\n     + \t\t\t struct strbuf *sb,\n     + \t\t\t const char *refname);\n     + \n     ++/**\n     ++ * Enable worktree config for the first time. This will make the following\n     ++ * adjustments:\n     ++ *\n     ++ * 1. Add extensions.worktreeConfig=true in the common config file.\n     ++ *\n     ++ * 2. If the common config file has a core.worktree value or core.bare is\n     ++ *    set to true, then those values are moved to the main worktree's\n     ++ *    config.worktree file.\n     ++ *\n     ++ * If extensions.worktreeConfig is already true, then this method\n     ++ * terminates early without any of the above steps. The existing config\n     ++ * arrangement is assumed to be intentional.\n     ++ *\n     ++ * Returns 0 on success. Reports an error message and returns non-zero\n     ++ * if any of these steps fail.\n     ++ */\n     ++int init_worktree_config(struct repository *r);\n     ++\n     + #endif\n 4:  45316cd01c9 ! 3:  110d5e0546c config: add repo_config_set_worktree_gently()\n     @@ config.c: int git_config_set_gently(const char *key, const char *value)\n      +\t\tfree(file);\n      +\t\treturn ret;\n      +\t}\n     -+\treturn repo_config_set_gently(r, key, value);\n     ++\treturn repo_config_set_multivar_gently(r, key, value, NULL, 0);\n      +}\n      +\n       void git_config_set(const char *key, const char *value)\n       {\n     - \trepo_config_set(the_repository, key, value);\n     -@@ config.c: int repo_config_set_multivar_gently(struct repository *r, const char *key,\n     - \t\t\t\t\t\t      flags);\n     - }\n     - \n     -+int repo_config_set_gently(struct repository *r,\n     -+\t\t\t   const char *key, const char *value)\n     -+{\n     -+\treturn repo_config_set_multivar_gently(r, key, value, NULL, 0);\n     + \tgit_config_set_multivar(key, value, NULL, 0);\n     +@@ config.c: void git_config_set_multivar_in_file(const char *config_filename,\n     + int git_config_set_multivar_gently(const char *key, const char *value,\n     + \t\t\t\t   const char *value_pattern, unsigned flags)\n     + {\n     +-\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n     +-\t\t\t\t\t\t      flags);\n     ++\treturn repo_config_set_multivar_gently(the_repository, key, value,\n     ++\t\t\t\t\t       value_pattern, flags);\n      +}\n      +\n     ++int repo_config_set_multivar_gently(struct repository *r, const char *key,\n     ++\t\t\t\t    const char *value,\n     ++\t\t\t\t    const char *value_pattern, unsigned flags)\n     ++{\n     ++\tchar *file = repo_git_path(r, \"config\");\n     ++\tint res = git_config_set_multivar_in_file_gently(file,\n     ++\t\t\t\t\t\t\t key, value,\n     ++\t\t\t\t\t\t\t value_pattern,\n     ++\t\t\t\t\t\t\t flags);\n     ++\tfree(file);\n     ++\treturn res;\n     + }\n     + \n       void git_config_set_multivar(const char *key, const char *value,\n       \t\t\t     const char *value_pattern, unsigned flags)\n       {\n     +-\tgit_config_set_multivar_in_file(NULL, key, value, value_pattern,\n     ++\tgit_config_set_multivar_in_file(git_path(\"config\"),\n     ++\t\t\t\t\tkey, value, value_pattern,\n     + \t\t\t\t\tflags);\n     + }\n     + \n      \n       ## config.h ##\n      @@ config.h: void git_config_set_in_file(const char *, const char *, const char *);\n     @@ config.h: void git_config_set_in_file(const char *, const char *, const char *);\n       /**\n        * write config values to `.git/config`, takes a key/value pair as parameter.\n        */\n     -@@ config.h: int git_config_set_multivar_gently(const char *, const char *, const char *, uns\n     +@@ config.h: int git_config_parse_key(const char *, char **, size_t *);\n     + \n     + int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n       void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n     - int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n     - void repo_config_set_multivar(struct repository *, const char *, const char *, const char *, unsigned);\n     -+int repo_config_set_gently(struct repository *, const char *, const char *);\n     ++int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n       int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n       \n       /**\n 5:  b200819c1bb ! 4:  fbfaa17797c sparse-checkout: use repo_config_set_worktree_gently()\n     @@ Metadata\n      Author: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## Commit message ##\n     -    sparse-checkout: use repo_config_set_worktree_gently()\n     +    sparse-checkout: set worktree-config correctly\n      \n          The previous change added repo_config_set_worktree_gently() to assist\n     -    writing config values into the worktree.config file, if enabled.\n     +    writing config values into the config.worktree file, if enabled. An\n     +    earlier change added init_worktree_config() as a helper to initialize\n     +    extensions.worktreeConfig if not already enabled.\n      \n     -    Let the sparse-checkout builtin use this helper instead of attempting to\n     +    Let the sparse-checkout builtin use these helpers instead of attempting to\n          initialize the worktree config on its own. This changes behavior of 'git\n          sparse-checkout set' in a few important ways:\n      \n     -     1. Git will no longer upgrade the repository format and add the\n     -        worktree config extension. The user should run 'git worktree\n     -        init-worktree-config' to enable this feature.\n     +     1. Git will no longer upgrade the repository format, since this is not\n     +        a requirement for understanding extensions.worktreeConfig.\n      \n     -     2. If worktree config is disabled, then this command will set the\n     -        core.sparseCheckout (and possibly core.sparseCheckoutCone and\n     -        index.sparse) values in the common config file.\n     -\n     -     3. If the main worktree is bare, then this command will not put the\n     +     2. If the main worktree is bare, then this command will not put the\n              worktree in a broken state.\n      \n          The main reason to use worktree-specific config for the sparse-checkout\n     @@ Commit message\n          sparse-checkout patterns file, then the sparse-checkout logic will not\n          kick in on that worktree.\n      \n     -    This new logic introduces a new user pattern that could lead to some\n     -    confusion. Suppose a user has not upgraded to worktree config and\n     -    follows these steps in order:\n     -\n     -     1. Enable sparse-checkout in a worktree.\n     -\n     -     2. Disable sparse-checkout in that worktree without deleting that\n     -        worktree's sparse-checkout file.\n     -\n     -     3. Enable sparse-checkout in another worktree.\n     -\n     -    After these steps, the first worktree will have sparse-checkout enabled\n     -    with whatever patterns exist. The worktree does not immediately have\n     -    those patterns applied, but a variety of Git commands would apply the\n     -    sparse-checkout patterns and update the worktree state to reflect those\n     -    patterns. This situation is likely very rare and the workaround is to\n     -    upgrade to worktree specific config on purpose. Users already in this\n     -    state used the sparse-checkout builtin with a version that upgraded to\n     -    worktree config, anyway.\n     -\n          Reported-by: Sean Allred <allred.sean@gmail.com>\n          Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n     + ## Documentation/git-sparse-checkout.txt ##\n     +@@ Documentation/git-sparse-checkout.txt: COMMANDS\n     + \tDescribe the patterns in the sparse-checkout file.\n     + \n     + 'set'::\n     +-\tEnable the necessary config settings\n     +-\t(extensions.worktreeConfig, core.sparseCheckout,\n     +-\tcore.sparseCheckoutCone) if they are not already enabled, and\n     +-\twrite a set of patterns to the sparse-checkout file from the\n     +-\tlist of arguments following the 'set' subcommand. Update the\n     +-\tworking directory to match the new patterns.\n     ++\tEnable the necessary sparse-checkout config settings\n     ++\t(`core.sparseCheckout` and possibly `core.sparseCheckoutCone`) if\n     ++\tthey are not already enabled, and write a set of patterns to the\n     ++\tsparse-checkout file from the list of arguments following the\n     ++\t'set' subcommand. Update the working directory to match the new\n     ++\tpatterns.\n     +++\n     ++To ensure that adjusting the sparse-checkout settings within a worktree\n     ++does not alter the sparse-checkout settings in other worktrees, the 'set'\n     ++subcommand will upgrade your repository config to use worktree-specific\n     ++config if not already present. The sparsity defined by the arguments to\n     ++the 'set' subcommand are stored in the worktree-specific sparse-checkout\n     ++file. See linkgit:git-worktree[1] and the documentation of\n     ++`extensions.worktreeConfig` in linkgit:git-config[1] for more details.\n     + +\n     + When the `--stdin` option is provided, the patterns are read from\n     + standard in as a newline-delimited list instead of from the arguments.\n     +@@ Documentation/git-sparse-checkout.txt: interact with your repository until it is disabled.\n     + \tBy default, these patterns are read from the command-line arguments,\n     + \tbut they can be read from stdin using the `--stdin` option. When\n     + \t`core.sparseCheckoutCone` is enabled, the given patterns are interpreted\n     +-\tas directory names as in the 'set' subcommand.\n     ++\tas directory names as in the 'set' subcommand. The sparsity defined\n     ++\tby the arguments to the 'add' subcommand are added to the patterns\n     ++\tin the worktree-specific sparse-checkout file.\n     + \n     + 'reapply'::\n     + \tReapply the sparsity pattern rules to paths in the working tree.\n     +\n       ## builtin/sparse-checkout.c ##\n     +@@\n     + #include \"wt-status.h\"\n     + #include \"quote.h\"\n     + #include \"sparse-index.h\"\n     ++#include \"worktree.h\"\n     + \n     + static const char *empty_base = \"\";\n     + \n      @@ builtin/sparse-checkout.c: enum sparse_checkout_mode {\n       \n       static int set_config(enum sparse_checkout_mode mode)\n       {\n      -\tconst char *config_path;\n      -\n     --\tif (upgrade_repository_format(the_repository, 1) < 0)\n     +-\tif (upgrade_repository_format(1) < 0)\n      -\t\tdie(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n      -\tif (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n      -\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n     -+\tif (repo_config_set_worktree_gently(the_repository,\n     -+\t\t\t\t\t    \"core.sparseCheckout\",\n     -+\t\t\t\t\t    mode ? \"true\" : \"false\") ||\n     -+\t    repo_config_set_worktree_gently(the_repository,\n     -+\t\t\t\t\t    \"core.sparseCheckoutCone\",\n     -+\t\t\t\t\t    mode == MODE_CONE_PATTERNS ?\n     -+\t\t\t\t\t\t\"true\" : \"false\"))\n     ++\t/* Update to use worktree config, if not already. */\n     ++\tif (init_worktree_config(the_repository)) {\n     ++\t\terror(_(\"failed to initialize worktree config\"));\n       \t\treturn 1;\n     --\t}\n     --\n     + \t}\n     + \n      -\tconfig_path = git_path(\"config.worktree\");\n      -\tgit_config_set_in_file_gently(config_path,\n      -\t\t\t\t      \"core.sparseCheckout\",\n     @@ builtin/sparse-checkout.c: enum sparse_checkout_mode {\n      -\tgit_config_set_in_file_gently(config_path,\n      -\t\t\t\t      \"core.sparseCheckoutCone\",\n      -\t\t\t\t      mode == MODE_CONE_PATTERNS ? \"true\" : NULL);\n     ++\tif (repo_config_set_worktree_gently(the_repository,\n     ++\t\t\t\t\t    \"core.sparseCheckout\",\n     ++\t\t\t\t\t    mode ? \"true\" : \"false\") ||\n     ++\t    repo_config_set_worktree_gently(the_repository,\n     ++\t\t\t\t\t    \"core.sparseCheckoutCone\",\n     ++\t\t\t\t\t    mode == MODE_CONE_PATTERNS ?\n     ++\t\t\t\t\t\t\"true\" : \"false\"))\n     ++\t\treturn 1;\n       \n       \tif (mode == MODE_NO_PATTERNS)\n      -\t\tset_sparse_index_config(the_repository, 0);\n     @@ sparse-index.c: static int convert_to_sparse_rec(struct index_state *istate,\n       \treturn res;\n      \n       ## t/t1091-sparse-checkout-builtin.sh ##\n     -@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'interaction with clone --no-checkout (unborn index)' '\n     - '\n     - \n     - test_expect_success 'set enables config' '\n     --\tgit init empty-config &&\n     -+\tgit init initial-config &&\n     - \t(\n     --\t\tcd empty-config &&\n     -+\t\tcd initial-config &&\n     -+\t\ttest_commit file file &&\n     -+\t\tmkdir dir &&\n     -+\t\ttest_commit dir dir/file &&\n     -+\t\tgit worktree add --detach ../initial-worktree &&\n     -+\t\tgit sparse-checkout set --cone\n     -+\t) &&\n     -+\ttest_cmp_config -C initial-config true core.sparseCheckout &&\n     -+\ttest_cmp_config -C initial-worktree true core.sparseCheckout &&\n     -+\ttest_cmp_config -C initial-config true core.sparseCheckoutCone &&\n     -+\ttest_cmp_config -C initial-worktree true core.sparseCheckoutCone &&\n     -+\n     -+\t# initial-config has a sparse-checkout file\n     -+\t# that only contains files at root.\n     -+\tls initial-config >only-file &&\n     -+\tcat >expect <<-EOF &&\n     -+\tfile\n     -+\tEOF\n     -+\ttest_cmp expect only-file &&\n     -+\n     -+\t# initial-worktree does not have its own sparse-checkout\n     -+\t# file, so the repply does not modify the worktree at all.\n     -+\tgit -C initial-worktree sparse-checkout reapply &&\n     -+\tls initial-worktree >all &&\n     -+\tcat >expect <<-EOF &&\n     -+\tdir\n     -+\tfile\n     -+\tEOF\n     -+\ttest_cmp expect all\n     -+'\n     -+\n     -+test_expect_success 'set enables worktree config, if enabled' '\n     -+\tgit init worktree-config &&\n     -+\t(\n     -+\t\tcd worktree-config &&\n     - \t\ttest_commit test file &&\n     --\t\ttest_path_is_missing .git/config.worktree &&\n     --\t\tgit sparse-checkout set nothing &&\n     --\t\ttest_path_is_file .git/config.worktree &&\n     --\t\ttest_cmp_config true core.sparseCheckout\n     --\t)\n     -+\t\tgit worktree add --detach ../worktree-config2 &&\n     -+\t\tgit worktree init-worktree-config &&\n     -+\t\tgit sparse-checkout set --cone &&\n     -+\t\tgit config --worktree core.sparseCheckout &&\n     -+\t\tgit config --worktree core.sparseCheckoutCone\n     -+\t) &&\n     -+\ttest_cmp_config -C worktree-config true core.sparseCheckout &&\n     -+\ttest_must_fail git -C worktree-config2 core.sparseCheckout &&\n     -+\ttest_cmp_config -C worktree-config true core.sparseCheckoutCone &&\n     -+\ttest_must_fail git -C worktree-config2 core.sparseCheckoutCone\n     - '\n     - \n     - test_expect_success 'set sparse-checkout using builtin' '\n     -@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'add to sparse-checkout' '\n     - '\n     - \n     - test_expect_success 'cone mode: match patterns' '\n     -+\tgit -C repo worktree init-worktree-config &&\n     - \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n     - \trm -rf repo/a repo/folder1 repo/folder2 &&\n     - \tgit -C repo read-tree -mu HEAD 2>err &&\n     +@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'switching to cone mode with non-cone mode patterns' '\n     + \t\tcd bad-patterns &&\n     + \t\tgit sparse-checkout init &&\n     + \t\tgit sparse-checkout add dir &&\n     +-\t\tgit config core.sparseCheckoutCone true &&\n     ++\t\tgit config --worktree core.sparseCheckoutCone true &&\n     + \t\ttest_must_fail git sparse-checkout add dir 2>err &&\n     + \t\tgrep \"existing sparse-checkout patterns do not use cone mode\" err\n     + \t)\n      @@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'sparse-index enabled and disabled' '\n     - \t\ttest-tool -C repo read-cache --table >cache &&\n     - \t\t! grep \" tree \" cache &&\n     + \t\ttest_cmp expect actual &&\n     + \n       \t\tgit -C repo config --list >config &&\n      -\t\t! grep index.sparse config\n      +\t\ttest_cmp_config -C repo false index.sparse\n       \t)\n       '\n       \n     -@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'fail when lock is taken' '\n     - '\n     - \n     - test_expect_success '.gitignore should not warn about cone mode' '\n     -+\tgit -C repo worktree init-worktree-config &&\n     - \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n     - \techo \"**/bin/*\" >repo/.gitignore &&\n     - \tgit -C repo reset --hard 2>err &&\n 6:  fcece09546c ! 5:  bb9e550ff3d worktree: copy sparse-checkout patterns and config on add\n     @@ Commit message\n          In addition to the sparse-checkout file, copy the worktree config file\n          if worktree config is enabled and the file exists. This will copy over\n          any important settings to ensure the new worktree behaves the same as\n     -    the current one.\n     +    the current one. The only exception we must continue to make is that\n     +    core.bare and core.worktree should become unset in the worktree's config\n     +    file.\n      \n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n     @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam\n      +\t}\n      +\n      +\t/*\n     -+\t * If we are using worktree config, then copy all currenct config\n     ++\t * If we are using worktree config, then copy all current config\n      +\t * values from the current worktree into the new one, that way the\n      +\t * new worktree behaves the same as this one.\n      +\t */\n     @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam\n      +\t\t\t\t\trealpath.buf, name);\n      +\n      +\t\tif (file_exists(from_file)) {\n     ++\t\t\tstruct config_set cs = { { 0 }};\n     ++\t\t\tconst char *str_value;\n     ++\t\t\tint bool_value;\n     ++\n      +\t\t\tif (safe_create_leading_directories(to_file) ||\n      +\t\t\t    copy_file(to_file, from_file, 0666))\n     -+\t\t\t\terror(_(\"failed to copy worktree config from '%s' to '%s'\"),\n     -+\t\t\t\t      from_file, to_file);\n     ++\t\t\t\tdie(_(\"failed to copy worktree config from '%s' to '%s'\"),\n     ++\t\t\t\t    from_file, to_file);\n     ++\n     ++\t\t\tgit_configset_init(&cs);\n     ++\t\t\tgit_configset_add_file(&cs, from_file);\n     ++\n     ++\t\t\tif (!git_configset_get_bool(&cs, \"core.bare\", &bool_value) &&\n     ++\t\t\t    bool_value &&\n     ++\t\t\t    git_config_set_multivar_in_file_gently(\n     ++\t\t\t\t\tto_file, \"core.bare\", NULL, \"true\", 0))\n     ++\t\t\t\terror(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n     ++\t\t\tif (!git_configset_get_value(&cs, \"core.worktree\", &str_value) &&\n     ++\t\t\t    git_config_set_in_file_gently(to_file,\n     ++\t\t\t\t\t\t\t  \"core.worktree\", NULL))\n     ++\t\t\t\terror(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n     ++\n     ++\t\t\tgit_configset_clear(&cs);\n      +\t\t}\n      +\n      +\t\tfree(from_file);\n     @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam\n       \tcp.git_cmd = 1;\n      \n       ## t/t1091-sparse-checkout-builtin.sh ##\n     -@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'set enables config' '\n     +@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'interaction with clone --no-checkout (unborn index)' '\n       '\n       \n     - test_expect_success 'set enables worktree config, if enabled' '\n     -+\tgit init worktree-patterns &&\n     -+\t(\n     -+\t\tcd worktree-patterns &&\n     -+\t\ttest_commit test file &&\n     -+\t\tmkdir dir dir2 &&\n     -+\t\ttest_commit dir dir/file &&\n     -+\t\ttest_commit dir2 dir2/file &&\n     -+\n     -+\t\t# By initializing the worktree config here...\n     -+\t\tgit worktree init-worktree-config &&\n     -+\n     -+\t\t# This set command places config values in worktree-\n     -+\t\t# specific config...\n     -+\t\tgit sparse-checkout set --cone dir &&\n     -+\n     -+\t\t# Which must be copied, along with the sparse-checkout\n     -+\t\t# patterns, here.\n     -+\t\tgit worktree add --detach ../worktree-patterns2\n     -+\t) &&\n     -+\ttest_cmp_config -C worktree-patterns true core.sparseCheckout &&\n     -+\ttest_cmp_config -C worktree-patterns2 true core.sparseCheckout &&\n     -+\ttest_cmp_config -C worktree-patterns true core.sparseCheckoutCone &&\n     -+\ttest_cmp_config -C worktree-patterns2 true core.sparseCheckoutCone &&\n     -+\ttest_cmp worktree-patterns/.git/info/sparse-checkout \\\n     -+\t\t worktree-patterns/.git/worktrees/worktree-patterns2/info/sparse-checkout &&\n     -+\n     -+\tls worktree-patterns >expect &&\n     -+\tls worktree-patterns2 >actual &&\n     -+\ttest_cmp expect actual &&\n     -+\n     -+\t# Double check that the copy works from a non-main worktree.\n     -+\t(\n     -+\t\tcd worktree-patterns2 &&\n     -+\t\tgit sparse-checkout set dir2 &&\n     -+\t\tgit worktree add --detach ../worktree-patterns3\n     -+\t) &&\n     -+\ttest_cmp_config -C worktree-patterns3 true core.sparseCheckout &&\n     -+\ttest_cmp_config -C worktree-patterns3 true core.sparseCheckoutCone &&\n     -+\ttest_cmp worktree-patterns/.git/worktrees/worktree-patterns2/info/sparse-checkout \\\n     -+\t\t worktree-patterns/.git/worktrees/worktree-patterns3/info/sparse-checkout &&\n     -+\n     -+\tls worktree-patterns2 >expect &&\n     -+\tls worktree-patterns3 >actual &&\n     -+\ttest_cmp expect actual\n     + test_expect_success 'set enables config' '\n     +-\tgit init empty-config &&\n     ++\tgit init worktree-config &&\n     + \t(\n     +-\t\tcd empty-config &&\n     ++\t\tcd worktree-config &&\n     + \t\ttest_commit test file &&\n     + \t\ttest_path_is_missing .git/config.worktree &&\n     + \t\tgit sparse-checkout set nothing &&\n     +@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'add to sparse-checkout' '\n     + \tcheck_files repo \"a folder1 folder2\"\n     + '\n     + \n     ++test_expect_success 'worktree: add copies sparse-checkout patterns' '\n     ++\tcat repo/.git/info/sparse-checkout >old &&\n     ++\ttest_when_finished cp old repo/.git/info/sparse-checkout &&\n     ++\ttest_when_finished git -C repo worktree remove ../worktree &&\n     ++\tgit -C repo sparse-checkout set \"/*\" &&\n     ++\tgit -C repo worktree add --quiet ../worktree 2>err &&\n     ++\ttest_must_be_empty err &&\n     ++\tnew=repo/.git/worktrees/worktree/info/sparse-checkout &&\n     ++\ttest_path_is_file $new &&\n     ++\ttest_cmp repo/.git/info/sparse-checkout $new &&\n     ++\tgit -C worktree sparse-checkout set --cone &&\n     ++\ttest_cmp_config -C worktree true core.sparseCheckoutCone &&\n     ++\ttest_must_fail git -C repo core.sparseCheckoutCone\n      +'\n      +\n     -+test_expect_success 'worktree add copies sparse-checkout patterns' '\n     - \tgit init worktree-config &&\n     - \t(\n     - \t\tcd worktree-config &&\n     + test_expect_success 'cone mode: match patterns' '\n     + \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n     + \trm -rf repo/a repo/folder1 repo/folder2 &&\n      @@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'interaction with submodules' '\n     + '\n       \n       test_expect_success 'different sparse-checkouts with worktrees' '\n     ++\tgit -C repo sparse-checkout set --cone deep folder1 &&\n       \tgit -C repo worktree add --detach ../worktree &&\n      -\tcheck_files worktree \"a deep folder1 folder2\" &&\n     -+\tcheck_files worktree \"a folder1\" &&\n     - \tgit -C worktree sparse-checkout init --cone &&\n     +-\tgit -C worktree sparse-checkout init --cone &&\n      -\tgit -C repo sparse-checkout set folder1 &&\n     -+\tgit -C repo sparse-checkout set folder1 folder2 &&\n     - \tgit -C worktree sparse-checkout set deep/deeper1 &&\n     +-\tgit -C worktree sparse-checkout set deep/deeper1 &&\n      -\tcheck_files repo a folder1 &&\n     -+\tcheck_files repo a folder1 folder2 &&\n     - \tcheck_files worktree a deep\n     +-\tcheck_files worktree a deep\n     ++\tcheck_files worktree \"a deep folder1\" &&\n     ++\tgit -C repo sparse-checkout set --cone folder1 &&\n     ++\tgit -C worktree sparse-checkout set --cone deep/deeper1 &&\n     ++\tcheck_files repo \"a folder1\" &&\n     ++\tcheck_files worktree \"a deep\"\n     + '\n     + \n     + test_expect_success 'set using filename keeps file on-disk' '\n     +\n     + ## t/t2400-worktree-add.sh ##\n     +@@ t/t2400-worktree-add.sh: test_expect_success '\"add\" default branch of a bare repo' '\n     + \t(\n     + \t\tgit clone --bare . bare2 &&\n     + \t\tcd bare2 &&\n     +-\t\tgit worktree add ../there3 main\n     +-\t)\n     ++\t\tgit worktree add ../there3 main &&\n     ++\t\tcd ../there3 &&\n     ++\t\tgit status\n     ++\t) &&\n     ++\tcat >expect <<-EOF &&\n     ++\tinit.t\n     ++\tEOF\n     ++\tls there3 >actual &&\n     ++\ttest_cmp expect actual\n     ++'\n     ++\n     ++test_expect_success '\"add\" to bare repo with worktree config' '\n     ++\t(\n     ++\t\tgit clone --bare . bare3 &&\n     ++\t\tcd bare3 &&\n     ++\t\tgit config extensions.worktreeconfig true &&\n     ++\t\tgit config --worktree core.bare true &&\n     ++\t\tgit config --worktree core.worktree \"$(pwd)\" &&\n     ++\t\tgit config --worktree bogus.key value &&\n     ++\t\tgit config --unset core.bare &&\n     ++\t\tgit worktree add ../there4 main &&\n     ++\t\tcd ../there4 &&\n     ++\t\tgit status &&\n     ++\t\tgit worktree add --detach ../there5 &&\n     ++\t\tcd ../there5 &&\n     ++\t\tgit status\n     ++\t) &&\n     ++\n     ++\t# the worktree has the arbitrary value copied.\n     ++\ttest_cmp_config -C there4 value bogus.key &&\n     ++\ttest_cmp_config -C there5 value bogus.key &&\n     ++\n     ++\t# however, core.bare and core.worktree were removed.\n     ++\ttest_must_fail git -C there4 config core.bare &&\n     ++\ttest_must_fail git -C there4 config core.worktree &&\n     ++\n     ++\tcat >expect <<-EOF &&\n     ++\tinit.t\n     ++\tEOF\n     ++\n     ++\tls there4 >actual &&\n     ++\ttest_cmp expect actual &&\n     ++\tls there5 >actual &&\n     ++\ttest_cmp expect actual\n       '\n       \n     + test_expect_success 'checkout with grafts' '\n\n-- \ngitgitgadget\n"},{"id":"446861","messageId":"110d5e0546c76848d6e942a6c9b69d5146b0e1b3.1643136134.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v4.git.1643136134.gitgitgadget@gmail.com","subject":"[PATCH v4 3/5] config: add repo_config_set_worktree_gently()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-25T18:42:12Z","receivedAt":"2022-01-25T18:43:11Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nSome config settings, such as those for sparse-checkout, are likely\nintended to only apply to one worktree at a time. To make this write\neasier, add a new config API method, repo_config_set_worktree_gently().\n\nThis method will attempt to write to the worktree-specific config, but\nwill instead write to the common config file if worktree config is not\nenabled.  The next change will introduce a consumer of this method.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n config.c | 35 ++++++++++++++++++++++++++++++++---\n config.h |  8 ++++++++\n 2 files changed, 40 insertions(+), 3 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex 2bffa8d4a01..1a03ced1a54 100644\n--- a/config.c\n+++ b/config.c\n@@ -21,6 +21,7 @@\n #include \"dir.h\"\n #include \"color.h\"\n #include \"refs.h\"\n+#include \"worktree.h\"\n \n struct config_source {\n \tstruct config_source *prev;\n@@ -2884,6 +2885,20 @@ int git_config_set_gently(const char *key, const char *value)\n \treturn git_config_set_multivar_gently(key, value, NULL, 0);\n }\n \n+int repo_config_set_worktree_gently(struct repository *r,\n+\t\t\t\t    const char *key, const char *value)\n+{\n+\t/* Only use worktree-specific config if it is is already enabled. */\n+\tif (repository_format_worktree_config) {\n+\t\tchar *file = repo_git_path(r, \"config.worktree\");\n+\t\tint ret = git_config_set_multivar_in_file_gently(\n+\t\t\t\t\tfile, key, value, NULL, 0);\n+\t\tfree(file);\n+\t\treturn ret;\n+\t}\n+\treturn repo_config_set_multivar_gently(r, key, value, NULL, 0);\n+}\n+\n void git_config_set(const char *key, const char *value)\n {\n \tgit_config_set_multivar(key, value, NULL, 0);\n@@ -3181,14 +3196,28 @@ void git_config_set_multivar_in_file(const char *config_filename,\n int git_config_set_multivar_gently(const char *key, const char *value,\n \t\t\t\t   const char *value_pattern, unsigned flags)\n {\n-\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n-\t\t\t\t\t\t      flags);\n+\treturn repo_config_set_multivar_gently(the_repository, key, value,\n+\t\t\t\t\t       value_pattern, flags);\n+}\n+\n+int repo_config_set_multivar_gently(struct repository *r, const char *key,\n+\t\t\t\t    const char *value,\n+\t\t\t\t    const char *value_pattern, unsigned flags)\n+{\n+\tchar *file = repo_git_path(r, \"config\");\n+\tint res = git_config_set_multivar_in_file_gently(file,\n+\t\t\t\t\t\t\t key, value,\n+\t\t\t\t\t\t\t value_pattern,\n+\t\t\t\t\t\t\t flags);\n+\tfree(file);\n+\treturn res;\n }\n \n void git_config_set_multivar(const char *key, const char *value,\n \t\t\t     const char *value_pattern, unsigned flags)\n {\n-\tgit_config_set_multivar_in_file(NULL, key, value, value_pattern,\n+\tgit_config_set_multivar_in_file(git_path(\"config\"),\n+\t\t\t\t\tkey, value, value_pattern,\n \t\t\t\t\tflags);\n }\n \ndiff --git a/config.h b/config.h\nindex f119de01309..1d98ad269bd 100644\n--- a/config.h\n+++ b/config.h\n@@ -253,6 +253,13 @@ void git_config_set_in_file(const char *, const char *, const char *);\n \n int git_config_set_gently(const char *, const char *);\n \n+/**\n+ * Write a config value that should apply to the current worktree. If\n+ * extensions.worktreeConfig is enabled, then the write will happen in the\n+ * current worktree's config. Otherwise, write to the common config file.\n+ */\n+int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n+\n /**\n  * write config values to `.git/config`, takes a key/value pair as parameter.\n  */\n@@ -281,6 +288,7 @@ int git_config_parse_key(const char *, char **, size_t *);\n \n int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n+int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n \n /**\n-- \ngitgitgadget\n\n"},{"id":"446862","messageId":"459e09dedd71fcb64b9796bac52c3812d78f2405.1643136134.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v4.git.1643136134.gitgitgadget@gmail.com","subject":"[PATCH v4 1/5] Documentation: add extensions.worktreeConfig details","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-25T18:42:10Z","receivedAt":"2022-01-25T18:43:15Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe extensions.worktreeConfig extension was added in 58b284a (worktree:\nadd per-worktree config files, 2018-10-21) and was somewhat documented\nin Documentation/git-config.txt. However, the extensions.worktreeConfig\nvalue was not specified further in the list of possible config keys. The\nlocation of the config.worktree file is not specified, and there are\nsome precautions that should be mentioned clearly, but are only\nmentioned in git-worktree.txt.\n\nExpand the documentation to help users discover the complexities of\nextensions.worktreeConfig by adding details and cross links in these\nlocations (relative to Documentation/):\n\n- config/extensions.txt\n- git-config.txt\n- git-worktree.txt\n\nThe updates focus on items such as\n\n* $GIT_DIR/config.worktree takes precedence over $GIT_COMMON_DIR/config.\n\n* The core.worktree and core.bare=true settings are incorrect to have in\n  the common config file when extensions.worktreeConfig is enabled.\n\n* The sparse-checkout settings core.sparseCheckout[Cone] are recommended\n  to be set in the worktree config.\n\nAs documented in 11664196ac (\"Revert \"check_repository_format_gently():\nrefuse extensions for old repositories\"\", 2020-07-15), this extension\nmust be considered regardless of the repository format version for\nhistorical reasons.\n\nA future change will update references to extensions.worktreeConfig\nwithin git-sparse-checkout.txt, but a behavior change is needed before\nmaking those updates.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/extensions.txt | 31 +++++++++++++++++++++++++++++\n Documentation/git-config.txt        |  8 ++++++--\n Documentation/git-worktree.txt      | 11 +++++++---\n 3 files changed, 45 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt\nindex 4e23d73cdca..5999dcb2a1f 100644\n--- a/Documentation/config/extensions.txt\n+++ b/Documentation/config/extensions.txt\n@@ -6,3 +6,34 @@ extensions.objectFormat::\n Note that this setting should only be set by linkgit:git-init[1] or\n linkgit:git-clone[1].  Trying to change it after initialization will not\n work and will produce hard-to-diagnose issues.\n+\n+extensions.worktreeConfig::\n+\tIf enabled, then worktrees will load config settings from the\n+\t`$GIT_DIR/config.worktree` file in addition to the\n+\t`$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and\n+\t`$GIT_DIR` are the same for the main worktree, while other\n+\tworktrees have `$GIT_DIR` equal to\n+\t`$GIT_COMMON_DIR/worktrees/<worktree-name>/`. The settings in the\n+\t`config.worktree` file will override settings from any other\n+\tconfig files.\n++\n+When enabling `extensions.worktreeConfig`, you must be careful to move\n+certain values from the common config file to the main worktree's\n+`config.worktree` file, if present:\n++\n+* `core.worktree` must be moved from `$GIT_COMMON_DIR/config` to\n+  `$GIT_COMMON_DIR/config.worktree`.\n+* If `core.bare` is true, then it must be moved from `$GIT_COMMON_DIR/config`\n+  to `$GIT_COMMON_DIR/config.worktree`.\n++\n+It may also be beneficial to adjust the locations of `core.sparseCheckout`\n+and `core.sparseCheckoutCone` depending on your desire for customizable\n+sparse-checkout settings for each worktree. By default, the `git\n+sparse-checkout` builtin enables `extensions.worktreeConfig`, assigns\n+these config values on a per-worktree basis, and uses the\n+`$GIT_DIR/info/sparse-checkout` file to specify the sparsity for each\n+worktree independently. See linkgit:git-sparse-checkout[1] for more\n+details.\n++\n+For historical reasons, `extensions.worktreeConfig` is respected\n+regardless of the `core.repositoryFormatVersion` setting.\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 2285effb363..95cefd5e399 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -141,9 +141,13 @@ from all available files.\n See also <<FILES>>.\n \n --worktree::\n-\tSimilar to `--local` except that `.git/config.worktree` is\n+\tSimilar to `--local` except that `$GIT_DIR/config.worktree` is\n \tread from or written to if `extensions.worktreeConfig` is\n-\tpresent. If not it's the same as `--local`.\n+\tenabled. If not it's the same as `--local`. Note that `$GIT_DIR`\n+\tis equal to `$GIT_COMMON_DIR` for the main worktree, but is of the\n+\tform `.git/worktrees/<worktree-name>/` for other worktrees. See\n+\tlinkgit:git-worktree[1] to learn how to enable\n+\t`extensions.worktreeConfig`.\n \n -f <config-file>::\n --file <config-file>::\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex 9e862fbcf79..ea0ee9f8bb5 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -286,8 +286,8 @@ CONFIGURATION FILE\n ------------------\n By default, the repository `config` file is shared across all working\n trees. If the config variables `core.bare` or `core.worktree` are\n-already present in the config file, they will be applied to the main\n-working trees only.\n+present in the common config file and `extensions.worktreeConfig` is\n+disabled, then they will be applied to the main working trees only.\n \n In order to have configuration specific to working trees, you can turn\n on the `worktreeConfig` extension, e.g.:\n@@ -307,11 +307,16 @@ them to the `config.worktree` of the main working tree. You may also\n take this opportunity to review and move other configuration that you\n do not want to share to all working trees:\n \n- - `core.worktree` and `core.bare` should never be shared\n+ - `core.worktree` should never be shared.\n+\n+ - `core.bare` should not be shared unless the value is `core.bare=false`.\n \n  - `core.sparseCheckout` is recommended per working tree, unless you\n    are sure you always use sparse checkout for all working trees.\n \n+See the documentation of `extensions.worktreeConfig` in\n+linkgit:git-config[1] for more details.\n+\n DETAILS\n -------\n Each linked working tree has a private sub-directory in the repository's\n-- \ngitgitgadget\n\n"},{"id":"446863","messageId":"fbfaa17797c023f4a86eeac200a83c2e1954daf5.1643136134.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v4.git.1643136134.gitgitgadget@gmail.com","subject":"[PATCH v4 4/5] sparse-checkout: set worktree-config correctly","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-25T18:42:13Z","receivedAt":"2022-01-25T18:43:28Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe previous change added repo_config_set_worktree_gently() to assist\nwriting config values into the config.worktree file, if enabled. An\nearlier change added init_worktree_config() as a helper to initialize\nextensions.worktreeConfig if not already enabled.\n\nLet the sparse-checkout builtin use these helpers instead of attempting to\ninitialize the worktree config on its own. This changes behavior of 'git\nsparse-checkout set' in a few important ways:\n\n 1. Git will no longer upgrade the repository format, since this is not\n    a requirement for understanding extensions.worktreeConfig.\n\n 2. If the main worktree is bare, then this command will not put the\n    worktree in a broken state.\n\nThe main reason to use worktree-specific config for the sparse-checkout\nbuiltin was to avoid enabling sparse-checkout patterns in one and\ncausing a loss of files in another. If a worktree does not have a\nsparse-checkout patterns file, then the sparse-checkout logic will not\nkick in on that worktree.\n\nReported-by: Sean Allred <allred.sean@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/git-sparse-checkout.txt | 24 ++++++++++++++++-------\n builtin/sparse-checkout.c             | 28 +++++++++++++--------------\n sparse-index.c                        | 10 +++-------\n t/t1091-sparse-checkout-builtin.sh    |  4 ++--\n 4 files changed, 35 insertions(+), 31 deletions(-)\n\ndiff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt\nindex b81dbe06543..c6eae3ec7fd 100644\n--- a/Documentation/git-sparse-checkout.txt\n+++ b/Documentation/git-sparse-checkout.txt\n@@ -31,12 +31,20 @@ COMMANDS\n \tDescribe the patterns in the sparse-checkout file.\n \n 'set'::\n-\tEnable the necessary config settings\n-\t(extensions.worktreeConfig, core.sparseCheckout,\n-\tcore.sparseCheckoutCone) if they are not already enabled, and\n-\twrite a set of patterns to the sparse-checkout file from the\n-\tlist of arguments following the 'set' subcommand. Update the\n-\tworking directory to match the new patterns.\n+\tEnable the necessary sparse-checkout config settings\n+\t(`core.sparseCheckout` and possibly `core.sparseCheckoutCone`) if\n+\tthey are not already enabled, and write a set of patterns to the\n+\tsparse-checkout file from the list of arguments following the\n+\t'set' subcommand. Update the working directory to match the new\n+\tpatterns.\n++\n+To ensure that adjusting the sparse-checkout settings within a worktree\n+does not alter the sparse-checkout settings in other worktrees, the 'set'\n+subcommand will upgrade your repository config to use worktree-specific\n+config if not already present. The sparsity defined by the arguments to\n+the 'set' subcommand are stored in the worktree-specific sparse-checkout\n+file. See linkgit:git-worktree[1] and the documentation of\n+`extensions.worktreeConfig` in linkgit:git-config[1] for more details.\n +\n When the `--stdin` option is provided, the patterns are read from\n standard in as a newline-delimited list instead of from the arguments.\n@@ -73,7 +81,9 @@ interact with your repository until it is disabled.\n \tBy default, these patterns are read from the command-line arguments,\n \tbut they can be read from stdin using the `--stdin` option. When\n \t`core.sparseCheckoutCone` is enabled, the given patterns are interpreted\n-\tas directory names as in the 'set' subcommand.\n+\tas directory names as in the 'set' subcommand. The sparsity defined\n+\tby the arguments to the 'add' subcommand are added to the patterns\n+\tin the worktree-specific sparse-checkout file.\n \n 'reapply'::\n \tReapply the sparsity pattern rules to paths in the working tree.\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 679c1070368..314c8d61f80 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -15,6 +15,7 @@\n #include \"wt-status.h\"\n #include \"quote.h\"\n #include \"sparse-index.h\"\n+#include \"worktree.h\"\n \n static const char *empty_base = \"\";\n \n@@ -359,26 +360,23 @@ enum sparse_checkout_mode {\n \n static int set_config(enum sparse_checkout_mode mode)\n {\n-\tconst char *config_path;\n-\n-\tif (upgrade_repository_format(1) < 0)\n-\t\tdie(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n-\tif (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n-\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n+\t/* Update to use worktree config, if not already. */\n+\tif (init_worktree_config(the_repository)) {\n+\t\terror(_(\"failed to initialize worktree config\"));\n \t\treturn 1;\n \t}\n \n-\tconfig_path = git_path(\"config.worktree\");\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckout\",\n-\t\t\t\t      mode ? \"true\" : NULL);\n-\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckoutCone\",\n-\t\t\t\t      mode == MODE_CONE_PATTERNS ? \"true\" : NULL);\n+\tif (repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckout\",\n+\t\t\t\t\t    mode ? \"true\" : \"false\") ||\n+\t    repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckoutCone\",\n+\t\t\t\t\t    mode == MODE_CONE_PATTERNS ?\n+\t\t\t\t\t\t\"true\" : \"false\"))\n+\t\treturn 1;\n \n \tif (mode == MODE_NO_PATTERNS)\n-\t\tset_sparse_index_config(the_repository, 0);\n+\t\treturn set_sparse_index_config(the_repository, 0);\n \n \treturn 0;\n }\ndiff --git a/sparse-index.c b/sparse-index.c\nindex a1d505d50e9..e93609999e0 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -99,13 +99,9 @@ static int convert_to_sparse_rec(struct index_state *istate,\n \n int set_sparse_index_config(struct repository *repo, int enable)\n {\n-\tint res;\n-\tchar *config_path = repo_git_path(repo, \"config.worktree\");\n-\tres = git_config_set_in_file_gently(config_path,\n-\t\t\t\t\t    \"index.sparse\",\n-\t\t\t\t\t    enable ? \"true\" : NULL);\n-\tfree(config_path);\n-\n+\tint res = repo_config_set_worktree_gently(repo,\n+\t\t\t\t\t\t  \"index.sparse\",\n+\t\t\t\t\t\t  enable ? \"true\" : \"false\");\n \tprepare_repo_settings(repo);\n \trepo->settings.sparse_index = enable;\n \treturn res;\ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex 42776984fe7..be6ea4ffe33 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -117,7 +117,7 @@ test_expect_success 'switching to cone mode with non-cone mode patterns' '\n \t\tcd bad-patterns &&\n \t\tgit sparse-checkout init &&\n \t\tgit sparse-checkout add dir &&\n-\t\tgit config core.sparseCheckoutCone true &&\n+\t\tgit config --worktree core.sparseCheckoutCone true &&\n \t\ttest_must_fail git sparse-checkout add dir 2>err &&\n \t\tgrep \"existing sparse-checkout patterns do not use cone mode\" err\n \t)\n@@ -256,7 +256,7 @@ test_expect_success 'sparse-index enabled and disabled' '\n \t\ttest_cmp expect actual &&\n \n \t\tgit -C repo config --list >config &&\n-\t\t! grep index.sparse config\n+\t\ttest_cmp_config -C repo false index.sparse\n \t)\n '\n \n-- \ngitgitgadget\n\n"},{"id":"446864","messageId":"bb9e550ff3d6c1432aa1d1d07d747e7e085216e8.1643136134.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v4.git.1643136134.gitgitgadget@gmail.com","subject":"[PATCH v4 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-25T18:42:14Z","receivedAt":"2022-01-25T18:43:32Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWhen adding a new worktree, it is reasonable to expect that we want to\nuse the current set of sparse-checkout settings for that new worktree.\nThis is particularly important for repositories where the worktree would\nbecome too large to be useful. This is even more important when using\npartial clone as well, since we want to avoid downloading the missing\nblobs for files that should not be written to the new worktree.\n\nThe only way to create such a worktree without this intermediate step of\nexpanding the full worktree is to copy the sparse-checkout patterns and\nconfig settings during 'git worktree add'. Each worktree has its own\nsparse-checkout patterns, and the default behavior when the\nsparse-checkout file is missing is to include all paths at HEAD. Thus,\nwe need to have patterns from somewhere, they might as well be the\ncurrent worktree's patterns. These are then modified independently in\nthe future.\n\nIn addition to the sparse-checkout file, copy the worktree config file\nif worktree config is enabled and the file exists. This will copy over\nany important settings to ensure the new worktree behaves the same as\nthe current one. The only exception we must continue to make is that\ncore.bare and core.worktree should become unset in the worktree's config\nfile.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/worktree.c                 | 60 ++++++++++++++++++++++++++++++\n t/t1091-sparse-checkout-builtin.sh | 31 +++++++++++----\n t/t2400-worktree-add.sh            | 46 ++++++++++++++++++++++-\n 3 files changed, 127 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 2838254f7f2..dc9cd6decc8 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -335,6 +335,66 @@ static int add_worktree(const char *path, const char *refname,\n \tstrbuf_addf(&sb, \"%s/commondir\", sb_repo.buf);\n \twrite_file(sb.buf, \"../..\");\n \n+\t/*\n+\t * If the current worktree has sparse-checkout enabled, then copy\n+\t * the sparse-checkout patterns from the current worktree.\n+\t */\n+\tif (core_apply_sparse_checkout) {\n+\t\tchar *from_file = git_pathdup(\"info/sparse-checkout\");\n+\t\tchar *to_file = xstrfmt(\"%s/worktrees/%s/info/sparse-checkout\",\n+\t\t\t\t\trealpath.buf, name);\n+\n+\t\tif (file_exists(from_file)) {\n+\t\t\tif (safe_create_leading_directories(to_file) ||\n+\t\t\t    copy_file(to_file, from_file, 0666))\n+\t\t\t\terror(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n+\t\t\t\t      from_file, to_file);\n+\t\t}\n+\n+\t\tfree(from_file);\n+\t\tfree(to_file);\n+\t}\n+\n+\t/*\n+\t * If we are using worktree config, then copy all current config\n+\t * values from the current worktree into the new one, that way the\n+\t * new worktree behaves the same as this one.\n+\t */\n+\tif (repository_format_worktree_config) {\n+\t\tchar *from_file = git_pathdup(\"config.worktree\");\n+\t\tchar *to_file = xstrfmt(\"%s/worktrees/%s/config.worktree\",\n+\t\t\t\t\trealpath.buf, name);\n+\n+\t\tif (file_exists(from_file)) {\n+\t\t\tstruct config_set cs = { { 0 }};\n+\t\t\tconst char *str_value;\n+\t\t\tint bool_value;\n+\n+\t\t\tif (safe_create_leading_directories(to_file) ||\n+\t\t\t    copy_file(to_file, from_file, 0666))\n+\t\t\t\tdie(_(\"failed to copy worktree config from '%s' to '%s'\"),\n+\t\t\t\t    from_file, to_file);\n+\n+\t\t\tgit_configset_init(&cs);\n+\t\t\tgit_configset_add_file(&cs, from_file);\n+\n+\t\t\tif (!git_configset_get_bool(&cs, \"core.bare\", &bool_value) &&\n+\t\t\t    bool_value &&\n+\t\t\t    git_config_set_multivar_in_file_gently(\n+\t\t\t\t\tto_file, \"core.bare\", NULL, \"true\", 0))\n+\t\t\t\terror(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n+\t\t\tif (!git_configset_get_value(&cs, \"core.worktree\", &str_value) &&\n+\t\t\t    git_config_set_in_file_gently(to_file,\n+\t\t\t\t\t\t\t  \"core.worktree\", NULL))\n+\t\t\t\terror(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n+\n+\t\t\tgit_configset_clear(&cs);\n+\t\t}\n+\n+\t\tfree(from_file);\n+\t\tfree(to_file);\n+\t}\n+\n \tstrvec_pushf(&child_env, \"%s=%s\", GIT_DIR_ENVIRONMENT, sb_git.buf);\n \tstrvec_pushf(&child_env, \"%s=%s\", GIT_WORK_TREE_ENVIRONMENT, path);\n \tcp.git_cmd = 1;\ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex be6ea4ffe33..d929772be96 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -146,9 +146,9 @@ test_expect_success 'interaction with clone --no-checkout (unborn index)' '\n '\n \n test_expect_success 'set enables config' '\n-\tgit init empty-config &&\n+\tgit init worktree-config &&\n \t(\n-\t\tcd empty-config &&\n+\t\tcd worktree-config &&\n \t\ttest_commit test file &&\n \t\ttest_path_is_missing .git/config.worktree &&\n \t\tgit sparse-checkout set nothing &&\n@@ -201,6 +201,21 @@ test_expect_success 'add to sparse-checkout' '\n \tcheck_files repo \"a folder1 folder2\"\n '\n \n+test_expect_success 'worktree: add copies sparse-checkout patterns' '\n+\tcat repo/.git/info/sparse-checkout >old &&\n+\ttest_when_finished cp old repo/.git/info/sparse-checkout &&\n+\ttest_when_finished git -C repo worktree remove ../worktree &&\n+\tgit -C repo sparse-checkout set \"/*\" &&\n+\tgit -C repo worktree add --quiet ../worktree 2>err &&\n+\ttest_must_be_empty err &&\n+\tnew=repo/.git/worktrees/worktree/info/sparse-checkout &&\n+\ttest_path_is_file $new &&\n+\ttest_cmp repo/.git/info/sparse-checkout $new &&\n+\tgit -C worktree sparse-checkout set --cone &&\n+\ttest_cmp_config -C worktree true core.sparseCheckoutCone &&\n+\ttest_must_fail git -C repo core.sparseCheckoutCone\n+'\n+\n test_expect_success 'cone mode: match patterns' '\n \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n \trm -rf repo/a repo/folder1 repo/folder2 &&\n@@ -520,13 +535,13 @@ test_expect_success 'interaction with submodules' '\n '\n \n test_expect_success 'different sparse-checkouts with worktrees' '\n+\tgit -C repo sparse-checkout set --cone deep folder1 &&\n \tgit -C repo worktree add --detach ../worktree &&\n-\tcheck_files worktree \"a deep folder1 folder2\" &&\n-\tgit -C worktree sparse-checkout init --cone &&\n-\tgit -C repo sparse-checkout set folder1 &&\n-\tgit -C worktree sparse-checkout set deep/deeper1 &&\n-\tcheck_files repo a folder1 &&\n-\tcheck_files worktree a deep\n+\tcheck_files worktree \"a deep folder1\" &&\n+\tgit -C repo sparse-checkout set --cone folder1 &&\n+\tgit -C worktree sparse-checkout set --cone deep/deeper1 &&\n+\tcheck_files repo \"a folder1\" &&\n+\tcheck_files worktree \"a deep\"\n '\n \n test_expect_success 'set using filename keeps file on-disk' '\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 37ad79470fb..3fb5b21b943 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -165,8 +165,50 @@ test_expect_success '\"add\" default branch of a bare repo' '\n \t(\n \t\tgit clone --bare . bare2 &&\n \t\tcd bare2 &&\n-\t\tgit worktree add ../there3 main\n-\t)\n+\t\tgit worktree add ../there3 main &&\n+\t\tcd ../there3 &&\n+\t\tgit status\n+\t) &&\n+\tcat >expect <<-EOF &&\n+\tinit.t\n+\tEOF\n+\tls there3 >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '\"add\" to bare repo with worktree config' '\n+\t(\n+\t\tgit clone --bare . bare3 &&\n+\t\tcd bare3 &&\n+\t\tgit config extensions.worktreeconfig true &&\n+\t\tgit config --worktree core.bare true &&\n+\t\tgit config --worktree core.worktree \"$(pwd)\" &&\n+\t\tgit config --worktree bogus.key value &&\n+\t\tgit config --unset core.bare &&\n+\t\tgit worktree add ../there4 main &&\n+\t\tcd ../there4 &&\n+\t\tgit status &&\n+\t\tgit worktree add --detach ../there5 &&\n+\t\tcd ../there5 &&\n+\t\tgit status\n+\t) &&\n+\n+\t# the worktree has the arbitrary value copied.\n+\ttest_cmp_config -C there4 value bogus.key &&\n+\ttest_cmp_config -C there5 value bogus.key &&\n+\n+\t# however, core.bare and core.worktree were removed.\n+\ttest_must_fail git -C there4 config core.bare &&\n+\ttest_must_fail git -C there4 config core.worktree &&\n+\n+\tcat >expect <<-EOF &&\n+\tinit.t\n+\tEOF\n+\n+\tls there4 >actual &&\n+\ttest_cmp expect actual &&\n+\tls there5 >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'checkout with grafts' '\n-- \ngitgitgadget\n"},{"id":"446915","messageId":"0e8ef47b-7bb7-df38-1d97-90fda4471bbb@gmail.com","threadId":"57125","inReplyTo":"459e09dedd71fcb64b9796bac52c3812d78f2405.1643136134.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 1/5] Documentation: add extensions.worktreeConfig details","fromName":"Bagas Sanjaya","fromEmail":"bagasdotme@gmail.com","sentAt":"2022-01-26T06:59:46Z","receivedAt":"2022-01-26T07:00:06Z","isPatch":true,"sender":{"key":"bagasdotme@gmail.com","avatar":"https://avatars.githubusercontent.com/u/40219486?v=4"},"body":"On 26/01/22 01.42, Derrick Stolee via GitGitGadget wrote:\n> As documented in 11664196ac (\"Revert \"check_repository_format_gently():\n> refuse extensions for old repositories\"\", 2020-07-15), this extension\n> must be considered regardless of the repository format version for\n> historical reasons.\n> \n...\n> +For historical reasons, `extensions.worktreeConfig` is respected\n> +regardless of the `core.repositoryFormatVersion` setting.\n\nThis implies `extensions.worktreeConfig` become integral part of every\nrepository format version, from the past until now and to the future,\nright?\n\n-- \nAn old man doll... just what I always wanted! - Clara\n"},{"id":"447048","messageId":"CABPp-BFiJnB0u6N5JViS7T_QrC=247gRto7R8DJ5s+f2nP_bKA@mail.gmail.com","threadId":"57125","inReplyTo":"459e09dedd71fcb64b9796bac52c3812d78f2405.1643136134.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 1/5] Documentation: add extensions.worktreeConfig details","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-01-27T06:43:51Z","receivedAt":"2022-01-27T06:44:06Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Jan 25, 2022 at 10:42 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Derrick Stolee <dstolee@microsoft.com>\n>\n> The extensions.worktreeConfig extension was added in 58b284a (worktree:\n> add per-worktree config files, 2018-10-21) and was somewhat documented\n> in Documentation/git-config.txt. However, the extensions.worktreeConfig\n> value was not specified further in the list of possible config keys. The\n> location of the config.worktree file is not specified, and there are\n> some precautions that should be mentioned clearly, but are only\n> mentioned in git-worktree.txt.\n>\n> Expand the documentation to help users discover the complexities of\n> extensions.worktreeConfig by adding details and cross links in these\n> locations (relative to Documentation/):\n>\n> - config/extensions.txt\n> - git-config.txt\n> - git-worktree.txt\n>\n> The updates focus on items such as\n>\n> * $GIT_DIR/config.worktree takes precedence over $GIT_COMMON_DIR/config.\n>\n> * The core.worktree and core.bare=true settings are incorrect to have in\n>   the common config file when extensions.worktreeConfig is enabled.\n>\n> * The sparse-checkout settings core.sparseCheckout[Cone] are recommended\n>   to be set in the worktree config.\n>\n> As documented in 11664196ac (\"Revert \"check_repository_format_gently():\n> refuse extensions for old repositories\"\", 2020-07-15), this extension\n> must be considered regardless of the repository format version for\n> historical reasons.\n>\n> A future change will update references to extensions.worktreeConfig\n> within git-sparse-checkout.txt, but a behavior change is needed before\n> making those updates.\n>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n>  Documentation/config/extensions.txt | 31 +++++++++++++++++++++++++++++\n>  Documentation/git-config.txt        |  8 ++++++--\n>  Documentation/git-worktree.txt      | 11 +++++++---\n>  3 files changed, 45 insertions(+), 5 deletions(-)\n>\n> diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt\n> index 4e23d73cdca..5999dcb2a1f 100644\n> --- a/Documentation/config/extensions.txt\n> +++ b/Documentation/config/extensions.txt\n> @@ -6,3 +6,34 @@ extensions.objectFormat::\n>  Note that this setting should only be set by linkgit:git-init[1] or\n>  linkgit:git-clone[1].  Trying to change it after initialization will not\n>  work and will produce hard-to-diagnose issues.\n> +\n> +extensions.worktreeConfig::\n> +       If enabled, then worktrees will load config settings from the\n> +       `$GIT_DIR/config.worktree` file in addition to the\n> +       `$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and\n> +       `$GIT_DIR` are the same for the main worktree, while other\n> +       worktrees have `$GIT_DIR` equal to\n> +       `$GIT_COMMON_DIR/worktrees/<worktree-name>/`. The settings in the\n> +       `config.worktree` file will override settings from any other\n> +       config files.\n> ++\n> +When enabling `extensions.worktreeConfig`, you must be careful to move\n> +certain values from the common config file to the main worktree's\n> +`config.worktree` file, if present:\n> ++\n> +* `core.worktree` must be moved from `$GIT_COMMON_DIR/config` to\n> +  `$GIT_COMMON_DIR/config.worktree`.\n> +* If `core.bare` is true, then it must be moved from `$GIT_COMMON_DIR/config`\n> +  to `$GIT_COMMON_DIR/config.worktree`.\n> ++\n> +It may also be beneficial to adjust the locations of `core.sparseCheckout`\n> +and `core.sparseCheckoutCone` depending on your desire for customizable\n> +sparse-checkout settings for each worktree. By default, the `git\n> +sparse-checkout` builtin enables `extensions.worktreeConfig`, assigns\n> +these config values on a per-worktree basis, and uses the\n> +`$GIT_DIR/info/sparse-checkout` file to specify the sparsity for each\n> +worktree independently. See linkgit:git-sparse-checkout[1] for more\n> +details.\n> ++\n> +For historical reasons, `extensions.worktreeConfig` is respected\n> +regardless of the `core.repositoryFormatVersion` setting.\n> diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> index 2285effb363..95cefd5e399 100644\n> --- a/Documentation/git-config.txt\n> +++ b/Documentation/git-config.txt\n> @@ -141,9 +141,13 @@ from all available files.\n>  See also <<FILES>>.\n>\n>  --worktree::\n> -       Similar to `--local` except that `.git/config.worktree` is\n> +       Similar to `--local` except that `$GIT_DIR/config.worktree` is\n>         read from or written to if `extensions.worktreeConfig` is\n> -       present. If not it's the same as `--local`.\n> +       enabled. If not it's the same as `--local`. Note that `$GIT_DIR`\n> +       is equal to `$GIT_COMMON_DIR` for the main worktree, but is of the\n> +       form `.git/worktrees/<worktree-name>/` for other worktrees. See\n\nis of the form `$GIT_DIR/worktrees/<worktree-name>/`; .git isn't even\na directory in other worktrees.\n\n> +       linkgit:git-worktree[1] to learn how to enable\n> +       `extensions.worktreeConfig`.\n>\n>  -f <config-file>::\n>  --file <config-file>::\n> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\n> index 9e862fbcf79..ea0ee9f8bb5 100644\n> --- a/Documentation/git-worktree.txt\n> +++ b/Documentation/git-worktree.txt\n> @@ -286,8 +286,8 @@ CONFIGURATION FILE\n>  ------------------\n>  By default, the repository `config` file is shared across all working\n>  trees. If the config variables `core.bare` or `core.worktree` are\n> -already present in the config file, they will be applied to the main\n> -working trees only.\n> +present in the common config file and `extensions.worktreeConfig` is\n> +disabled, then they will be applied to the main working trees only.\n\n\"main working trees\"?  Is that an accidental plural?\n\n>  In order to have configuration specific to working trees, you can turn\n>  on the `worktreeConfig` extension, e.g.:\n> @@ -307,11 +307,16 @@ them to the `config.worktree` of the main working tree. You may also\n>  take this opportunity to review and move other configuration that you\n>  do not want to share to all working trees:\n>\n> - - `core.worktree` and `core.bare` should never be shared\n> + - `core.worktree` should never be shared.\n> +\n> + - `core.bare` should not be shared unless the value is `core.bare=false`.\n\nThe double negative makes for harder parsing.  Perhaps\n    - `core.bare` should not be shared if the value is `core.bare=true`\n?\n\n>   - `core.sparseCheckout` is recommended per working tree, unless you\n>     are sure you always use sparse checkout for all working trees.\n>\n> +See the documentation of `extensions.worktreeConfig` in\n> +linkgit:git-config[1] for more details.\n> +\n>  DETAILS\n>  -------\n>  Each linked working tree has a private sub-directory in the repository's\n> --\n> gitgitgadget\n\nThanks for documenting these details; I had some very minor comments\nbut this is well written and very helpful.\n"},{"id":"447049","messageId":"CABPp-BGjDW8-epG8NXr7tF2nibEty=W+Z058-aWB7wQAn86CNA@mail.gmail.com","threadId":"57125","inReplyTo":"d262a76b448a495f00f460e9ed9439a74946de49.1643136134.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 2/5] worktree: create init_worktree_config()","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-01-27T07:01:58Z","receivedAt":"2022-01-27T07:02:14Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Jan 25, 2022 at 10:42 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Derrick Stolee <dstolee@microsoft.com>\n>\n> Upgrading a repository to use extensions.worktreeConfig is non-trivial.\n> There are several steps involved, including moving some config settings\n> from the common config file to the main worktree's config.worktree file.\n> The previous change updated the documentation with all of these details.\n>\n> Commands such as 'git sparse-checkout set' upgrade the repository to use\n> extensions.worktreeConfig without following these steps, causing some\n> user pain in some special cases.\n>\n> Create a helper method, init_worktree_config(), that will be used in a\n> later change to fix this behavior within 'git sparse-checkout set'. The\n> method is carefully documented in worktree.h.\n\nI was curious why you were only fixing `set` and not `init`, but I\nlooked ahead and it appears you are fixing both, since both use\nset_config().  And I can see leaving out the mention of `init` since\nit's deprecated.  Anyway, it's all good here, I'm basically just\nthinking out loud...\n\n> Note that we do _not_ upgrade the repository format version to 1 during\n> this process. The worktree config extension must be considered by Git\n> and third-party tools even if core.repositoryFormatVersion is 0 for\n> historical reasons documented in 11664196ac (\"Revert\n> \"check_repository_format_gently(): refuse extensions for old\n> repositories\"\", 2020-07-15). This is a special case for this extension,\n> and newer extensions (such as extensions.objectFormat) still need to\n> upgrade the repository format version.\n>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n>  worktree.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n>  worktree.h | 19 +++++++++++++++\n>  2 files changed, 89 insertions(+)\n>\n> diff --git a/worktree.c b/worktree.c\n> index 6f598dcfcdf..dc4ead4c8fb 100644\n> --- a/worktree.c\n> +++ b/worktree.c\n> @@ -5,6 +5,7 @@\n>  #include \"worktree.h\"\n>  #include \"dir.h\"\n>  #include \"wt-status.h\"\n> +#include \"config.h\"\n>\n>  void free_worktrees(struct worktree **worktrees)\n>  {\n> @@ -826,3 +827,72 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,\n>         *wtpath = path;\n>         return 0;\n>  }\n> +\n> +static int move_config_setting(const char *key, const char *value,\n> +                              const char *from_file, const char *to_file)\n> +{\n> +       if (git_config_set_in_file_gently(to_file, key, value))\n> +               return error(_(\"unable to set %s in '%s'\"), key, to_file);\n> +       if (git_config_set_in_file_gently(from_file, key, NULL))\n> +               return error(_(\"unable to unset %s in '%s'\"), key, from_file);\n> +       return 0;\n> +}\n> +\n> +int init_worktree_config(struct repository *r)\n> +{\n> +       int res = 0;\n> +       int bare = 0;\n> +       struct config_set cs = { { 0 } };\n> +       const char *core_worktree;\n> +       char *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n> +       char *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n> +\n> +       /*\n> +        * If the extension is already enabled, then we can skip the\n> +        * upgrade process.\n> +        */\n> +       if (repository_format_worktree_config)\n> +               return 0;\n> +       if ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\")))\n> +               return error(_(\"failed to set extensions.worktreeConfig setting\"));\n> +\n> +       git_configset_init(&cs);\n> +       git_configset_add_file(&cs, common_config_file);\n> +\n> +       /*\n> +        * If core.bare is true in the common config file, then we need to\n> +        * move it to the base worktree's config file or it will break all\n> +        * worktrees. If it is false, then leave it in place because it\n> +        * _could_ be negating a global core.bare=true.\n> +        */\n> +       if (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n> +               if ((res = move_config_setting(\"core.bare\", \"true\",\n> +                                              common_config_file,\n> +                                              main_worktree_file)))\n> +                       goto cleanup;\n> +       }\n> +       /*\n> +        * If core.worktree is set, then the base worktree is located\n> +        * somewhere different than the parent of the common Git dir.\n> +        * Relocate that value to avoid breaking all worktrees with this\n> +        * upgrade to worktree config.\n> +        */\n> +       if (!git_configset_get_string_tmp(&cs, \"core.worktree\", &core_worktree)) {\n> +               if ((res = move_config_setting(\"core.worktree\", core_worktree,\n> +                                              common_config_file,\n> +                                              main_worktree_file)))\n> +                       goto cleanup;\n> +       }\n> +\n> +       /*\n> +        * Ensure that we use worktree config for the remaining lifetime\n> +        * of the current process.\n> +        */\n> +       repository_format_worktree_config = 1;\n> +\n> +cleanup:\n> +       git_configset_clear(&cs);\n> +       free(common_config_file);\n> +       free(main_worktree_file);\n> +       return res;\n> +}\n> diff --git a/worktree.h b/worktree.h\n> index 9e06fcbdf3d..5ea5fcc3647 100644\n> --- a/worktree.h\n> +++ b/worktree.h\n> @@ -183,4 +183,23 @@ void strbuf_worktree_ref(const struct worktree *wt,\n>                          struct strbuf *sb,\n>                          const char *refname);\n>\n> +/**\n> + * Enable worktree config for the first time. This will make the following\n> + * adjustments:\n> + *\n> + * 1. Add extensions.worktreeConfig=true in the common config file.\n> + *\n> + * 2. If the common config file has a core.worktree value or core.bare is\n> + *    set to true, then those values are moved to the main worktree's\n> + *    config.worktree file.\n\nThis is a bit ambiguous.  If core.worktree is set and core.bare is\nfalse, are both moved or only one?  I'm afraid folks won't understand\nthat it's just one from this description.\n\n\n> + *\n> + * If extensions.worktreeConfig is already true, then this method\n> + * terminates early without any of the above steps. The existing config\n> + * arrangement is assumed to be intentional.\n> + *\n> + * Returns 0 on success. Reports an error message and returns non-zero\n> + * if any of these steps fail.\n> + */\n> +int init_worktree_config(struct repository *r);\n> +\n>  #endif\n> --\n\nOther than the ambiguity in worktree.h, this patch looks solid.\n"},{"id":"447051","messageId":"CABPp-BHog9yoqC+M=L6y3uBfmfPOTdhy=hErwBozhbqL_yQ+-g@mail.gmail.com","threadId":"57125","inReplyTo":"bb9e550ff3d6c1432aa1d1d07d747e7e085216e8.1643136134.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-01-27T07:09:36Z","receivedAt":"2022-01-27T07:09:53Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Jan 25, 2022 at 10:42 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Derrick Stolee <dstolee@microsoft.com>\n>\n> When adding a new worktree, it is reasonable to expect that we want to\n> use the current set of sparse-checkout settings for that new worktree.\n> This is particularly important for repositories where the worktree would\n> become too large to be useful. This is even more important when using\n> partial clone as well, since we want to avoid downloading the missing\n> blobs for files that should not be written to the new worktree.\n>\n> The only way to create such a worktree without this intermediate step of\n> expanding the full worktree is to copy the sparse-checkout patterns and\n> config settings during 'git worktree add'. Each worktree has its own\n> sparse-checkout patterns, and the default behavior when the\n> sparse-checkout file is missing is to include all paths at HEAD. Thus,\n> we need to have patterns from somewhere, they might as well be the\n> current worktree's patterns. These are then modified independently in\n> the future.\n>\n> In addition to the sparse-checkout file, copy the worktree config file\n> if worktree config is enabled and the file exists. This will copy over\n> any important settings to ensure the new worktree behaves the same as\n> the current one. The only exception we must continue to make is that\n> core.bare and core.worktree should become unset in the worktree's config\n> file.\n>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n>  builtin/worktree.c                 | 60 ++++++++++++++++++++++++++++++\n>  t/t1091-sparse-checkout-builtin.sh | 31 +++++++++++----\n>  t/t2400-worktree-add.sh            | 46 ++++++++++++++++++++++-\n>  3 files changed, 127 insertions(+), 10 deletions(-)\n>\n> diff --git a/builtin/worktree.c b/builtin/worktree.c\n> index 2838254f7f2..dc9cd6decc8 100644\n> --- a/builtin/worktree.c\n> +++ b/builtin/worktree.c\n> @@ -335,6 +335,66 @@ static int add_worktree(const char *path, const char *refname,\n>         strbuf_addf(&sb, \"%s/commondir\", sb_repo.buf);\n>         write_file(sb.buf, \"../..\");\n>\n> +       /*\n> +        * If the current worktree has sparse-checkout enabled, then copy\n> +        * the sparse-checkout patterns from the current worktree.\n> +        */\n> +       if (core_apply_sparse_checkout) {\n> +               char *from_file = git_pathdup(\"info/sparse-checkout\");\n> +               char *to_file = xstrfmt(\"%s/worktrees/%s/info/sparse-checkout\",\n> +                                       realpath.buf, name);\n> +\n> +               if (file_exists(from_file)) {\n> +                       if (safe_create_leading_directories(to_file) ||\n> +                           copy_file(to_file, from_file, 0666))\n> +                               error(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n> +                                     from_file, to_file);\n> +               }\n> +\n> +               free(from_file);\n> +               free(to_file);\n> +       }\n> +\n> +       /*\n> +        * If we are using worktree config, then copy all current config\n> +        * values from the current worktree into the new one, that way the\n> +        * new worktree behaves the same as this one.\n> +        */\n> +       if (repository_format_worktree_config) {\n> +               char *from_file = git_pathdup(\"config.worktree\");\n> +               char *to_file = xstrfmt(\"%s/worktrees/%s/config.worktree\",\n> +                                       realpath.buf, name);\n> +\n> +               if (file_exists(from_file)) {\n> +                       struct config_set cs = { { 0 }};\n> +                       const char *str_value;\n> +                       int bool_value;\n> +\n> +                       if (safe_create_leading_directories(to_file) ||\n> +                           copy_file(to_file, from_file, 0666))\n> +                               die(_(\"failed to copy worktree config from '%s' to '%s'\"),\n> +                                   from_file, to_file);\n> +\n> +                       git_configset_init(&cs);\n> +                       git_configset_add_file(&cs, from_file);\n> +\n> +                       if (!git_configset_get_bool(&cs, \"core.bare\", &bool_value) &&\n> +                           bool_value &&\n> +                           git_config_set_multivar_in_file_gently(\n> +                                       to_file, \"core.bare\", NULL, \"true\", 0))\n> +                               error(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n> +                       if (!git_configset_get_value(&cs, \"core.worktree\", &str_value) &&\n> +                           git_config_set_in_file_gently(to_file,\n> +                                                         \"core.worktree\", NULL))\n> +                               error(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n> +\n> +                       git_configset_clear(&cs);\n> +               }\n> +\n> +               free(from_file);\n> +               free(to_file);\n> +       }\n> +\n>         strvec_pushf(&child_env, \"%s=%s\", GIT_DIR_ENVIRONMENT, sb_git.buf);\n>         strvec_pushf(&child_env, \"%s=%s\", GIT_WORK_TREE_ENVIRONMENT, path);\n>         cp.git_cmd = 1;\n> diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\n> index be6ea4ffe33..d929772be96 100755\n> --- a/t/t1091-sparse-checkout-builtin.sh\n> +++ b/t/t1091-sparse-checkout-builtin.sh\n> @@ -146,9 +146,9 @@ test_expect_success 'interaction with clone --no-checkout (unborn index)' '\n>  '\n>\n>  test_expect_success 'set enables config' '\n> -       git init empty-config &&\n> +       git init worktree-config &&\n>         (\n> -               cd empty-config &&\n> +               cd worktree-config &&\n>                 test_commit test file &&\n>                 test_path_is_missing .git/config.worktree &&\n>                 git sparse-checkout set nothing &&\n> @@ -201,6 +201,21 @@ test_expect_success 'add to sparse-checkout' '\n>         check_files repo \"a folder1 folder2\"\n>  '\n>\n> +test_expect_success 'worktree: add copies sparse-checkout patterns' '\n> +       cat repo/.git/info/sparse-checkout >old &&\n> +       test_when_finished cp old repo/.git/info/sparse-checkout &&\n> +       test_when_finished git -C repo worktree remove ../worktree &&\n> +       git -C repo sparse-checkout set \"/*\" &&\n\nCould we add --no-cone to tests using that mode in anticipation of\nswitching the default?\n\n> +       git -C repo worktree add --quiet ../worktree 2>err &&\n> +       test_must_be_empty err &&\n> +       new=repo/.git/worktrees/worktree/info/sparse-checkout &&\n> +       test_path_is_file $new &&\n> +       test_cmp repo/.git/info/sparse-checkout $new &&\n> +       git -C worktree sparse-checkout set --cone &&\n> +       test_cmp_config -C worktree true core.sparseCheckoutCone &&\n> +       test_must_fail git -C repo core.sparseCheckoutCone\n> +'\n> +\n>  test_expect_success 'cone mode: match patterns' '\n>         git -C repo config --worktree core.sparseCheckoutCone true &&\n>         rm -rf repo/a repo/folder1 repo/folder2 &&\n> @@ -520,13 +535,13 @@ test_expect_success 'interaction with submodules' '\n>  '\n>\n>  test_expect_success 'different sparse-checkouts with worktrees' '\n> +       git -C repo sparse-checkout set --cone deep folder1 &&\n>         git -C repo worktree add --detach ../worktree &&\n> -       check_files worktree \"a deep folder1 folder2\" &&\n> -       git -C worktree sparse-checkout init --cone &&\n> -       git -C repo sparse-checkout set folder1 &&\n> -       git -C worktree sparse-checkout set deep/deeper1 &&\n> -       check_files repo a folder1 &&\n> -       check_files worktree a deep\n> +       check_files worktree \"a deep folder1\" &&\n> +       git -C repo sparse-checkout set --cone folder1 &&\n> +       git -C worktree sparse-checkout set --cone deep/deeper1 &&\n> +       check_files repo \"a folder1\" &&\n> +       check_files worktree \"a deep\"\n>  '\n>\n>  test_expect_success 'set using filename keeps file on-disk' '\n> diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\n> index 37ad79470fb..3fb5b21b943 100755\n> --- a/t/t2400-worktree-add.sh\n> +++ b/t/t2400-worktree-add.sh\n> @@ -165,8 +165,50 @@ test_expect_success '\"add\" default branch of a bare repo' '\n>         (\n>                 git clone --bare . bare2 &&\n>                 cd bare2 &&\n> -               git worktree add ../there3 main\n> -       )\n> +               git worktree add ../there3 main &&\n> +               cd ../there3 &&\n> +               git status\n> +       ) &&\n> +       cat >expect <<-EOF &&\n> +       init.t\n> +       EOF\n> +       ls there3 >actual &&\n> +       test_cmp expect actual\n> +'\n> +\n> +test_expect_success '\"add\" to bare repo with worktree config' '\n> +       (\n> +               git clone --bare . bare3 &&\n> +               cd bare3 &&\n> +               git config extensions.worktreeconfig true &&\n> +               git config --worktree core.bare true &&\n> +               git config --worktree core.worktree \"$(pwd)\" &&\n> +               git config --worktree bogus.key value &&\n> +               git config --unset core.bare &&\n> +               git worktree add ../there4 main &&\n> +               cd ../there4 &&\n> +               git status &&\n> +               git worktree add --detach ../there5 &&\n> +               cd ../there5 &&\n> +               git status\n> +       ) &&\n> +\n> +       # the worktree has the arbitrary value copied.\n> +       test_cmp_config -C there4 value bogus.key &&\n> +       test_cmp_config -C there5 value bogus.key &&\n> +\n> +       # however, core.bare and core.worktree were removed.\n> +       test_must_fail git -C there4 config core.bare &&\n> +       test_must_fail git -C there4 config core.worktree &&\n> +\n> +       cat >expect <<-EOF &&\n> +       init.t\n> +       EOF\n> +\n> +       ls there4 >actual &&\n> +       test_cmp expect actual &&\n> +       ls there5 >actual &&\n> +       test_cmp expect actual\n>  '\n>\n>  test_expect_success 'checkout with grafts' '\n> --\n> gitgitgadget\n\nThis patch is so awesome.  Totally looking forward to seeing it\nincluded.  I was only able to spot one micro nit.\n"},{"id":"447052","messageId":"CABPp-BHBMUar-ysiJsjmpqQ3NPjRUiBPKEdHty3ADBYaf0EMug@mail.gmail.com","threadId":"57125","inReplyTo":"fbfaa17797c023f4a86eeac200a83c2e1954daf5.1643136134.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 4/5] sparse-checkout: set worktree-config correctly","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-01-27T07:15:23Z","receivedAt":"2022-01-27T07:15:37Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Jan 25, 2022 at 10:42 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Derrick Stolee <dstolee@microsoft.com>\n>\n> The previous change added repo_config_set_worktree_gently() to assist\n> writing config values into the config.worktree file, if enabled. An\n> earlier change added init_worktree_config() as a helper to initialize\n> extensions.worktreeConfig if not already enabled.\n>\n> Let the sparse-checkout builtin use these helpers instead of attempting to\n> initialize the worktree config on its own. This changes behavior of 'git\n> sparse-checkout set' in a few important ways:\n>\n>  1. Git will no longer upgrade the repository format, since this is not\n>     a requirement for understanding extensions.worktreeConfig.\n>\n>  2. If the main worktree is bare, then this command will not put the\n>     worktree in a broken state.\n>\n> The main reason to use worktree-specific config for the sparse-checkout\n> builtin was to avoid enabling sparse-checkout patterns in one and\n> causing a loss of files in another. If a worktree does not have a\n> sparse-checkout patterns file, then the sparse-checkout logic will not\n> kick in on that worktree.\n>\n> Reported-by: Sean Allred <allred.sean@gmail.com>\n> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n>  Documentation/git-sparse-checkout.txt | 24 ++++++++++++++++-------\n>  builtin/sparse-checkout.c             | 28 +++++++++++++--------------\n>  sparse-index.c                        | 10 +++-------\n>  t/t1091-sparse-checkout-builtin.sh    |  4 ++--\n>  4 files changed, 35 insertions(+), 31 deletions(-)\n>\n> diff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt\n> index b81dbe06543..c6eae3ec7fd 100644\n> --- a/Documentation/git-sparse-checkout.txt\n> +++ b/Documentation/git-sparse-checkout.txt\n> @@ -31,12 +31,20 @@ COMMANDS\n>         Describe the patterns in the sparse-checkout file.\n>\n>  'set'::\n> -       Enable the necessary config settings\n> -       (extensions.worktreeConfig, core.sparseCheckout,\n> -       core.sparseCheckoutCone) if they are not already enabled, and\n> -       write a set of patterns to the sparse-checkout file from the\n> -       list of arguments following the 'set' subcommand. Update the\n> -       working directory to match the new patterns.\n> +       Enable the necessary sparse-checkout config settings\n> +       (`core.sparseCheckout` and possibly `core.sparseCheckoutCone`) if\n\nand possibly index.sparse as well, right?\n\n> +       they are not already enabled, and write a set of patterns to the\n> +       sparse-checkout file from the list of arguments following the\n> +       'set' subcommand. Update the working directory to match the new\n> +       patterns.\n> ++\n> +To ensure that adjusting the sparse-checkout settings within a worktree\n> +does not alter the sparse-checkout settings in other worktrees, the 'set'\n> +subcommand will upgrade your repository config to use worktree-specific\n> +config if not already present. The sparsity defined by the arguments to\n> +the 'set' subcommand are stored in the worktree-specific sparse-checkout\n> +file. See linkgit:git-worktree[1] and the documentation of\n> +`extensions.worktreeConfig` in linkgit:git-config[1] for more details.\n>  +\n>  When the `--stdin` option is provided, the patterns are read from\n>  standard in as a newline-delimited list instead of from the arguments.\n> @@ -73,7 +81,9 @@ interact with your repository until it is disabled.\n>         By default, these patterns are read from the command-line arguments,\n>         but they can be read from stdin using the `--stdin` option. When\n>         `core.sparseCheckoutCone` is enabled, the given patterns are interpreted\n> -       as directory names as in the 'set' subcommand.\n> +       as directory names as in the 'set' subcommand. The sparsity defined\n> +       by the arguments to the 'add' subcommand are added to the patterns\n> +       in the worktree-specific sparse-checkout file.\n\nThis sentence addition makes your series conflict with patch 4 of my\nen/present-despite-skipped series.\n\nThe sentence also seems somewhat redundant with the first sentence of\nthe paragraph (not quoted here).  Perhaps consider just dropping it to\nmake it easier for Junio to integrate?\n\n>  'reapply'::\n>         Reapply the sparsity pattern rules to paths in the working tree.\n> diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\n> index 679c1070368..314c8d61f80 100644\n> --- a/builtin/sparse-checkout.c\n> +++ b/builtin/sparse-checkout.c\n> @@ -15,6 +15,7 @@\n>  #include \"wt-status.h\"\n>  #include \"quote.h\"\n>  #include \"sparse-index.h\"\n> +#include \"worktree.h\"\n>\n>  static const char *empty_base = \"\";\n>\n> @@ -359,26 +360,23 @@ enum sparse_checkout_mode {\n>\n>  static int set_config(enum sparse_checkout_mode mode)\n>  {\n> -       const char *config_path;\n> -\n> -       if (upgrade_repository_format(1) < 0)\n> -               die(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n\nWait, this got added in mid-2020, since v2.28.0 -- and I missed it but\nit hasn't bit us yet??\n\nI'm so sorry.  Earlier when I objected to setting this, I was worried\nit was a new change and would introduce some new failures, but clearly\nit was already introduced...and it turns out we've been fine.  So, I\nwas wrong to worry about this.\n\n(Which is to say, if you want to add it back, I'm fine with it given\nthis info.  If you'd rather still take it out, I'm fine with that\ntoo.)\n\n\n> -       if (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n> -               error(_(\"failed to set extensions.worktreeConfig setting\"));\n> +       /* Update to use worktree config, if not already. */\n> +       if (init_worktree_config(the_repository)) {\n> +               error(_(\"failed to initialize worktree config\"));\n>                 return 1;\n>         }\n>\n> -       config_path = git_path(\"config.worktree\");\n> -       git_config_set_in_file_gently(config_path,\n> -                                     \"core.sparseCheckout\",\n> -                                     mode ? \"true\" : NULL);\n> -\n> -       git_config_set_in_file_gently(config_path,\n> -                                     \"core.sparseCheckoutCone\",\n> -                                     mode == MODE_CONE_PATTERNS ? \"true\" : NULL);\n> +       if (repo_config_set_worktree_gently(the_repository,\n> +                                           \"core.sparseCheckout\",\n> +                                           mode ? \"true\" : \"false\") ||\n> +           repo_config_set_worktree_gently(the_repository,\n> +                                           \"core.sparseCheckoutCone\",\n> +                                           mode == MODE_CONE_PATTERNS ?\n> +                                               \"true\" : \"false\"))\n> +               return 1;\n>\n>         if (mode == MODE_NO_PATTERNS)\n> -               set_sparse_index_config(the_repository, 0);\n> +               return set_sparse_index_config(the_repository, 0);\n>\n>         return 0;\n>  }\n> diff --git a/sparse-index.c b/sparse-index.c\n> index a1d505d50e9..e93609999e0 100644\n> --- a/sparse-index.c\n> +++ b/sparse-index.c\n> @@ -99,13 +99,9 @@ static int convert_to_sparse_rec(struct index_state *istate,\n>\n>  int set_sparse_index_config(struct repository *repo, int enable)\n>  {\n> -       int res;\n> -       char *config_path = repo_git_path(repo, \"config.worktree\");\n> -       res = git_config_set_in_file_gently(config_path,\n> -                                           \"index.sparse\",\n> -                                           enable ? \"true\" : NULL);\n> -       free(config_path);\n> -\n> +       int res = repo_config_set_worktree_gently(repo,\n> +                                                 \"index.sparse\",\n> +                                                 enable ? \"true\" : \"false\");\n>         prepare_repo_settings(repo);\n>         repo->settings.sparse_index = enable;\n>         return res;\n> diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\n> index 42776984fe7..be6ea4ffe33 100755\n> --- a/t/t1091-sparse-checkout-builtin.sh\n> +++ b/t/t1091-sparse-checkout-builtin.sh\n> @@ -117,7 +117,7 @@ test_expect_success 'switching to cone mode with non-cone mode patterns' '\n>                 cd bad-patterns &&\n>                 git sparse-checkout init &&\n>                 git sparse-checkout add dir &&\n> -               git config core.sparseCheckoutCone true &&\n> +               git config --worktree core.sparseCheckoutCone true &&\n>                 test_must_fail git sparse-checkout add dir 2>err &&\n>                 grep \"existing sparse-checkout patterns do not use cone mode\" err\n>         )\n> @@ -256,7 +256,7 @@ test_expect_success 'sparse-index enabled and disabled' '\n>                 test_cmp expect actual &&\n>\n>                 git -C repo config --list >config &&\n> -               ! grep index.sparse config\n> +               test_cmp_config -C repo false index.sparse\n>         )\n>  '\n>\n> --\n> gitgitgadget\n\nPatch looks good; I only had a few very minor comments.\n"},{"id":"447053","messageId":"CABPp-BHm3GRA3HWQwK2JiTKWye80ovwh-DRUOTys7g2D4UmwsA@mail.gmail.com","threadId":"57125","inReplyTo":"pull.1101.v4.git.1643136134.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-01-27T07:20:28Z","receivedAt":"2022-01-27T07:20:44Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Jan 25, 2022 at 10:42 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> This series is now based on v2.35.0 since that contains all of the necessary\n> topics.\n\nHeads up for the maintainer: has a minor textual conflict with\nen/present-despite-skipped (this series modifies the end of a\nparagraph that the other series modifies the beginning of).\n\n> This patch series includes a fix to the bug reported by Sean Allred [1] and\n> diagnosed by Eric Sunshine [2].\n>\n> The root cause is that 'git sparse-checkout init' writes to the worktree\n> config without checking that core.bare or core.worktree are set in the\n> common config file. This series fixes this, but also puts in place some\n> helpers to prevent this from happening in the future.\n>\n> ATTENTION: I have significantly redesigned the series since previous\n> versions, so most of this cover letter is new.\n>\n>  * Patch 1 updates documentation around extensions.worktreeConfig in a few\n>    places to improve discoverability. Several cross links are added to make\n>    it easy to find the related areas. (The documentation for the changes to\n>    'git sparse-checkout' are delayed to patch 4.)\n>\n>  * Patch 2 introduces the init_worktree_config() helper which follows the\n>    documented instructions to enable extensions.worktreeConfig as well as\n>    move the core.bare and core.worktree config values. This update does not\n>    modify core.repositoryFormatVersion, since this is not needed\n>    specifically for extensions.worktreeConfig.\n>\n>  * Patch 3 adds a new repo_config_set_worktree_gently() helper method so we\n>    can internally adjust a config value within a worktree, at least if\n>    extensions.worktreeConfig is enabled. (It will write to the common config\n>    file if the extension is not enabled.)\n>\n>  * Patch 4 modifies the sparse-checkout builtin to use\n>    init_worktree_config() and repo_config_set_worktree_gently() in ways that\n>    fix the reported bug. The behavior change here is that it will no longer\n>    upgrade the repository format version, since that is not needed for\n>    extensions.worktreeConfig.\n>\n>  * Patch 5 updates 'git worktree add' to copy the worktree config from the\n>    current worktree to the new one (while unsetting core.bare=true and\n>    core.worktree=*) along with copying the sparse-checkout patterns file.\n>\n> [1]\n> https://lore.kernel.org/git/CABceR4bZmtC4rCwgxZ1BBYZP69VOUca1f_moJoP989vTUZWu9Q@mail.gmail.com/\n> [2]\n> https://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n>\n>\n> Updates in v4\n> =============\n>\n>  * Rebased to v2.35.0\n>  * Fixed memory leak (was leaking repo_git_path() result)\n>  * Added additional documentation updates so curious users can discover the\n>    intricacies of extensions.worktreeConfig from multiple entry points.\n>  * Significantly reduced the amount of changes to config.c.\n>  * 'git sparse-checkout' no longer upgrades the repository format.\n>  * Dropped the update to upgrade_repository_format(), since it is not\n>    needed.\n>  * Dropped the 'git worktree init-worktree-config' subcommand in favor of a\n>    helper method called by 'git sparse-checkout'\n>  * Many others because of the significant changes required by the above\n>    items.\n\nThis series has become pretty solid.  I had only very minor comments\non the patches.  Thanks for working on this.\n\n> Thanks, -Stolee\n>\n> Derrick Stolee (5):\n>   Documentation: add extensions.worktreeConfig details\n>   worktree: create init_worktree_config()\n>   config: add repo_config_set_worktree_gently()\n>   sparse-checkout: set worktree-config correctly\n>   worktree: copy sparse-checkout patterns and config on add\n>\n>  Documentation/config/extensions.txt   | 31 ++++++++++++\n>  Documentation/git-config.txt          |  8 ++-\n>  Documentation/git-sparse-checkout.txt | 24 ++++++---\n>  Documentation/git-worktree.txt        | 11 +++--\n>  builtin/sparse-checkout.c             | 28 +++++------\n>  builtin/worktree.c                    | 60 +++++++++++++++++++++++\n>  config.c                              | 35 ++++++++++++--\n>  config.h                              |  8 +++\n>  sparse-index.c                        | 10 ++--\n>  t/t1091-sparse-checkout-builtin.sh    | 35 ++++++++++----\n>  t/t2400-worktree-add.sh               | 46 +++++++++++++++++-\n>  worktree.c                            | 70 +++++++++++++++++++++++++++\n>  worktree.h                            | 19 ++++++++\n>  13 files changed, 336 insertions(+), 49 deletions(-)\n>\n>\n> base-commit: 89bece5c8c96f0b962cfc89e63f82d603fd60bed\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1101%2Fderrickstolee%2Fsparse-checkout%2Fbare-worktree-bug-v4\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1101/derrickstolee/sparse-checkout/bare-worktree-bug-v4\n> Pull-Request: https://github.com/gitgitgadget/git/pull/1101\n>\n> Range-diff vs v3:\n>\n>  1:  749ba67d21e < -:  ----------- setup: use a repository when upgrading format\n>  2:  61b96937016 < -:  ----------- config: make some helpers repo-aware\n>  -:  ----------- > 1:  459e09dedd7 Documentation: add extensions.worktreeConfig details\n>  3:  e2a0a458115 ! 2:  d262a76b448 worktree: add 'init-worktree-config' subcommand\n>      @@ Metadata\n>       Author: Derrick Stolee <dstolee@microsoft.com>\n>\n>        ## Commit message ##\n>      -    worktree: add 'init-worktree-config' subcommand\n>      +    worktree: create init_worktree_config()\n>\n>      -    Some features, such as the sparse-checkout builtin, currently use the\n>      -    worktree config extension. It might seem simple to upgrade the\n>      -    repository format and add extensions.worktreeConfig, which is what\n>      -    happens in the sparse-checkout builtin. However, this is overly\n>      -    simplistic and can cause issues in some cases. We will transition away\n>      -    from making this upgrade automatically, but first we will make an easy\n>      -    way for users to upgrade their repositories correctly.\n>      +    Upgrading a repository to use extensions.worktreeConfig is non-trivial.\n>      +    There are several steps involved, including moving some config settings\n>      +    from the common config file to the main worktree's config.worktree file.\n>      +    The previous change updated the documentation with all of these details.\n>\n>      -    Transitioning from one config file to multiple has some strange\n>      -    side-effects. In particular, if the base repository is bare and the\n>      -    worktree is not, Git knows to treat the worktree as non-bare as a\n>      -    special case when not using worktree config. Once worktree config is\n>      -    enabled, Git stops that special case since the core.bare setting could\n>      -    apply at the worktree config level.\n>      +    Commands such as 'git sparse-checkout set' upgrade the repository to use\n>      +    extensions.worktreeConfig without following these steps, causing some\n>      +    user pain in some special cases.\n>\n>      -    Similarly, the core.worktree config setting is a precursor to the 'git\n>      -    worktree' feature, allowing config to point to a different worktree,\n>      -    presumably temporarily. This is special-cased to be ignored in a\n>      -    worktree, but that case is dropped when worktree config is enabled.\n>      +    Create a helper method, init_worktree_config(), that will be used in a\n>      +    later change to fix this behavior within 'git sparse-checkout set'. The\n>      +    method is carefully documented in worktree.h.\n>\n>      -    To help resolve this transition, create the 'git worktree\n>      -    init-worktree-config' helper. This new subcommand does the following:\n>      +    Note that we do _not_ upgrade the repository format version to 1 during\n>      +    this process. The worktree config extension must be considered by Git\n>      +    and third-party tools even if core.repositoryFormatVersion is 0 for\n>      +    historical reasons documented in 11664196ac (\"Revert\n>      +    \"check_repository_format_gently(): refuse extensions for old\n>      +    repositories\"\", 2020-07-15). This is a special case for this extension,\n>      +    and newer extensions (such as extensions.objectFormat) still need to\n>      +    upgrade the repository format version.\n>\n>      -     1. Set core.repositoryFormatVersion to 1 in the common config file.\n>      -     2. Set extensions.worktreeConfig to true in the common config file.\n>      -     3. If core.bare is true in the common config file, then move that\n>      -        setting to the main worktree's config file.\n>      -     4. Move the core.worktree config value to the main worktree's config\n>      -        file.\n>      -\n>      -    If the repository is already configured to use worktree config, then\n>      -    none of these steps happen. This preserves any state that the user might\n>      -    have created on purpose.\n>      -\n>      -    Update the documentation to mention this subcommand as the proper way to\n>      -    upgrade to worktree config files.\n>      -\n>      -    To gain access to the core repository's config and config.worktree file,\n>      -    we reference a repository struct's 'commondir' member. If the repository\n>      -    was a submodule instead of a worktree, then this still applies\n>      -    correctly.\n>      -\n>      -    Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n>           Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n>\n>      - ## Documentation/git-worktree.txt ##\n>      -@@ Documentation/git-worktree.txt: SYNOPSIS\n>      - --------\n>      - [verse]\n>      - 'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]\n>      -+'git worktree init-worktree-config'\n>      - 'git worktree list' [-v | --porcelain]\n>      - 'git worktree lock' [--reason <string>] <worktree>\n>      - 'git worktree move' <worktree> <new-path>\n>      -@@ Documentation/git-worktree.txt: checked out in the new working tree, if it's not checked out anywhere\n>      - else, otherwise the command will refuse to create the working tree (unless\n>      - `--force` is used).\n>      -\n>      -+init-worktree-config::\n>      -+\n>      -+Initialize config settings to enable worktree-specific config settings.\n>      -+This will set `core.repositoryFormatversion=1` and enable\n>      -+`extensions.worktreeConfig`, which might cause some third-party tools from\n>      -+being able to operate on your repository. See CONFIGURATION FILE for more\n>      -+details.\n>      -+\n>      - list::\n>      -\n>      - List details of each working tree.  The main working tree is listed first,\n>      -@@ Documentation/git-worktree.txt: already present in the config file, they will be applied to the main\n>      - working trees only.\n>      -\n>      - In order to have configuration specific to working trees, you can turn\n>      --on the `worktreeConfig` extension, e.g.:\n>      -+on the `worktreeConfig` extension, using this command:\n>      -\n>      - ------------\n>      --$ git config extensions.worktreeConfig true\n>      -+$ git worktree init-worktree-config\n>      - ------------\n>      -\n>      - In this mode, specific configuration stays in the path pointed by `git\n>      -@@ Documentation/git-worktree.txt: versions will refuse to access repositories with this extension.\n>      -\n>      - Note that in this file, the exception for `core.bare` and `core.worktree`\n>      - is gone. If they exist in `$GIT_DIR/config`, you must move\n>      --them to the `config.worktree` of the main working tree. You may also\n>      --take this opportunity to review and move other configuration that you\n>      --do not want to share to all working trees:\n>      -+them to the `config.worktree` of the main working tree. These keys are\n>      -+moved automatically when you use the `git worktree init-worktree-config`\n>      -+command.\n>      -+\n>      -+You may also take this opportunity to review and move other configuration\n>      -+that you do not want to share to all working trees:\n>      -\n>      -  - `core.worktree` and `core.bare` should never be shared\n>      -\n>      -\n>      - ## builtin/worktree.c ##\n>      + ## worktree.c ##\n>       @@\n>      + #include \"worktree.h\"\n>      + #include \"dir.h\"\n>      + #include \"wt-status.h\"\n>      ++#include \"config.h\"\n>\n>      - static const char * const worktree_usage[] = {\n>      -  N_(\"git worktree add [<options>] <path> [<commit-ish>]\"),\n>      -+ N_(\"git worktree init-worktree-config\"),\n>      -  N_(\"git worktree list [<options>]\"),\n>      -  N_(\"git worktree lock [<options>] <path>\"),\n>      -  N_(\"git worktree move <worktree> <new-path>\"),\n>      -@@ builtin/worktree.c: static int repair(int ac, const char **av, const char *prefix)\n>      -  return rc;\n>      + void free_worktrees(struct worktree **worktrees)\n>      + {\n>      +@@ worktree.c: int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,\n>      +  *wtpath = path;\n>      +  return 0;\n>        }\n>      -\n>      ++\n>       +static int move_config_setting(const char *key, const char *value,\n>       +                        const char *from_file, const char *to_file)\n>       +{\n>      @@ builtin/worktree.c: static int repair(int ac, const char **av, const char *prefi\n>       + return 0;\n>       +}\n>       +\n>      -+static int init_worktree_config(int ac, const char **av, const char *prefix)\n>      ++int init_worktree_config(struct repository *r)\n>       +{\n>      -+ struct repository *r = the_repository;\n>      -+ struct option options[] = {\n>      -+         OPT_END()\n>      -+ };\n>       + int res = 0;\n>       + int bare = 0;\n>      -+ struct config_set cs = { 0 };\n>      ++ struct config_set cs = { { 0 } };\n>       + const char *core_worktree;\n>       + char *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n>       + char *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n>       +\n>      -+ /* Report error on any arguments */\n>      -+ ac = parse_options(ac, av, prefix, options, worktree_usage, 0);\n>      -+ if (ac)\n>      -+         usage_with_options(worktree_usage, options);\n>      -+\n>      -+ git_configset_init(&cs);\n>      -+ git_configset_add_file(&cs, common_config_file);\n>      -+\n>       + /*\n>      -+  * If the format and extension are already enabled, then we can\n>      -+  * skip the upgrade process.\n>      ++  * If the extension is already enabled, then we can skip the\n>      ++  * upgrade process.\n>       +  */\n>       + if (repository_format_worktree_config)\n>       +         return 0;\n>      ++ if ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\")))\n>      ++         return error(_(\"failed to set extensions.worktreeConfig setting\"));\n>       +\n>      -+ if (upgrade_repository_format(r, 1) < 0) {\n>      -+         res = error(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n>      -+         goto cleanup;\n>      -+ }\n>      -+ if ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\"))) {\n>      -+         error(_(\"failed to set extensions.worktreeConfig setting\"));\n>      -+         goto cleanup;\n>      -+ }\n>      ++ git_configset_init(&cs);\n>      ++ git_configset_add_file(&cs, common_config_file);\n>       +\n>       + /*\n>       +  * If core.bare is true in the common config file, then we need to\n>      @@ builtin/worktree.c: static int repair(int ac, const char **av, const char *prefi\n>       +                 goto cleanup;\n>       + }\n>       +\n>      ++ /*\n>      ++  * Ensure that we use worktree config for the remaining lifetime\n>      ++  * of the current process.\n>      ++  */\n>      ++ repository_format_worktree_config = 1;\n>      ++\n>       +cleanup:\n>       + git_configset_clear(&cs);\n>       + free(common_config_file);\n>       + free(main_worktree_file);\n>       + return res;\n>       +}\n>      -+\n>      - int cmd_worktree(int ac, const char **av, const char *prefix)\n>      - {\n>      -  struct option options[] = {\n>      -@@ builtin/worktree.c: int cmd_worktree(int ac, const char **av, const char *prefix)\n>      -          prefix = \"\";\n>      -  if (!strcmp(av[1], \"add\"))\n>      -          return add(ac - 1, av + 1, prefix);\n>      -+ if (!strcmp(av[1], \"init-worktree-config\"))\n>      -+         return init_worktree_config(ac - 1, av + 1, prefix);\n>      -  if (!strcmp(av[1], \"prune\"))\n>      -          return prune(ac - 1, av + 1, prefix);\n>      -  if (!strcmp(av[1], \"list\"))\n>\n>      - ## t/t2407-worktree-init-worktree-config.sh (new) ##\n>      -@@\n>      -+#!/bin/sh\n>      -+\n>      -+test_description='test git worktree init-worktree-config'\n>      -+\n>      -+. ./test-lib.sh\n>      -+\n>      -+test_expect_success setup '\n>      -+ git init base &&\n>      -+ test_commit -C base commit &&\n>      -+ git -C base worktree add --detach worktree\n>      -+'\n>      -+\n>      -+reset_config_when_finished () {\n>      -+ test_when_finished git -C base config --unset core.repositoryFormatVersion &&\n>      -+ test_when_finished git -C base config --unset extensions.worktreeConfig &&\n>      -+ rm -rf base/.git/config.worktree &&\n>      -+ rm -rf base/.git/worktrees/worktree/config.worktree\n>      -+}\n>      -+\n>      -+test_expect_success 'upgrades repo format and adds extension' '\n>      -+ reset_config_when_finished &&\n>      -+ git -C base worktree init-worktree-config >out 2>err &&\n>      -+ test_must_be_empty out &&\n>      -+ test_must_be_empty err &&\n>      -+ test_cmp_config -C base 1 core.repositoryFormatVersion &&\n>      -+ test_cmp_config -C base true extensions.worktreeConfig\n>      -+'\n>      -+\n>      -+test_expect_success 'relocates core.worktree' '\n>      -+ reset_config_when_finished &&\n>      -+ mkdir dir &&\n>      -+ git -C base config core.worktree ../../dir &&\n>      -+ git -C base worktree init-worktree-config >out 2>err &&\n>      -+ test_must_be_empty out &&\n>      -+ test_must_be_empty err &&\n>      -+ test_cmp_config -C base 1 core.repositoryFormatVersion &&\n>      -+ test_cmp_config -C base true extensions.worktreeConfig &&\n>      -+ test_cmp_config -C base ../../dir core.worktree &&\n>      -+ test_must_fail git -C worktree core.worktree\n>      -+'\n>      -+\n>      -+test_expect_success 'relocates core.bare' '\n>      -+ reset_config_when_finished &&\n>      -+ git -C base config core.bare true &&\n>      -+ git -C base worktree init-worktree-config >out 2>err &&\n>      -+ test_must_be_empty out &&\n>      -+ test_must_be_empty err &&\n>      -+ test_cmp_config -C base 1 core.repositoryFormatVersion &&\n>      -+ test_cmp_config -C base true extensions.worktreeConfig &&\n>      -+ test_cmp_config -C base true core.bare &&\n>      -+ test_must_fail git -C worktree core.bare\n>      -+'\n>      -+\n>      -+test_expect_success 'skips upgrade is already upgraded' '\n>      -+ reset_config_when_finished &&\n>      -+ git -C base worktree init-worktree-config &&\n>      -+ git -C base config core.bare true &&\n>      -+\n>      -+ # this should be a no-op, even though core.bare\n>      -+ # makes the worktree be broken.\n>      -+ git -C base worktree init-worktree-config >out 2>err &&\n>      -+ test_must_be_empty out &&\n>      -+ test_must_be_empty err &&\n>      -+ test_must_fail git -C base config --worktree core.bare &&\n>      -+ git -C base config core.bare\n>      -+'\n>      -+\n>      -+test_done\n>      + ## worktree.h ##\n>      +@@ worktree.h: void strbuf_worktree_ref(const struct worktree *wt,\n>      +                   struct strbuf *sb,\n>      +                   const char *refname);\n>      +\n>      ++/**\n>      ++ * Enable worktree config for the first time. This will make the following\n>      ++ * adjustments:\n>      ++ *\n>      ++ * 1. Add extensions.worktreeConfig=true in the common config file.\n>      ++ *\n>      ++ * 2. If the common config file has a core.worktree value or core.bare is\n>      ++ *    set to true, then those values are moved to the main worktree's\n>      ++ *    config.worktree file.\n>      ++ *\n>      ++ * If extensions.worktreeConfig is already true, then this method\n>      ++ * terminates early without any of the above steps. The existing config\n>      ++ * arrangement is assumed to be intentional.\n>      ++ *\n>      ++ * Returns 0 on success. Reports an error message and returns non-zero\n>      ++ * if any of these steps fail.\n>      ++ */\n>      ++int init_worktree_config(struct repository *r);\n>      ++\n>      + #endif\n>  4:  45316cd01c9 ! 3:  110d5e0546c config: add repo_config_set_worktree_gently()\n>      @@ config.c: int git_config_set_gently(const char *key, const char *value)\n>       +         free(file);\n>       +         return ret;\n>       + }\n>      -+ return repo_config_set_gently(r, key, value);\n>      ++ return repo_config_set_multivar_gently(r, key, value, NULL, 0);\n>       +}\n>       +\n>        void git_config_set(const char *key, const char *value)\n>        {\n>      -  repo_config_set(the_repository, key, value);\n>      -@@ config.c: int repo_config_set_multivar_gently(struct repository *r, const char *key,\n>      -                                                flags);\n>      - }\n>      -\n>      -+int repo_config_set_gently(struct repository *r,\n>      -+                    const char *key, const char *value)\n>      -+{\n>      -+ return repo_config_set_multivar_gently(r, key, value, NULL, 0);\n>      +  git_config_set_multivar(key, value, NULL, 0);\n>      +@@ config.c: void git_config_set_multivar_in_file(const char *config_filename,\n>      + int git_config_set_multivar_gently(const char *key, const char *value,\n>      +                             const char *value_pattern, unsigned flags)\n>      + {\n>      +- return git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n>      +-                                               flags);\n>      ++ return repo_config_set_multivar_gently(the_repository, key, value,\n>      ++                                        value_pattern, flags);\n>       +}\n>       +\n>      ++int repo_config_set_multivar_gently(struct repository *r, const char *key,\n>      ++                             const char *value,\n>      ++                             const char *value_pattern, unsigned flags)\n>      ++{\n>      ++ char *file = repo_git_path(r, \"config\");\n>      ++ int res = git_config_set_multivar_in_file_gently(file,\n>      ++                                                  key, value,\n>      ++                                                  value_pattern,\n>      ++                                                  flags);\n>      ++ free(file);\n>      ++ return res;\n>      + }\n>      +\n>        void git_config_set_multivar(const char *key, const char *value,\n>                              const char *value_pattern, unsigned flags)\n>        {\n>      +- git_config_set_multivar_in_file(NULL, key, value, value_pattern,\n>      ++ git_config_set_multivar_in_file(git_path(\"config\"),\n>      ++                                 key, value, value_pattern,\n>      +                                  flags);\n>      + }\n>      +\n>\n>        ## config.h ##\n>       @@ config.h: void git_config_set_in_file(const char *, const char *, const char *);\n>      @@ config.h: void git_config_set_in_file(const char *, const char *, const char *);\n>        /**\n>         * write config values to `.git/config`, takes a key/value pair as parameter.\n>         */\n>      -@@ config.h: int git_config_set_multivar_gently(const char *, const char *, const char *, uns\n>      +@@ config.h: int git_config_parse_key(const char *, char **, size_t *);\n>      +\n>      + int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n>        void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n>      - int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n>      - void repo_config_set_multivar(struct repository *, const char *, const char *, const char *, unsigned);\n>      -+int repo_config_set_gently(struct repository *, const char *, const char *);\n>      ++int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n>        int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n>\n>        /**\n>  5:  b200819c1bb ! 4:  fbfaa17797c sparse-checkout: use repo_config_set_worktree_gently()\n>      @@ Metadata\n>       Author: Derrick Stolee <dstolee@microsoft.com>\n>\n>        ## Commit message ##\n>      -    sparse-checkout: use repo_config_set_worktree_gently()\n>      +    sparse-checkout: set worktree-config correctly\n>\n>           The previous change added repo_config_set_worktree_gently() to assist\n>      -    writing config values into the worktree.config file, if enabled.\n>      +    writing config values into the config.worktree file, if enabled. An\n>      +    earlier change added init_worktree_config() as a helper to initialize\n>      +    extensions.worktreeConfig if not already enabled.\n>\n>      -    Let the sparse-checkout builtin use this helper instead of attempting to\n>      +    Let the sparse-checkout builtin use these helpers instead of attempting to\n>           initialize the worktree config on its own. This changes behavior of 'git\n>           sparse-checkout set' in a few important ways:\n>\n>      -     1. Git will no longer upgrade the repository format and add the\n>      -        worktree config extension. The user should run 'git worktree\n>      -        init-worktree-config' to enable this feature.\n>      +     1. Git will no longer upgrade the repository format, since this is not\n>      +        a requirement for understanding extensions.worktreeConfig.\n>\n>      -     2. If worktree config is disabled, then this command will set the\n>      -        core.sparseCheckout (and possibly core.sparseCheckoutCone and\n>      -        index.sparse) values in the common config file.\n>      -\n>      -     3. If the main worktree is bare, then this command will not put the\n>      +     2. If the main worktree is bare, then this command will not put the\n>               worktree in a broken state.\n>\n>           The main reason to use worktree-specific config for the sparse-checkout\n>      @@ Commit message\n>           sparse-checkout patterns file, then the sparse-checkout logic will not\n>           kick in on that worktree.\n>\n>      -    This new logic introduces a new user pattern that could lead to some\n>      -    confusion. Suppose a user has not upgraded to worktree config and\n>      -    follows these steps in order:\n>      -\n>      -     1. Enable sparse-checkout in a worktree.\n>      -\n>      -     2. Disable sparse-checkout in that worktree without deleting that\n>      -        worktree's sparse-checkout file.\n>      -\n>      -     3. Enable sparse-checkout in another worktree.\n>      -\n>      -    After these steps, the first worktree will have sparse-checkout enabled\n>      -    with whatever patterns exist. The worktree does not immediately have\n>      -    those patterns applied, but a variety of Git commands would apply the\n>      -    sparse-checkout patterns and update the worktree state to reflect those\n>      -    patterns. This situation is likely very rare and the workaround is to\n>      -    upgrade to worktree specific config on purpose. Users already in this\n>      -    state used the sparse-checkout builtin with a version that upgraded to\n>      -    worktree config, anyway.\n>      -\n>           Reported-by: Sean Allred <allred.sean@gmail.com>\n>           Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n>           Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n>\n>      + ## Documentation/git-sparse-checkout.txt ##\n>      +@@ Documentation/git-sparse-checkout.txt: COMMANDS\n>      +  Describe the patterns in the sparse-checkout file.\n>      +\n>      + 'set'::\n>      +- Enable the necessary config settings\n>      +- (extensions.worktreeConfig, core.sparseCheckout,\n>      +- core.sparseCheckoutCone) if they are not already enabled, and\n>      +- write a set of patterns to the sparse-checkout file from the\n>      +- list of arguments following the 'set' subcommand. Update the\n>      +- working directory to match the new patterns.\n>      ++ Enable the necessary sparse-checkout config settings\n>      ++ (`core.sparseCheckout` and possibly `core.sparseCheckoutCone`) if\n>      ++ they are not already enabled, and write a set of patterns to the\n>      ++ sparse-checkout file from the list of arguments following the\n>      ++ 'set' subcommand. Update the working directory to match the new\n>      ++ patterns.\n>      +++\n>      ++To ensure that adjusting the sparse-checkout settings within a worktree\n>      ++does not alter the sparse-checkout settings in other worktrees, the 'set'\n>      ++subcommand will upgrade your repository config to use worktree-specific\n>      ++config if not already present. The sparsity defined by the arguments to\n>      ++the 'set' subcommand are stored in the worktree-specific sparse-checkout\n>      ++file. See linkgit:git-worktree[1] and the documentation of\n>      ++`extensions.worktreeConfig` in linkgit:git-config[1] for more details.\n>      + +\n>      + When the `--stdin` option is provided, the patterns are read from\n>      + standard in as a newline-delimited list instead of from the arguments.\n>      +@@ Documentation/git-sparse-checkout.txt: interact with your repository until it is disabled.\n>      +  By default, these patterns are read from the command-line arguments,\n>      +  but they can be read from stdin using the `--stdin` option. When\n>      +  `core.sparseCheckoutCone` is enabled, the given patterns are interpreted\n>      +- as directory names as in the 'set' subcommand.\n>      ++ as directory names as in the 'set' subcommand. The sparsity defined\n>      ++ by the arguments to the 'add' subcommand are added to the patterns\n>      ++ in the worktree-specific sparse-checkout file.\n>      +\n>      + 'reapply'::\n>      +  Reapply the sparsity pattern rules to paths in the working tree.\n>      +\n>        ## builtin/sparse-checkout.c ##\n>      +@@\n>      + #include \"wt-status.h\"\n>      + #include \"quote.h\"\n>      + #include \"sparse-index.h\"\n>      ++#include \"worktree.h\"\n>      +\n>      + static const char *empty_base = \"\";\n>      +\n>       @@ builtin/sparse-checkout.c: enum sparse_checkout_mode {\n>\n>        static int set_config(enum sparse_checkout_mode mode)\n>        {\n>       - const char *config_path;\n>       -\n>      -- if (upgrade_repository_format(the_repository, 1) < 0)\n>      +- if (upgrade_repository_format(1) < 0)\n>       -         die(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n>       - if (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n>       -         error(_(\"failed to set extensions.worktreeConfig setting\"));\n>      -+ if (repo_config_set_worktree_gently(the_repository,\n>      -+                                     \"core.sparseCheckout\",\n>      -+                                     mode ? \"true\" : \"false\") ||\n>      -+     repo_config_set_worktree_gently(the_repository,\n>      -+                                     \"core.sparseCheckoutCone\",\n>      -+                                     mode == MODE_CONE_PATTERNS ?\n>      -+                                         \"true\" : \"false\"))\n>      ++ /* Update to use worktree config, if not already. */\n>      ++ if (init_worktree_config(the_repository)) {\n>      ++         error(_(\"failed to initialize worktree config\"));\n>                 return 1;\n>      -- }\n>      --\n>      +  }\n>      +\n>       - config_path = git_path(\"config.worktree\");\n>       - git_config_set_in_file_gently(config_path,\n>       -                               \"core.sparseCheckout\",\n>      @@ builtin/sparse-checkout.c: enum sparse_checkout_mode {\n>       - git_config_set_in_file_gently(config_path,\n>       -                               \"core.sparseCheckoutCone\",\n>       -                               mode == MODE_CONE_PATTERNS ? \"true\" : NULL);\n>      ++ if (repo_config_set_worktree_gently(the_repository,\n>      ++                                     \"core.sparseCheckout\",\n>      ++                                     mode ? \"true\" : \"false\") ||\n>      ++     repo_config_set_worktree_gently(the_repository,\n>      ++                                     \"core.sparseCheckoutCone\",\n>      ++                                     mode == MODE_CONE_PATTERNS ?\n>      ++                                         \"true\" : \"false\"))\n>      ++         return 1;\n>\n>         if (mode == MODE_NO_PATTERNS)\n>       -         set_sparse_index_config(the_repository, 0);\n>      @@ sparse-index.c: static int convert_to_sparse_rec(struct index_state *istate,\n>         return res;\n>\n>        ## t/t1091-sparse-checkout-builtin.sh ##\n>      -@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'interaction with clone --no-checkout (unborn index)' '\n>      - '\n>      -\n>      - test_expect_success 'set enables config' '\n>      -- git init empty-config &&\n>      -+ git init initial-config &&\n>      -  (\n>      --         cd empty-config &&\n>      -+         cd initial-config &&\n>      -+         test_commit file file &&\n>      -+         mkdir dir &&\n>      -+         test_commit dir dir/file &&\n>      -+         git worktree add --detach ../initial-worktree &&\n>      -+         git sparse-checkout set --cone\n>      -+ ) &&\n>      -+ test_cmp_config -C initial-config true core.sparseCheckout &&\n>      -+ test_cmp_config -C initial-worktree true core.sparseCheckout &&\n>      -+ test_cmp_config -C initial-config true core.sparseCheckoutCone &&\n>      -+ test_cmp_config -C initial-worktree true core.sparseCheckoutCone &&\n>      -+\n>      -+ # initial-config has a sparse-checkout file\n>      -+ # that only contains files at root.\n>      -+ ls initial-config >only-file &&\n>      -+ cat >expect <<-EOF &&\n>      -+ file\n>      -+ EOF\n>      -+ test_cmp expect only-file &&\n>      -+\n>      -+ # initial-worktree does not have its own sparse-checkout\n>      -+ # file, so the repply does not modify the worktree at all.\n>      -+ git -C initial-worktree sparse-checkout reapply &&\n>      -+ ls initial-worktree >all &&\n>      -+ cat >expect <<-EOF &&\n>      -+ dir\n>      -+ file\n>      -+ EOF\n>      -+ test_cmp expect all\n>      -+'\n>      -+\n>      -+test_expect_success 'set enables worktree config, if enabled' '\n>      -+ git init worktree-config &&\n>      -+ (\n>      -+         cd worktree-config &&\n>      -          test_commit test file &&\n>      --         test_path_is_missing .git/config.worktree &&\n>      --         git sparse-checkout set nothing &&\n>      --         test_path_is_file .git/config.worktree &&\n>      --         test_cmp_config true core.sparseCheckout\n>      -- )\n>      -+         git worktree add --detach ../worktree-config2 &&\n>      -+         git worktree init-worktree-config &&\n>      -+         git sparse-checkout set --cone &&\n>      -+         git config --worktree core.sparseCheckout &&\n>      -+         git config --worktree core.sparseCheckoutCone\n>      -+ ) &&\n>      -+ test_cmp_config -C worktree-config true core.sparseCheckout &&\n>      -+ test_must_fail git -C worktree-config2 core.sparseCheckout &&\n>      -+ test_cmp_config -C worktree-config true core.sparseCheckoutCone &&\n>      -+ test_must_fail git -C worktree-config2 core.sparseCheckoutCone\n>      - '\n>      -\n>      - test_expect_success 'set sparse-checkout using builtin' '\n>      -@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'add to sparse-checkout' '\n>      - '\n>      -\n>      - test_expect_success 'cone mode: match patterns' '\n>      -+ git -C repo worktree init-worktree-config &&\n>      -  git -C repo config --worktree core.sparseCheckoutCone true &&\n>      -  rm -rf repo/a repo/folder1 repo/folder2 &&\n>      -  git -C repo read-tree -mu HEAD 2>err &&\n>      +@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'switching to cone mode with non-cone mode patterns' '\n>      +          cd bad-patterns &&\n>      +          git sparse-checkout init &&\n>      +          git sparse-checkout add dir &&\n>      +-         git config core.sparseCheckoutCone true &&\n>      ++         git config --worktree core.sparseCheckoutCone true &&\n>      +          test_must_fail git sparse-checkout add dir 2>err &&\n>      +          grep \"existing sparse-checkout patterns do not use cone mode\" err\n>      +  )\n>       @@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'sparse-index enabled and disabled' '\n>      -          test-tool -C repo read-cache --table >cache &&\n>      -          ! grep \" tree \" cache &&\n>      +          test_cmp expect actual &&\n>      +\n>                 git -C repo config --list >config &&\n>       -         ! grep index.sparse config\n>       +         test_cmp_config -C repo false index.sparse\n>         )\n>        '\n>\n>      -@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'fail when lock is taken' '\n>      - '\n>      -\n>      - test_expect_success '.gitignore should not warn about cone mode' '\n>      -+ git -C repo worktree init-worktree-config &&\n>      -  git -C repo config --worktree core.sparseCheckoutCone true &&\n>      -  echo \"**/bin/*\" >repo/.gitignore &&\n>      -  git -C repo reset --hard 2>err &&\n>  6:  fcece09546c ! 5:  bb9e550ff3d worktree: copy sparse-checkout patterns and config on add\n>      @@ Commit message\n>           In addition to the sparse-checkout file, copy the worktree config file\n>           if worktree config is enabled and the file exists. This will copy over\n>           any important settings to ensure the new worktree behaves the same as\n>      -    the current one.\n>      +    the current one. The only exception we must continue to make is that\n>      +    core.bare and core.worktree should become unset in the worktree's config\n>      +    file.\n>\n>           Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n>\n>      @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam\n>       + }\n>       +\n>       + /*\n>      -+  * If we are using worktree config, then copy all currenct config\n>      ++  * If we are using worktree config, then copy all current config\n>       +  * values from the current worktree into the new one, that way the\n>       +  * new worktree behaves the same as this one.\n>       +  */\n>      @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam\n>       +                                 realpath.buf, name);\n>       +\n>       +         if (file_exists(from_file)) {\n>      ++                 struct config_set cs = { { 0 }};\n>      ++                 const char *str_value;\n>      ++                 int bool_value;\n>      ++\n>       +                 if (safe_create_leading_directories(to_file) ||\n>       +                     copy_file(to_file, from_file, 0666))\n>      -+                         error(_(\"failed to copy worktree config from '%s' to '%s'\"),\n>      -+                               from_file, to_file);\n>      ++                         die(_(\"failed to copy worktree config from '%s' to '%s'\"),\n>      ++                             from_file, to_file);\n>      ++\n>      ++                 git_configset_init(&cs);\n>      ++                 git_configset_add_file(&cs, from_file);\n>      ++\n>      ++                 if (!git_configset_get_bool(&cs, \"core.bare\", &bool_value) &&\n>      ++                     bool_value &&\n>      ++                     git_config_set_multivar_in_file_gently(\n>      ++                                 to_file, \"core.bare\", NULL, \"true\", 0))\n>      ++                         error(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n>      ++                 if (!git_configset_get_value(&cs, \"core.worktree\", &str_value) &&\n>      ++                     git_config_set_in_file_gently(to_file,\n>      ++                                                   \"core.worktree\", NULL))\n>      ++                         error(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n>      ++\n>      ++                 git_configset_clear(&cs);\n>       +         }\n>       +\n>       +         free(from_file);\n>      @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam\n>         cp.git_cmd = 1;\n>\n>        ## t/t1091-sparse-checkout-builtin.sh ##\n>      -@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'set enables config' '\n>      +@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'interaction with clone --no-checkout (unborn index)' '\n>        '\n>\n>      - test_expect_success 'set enables worktree config, if enabled' '\n>      -+ git init worktree-patterns &&\n>      -+ (\n>      -+         cd worktree-patterns &&\n>      -+         test_commit test file &&\n>      -+         mkdir dir dir2 &&\n>      -+         test_commit dir dir/file &&\n>      -+         test_commit dir2 dir2/file &&\n>      -+\n>      -+         # By initializing the worktree config here...\n>      -+         git worktree init-worktree-config &&\n>      -+\n>      -+         # This set command places config values in worktree-\n>      -+         # specific config...\n>      -+         git sparse-checkout set --cone dir &&\n>      -+\n>      -+         # Which must be copied, along with the sparse-checkout\n>      -+         # patterns, here.\n>      -+         git worktree add --detach ../worktree-patterns2\n>      -+ ) &&\n>      -+ test_cmp_config -C worktree-patterns true core.sparseCheckout &&\n>      -+ test_cmp_config -C worktree-patterns2 true core.sparseCheckout &&\n>      -+ test_cmp_config -C worktree-patterns true core.sparseCheckoutCone &&\n>      -+ test_cmp_config -C worktree-patterns2 true core.sparseCheckoutCone &&\n>      -+ test_cmp worktree-patterns/.git/info/sparse-checkout \\\n>      -+          worktree-patterns/.git/worktrees/worktree-patterns2/info/sparse-checkout &&\n>      -+\n>      -+ ls worktree-patterns >expect &&\n>      -+ ls worktree-patterns2 >actual &&\n>      -+ test_cmp expect actual &&\n>      -+\n>      -+ # Double check that the copy works from a non-main worktree.\n>      -+ (\n>      -+         cd worktree-patterns2 &&\n>      -+         git sparse-checkout set dir2 &&\n>      -+         git worktree add --detach ../worktree-patterns3\n>      -+ ) &&\n>      -+ test_cmp_config -C worktree-patterns3 true core.sparseCheckout &&\n>      -+ test_cmp_config -C worktree-patterns3 true core.sparseCheckoutCone &&\n>      -+ test_cmp worktree-patterns/.git/worktrees/worktree-patterns2/info/sparse-checkout \\\n>      -+          worktree-patterns/.git/worktrees/worktree-patterns3/info/sparse-checkout &&\n>      -+\n>      -+ ls worktree-patterns2 >expect &&\n>      -+ ls worktree-patterns3 >actual &&\n>      -+ test_cmp expect actual\n>      + test_expect_success 'set enables config' '\n>      +- git init empty-config &&\n>      ++ git init worktree-config &&\n>      +  (\n>      +-         cd empty-config &&\n>      ++         cd worktree-config &&\n>      +          test_commit test file &&\n>      +          test_path_is_missing .git/config.worktree &&\n>      +          git sparse-checkout set nothing &&\n>      +@@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'add to sparse-checkout' '\n>      +  check_files repo \"a folder1 folder2\"\n>      + '\n>      +\n>      ++test_expect_success 'worktree: add copies sparse-checkout patterns' '\n>      ++ cat repo/.git/info/sparse-checkout >old &&\n>      ++ test_when_finished cp old repo/.git/info/sparse-checkout &&\n>      ++ test_when_finished git -C repo worktree remove ../worktree &&\n>      ++ git -C repo sparse-checkout set \"/*\" &&\n>      ++ git -C repo worktree add --quiet ../worktree 2>err &&\n>      ++ test_must_be_empty err &&\n>      ++ new=repo/.git/worktrees/worktree/info/sparse-checkout &&\n>      ++ test_path_is_file $new &&\n>      ++ test_cmp repo/.git/info/sparse-checkout $new &&\n>      ++ git -C worktree sparse-checkout set --cone &&\n>      ++ test_cmp_config -C worktree true core.sparseCheckoutCone &&\n>      ++ test_must_fail git -C repo core.sparseCheckoutCone\n>       +'\n>       +\n>      -+test_expect_success 'worktree add copies sparse-checkout patterns' '\n>      -  git init worktree-config &&\n>      -  (\n>      -          cd worktree-config &&\n>      + test_expect_success 'cone mode: match patterns' '\n>      +  git -C repo config --worktree core.sparseCheckoutCone true &&\n>      +  rm -rf repo/a repo/folder1 repo/folder2 &&\n>       @@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'interaction with submodules' '\n>      + '\n>\n>        test_expect_success 'different sparse-checkouts with worktrees' '\n>      ++ git -C repo sparse-checkout set --cone deep folder1 &&\n>         git -C repo worktree add --detach ../worktree &&\n>       - check_files worktree \"a deep folder1 folder2\" &&\n>      -+ check_files worktree \"a folder1\" &&\n>      -  git -C worktree sparse-checkout init --cone &&\n>      +- git -C worktree sparse-checkout init --cone &&\n>       - git -C repo sparse-checkout set folder1 &&\n>      -+ git -C repo sparse-checkout set folder1 folder2 &&\n>      -  git -C worktree sparse-checkout set deep/deeper1 &&\n>      +- git -C worktree sparse-checkout set deep/deeper1 &&\n>       - check_files repo a folder1 &&\n>      -+ check_files repo a folder1 folder2 &&\n>      -  check_files worktree a deep\n>      +- check_files worktree a deep\n>      ++ check_files worktree \"a deep folder1\" &&\n>      ++ git -C repo sparse-checkout set --cone folder1 &&\n>      ++ git -C worktree sparse-checkout set --cone deep/deeper1 &&\n>      ++ check_files repo \"a folder1\" &&\n>      ++ check_files worktree \"a deep\"\n>      + '\n>      +\n>      + test_expect_success 'set using filename keeps file on-disk' '\n>      +\n>      + ## t/t2400-worktree-add.sh ##\n>      +@@ t/t2400-worktree-add.sh: test_expect_success '\"add\" default branch of a bare repo' '\n>      +  (\n>      +          git clone --bare . bare2 &&\n>      +          cd bare2 &&\n>      +-         git worktree add ../there3 main\n>      +- )\n>      ++         git worktree add ../there3 main &&\n>      ++         cd ../there3 &&\n>      ++         git status\n>      ++ ) &&\n>      ++ cat >expect <<-EOF &&\n>      ++ init.t\n>      ++ EOF\n>      ++ ls there3 >actual &&\n>      ++ test_cmp expect actual\n>      ++'\n>      ++\n>      ++test_expect_success '\"add\" to bare repo with worktree config' '\n>      ++ (\n>      ++         git clone --bare . bare3 &&\n>      ++         cd bare3 &&\n>      ++         git config extensions.worktreeconfig true &&\n>      ++         git config --worktree core.bare true &&\n>      ++         git config --worktree core.worktree \"$(pwd)\" &&\n>      ++         git config --worktree bogus.key value &&\n>      ++         git config --unset core.bare &&\n>      ++         git worktree add ../there4 main &&\n>      ++         cd ../there4 &&\n>      ++         git status &&\n>      ++         git worktree add --detach ../there5 &&\n>      ++         cd ../there5 &&\n>      ++         git status\n>      ++ ) &&\n>      ++\n>      ++ # the worktree has the arbitrary value copied.\n>      ++ test_cmp_config -C there4 value bogus.key &&\n>      ++ test_cmp_config -C there5 value bogus.key &&\n>      ++\n>      ++ # however, core.bare and core.worktree were removed.\n>      ++ test_must_fail git -C there4 config core.bare &&\n>      ++ test_must_fail git -C there4 config core.worktree &&\n>      ++\n>      ++ cat >expect <<-EOF &&\n>      ++ init.t\n>      ++ EOF\n>      ++\n>      ++ ls there4 >actual &&\n>      ++ test_cmp expect actual &&\n>      ++ ls there5 >actual &&\n>      ++ test_cmp expect actual\n>        '\n>\n>      + test_expect_success 'checkout with grafts' '\n>\n> --\n> gitgitgadget\n"},{"id":"447076","messageId":"8ef22055-081f-9c52-e290-5284ee4017e5@gmail.com","threadId":"57125","inReplyTo":"0e8ef47b-7bb7-df38-1d97-90fda4471bbb@gmail.com","subject":"Re: [PATCH v4 1/5] Documentation: add extensions.worktreeConfig details","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-01-27T14:15:41Z","receivedAt":"2022-01-27T14:15:51Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 1/26/2022 1:59 AM, Bagas Sanjaya wrote:\n> On 26/01/22 01.42, Derrick Stolee via GitGitGadget wrote:\n>> As documented in 11664196ac (\"Revert \"check_repository_format_gently():\n>> refuse extensions for old repositories\"\", 2020-07-15), this extension\n>> must be considered regardless of the repository format version for\n>> historical reasons.\n>>\n> ...\n>> +For historical reasons, `extensions.worktreeConfig` is respected\n>> +regardless of the `core.repositoryFormatVersion` setting.\n> \n> This implies `extensions.worktreeConfig` become integral part of every\n> repository format version, from the past until now and to the future,\n> right?\n \nRight. What I'm saying is that this is already the case. I'm not\nchanging it. The commit message in 116664196ac discusses this in\ndepth. I'm pointing it out carefully because it is news to me\n(hence why the sparse-checkout builtin updates the repository\nformat version before this series).\n\nThanks,\n-Stolee\n"},{"id":"447077","messageId":"58353c02-70bb-b05a-16e8-cfc37b290e29@gmail.com","threadId":"57125","inReplyTo":"CABPp-BFiJnB0u6N5JViS7T_QrC=247gRto7R8DJ5s+f2nP_bKA@mail.gmail.com","subject":"Re: [PATCH v4 1/5] Documentation: add extensions.worktreeConfig details","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-01-27T14:17:29Z","receivedAt":"2022-01-27T14:17:33Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 1/27/2022 1:43 AM, Elijah Newren wrote:\n> On Tue, Jan 25, 2022 at 10:42 AM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n\n>>  --worktree::\n>> -       Similar to `--local` except that `.git/config.worktree` is\n>> +       Similar to `--local` except that `$GIT_DIR/config.worktree` is\n>>         read from or written to if `extensions.worktreeConfig` is\n>> -       present. If not it's the same as `--local`.\n>> +       enabled. If not it's the same as `--local`. Note that `$GIT_DIR`\n>> +       is equal to `$GIT_COMMON_DIR` for the main worktree, but is of the\n>> +       form `.git/worktrees/<worktree-name>/` for other worktrees. See\n> \n> is of the form `$GIT_DIR/worktrees/<worktree-name>/`; .git isn't even\n> a directory in other worktrees.\n\nThanks. I tried to catch all of these, but I missed one.\n \n>> +       linkgit:git-worktree[1] to learn how to enable\n>> +       `extensions.worktreeConfig`.\n>>\n>>  -f <config-file>::\n>>  --file <config-file>::\n>> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\n>> index 9e862fbcf79..ea0ee9f8bb5 100644\n>> --- a/Documentation/git-worktree.txt\n>> +++ b/Documentation/git-worktree.txt\n>> @@ -286,8 +286,8 @@ CONFIGURATION FILE\n>>  ------------------\n>>  By default, the repository `config` file is shared across all working\n>>  trees. If the config variables `core.bare` or `core.worktree` are\n>> -already present in the config file, they will be applied to the main\n>> -working trees only.\n>> +present in the common config file and `extensions.worktreeConfig` is\n>> +disabled, then they will be applied to the main working trees only.\n> \n> \"main working trees\"?  Is that an accidental plural?\n\nYes. Thanks.\n\n>>  In order to have configuration specific to working trees, you can turn\n>>  on the `worktreeConfig` extension, e.g.:\n>> @@ -307,11 +307,16 @@ them to the `config.worktree` of the main working tree. You may also\n>>  take this opportunity to review and move other configuration that you\n>>  do not want to share to all working trees:\n>>\n>> - - `core.worktree` and `core.bare` should never be shared\n>> + - `core.worktree` should never be shared.\n>> +\n>> + - `core.bare` should not be shared unless the value is `core.bare=false`.\n> \n> The double negative makes for harder parsing.  Perhaps\n>     - `core.bare` should not be shared if the value is `core.bare=true`\n> ?\n\nThat is cleaner. Excellent.\n\n>>   - `core.sparseCheckout` is recommended per working tree, unless you\n>>     are sure you always use sparse checkout for all working trees.\n>>\n>> +See the documentation of `extensions.worktreeConfig` in\n>> +linkgit:git-config[1] for more details.\n>> +\n>>  DETAILS\n>>  -------\n>>  Each linked working tree has a private sub-directory in the repository's\n>> --\n>> gitgitgadget\n> \n> Thanks for documenting these details; I had some very minor comments\n> but this is well written and very helpful.\n\nThanks for your recommended improvements.\n-Stolee\n"},{"id":"447078","messageId":"eb7e927b-b47e-1d92-164d-d0a4d30f6ac6@gmail.com","threadId":"57125","inReplyTo":"CABPp-BHBMUar-ysiJsjmpqQ3NPjRUiBPKEdHty3ADBYaf0EMug@mail.gmail.com","subject":"Re: [PATCH v4 4/5] sparse-checkout: set worktree-config correctly","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-01-27T14:24:55Z","receivedAt":"2022-01-27T14:25:01Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 1/27/2022 2:15 AM, Elijah Newren wrote:\n> On Tue, Jan 25, 2022 at 10:42 AM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>>  'set'::\n>> -       Enable the necessary config settings\n>> -       (extensions.worktreeConfig, core.sparseCheckout,\n>> -       core.sparseCheckoutCone) if they are not already enabled, and\n>> -       write a set of patterns to the sparse-checkout file from the\n>> -       list of arguments following the 'set' subcommand. Update the\n>> -       working directory to match the new patterns.\n>> +       Enable the necessary sparse-checkout config settings\n>> +       (`core.sparseCheckout` and possibly `core.sparseCheckoutCone`) if\n> \n> and possibly index.sparse as well, right?\n\nGood catch.\n\n>> +       they are not already enabled, and write a set of patterns to the\n>> +       sparse-checkout file from the list of arguments following the\n>> +       'set' subcommand. Update the working directory to match the new\n>> +       patterns.\n>> ++\n>> +To ensure that adjusting the sparse-checkout settings within a worktree\n>> +does not alter the sparse-checkout settings in other worktrees, the 'set'\n>> +subcommand will upgrade your repository config to use worktree-specific\n>> +config if not already present. The sparsity defined by the arguments to\n>> +the 'set' subcommand are stored in the worktree-specific sparse-checkout\n>> +file. See linkgit:git-worktree[1] and the documentation of\n>> +`extensions.worktreeConfig` in linkgit:git-config[1] for more details.\n>>  +\n>>  When the `--stdin` option is provided, the patterns are read from\n>>  standard in as a newline-delimited list instead of from the arguments.\n>> @@ -73,7 +81,9 @@ interact with your repository until it is disabled.\n>>         By default, these patterns are read from the command-line arguments,\n>>         but they can be read from stdin using the `--stdin` option. When\n>>         `core.sparseCheckoutCone` is enabled, the given patterns are interpreted\n>> -       as directory names as in the 'set' subcommand.\n>> +       as directory names as in the 'set' subcommand. The sparsity defined\n>> +       by the arguments to the 'add' subcommand are added to the patterns\n>> +       in the worktree-specific sparse-checkout file.\n> \n> This sentence addition makes your series conflict with patch 4 of my\n> en/present-despite-skipped series.\n> \n> The sentence also seems somewhat redundant with the first sentence of\n> the paragraph (not quoted here).  Perhaps consider just dropping it to\n> make it easier for Junio to integrate?\n\nI'll just drop it. Thanks.\n\n>>  'reapply'::\n>>         Reapply the sparsity pattern rules to paths in the working tree.\n>> diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\n>> index 679c1070368..314c8d61f80 100644\n>> --- a/builtin/sparse-checkout.c\n>> +++ b/builtin/sparse-checkout.c\n>> @@ -15,6 +15,7 @@\n>>  #include \"wt-status.h\"\n>>  #include \"quote.h\"\n>>  #include \"sparse-index.h\"\n>> +#include \"worktree.h\"\n>>\n>>  static const char *empty_base = \"\";\n>>\n>> @@ -359,26 +360,23 @@ enum sparse_checkout_mode {\n>>\n>>  static int set_config(enum sparse_checkout_mode mode)\n>>  {\n>> -       const char *config_path;\n>> -\n>> -       if (upgrade_repository_format(1) < 0)\n>> -               die(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n> \n> Wait, this got added in mid-2020, since v2.28.0 -- and I missed it but\n> it hasn't bit us yet??\n> \n> I'm so sorry.  Earlier when I objected to setting this, I was worried\n> it was a new change and would introduce some new failures, but clearly\n> it was already introduced...and it turns out we've been fine.  So, I\n> was wrong to worry about this.\n> \n> (Which is to say, if you want to add it back, I'm fine with it given\n> this info.  If you'd rather still take it out, I'm fine with that\n> too.)\n\nYes, this has been around for a while. However, based on our discussion on\nthis topic, this was never necessary for the worktreeConfig extension.\n\nRemoving it seems to increase our compatibility, but it would be interesting\nif we catch new compatibility problems. I'm thinking specifically about\nthird-party tools that should complain about extensions.worktreeConfig and\nspecifically reading sparse-checkout settings from worktree config files.\n\n> Patch looks good; I only had a few very minor comments.\n\nThanks,\n-Stolee\n"},{"id":"447079","messageId":"c693f0bf-dafd-e4da-dd5c-ca44f20fe588@gmail.com","threadId":"57125","inReplyTo":"CABPp-BHm3GRA3HWQwK2JiTKWye80ovwh-DRUOTys7g2D4UmwsA@mail.gmail.com","subject":"Re: [PATCH v4 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-01-27T14:29:00Z","receivedAt":"2022-01-27T14:29:04Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 1/27/2022 2:20 AM, Elijah Newren wrote:\n> On Tue, Jan 25, 2022 at 10:42 AM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>>\n>> This series is now based on v2.35.0 since that contains all of the necessary\n>> topics.\n> \n> Heads up for the maintainer: has a minor textual conflict with\n> en/present-despite-skipped (this series modifies the end of a\n> paragraph that the other series modifies the beginning of).\n\nThanks for pointing this out. My next version will not have this conflict,\nso feel free to resolve the conflict by dropping the changes on this side.\n\n>> This patch series includes a fix to the bug reported by Sean Allred [1] and\n>> diagnosed by Eric Sunshine [2].\n>>\n>> The root cause is that 'git sparse-checkout init' writes to the worktree\n>> config without checking that core.bare or core.worktree are set in the\n>> common config file. This series fixes this, but also puts in place some\n>> helpers to prevent this from happening in the future.\n> This series has become pretty solid.  I had only very minor comments\n> on the patches.  Thanks for working on this.\n\nThank you for your careful review. I'll let it simmer over the weekend\nbefore sending a v5 that includes your recommended changes.\n\nThanks,\n-Stolee\n"},{"id":"447333","messageId":"pull.1101.v5.git.1643641259.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v4.git.1643136134.gitgitgadget@gmail.com","subject":"[PATCH v5 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-31T15:00:54Z","receivedAt":"2022-01-31T15:01:05Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"This series is now based on v2.35.0 since that contains all of the necessary\ntopics.\n\nThis patch series includes a fix to the bug reported by Sean Allred [1] and\ndiagnosed by Eric Sunshine [2].\n\nThe root cause is that 'git sparse-checkout init' writes to the worktree\nconfig without checking that core.bare or core.worktree are set in the\ncommon config file. This series fixes this, but also puts in place some\nhelpers to prevent this from happening in the future.\n\nATTENTION: I have significantly redesigned the series since previous\nversions, so most of this cover letter is new.\n\n * Patch 1 updates documentation around extensions.worktreeConfig in a few\n   places to improve discoverability. Several cross links are added to make\n   it easy to find the related areas. (The documentation for the changes to\n   'git sparse-checkout' are delayed to patch 4.)\n\n * Patch 2 introduces the init_worktree_config() helper which follows the\n   documented instructions to enable extensions.worktreeConfig as well as\n   move the core.bare and core.worktree config values. This update does not\n   modify core.repositoryFormatVersion, since this is not needed\n   specifically for extensions.worktreeConfig.\n\n * Patch 3 adds a new repo_config_set_worktree_gently() helper method so we\n   can internally adjust a config value within a worktree, at least if\n   extensions.worktreeConfig is enabled. (It will write to the common config\n   file if the extension is not enabled.)\n\n * Patch 4 modifies the sparse-checkout builtin to use\n   init_worktree_config() and repo_config_set_worktree_gently() in ways that\n   fix the reported bug. The behavior change here is that it will no longer\n   upgrade the repository format version, since that is not needed for\n   extensions.worktreeConfig.\n\n * Patch 5 updates 'git worktree add' to copy the worktree config from the\n   current worktree to the new one (while unsetting core.bare=true and\n   core.worktree=*) along with copying the sparse-checkout patterns file.\n\n[1]\nhttps://lore.kernel.org/git/CABceR4bZmtC4rCwgxZ1BBYZP69VOUca1f_moJoP989vTUZWu9Q@mail.gmail.com/\n[2]\nhttps://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n\n\nUpdates in v5\n=============\n\n * Cleaned up documentation as per Elijah's suggestions.\n * Removed unnecessary conflicting change in git-sparse-checkout.txt\n * Fixed an ambiguous comment about moving config values.\n\n\nUpdates in v4\n=============\n\n * Rebased to v2.35.0\n * Fixed memory leak (was leaking repo_git_path() result)\n * Added additional documentation updates so curious users can discover the\n   intricacies of extensions.worktreeConfig from multiple entry points.\n * Significantly reduced the amount of changes to config.c.\n * 'git sparse-checkout' no longer upgrades the repository format.\n * Dropped the update to upgrade_repository_format(), since it is not\n   needed.\n * Dropped the 'git worktree init-worktree-config' subcommand in favor of a\n   helper method called by 'git sparse-checkout'\n * Many others because of the significant changes required by the above\n   items.\n\nThanks, -Stolee\n\nDerrick Stolee (5):\n  Documentation: add extensions.worktreeConfig details\n  worktree: create init_worktree_config()\n  config: add repo_config_set_worktree_gently()\n  sparse-checkout: set worktree-config correctly\n  worktree: copy sparse-checkout patterns and config on add\n\n Documentation/config/extensions.txt   | 31 ++++++++++++\n Documentation/git-config.txt          |  8 ++-\n Documentation/git-sparse-checkout.txt | 16 ++++--\n Documentation/git-worktree.txt        | 11 +++--\n builtin/sparse-checkout.c             | 28 +++++------\n builtin/worktree.c                    | 60 +++++++++++++++++++++++\n config.c                              | 35 ++++++++++++--\n config.h                              |  8 +++\n sparse-index.c                        | 10 ++--\n t/t1091-sparse-checkout-builtin.sh    | 35 ++++++++++----\n t/t2400-worktree-add.sh               | 46 +++++++++++++++++-\n worktree.c                            | 70 +++++++++++++++++++++++++++\n worktree.h                            | 21 ++++++++\n 13 files changed, 333 insertions(+), 46 deletions(-)\n\n\nbase-commit: 89bece5c8c96f0b962cfc89e63f82d603fd60bed\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1101%2Fderrickstolee%2Fsparse-checkout%2Fbare-worktree-bug-v5\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1101/derrickstolee/sparse-checkout/bare-worktree-bug-v5\nPull-Request: https://github.com/gitgitgadget/git/pull/1101\n\nRange-diff vs v4:\n\n 1:  459e09dedd7 ! 1:  1bd5f26271c Documentation: add extensions.worktreeConfig details\n     @@ Commit message\n          within git-sparse-checkout.txt, but a behavior change is needed before\n          making those updates.\n      \n     +    Helped-by: Elijah Newren <newren@gmail.com>\n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## Documentation/config/extensions.txt ##\n     @@ Documentation/git-config.txt: from all available files.\n      -\tpresent. If not it's the same as `--local`.\n      +\tenabled. If not it's the same as `--local`. Note that `$GIT_DIR`\n      +\tis equal to `$GIT_COMMON_DIR` for the main worktree, but is of the\n     -+\tform `.git/worktrees/<worktree-name>/` for other worktrees. See\n     ++\tform `$GIT_DIR/worktrees/<worktree-name>/` for other worktrees. See\n      +\tlinkgit:git-worktree[1] to learn how to enable\n      +\t`extensions.worktreeConfig`.\n       \n     @@ Documentation/git-worktree.txt: CONFIGURATION FILE\n      -already present in the config file, they will be applied to the main\n      -working trees only.\n      +present in the common config file and `extensions.worktreeConfig` is\n     -+disabled, then they will be applied to the main working trees only.\n     ++disabled, then they will be applied to the main working tree only.\n       \n       In order to have configuration specific to working trees, you can turn\n       on the `worktreeConfig` extension, e.g.:\n     @@ Documentation/git-worktree.txt: them to the `config.worktree` of the main workin\n      - - `core.worktree` and `core.bare` should never be shared\n      + - `core.worktree` should never be shared.\n      +\n     -+ - `core.bare` should not be shared unless the value is `core.bare=false`.\n     ++ - `core.bare` should not be shared if the value is `core.bare=true`.\n       \n        - `core.sparseCheckout` is recommended per working tree, unless you\n          are sure you always use sparse checkout for all working trees.\n 2:  d262a76b448 ! 2:  2a2c350112e worktree: create init_worktree_config()\n     @@ worktree.h: void strbuf_worktree_ref(const struct worktree *wt,\n      + *\n      + * 1. Add extensions.worktreeConfig=true in the common config file.\n      + *\n     -+ * 2. If the common config file has a core.worktree value or core.bare is\n     -+ *    set to true, then those values are moved to the main worktree's\n     -+ *    config.worktree file.\n     ++ * 2. If the common config file has a core.worktree value, then that value\n     ++ *    is moved to the main worktree's config.worktree file.\n     ++ *\n     ++ * 3. If the common config file has a core.bare enabled, then that value\n     ++ *    is moved to the main worktree's config.worktree file.\n      + *\n      + * If extensions.worktreeConfig is already true, then this method\n      + * terminates early without any of the above steps. The existing config\n 3:  110d5e0546c = 3:  802b28a9510 config: add repo_config_set_worktree_gently()\n 4:  fbfaa17797c ! 4:  08b89d17ccf sparse-checkout: set worktree-config correctly\n     @@ Documentation/git-sparse-checkout.txt: COMMANDS\n      -\t(extensions.worktreeConfig, core.sparseCheckout,\n      -\tcore.sparseCheckoutCone) if they are not already enabled, and\n      -\twrite a set of patterns to the sparse-checkout file from the\n     --\tlist of arguments following the 'set' subcommand. Update the\n     --\tworking directory to match the new patterns.\n      +\tEnable the necessary sparse-checkout config settings\n     -+\t(`core.sparseCheckout` and possibly `core.sparseCheckoutCone`) if\n     -+\tthey are not already enabled, and write a set of patterns to the\n     -+\tsparse-checkout file from the list of arguments following the\n     -+\t'set' subcommand. Update the working directory to match the new\n     -+\tpatterns.\n     -++\n     ++\t(`core.sparseCheckout`, `core.sparseCheckoutCone`, and\n     ++\t`index.sparse`) if they are not already set to the desired values,\n     ++\tand write a set of patterns to the sparse-checkout file from the\n     + \tlist of arguments following the 'set' subcommand. Update the\n     + \tworking directory to match the new patterns.\n     + +\n      +To ensure that adjusting the sparse-checkout settings within a worktree\n      +does not alter the sparse-checkout settings in other worktrees, the 'set'\n      +subcommand will upgrade your repository config to use worktree-specific\n     @@ Documentation/git-sparse-checkout.txt: COMMANDS\n      +the 'set' subcommand are stored in the worktree-specific sparse-checkout\n      +file. See linkgit:git-worktree[1] and the documentation of\n      +`extensions.worktreeConfig` in linkgit:git-config[1] for more details.\n     - +\n     +++\n       When the `--stdin` option is provided, the patterns are read from\n       standard in as a newline-delimited list instead of from the arguments.\n     -@@ Documentation/git-sparse-checkout.txt: interact with your repository until it is disabled.\n     - \tBy default, these patterns are read from the command-line arguments,\n     - \tbut they can be read from stdin using the `--stdin` option. When\n     - \t`core.sparseCheckoutCone` is enabled, the given patterns are interpreted\n     --\tas directory names as in the 'set' subcommand.\n     -+\tas directory names as in the 'set' subcommand. The sparsity defined\n     -+\tby the arguments to the 'add' subcommand are added to the patterns\n     -+\tin the worktree-specific sparse-checkout file.\n     - \n     - 'reapply'::\n     - \tReapply the sparsity pattern rules to paths in the working tree.\n     + +\n      \n       ## builtin/sparse-checkout.c ##\n      @@\n 5:  bb9e550ff3d ! 5:  85779dfaed3 worktree: copy sparse-checkout patterns and config on add\n     @@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'add to sparse-checkout'\n      +\tcat repo/.git/info/sparse-checkout >old &&\n      +\ttest_when_finished cp old repo/.git/info/sparse-checkout &&\n      +\ttest_when_finished git -C repo worktree remove ../worktree &&\n     -+\tgit -C repo sparse-checkout set \"/*\" &&\n     ++\tgit -C repo sparse-checkout set --no-cone \"/*\" &&\n      +\tgit -C repo worktree add --quiet ../worktree 2>err &&\n      +\ttest_must_be_empty err &&\n      +\tnew=repo/.git/worktrees/worktree/info/sparse-checkout &&\n\n-- \ngitgitgadget\n"},{"id":"447334","messageId":"1bd5f26271c9b1cff6c920bb91738ae903dccc61.1643641259.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v5.git.1643641259.gitgitgadget@gmail.com","subject":"[PATCH v5 1/5] Documentation: add extensions.worktreeConfig details","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-31T15:00:55Z","receivedAt":"2022-01-31T15:01:06Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe extensions.worktreeConfig extension was added in 58b284a (worktree:\nadd per-worktree config files, 2018-10-21) and was somewhat documented\nin Documentation/git-config.txt. However, the extensions.worktreeConfig\nvalue was not specified further in the list of possible config keys. The\nlocation of the config.worktree file is not specified, and there are\nsome precautions that should be mentioned clearly, but are only\nmentioned in git-worktree.txt.\n\nExpand the documentation to help users discover the complexities of\nextensions.worktreeConfig by adding details and cross links in these\nlocations (relative to Documentation/):\n\n- config/extensions.txt\n- git-config.txt\n- git-worktree.txt\n\nThe updates focus on items such as\n\n* $GIT_DIR/config.worktree takes precedence over $GIT_COMMON_DIR/config.\n\n* The core.worktree and core.bare=true settings are incorrect to have in\n  the common config file when extensions.worktreeConfig is enabled.\n\n* The sparse-checkout settings core.sparseCheckout[Cone] are recommended\n  to be set in the worktree config.\n\nAs documented in 11664196ac (\"Revert \"check_repository_format_gently():\nrefuse extensions for old repositories\"\", 2020-07-15), this extension\nmust be considered regardless of the repository format version for\nhistorical reasons.\n\nA future change will update references to extensions.worktreeConfig\nwithin git-sparse-checkout.txt, but a behavior change is needed before\nmaking those updates.\n\nHelped-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/extensions.txt | 31 +++++++++++++++++++++++++++++\n Documentation/git-config.txt        |  8 ++++++--\n Documentation/git-worktree.txt      | 11 +++++++---\n 3 files changed, 45 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt\nindex 4e23d73cdca..5999dcb2a1f 100644\n--- a/Documentation/config/extensions.txt\n+++ b/Documentation/config/extensions.txt\n@@ -6,3 +6,34 @@ extensions.objectFormat::\n Note that this setting should only be set by linkgit:git-init[1] or\n linkgit:git-clone[1].  Trying to change it after initialization will not\n work and will produce hard-to-diagnose issues.\n+\n+extensions.worktreeConfig::\n+\tIf enabled, then worktrees will load config settings from the\n+\t`$GIT_DIR/config.worktree` file in addition to the\n+\t`$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and\n+\t`$GIT_DIR` are the same for the main worktree, while other\n+\tworktrees have `$GIT_DIR` equal to\n+\t`$GIT_COMMON_DIR/worktrees/<worktree-name>/`. The settings in the\n+\t`config.worktree` file will override settings from any other\n+\tconfig files.\n++\n+When enabling `extensions.worktreeConfig`, you must be careful to move\n+certain values from the common config file to the main worktree's\n+`config.worktree` file, if present:\n++\n+* `core.worktree` must be moved from `$GIT_COMMON_DIR/config` to\n+  `$GIT_COMMON_DIR/config.worktree`.\n+* If `core.bare` is true, then it must be moved from `$GIT_COMMON_DIR/config`\n+  to `$GIT_COMMON_DIR/config.worktree`.\n++\n+It may also be beneficial to adjust the locations of `core.sparseCheckout`\n+and `core.sparseCheckoutCone` depending on your desire for customizable\n+sparse-checkout settings for each worktree. By default, the `git\n+sparse-checkout` builtin enables `extensions.worktreeConfig`, assigns\n+these config values on a per-worktree basis, and uses the\n+`$GIT_DIR/info/sparse-checkout` file to specify the sparsity for each\n+worktree independently. See linkgit:git-sparse-checkout[1] for more\n+details.\n++\n+For historical reasons, `extensions.worktreeConfig` is respected\n+regardless of the `core.repositoryFormatVersion` setting.\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 2285effb363..a48f7529fbc 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -141,9 +141,13 @@ from all available files.\n See also <<FILES>>.\n \n --worktree::\n-\tSimilar to `--local` except that `.git/config.worktree` is\n+\tSimilar to `--local` except that `$GIT_DIR/config.worktree` is\n \tread from or written to if `extensions.worktreeConfig` is\n-\tpresent. If not it's the same as `--local`.\n+\tenabled. If not it's the same as `--local`. Note that `$GIT_DIR`\n+\tis equal to `$GIT_COMMON_DIR` for the main worktree, but is of the\n+\tform `$GIT_DIR/worktrees/<worktree-name>/` for other worktrees. See\n+\tlinkgit:git-worktree[1] to learn how to enable\n+\t`extensions.worktreeConfig`.\n \n -f <config-file>::\n --file <config-file>::\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex 9e862fbcf79..b8d53c48303 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -286,8 +286,8 @@ CONFIGURATION FILE\n ------------------\n By default, the repository `config` file is shared across all working\n trees. If the config variables `core.bare` or `core.worktree` are\n-already present in the config file, they will be applied to the main\n-working trees only.\n+present in the common config file and `extensions.worktreeConfig` is\n+disabled, then they will be applied to the main working tree only.\n \n In order to have configuration specific to working trees, you can turn\n on the `worktreeConfig` extension, e.g.:\n@@ -307,11 +307,16 @@ them to the `config.worktree` of the main working tree. You may also\n take this opportunity to review and move other configuration that you\n do not want to share to all working trees:\n \n- - `core.worktree` and `core.bare` should never be shared\n+ - `core.worktree` should never be shared.\n+\n+ - `core.bare` should not be shared if the value is `core.bare=true`.\n \n  - `core.sparseCheckout` is recommended per working tree, unless you\n    are sure you always use sparse checkout for all working trees.\n \n+See the documentation of `extensions.worktreeConfig` in\n+linkgit:git-config[1] for more details.\n+\n DETAILS\n -------\n Each linked working tree has a private sub-directory in the repository's\n-- \ngitgitgadget\n\n"},{"id":"447335","messageId":"2a2c350112e647510c5f7c81e831661948cfb68d.1643641259.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v5.git.1643641259.gitgitgadget@gmail.com","subject":"[PATCH v5 2/5] worktree: create init_worktree_config()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-31T15:00:56Z","receivedAt":"2022-01-31T15:01:07Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nUpgrading a repository to use extensions.worktreeConfig is non-trivial.\nThere are several steps involved, including moving some config settings\nfrom the common config file to the main worktree's config.worktree file.\nThe previous change updated the documentation with all of these details.\n\nCommands such as 'git sparse-checkout set' upgrade the repository to use\nextensions.worktreeConfig without following these steps, causing some\nuser pain in some special cases.\n\nCreate a helper method, init_worktree_config(), that will be used in a\nlater change to fix this behavior within 'git sparse-checkout set'. The\nmethod is carefully documented in worktree.h.\n\nNote that we do _not_ upgrade the repository format version to 1 during\nthis process. The worktree config extension must be considered by Git\nand third-party tools even if core.repositoryFormatVersion is 0 for\nhistorical reasons documented in 11664196ac (\"Revert\n\"check_repository_format_gently(): refuse extensions for old\nrepositories\"\", 2020-07-15). This is a special case for this extension,\nand newer extensions (such as extensions.objectFormat) still need to\nupgrade the repository format version.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n worktree.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n worktree.h | 21 ++++++++++++++++\n 2 files changed, 91 insertions(+)\n\ndiff --git a/worktree.c b/worktree.c\nindex 6f598dcfcdf..dc4ead4c8fb 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -5,6 +5,7 @@\n #include \"worktree.h\"\n #include \"dir.h\"\n #include \"wt-status.h\"\n+#include \"config.h\"\n \n void free_worktrees(struct worktree **worktrees)\n {\n@@ -826,3 +827,72 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,\n \t*wtpath = path;\n \treturn 0;\n }\n+\n+static int move_config_setting(const char *key, const char *value,\n+\t\t\t       const char *from_file, const char *to_file)\n+{\n+\tif (git_config_set_in_file_gently(to_file, key, value))\n+\t\treturn error(_(\"unable to set %s in '%s'\"), key, to_file);\n+\tif (git_config_set_in_file_gently(from_file, key, NULL))\n+\t\treturn error(_(\"unable to unset %s in '%s'\"), key, from_file);\n+\treturn 0;\n+}\n+\n+int init_worktree_config(struct repository *r)\n+{\n+\tint res = 0;\n+\tint bare = 0;\n+\tstruct config_set cs = { { 0 } };\n+\tconst char *core_worktree;\n+\tchar *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n+\tchar *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n+\n+\t/*\n+\t * If the extension is already enabled, then we can skip the\n+\t * upgrade process.\n+\t */\n+\tif (repository_format_worktree_config)\n+\t\treturn 0;\n+\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\")))\n+\t\treturn error(_(\"failed to set extensions.worktreeConfig setting\"));\n+\n+\tgit_configset_init(&cs);\n+\tgit_configset_add_file(&cs, common_config_file);\n+\n+\t/*\n+\t * If core.bare is true in the common config file, then we need to\n+\t * move it to the base worktree's config file or it will break all\n+\t * worktrees. If it is false, then leave it in place because it\n+\t * _could_ be negating a global core.bare=true.\n+\t */\n+\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n+\t\tif ((res = move_config_setting(\"core.bare\", \"true\",\n+\t\t\t\t\t       common_config_file,\n+\t\t\t\t\t       main_worktree_file)))\n+\t\t\tgoto cleanup;\n+\t}\n+\t/*\n+\t * If core.worktree is set, then the base worktree is located\n+\t * somewhere different than the parent of the common Git dir.\n+\t * Relocate that value to avoid breaking all worktrees with this\n+\t * upgrade to worktree config.\n+\t */\n+\tif (!git_configset_get_string_tmp(&cs, \"core.worktree\", &core_worktree)) {\n+\t\tif ((res = move_config_setting(\"core.worktree\", core_worktree,\n+\t\t\t\t\t       common_config_file,\n+\t\t\t\t\t       main_worktree_file)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\t/*\n+\t * Ensure that we use worktree config for the remaining lifetime\n+\t * of the current process.\n+\t */\n+\trepository_format_worktree_config = 1;\n+\n+cleanup:\n+\tgit_configset_clear(&cs);\n+\tfree(common_config_file);\n+\tfree(main_worktree_file);\n+\treturn res;\n+}\ndiff --git a/worktree.h b/worktree.h\nindex 9e06fcbdf3d..e9e839926b0 100644\n--- a/worktree.h\n+++ b/worktree.h\n@@ -183,4 +183,25 @@ void strbuf_worktree_ref(const struct worktree *wt,\n \t\t\t struct strbuf *sb,\n \t\t\t const char *refname);\n \n+/**\n+ * Enable worktree config for the first time. This will make the following\n+ * adjustments:\n+ *\n+ * 1. Add extensions.worktreeConfig=true in the common config file.\n+ *\n+ * 2. If the common config file has a core.worktree value, then that value\n+ *    is moved to the main worktree's config.worktree file.\n+ *\n+ * 3. If the common config file has a core.bare enabled, then that value\n+ *    is moved to the main worktree's config.worktree file.\n+ *\n+ * If extensions.worktreeConfig is already true, then this method\n+ * terminates early without any of the above steps. The existing config\n+ * arrangement is assumed to be intentional.\n+ *\n+ * Returns 0 on success. Reports an error message and returns non-zero\n+ * if any of these steps fail.\n+ */\n+int init_worktree_config(struct repository *r);\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"447336","messageId":"802b28a95103396d305eaf62ea98b492b4f21871.1643641259.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v5.git.1643641259.gitgitgadget@gmail.com","subject":"[PATCH v5 3/5] config: add repo_config_set_worktree_gently()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-31T15:00:57Z","receivedAt":"2022-01-31T15:01:09Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nSome config settings, such as those for sparse-checkout, are likely\nintended to only apply to one worktree at a time. To make this write\neasier, add a new config API method, repo_config_set_worktree_gently().\n\nThis method will attempt to write to the worktree-specific config, but\nwill instead write to the common config file if worktree config is not\nenabled.  The next change will introduce a consumer of this method.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n config.c | 35 ++++++++++++++++++++++++++++++++---\n config.h |  8 ++++++++\n 2 files changed, 40 insertions(+), 3 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex 2bffa8d4a01..1a03ced1a54 100644\n--- a/config.c\n+++ b/config.c\n@@ -21,6 +21,7 @@\n #include \"dir.h\"\n #include \"color.h\"\n #include \"refs.h\"\n+#include \"worktree.h\"\n \n struct config_source {\n \tstruct config_source *prev;\n@@ -2884,6 +2885,20 @@ int git_config_set_gently(const char *key, const char *value)\n \treturn git_config_set_multivar_gently(key, value, NULL, 0);\n }\n \n+int repo_config_set_worktree_gently(struct repository *r,\n+\t\t\t\t    const char *key, const char *value)\n+{\n+\t/* Only use worktree-specific config if it is is already enabled. */\n+\tif (repository_format_worktree_config) {\n+\t\tchar *file = repo_git_path(r, \"config.worktree\");\n+\t\tint ret = git_config_set_multivar_in_file_gently(\n+\t\t\t\t\tfile, key, value, NULL, 0);\n+\t\tfree(file);\n+\t\treturn ret;\n+\t}\n+\treturn repo_config_set_multivar_gently(r, key, value, NULL, 0);\n+}\n+\n void git_config_set(const char *key, const char *value)\n {\n \tgit_config_set_multivar(key, value, NULL, 0);\n@@ -3181,14 +3196,28 @@ void git_config_set_multivar_in_file(const char *config_filename,\n int git_config_set_multivar_gently(const char *key, const char *value,\n \t\t\t\t   const char *value_pattern, unsigned flags)\n {\n-\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n-\t\t\t\t\t\t      flags);\n+\treturn repo_config_set_multivar_gently(the_repository, key, value,\n+\t\t\t\t\t       value_pattern, flags);\n+}\n+\n+int repo_config_set_multivar_gently(struct repository *r, const char *key,\n+\t\t\t\t    const char *value,\n+\t\t\t\t    const char *value_pattern, unsigned flags)\n+{\n+\tchar *file = repo_git_path(r, \"config\");\n+\tint res = git_config_set_multivar_in_file_gently(file,\n+\t\t\t\t\t\t\t key, value,\n+\t\t\t\t\t\t\t value_pattern,\n+\t\t\t\t\t\t\t flags);\n+\tfree(file);\n+\treturn res;\n }\n \n void git_config_set_multivar(const char *key, const char *value,\n \t\t\t     const char *value_pattern, unsigned flags)\n {\n-\tgit_config_set_multivar_in_file(NULL, key, value, value_pattern,\n+\tgit_config_set_multivar_in_file(git_path(\"config\"),\n+\t\t\t\t\tkey, value, value_pattern,\n \t\t\t\t\tflags);\n }\n \ndiff --git a/config.h b/config.h\nindex f119de01309..1d98ad269bd 100644\n--- a/config.h\n+++ b/config.h\n@@ -253,6 +253,13 @@ void git_config_set_in_file(const char *, const char *, const char *);\n \n int git_config_set_gently(const char *, const char *);\n \n+/**\n+ * Write a config value that should apply to the current worktree. If\n+ * extensions.worktreeConfig is enabled, then the write will happen in the\n+ * current worktree's config. Otherwise, write to the common config file.\n+ */\n+int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n+\n /**\n  * write config values to `.git/config`, takes a key/value pair as parameter.\n  */\n@@ -281,6 +288,7 @@ int git_config_parse_key(const char *, char **, size_t *);\n \n int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n+int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n \n /**\n-- \ngitgitgadget\n\n"},{"id":"447337","messageId":"08b89d17ccfab93546c9e84accaea84ce93ab932.1643641259.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v5.git.1643641259.gitgitgadget@gmail.com","subject":"[PATCH v5 4/5] sparse-checkout: set worktree-config correctly","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-31T15:00:58Z","receivedAt":"2022-01-31T15:01:12Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe previous change added repo_config_set_worktree_gently() to assist\nwriting config values into the config.worktree file, if enabled. An\nearlier change added init_worktree_config() as a helper to initialize\nextensions.worktreeConfig if not already enabled.\n\nLet the sparse-checkout builtin use these helpers instead of attempting to\ninitialize the worktree config on its own. This changes behavior of 'git\nsparse-checkout set' in a few important ways:\n\n 1. Git will no longer upgrade the repository format, since this is not\n    a requirement for understanding extensions.worktreeConfig.\n\n 2. If the main worktree is bare, then this command will not put the\n    worktree in a broken state.\n\nThe main reason to use worktree-specific config for the sparse-checkout\nbuiltin was to avoid enabling sparse-checkout patterns in one and\ncausing a loss of files in another. If a worktree does not have a\nsparse-checkout patterns file, then the sparse-checkout logic will not\nkick in on that worktree.\n\nReported-by: Sean Allred <allred.sean@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/git-sparse-checkout.txt | 16 +++++++++++----\n builtin/sparse-checkout.c             | 28 +++++++++++++--------------\n sparse-index.c                        | 10 +++-------\n t/t1091-sparse-checkout-builtin.sh    |  4 ++--\n 4 files changed, 30 insertions(+), 28 deletions(-)\n\ndiff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt\nindex b81dbe06543..94dad137b96 100644\n--- a/Documentation/git-sparse-checkout.txt\n+++ b/Documentation/git-sparse-checkout.txt\n@@ -31,13 +31,21 @@ COMMANDS\n \tDescribe the patterns in the sparse-checkout file.\n \n 'set'::\n-\tEnable the necessary config settings\n-\t(extensions.worktreeConfig, core.sparseCheckout,\n-\tcore.sparseCheckoutCone) if they are not already enabled, and\n-\twrite a set of patterns to the sparse-checkout file from the\n+\tEnable the necessary sparse-checkout config settings\n+\t(`core.sparseCheckout`, `core.sparseCheckoutCone`, and\n+\t`index.sparse`) if they are not already set to the desired values,\n+\tand write a set of patterns to the sparse-checkout file from the\n \tlist of arguments following the 'set' subcommand. Update the\n \tworking directory to match the new patterns.\n +\n+To ensure that adjusting the sparse-checkout settings within a worktree\n+does not alter the sparse-checkout settings in other worktrees, the 'set'\n+subcommand will upgrade your repository config to use worktree-specific\n+config if not already present. The sparsity defined by the arguments to\n+the 'set' subcommand are stored in the worktree-specific sparse-checkout\n+file. See linkgit:git-worktree[1] and the documentation of\n+`extensions.worktreeConfig` in linkgit:git-config[1] for more details.\n++\n When the `--stdin` option is provided, the patterns are read from\n standard in as a newline-delimited list instead of from the arguments.\n +\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 679c1070368..314c8d61f80 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -15,6 +15,7 @@\n #include \"wt-status.h\"\n #include \"quote.h\"\n #include \"sparse-index.h\"\n+#include \"worktree.h\"\n \n static const char *empty_base = \"\";\n \n@@ -359,26 +360,23 @@ enum sparse_checkout_mode {\n \n static int set_config(enum sparse_checkout_mode mode)\n {\n-\tconst char *config_path;\n-\n-\tif (upgrade_repository_format(1) < 0)\n-\t\tdie(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n-\tif (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n-\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n+\t/* Update to use worktree config, if not already. */\n+\tif (init_worktree_config(the_repository)) {\n+\t\terror(_(\"failed to initialize worktree config\"));\n \t\treturn 1;\n \t}\n \n-\tconfig_path = git_path(\"config.worktree\");\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckout\",\n-\t\t\t\t      mode ? \"true\" : NULL);\n-\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckoutCone\",\n-\t\t\t\t      mode == MODE_CONE_PATTERNS ? \"true\" : NULL);\n+\tif (repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckout\",\n+\t\t\t\t\t    mode ? \"true\" : \"false\") ||\n+\t    repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckoutCone\",\n+\t\t\t\t\t    mode == MODE_CONE_PATTERNS ?\n+\t\t\t\t\t\t\"true\" : \"false\"))\n+\t\treturn 1;\n \n \tif (mode == MODE_NO_PATTERNS)\n-\t\tset_sparse_index_config(the_repository, 0);\n+\t\treturn set_sparse_index_config(the_repository, 0);\n \n \treturn 0;\n }\ndiff --git a/sparse-index.c b/sparse-index.c\nindex a1d505d50e9..e93609999e0 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -99,13 +99,9 @@ static int convert_to_sparse_rec(struct index_state *istate,\n \n int set_sparse_index_config(struct repository *repo, int enable)\n {\n-\tint res;\n-\tchar *config_path = repo_git_path(repo, \"config.worktree\");\n-\tres = git_config_set_in_file_gently(config_path,\n-\t\t\t\t\t    \"index.sparse\",\n-\t\t\t\t\t    enable ? \"true\" : NULL);\n-\tfree(config_path);\n-\n+\tint res = repo_config_set_worktree_gently(repo,\n+\t\t\t\t\t\t  \"index.sparse\",\n+\t\t\t\t\t\t  enable ? \"true\" : \"false\");\n \tprepare_repo_settings(repo);\n \trepo->settings.sparse_index = enable;\n \treturn res;\ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex 42776984fe7..be6ea4ffe33 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -117,7 +117,7 @@ test_expect_success 'switching to cone mode with non-cone mode patterns' '\n \t\tcd bad-patterns &&\n \t\tgit sparse-checkout init &&\n \t\tgit sparse-checkout add dir &&\n-\t\tgit config core.sparseCheckoutCone true &&\n+\t\tgit config --worktree core.sparseCheckoutCone true &&\n \t\ttest_must_fail git sparse-checkout add dir 2>err &&\n \t\tgrep \"existing sparse-checkout patterns do not use cone mode\" err\n \t)\n@@ -256,7 +256,7 @@ test_expect_success 'sparse-index enabled and disabled' '\n \t\ttest_cmp expect actual &&\n \n \t\tgit -C repo config --list >config &&\n-\t\t! grep index.sparse config\n+\t\ttest_cmp_config -C repo false index.sparse\n \t)\n '\n \n-- \ngitgitgadget\n\n"},{"id":"447338","messageId":"85779dfaed39220e18129e823aff9c95ade5985b.1643641259.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v5.git.1643641259.gitgitgadget@gmail.com","subject":"[PATCH v5 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-31T15:00:59Z","receivedAt":"2022-01-31T15:01:13Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWhen adding a new worktree, it is reasonable to expect that we want to\nuse the current set of sparse-checkout settings for that new worktree.\nThis is particularly important for repositories where the worktree would\nbecome too large to be useful. This is even more important when using\npartial clone as well, since we want to avoid downloading the missing\nblobs for files that should not be written to the new worktree.\n\nThe only way to create such a worktree without this intermediate step of\nexpanding the full worktree is to copy the sparse-checkout patterns and\nconfig settings during 'git worktree add'. Each worktree has its own\nsparse-checkout patterns, and the default behavior when the\nsparse-checkout file is missing is to include all paths at HEAD. Thus,\nwe need to have patterns from somewhere, they might as well be the\ncurrent worktree's patterns. These are then modified independently in\nthe future.\n\nIn addition to the sparse-checkout file, copy the worktree config file\nif worktree config is enabled and the file exists. This will copy over\nany important settings to ensure the new worktree behaves the same as\nthe current one. The only exception we must continue to make is that\ncore.bare and core.worktree should become unset in the worktree's config\nfile.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/worktree.c                 | 60 ++++++++++++++++++++++++++++++\n t/t1091-sparse-checkout-builtin.sh | 31 +++++++++++----\n t/t2400-worktree-add.sh            | 46 ++++++++++++++++++++++-\n 3 files changed, 127 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 2838254f7f2..dc9cd6decc8 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -335,6 +335,66 @@ static int add_worktree(const char *path, const char *refname,\n \tstrbuf_addf(&sb, \"%s/commondir\", sb_repo.buf);\n \twrite_file(sb.buf, \"../..\");\n \n+\t/*\n+\t * If the current worktree has sparse-checkout enabled, then copy\n+\t * the sparse-checkout patterns from the current worktree.\n+\t */\n+\tif (core_apply_sparse_checkout) {\n+\t\tchar *from_file = git_pathdup(\"info/sparse-checkout\");\n+\t\tchar *to_file = xstrfmt(\"%s/worktrees/%s/info/sparse-checkout\",\n+\t\t\t\t\trealpath.buf, name);\n+\n+\t\tif (file_exists(from_file)) {\n+\t\t\tif (safe_create_leading_directories(to_file) ||\n+\t\t\t    copy_file(to_file, from_file, 0666))\n+\t\t\t\terror(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n+\t\t\t\t      from_file, to_file);\n+\t\t}\n+\n+\t\tfree(from_file);\n+\t\tfree(to_file);\n+\t}\n+\n+\t/*\n+\t * If we are using worktree config, then copy all current config\n+\t * values from the current worktree into the new one, that way the\n+\t * new worktree behaves the same as this one.\n+\t */\n+\tif (repository_format_worktree_config) {\n+\t\tchar *from_file = git_pathdup(\"config.worktree\");\n+\t\tchar *to_file = xstrfmt(\"%s/worktrees/%s/config.worktree\",\n+\t\t\t\t\trealpath.buf, name);\n+\n+\t\tif (file_exists(from_file)) {\n+\t\t\tstruct config_set cs = { { 0 }};\n+\t\t\tconst char *str_value;\n+\t\t\tint bool_value;\n+\n+\t\t\tif (safe_create_leading_directories(to_file) ||\n+\t\t\t    copy_file(to_file, from_file, 0666))\n+\t\t\t\tdie(_(\"failed to copy worktree config from '%s' to '%s'\"),\n+\t\t\t\t    from_file, to_file);\n+\n+\t\t\tgit_configset_init(&cs);\n+\t\t\tgit_configset_add_file(&cs, from_file);\n+\n+\t\t\tif (!git_configset_get_bool(&cs, \"core.bare\", &bool_value) &&\n+\t\t\t    bool_value &&\n+\t\t\t    git_config_set_multivar_in_file_gently(\n+\t\t\t\t\tto_file, \"core.bare\", NULL, \"true\", 0))\n+\t\t\t\terror(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n+\t\t\tif (!git_configset_get_value(&cs, \"core.worktree\", &str_value) &&\n+\t\t\t    git_config_set_in_file_gently(to_file,\n+\t\t\t\t\t\t\t  \"core.worktree\", NULL))\n+\t\t\t\terror(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n+\n+\t\t\tgit_configset_clear(&cs);\n+\t\t}\n+\n+\t\tfree(from_file);\n+\t\tfree(to_file);\n+\t}\n+\n \tstrvec_pushf(&child_env, \"%s=%s\", GIT_DIR_ENVIRONMENT, sb_git.buf);\n \tstrvec_pushf(&child_env, \"%s=%s\", GIT_WORK_TREE_ENVIRONMENT, path);\n \tcp.git_cmd = 1;\ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex be6ea4ffe33..8b92e307318 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -146,9 +146,9 @@ test_expect_success 'interaction with clone --no-checkout (unborn index)' '\n '\n \n test_expect_success 'set enables config' '\n-\tgit init empty-config &&\n+\tgit init worktree-config &&\n \t(\n-\t\tcd empty-config &&\n+\t\tcd worktree-config &&\n \t\ttest_commit test file &&\n \t\ttest_path_is_missing .git/config.worktree &&\n \t\tgit sparse-checkout set nothing &&\n@@ -201,6 +201,21 @@ test_expect_success 'add to sparse-checkout' '\n \tcheck_files repo \"a folder1 folder2\"\n '\n \n+test_expect_success 'worktree: add copies sparse-checkout patterns' '\n+\tcat repo/.git/info/sparse-checkout >old &&\n+\ttest_when_finished cp old repo/.git/info/sparse-checkout &&\n+\ttest_when_finished git -C repo worktree remove ../worktree &&\n+\tgit -C repo sparse-checkout set --no-cone \"/*\" &&\n+\tgit -C repo worktree add --quiet ../worktree 2>err &&\n+\ttest_must_be_empty err &&\n+\tnew=repo/.git/worktrees/worktree/info/sparse-checkout &&\n+\ttest_path_is_file $new &&\n+\ttest_cmp repo/.git/info/sparse-checkout $new &&\n+\tgit -C worktree sparse-checkout set --cone &&\n+\ttest_cmp_config -C worktree true core.sparseCheckoutCone &&\n+\ttest_must_fail git -C repo core.sparseCheckoutCone\n+'\n+\n test_expect_success 'cone mode: match patterns' '\n \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n \trm -rf repo/a repo/folder1 repo/folder2 &&\n@@ -520,13 +535,13 @@ test_expect_success 'interaction with submodules' '\n '\n \n test_expect_success 'different sparse-checkouts with worktrees' '\n+\tgit -C repo sparse-checkout set --cone deep folder1 &&\n \tgit -C repo worktree add --detach ../worktree &&\n-\tcheck_files worktree \"a deep folder1 folder2\" &&\n-\tgit -C worktree sparse-checkout init --cone &&\n-\tgit -C repo sparse-checkout set folder1 &&\n-\tgit -C worktree sparse-checkout set deep/deeper1 &&\n-\tcheck_files repo a folder1 &&\n-\tcheck_files worktree a deep\n+\tcheck_files worktree \"a deep folder1\" &&\n+\tgit -C repo sparse-checkout set --cone folder1 &&\n+\tgit -C worktree sparse-checkout set --cone deep/deeper1 &&\n+\tcheck_files repo \"a folder1\" &&\n+\tcheck_files worktree \"a deep\"\n '\n \n test_expect_success 'set using filename keeps file on-disk' '\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 37ad79470fb..3fb5b21b943 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -165,8 +165,50 @@ test_expect_success '\"add\" default branch of a bare repo' '\n \t(\n \t\tgit clone --bare . bare2 &&\n \t\tcd bare2 &&\n-\t\tgit worktree add ../there3 main\n-\t)\n+\t\tgit worktree add ../there3 main &&\n+\t\tcd ../there3 &&\n+\t\tgit status\n+\t) &&\n+\tcat >expect <<-EOF &&\n+\tinit.t\n+\tEOF\n+\tls there3 >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '\"add\" to bare repo with worktree config' '\n+\t(\n+\t\tgit clone --bare . bare3 &&\n+\t\tcd bare3 &&\n+\t\tgit config extensions.worktreeconfig true &&\n+\t\tgit config --worktree core.bare true &&\n+\t\tgit config --worktree core.worktree \"$(pwd)\" &&\n+\t\tgit config --worktree bogus.key value &&\n+\t\tgit config --unset core.bare &&\n+\t\tgit worktree add ../there4 main &&\n+\t\tcd ../there4 &&\n+\t\tgit status &&\n+\t\tgit worktree add --detach ../there5 &&\n+\t\tcd ../there5 &&\n+\t\tgit status\n+\t) &&\n+\n+\t# the worktree has the arbitrary value copied.\n+\ttest_cmp_config -C there4 value bogus.key &&\n+\ttest_cmp_config -C there5 value bogus.key &&\n+\n+\t# however, core.bare and core.worktree were removed.\n+\ttest_must_fail git -C there4 config core.bare &&\n+\ttest_must_fail git -C there4 config core.worktree &&\n+\n+\tcat >expect <<-EOF &&\n+\tinit.t\n+\tEOF\n+\n+\tls there4 >actual &&\n+\ttest_cmp expect actual &&\n+\tls there5 >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'checkout with grafts' '\n-- \ngitgitgadget\n"},{"id":"447341","messageId":"CABPp-BGA_e4oaVzmssHgejYPmjWMCNFDJ2-hbUU3yFbONyaG8Q@mail.gmail.com","threadId":"57125","inReplyTo":"pull.1101.v5.git.1643641259.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 0/5] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-01-31T16:17:23Z","receivedAt":"2022-01-31T16:17:56Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Jan 31, 2022 at 7:01 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> This series is now based on v2.35.0 since that contains all of the necessary\n> topics.\n>\n> This patch series includes a fix to the bug reported by Sean Allred [1] and\n> diagnosed by Eric Sunshine [2].\n>\n> The root cause is that 'git sparse-checkout init' writes to the worktree\n> config without checking that core.bare or core.worktree are set in the\n> common config file. This series fixes this, but also puts in place some\n> helpers to prevent this from happening in the future.\n>\n> ATTENTION: I have significantly redesigned the series since previous\n> versions, so most of this cover letter is new.\n>\n>  * Patch 1 updates documentation around extensions.worktreeConfig in a few\n>    places to improve discoverability. Several cross links are added to make\n>    it easy to find the related areas. (The documentation for the changes to\n>    'git sparse-checkout' are delayed to patch 4.)\n>\n>  * Patch 2 introduces the init_worktree_config() helper which follows the\n>    documented instructions to enable extensions.worktreeConfig as well as\n>    move the core.bare and core.worktree config values. This update does not\n>    modify core.repositoryFormatVersion, since this is not needed\n>    specifically for extensions.worktreeConfig.\n>\n>  * Patch 3 adds a new repo_config_set_worktree_gently() helper method so we\n>    can internally adjust a config value within a worktree, at least if\n>    extensions.worktreeConfig is enabled. (It will write to the common config\n>    file if the extension is not enabled.)\n>\n>  * Patch 4 modifies the sparse-checkout builtin to use\n>    init_worktree_config() and repo_config_set_worktree_gently() in ways that\n>    fix the reported bug. The behavior change here is that it will no longer\n>    upgrade the repository format version, since that is not needed for\n>    extensions.worktreeConfig.\n>\n>  * Patch 5 updates 'git worktree add' to copy the worktree config from the\n>    current worktree to the new one (while unsetting core.bare=true and\n>    core.worktree=*) along with copying the sparse-checkout patterns file.\n>\n> [1]\n> https://lore.kernel.org/git/CABceR4bZmtC4rCwgxZ1BBYZP69VOUca1f_moJoP989vTUZWu9Q@mail.gmail.com/\n> [2]\n> https://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n>\n>\n> Updates in v5\n> =============\n>\n>  * Cleaned up documentation as per Elijah's suggestions.\n>  * Removed unnecessary conflicting change in git-sparse-checkout.txt\n>  * Fixed an ambiguous comment about moving config values.\n\nThanks for patiently addressing all my feedback.  These last two\nrounds look really good, and this one you've addressed all my feedback\nand I can't find any issues:\n\nReviewed-by: Elijah Newren <newren@gmail.com>\n\n> Updates in v4\n> =============\n>\n>  * Rebased to v2.35.0\n>  * Fixed memory leak (was leaking repo_git_path() result)\n>  * Added additional documentation updates so curious users can discover the\n>    intricacies of extensions.worktreeConfig from multiple entry points.\n>  * Significantly reduced the amount of changes to config.c.\n>  * 'git sparse-checkout' no longer upgrades the repository format.\n>  * Dropped the update to upgrade_repository_format(), since it is not\n>    needed.\n>  * Dropped the 'git worktree init-worktree-config' subcommand in favor of a\n>    helper method called by 'git sparse-checkout'\n>  * Many others because of the significant changes required by the above\n>    items.\n>\n> Thanks, -Stolee\n>\n> Derrick Stolee (5):\n>   Documentation: add extensions.worktreeConfig details\n>   worktree: create init_worktree_config()\n>   config: add repo_config_set_worktree_gently()\n>   sparse-checkout: set worktree-config correctly\n>   worktree: copy sparse-checkout patterns and config on add\n>\n>  Documentation/config/extensions.txt   | 31 ++++++++++++\n>  Documentation/git-config.txt          |  8 ++-\n>  Documentation/git-sparse-checkout.txt | 16 ++++--\n>  Documentation/git-worktree.txt        | 11 +++--\n>  builtin/sparse-checkout.c             | 28 +++++------\n>  builtin/worktree.c                    | 60 +++++++++++++++++++++++\n>  config.c                              | 35 ++++++++++++--\n>  config.h                              |  8 +++\n>  sparse-index.c                        | 10 ++--\n>  t/t1091-sparse-checkout-builtin.sh    | 35 ++++++++++----\n>  t/t2400-worktree-add.sh               | 46 +++++++++++++++++-\n>  worktree.c                            | 70 +++++++++++++++++++++++++++\n>  worktree.h                            | 21 ++++++++\n>  13 files changed, 333 insertions(+), 46 deletions(-)\n>\n>\n> base-commit: 89bece5c8c96f0b962cfc89e63f82d603fd60bed\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1101%2Fderrickstolee%2Fsparse-checkout%2Fbare-worktree-bug-v5\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1101/derrickstolee/sparse-checkout/bare-worktree-bug-v5\n> Pull-Request: https://github.com/gitgitgadget/git/pull/1101\n>\n> Range-diff vs v4:\n>\n>  1:  459e09dedd7 ! 1:  1bd5f26271c Documentation: add extensions.worktreeConfig details\n>      @@ Commit message\n>           within git-sparse-checkout.txt, but a behavior change is needed before\n>           making those updates.\n>\n>      +    Helped-by: Elijah Newren <newren@gmail.com>\n>           Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n>\n>        ## Documentation/config/extensions.txt ##\n>      @@ Documentation/git-config.txt: from all available files.\n>       - present. If not it's the same as `--local`.\n>       + enabled. If not it's the same as `--local`. Note that `$GIT_DIR`\n>       + is equal to `$GIT_COMMON_DIR` for the main worktree, but is of the\n>      -+ form `.git/worktrees/<worktree-name>/` for other worktrees. See\n>      ++ form `$GIT_DIR/worktrees/<worktree-name>/` for other worktrees. See\n>       + linkgit:git-worktree[1] to learn how to enable\n>       + `extensions.worktreeConfig`.\n>\n>      @@ Documentation/git-worktree.txt: CONFIGURATION FILE\n>       -already present in the config file, they will be applied to the main\n>       -working trees only.\n>       +present in the common config file and `extensions.worktreeConfig` is\n>      -+disabled, then they will be applied to the main working trees only.\n>      ++disabled, then they will be applied to the main working tree only.\n>\n>        In order to have configuration specific to working trees, you can turn\n>        on the `worktreeConfig` extension, e.g.:\n>      @@ Documentation/git-worktree.txt: them to the `config.worktree` of the main workin\n>       - - `core.worktree` and `core.bare` should never be shared\n>       + - `core.worktree` should never be shared.\n>       +\n>      -+ - `core.bare` should not be shared unless the value is `core.bare=false`.\n>      ++ - `core.bare` should not be shared if the value is `core.bare=true`.\n>\n>         - `core.sparseCheckout` is recommended per working tree, unless you\n>           are sure you always use sparse checkout for all working trees.\n>  2:  d262a76b448 ! 2:  2a2c350112e worktree: create init_worktree_config()\n>      @@ worktree.h: void strbuf_worktree_ref(const struct worktree *wt,\n>       + *\n>       + * 1. Add extensions.worktreeConfig=true in the common config file.\n>       + *\n>      -+ * 2. If the common config file has a core.worktree value or core.bare is\n>      -+ *    set to true, then those values are moved to the main worktree's\n>      -+ *    config.worktree file.\n>      ++ * 2. If the common config file has a core.worktree value, then that value\n>      ++ *    is moved to the main worktree's config.worktree file.\n>      ++ *\n>      ++ * 3. If the common config file has a core.bare enabled, then that value\n>      ++ *    is moved to the main worktree's config.worktree file.\n>       + *\n>       + * If extensions.worktreeConfig is already true, then this method\n>       + * terminates early without any of the above steps. The existing config\n>  3:  110d5e0546c = 3:  802b28a9510 config: add repo_config_set_worktree_gently()\n>  4:  fbfaa17797c ! 4:  08b89d17ccf sparse-checkout: set worktree-config correctly\n>      @@ Documentation/git-sparse-checkout.txt: COMMANDS\n>       - (extensions.worktreeConfig, core.sparseCheckout,\n>       - core.sparseCheckoutCone) if they are not already enabled, and\n>       - write a set of patterns to the sparse-checkout file from the\n>      -- list of arguments following the 'set' subcommand. Update the\n>      -- working directory to match the new patterns.\n>       + Enable the necessary sparse-checkout config settings\n>      -+ (`core.sparseCheckout` and possibly `core.sparseCheckoutCone`) if\n>      -+ they are not already enabled, and write a set of patterns to the\n>      -+ sparse-checkout file from the list of arguments following the\n>      -+ 'set' subcommand. Update the working directory to match the new\n>      -+ patterns.\n>      -++\n>      ++ (`core.sparseCheckout`, `core.sparseCheckoutCone`, and\n>      ++ `index.sparse`) if they are not already set to the desired values,\n>      ++ and write a set of patterns to the sparse-checkout file from the\n>      +  list of arguments following the 'set' subcommand. Update the\n>      +  working directory to match the new patterns.\n>      + +\n>       +To ensure that adjusting the sparse-checkout settings within a worktree\n>       +does not alter the sparse-checkout settings in other worktrees, the 'set'\n>       +subcommand will upgrade your repository config to use worktree-specific\n>      @@ Documentation/git-sparse-checkout.txt: COMMANDS\n>       +the 'set' subcommand are stored in the worktree-specific sparse-checkout\n>       +file. See linkgit:git-worktree[1] and the documentation of\n>       +`extensions.worktreeConfig` in linkgit:git-config[1] for more details.\n>      - +\n>      +++\n>        When the `--stdin` option is provided, the patterns are read from\n>        standard in as a newline-delimited list instead of from the arguments.\n>      -@@ Documentation/git-sparse-checkout.txt: interact with your repository until it is disabled.\n>      -  By default, these patterns are read from the command-line arguments,\n>      -  but they can be read from stdin using the `--stdin` option. When\n>      -  `core.sparseCheckoutCone` is enabled, the given patterns are interpreted\n>      -- as directory names as in the 'set' subcommand.\n>      -+ as directory names as in the 'set' subcommand. The sparsity defined\n>      -+ by the arguments to the 'add' subcommand are added to the patterns\n>      -+ in the worktree-specific sparse-checkout file.\n>      -\n>      - 'reapply'::\n>      -  Reapply the sparsity pattern rules to paths in the working tree.\n>      + +\n>\n>        ## builtin/sparse-checkout.c ##\n>       @@\n>  5:  bb9e550ff3d ! 5:  85779dfaed3 worktree: copy sparse-checkout patterns and config on add\n>      @@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'add to sparse-checkout'\n>       + cat repo/.git/info/sparse-checkout >old &&\n>       + test_when_finished cp old repo/.git/info/sparse-checkout &&\n>       + test_when_finished git -C repo worktree remove ../worktree &&\n>      -+ git -C repo sparse-checkout set \"/*\" &&\n>      ++ git -C repo sparse-checkout set --no-cone \"/*\" &&\n>       + git -C repo worktree add --quiet ../worktree 2>err &&\n>       + test_must_be_empty err &&\n>       + new=repo/.git/worktrees/worktree/info/sparse-checkout &&\n>\n> --\n> gitgitgadget\n"},{"id":"447831","messageId":"CAPig+cS-3CxxyPGcy_vkeN_WYTRo1b-ZhJNdPy8ARZSNKkF1Xg@mail.gmail.com","threadId":"57125","inReplyTo":"1bd5f26271c9b1cff6c920bb91738ae903dccc61.1643641259.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 1/5] Documentation: add extensions.worktreeConfig details","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-02-06T09:17:55Z","receivedAt":"2022-02-06T09:23:50Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jan 31, 2022 at 10:01 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> [...]\n> Expand the documentation to help users discover the complexities of\n> extensions.worktreeConfig by adding details and cross links in these\n> locations (relative to Documentation/):\n> [...]\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n\nA few minor comments, which can be addressed later or not at all, and\nlikely are not worth holding up the series...\n\n> diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt\n> @@ -6,3 +6,34 @@ extensions.objectFormat::\n> +extensions.worktreeConfig::\n> +       If enabled, then worktrees will load config settings from the\n> +       `$GIT_DIR/config.worktree` file in addition to the\n> +       `$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and\n> +       `$GIT_DIR` are the same for the main worktree, while other\n> +       worktrees have `$GIT_DIR` equal to\n> +       `$GIT_COMMON_DIR/worktrees/<worktree-name>/`. The settings in the\n> +       `config.worktree` file will override settings from any other\n> +       config files.\n\nThere have been some efforts[1][2] in the past to settle upon the term\n\"working tree\" instead of \"worktree\" when talking about worktrees in\nprose. (The term \"worktree\" is perfectly fine in paths, such as\n`.git/worktrees/`, and in the command name `git worktree`, of course.)\n\nDocumentation/git-worktree.txt calls it \"<id>\" rather than\n\"<worktree-name>\" since it is a unique identifier for the worktree\nwhich may or may not match the worktree's basename.\n\nDocumentation/git-worktree.txt talks simply about the path\n`.git/worktrees/` under the assumption that people will understand\nthat `.git/` is the repository's common directory (which may not even\nbe named `.git/` for a bare repository). Although saying\n$GIT_COMMON_DIR is certainly technically accurate, the simpler `.git/`\ndoesn't seem to have caused any consternation. (Not a big deal. I\nmention it only to highlight the inconsistency between the existing\nand new documentation added here.)\n\nThe same comments apply to the rest of the patch.\n\n[1]: bc483285b7 (Documentation/git-worktree: consistently use term\n\"linked working tree\", 2015-07-20)\n[2]: 4f375b2678 (git-worktree.txt: consistently use term \"working\ntree\", 2020-08-03)\n"},{"id":"447832","messageId":"CAPig+cQiZS5-ScE1Fedm194=dHXCNhGdphJf-BV2uagO+X+wng@mail.gmail.com","threadId":"57125","inReplyTo":"2a2c350112e647510c5f7c81e831661948cfb68d.1643641259.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 2/5] worktree: create init_worktree_config()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-02-06T09:32:03Z","receivedAt":"2022-02-06T09:32:18Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jan 31, 2022 at 10:01 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> [...]\n> Create a helper method, init_worktree_config(), that will be used in a\n> later change to fix this behavior within 'git sparse-checkout set'. The\n> method is carefully documented in worktree.h.\n> [...]\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/worktree.c b/worktree.c\n> @@ -826,3 +827,72 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,\n> +int init_worktree_config(struct repository *r)\n> +{\n> +       int res = 0;\n> +       int bare = 0;\n> +       struct config_set cs = { { 0 } };\n> +       const char *core_worktree;\n> +       char *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n> +       char *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n> +\n> +       /*\n> +        * If the extension is already enabled, then we can skip the\n> +        * upgrade process.\n> +        */\n> +       if (repository_format_worktree_config)\n> +               return 0;\n> +       if ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\")))\n> +               return error(_(\"failed to set extensions.worktreeConfig setting\"));\n\nThese two early returns are leaking `common_config_file` and\n`main_worktree_file` which have already been allocated by xstrfmt().\n\n> +\n> +       git_configset_init(&cs);\n> +       git_configset_add_file(&cs, common_config_file);\n> +\n> +       /*\n> +        * If core.bare is true in the common config file, then we need to\n> +        * move it to the base worktree's config file or it will break all\n> +        * worktrees. If it is false, then leave it in place because it\n> +        * _could_ be negating a global core.bare=true.\n> +        */\n\nThe terminology \"base worktree\", which is not otherwise in the\nproject's lexicon, seems to be a leftover from earlier versions of\nthis series. Perhaps it could say instead \"the repository-specific\nconfiguration in $GIT_COMMON_DIR/worktree.config\" or something?\n\n> +       if (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n> +               if ((res = move_config_setting(\"core.bare\", \"true\",\n> +                                              common_config_file,\n> +                                              main_worktree_file)))\n> +                       goto cleanup;\n> +       }\n> +       /*\n> +        * If core.worktree is set, then the base worktree is located\n> +        * somewhere different than the parent of the common Git dir.\n> +        * Relocate that value to avoid breaking all worktrees with this\n> +        * upgrade to worktree config.\n> +        */\n\ns/base worktree/main worktree/\n\n> +       if (!git_configset_get_string_tmp(&cs, \"core.worktree\", &core_worktree)) {\n> +               if ((res = move_config_setting(\"core.worktree\", core_worktree,\n> +                                              common_config_file,\n> +                                              main_worktree_file)))\n> +                       goto cleanup;\n> +       }\n> +\n> +       /*\n> +        * Ensure that we use worktree config for the remaining lifetime\n> +        * of the current process.\n> +        */\n> +       repository_format_worktree_config = 1;\n> +\n> +cleanup:\n> +       git_configset_clear(&cs);\n> +       free(common_config_file);\n> +       free(main_worktree_file);\n> +       return res;\n> +}\n"},{"id":"447836","messageId":"CAPig+cTqmjrciKZa7DPA8B5Em6bNPprrJGaO6xCkRvmxvzQgHw@mail.gmail.com","threadId":"57125","inReplyTo":"08b89d17ccfab93546c9e84accaea84ce93ab932.1643641259.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 4/5] sparse-checkout: set worktree-config correctly","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-02-06T10:21:03Z","receivedAt":"2022-02-06T10:21:18Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jan 31, 2022 at 10:01 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> The previous change added repo_config_set_worktree_gently() to assist\n> writing config values into the config.worktree file, if enabled. An\n> earlier change added init_worktree_config() as a helper to initialize\n> extensions.worktreeConfig if not already enabled.\n>\n> Let the sparse-checkout builtin use these helpers instead of attempting to\n> initialize the worktree config on its own. This changes behavior of 'git\n> sparse-checkout set' in a few important ways:\n>\n>  1. Git will no longer upgrade the repository format, since this is not\n>     a requirement for understanding extensions.worktreeConfig.\n>\n>  2. If the main worktree is bare, then this command will not put the\n>     worktree in a broken state.\n\nAlthough the three of four of us involved in this discussion\nunderstand what \"broken state\" means, such a description may be too\nvague for future readers. To help them out, perhaps we can do a better\njob of conveying the nature of the actual breakage by rewriting all of\nthe above like this:\n\n    `git sparse-checkout set/init` enables worktree-specific\n    configuration[*] by setting extensions.worktreeConfig=true, but\n    neglects to perform the additional necessary bookkeeping of\n    relocating `core.bare=true` and `core.worktree` from\n    $GIT_COMMON_DIR/config to $GIT_COMMON_DIR/config.worktree, as\n    documented in git-worktree.txt. As a result of this oversight,\n    these settings, which are nonsensical for secondary worktrees, can\n    cause Git commands to incorrectly consider a worktree bare (in the\n    case of `core.bare`) or operate on the wrong worktree (in the case\n    of `core.worktree`). Fix this problem by taking advantage of the\n    recently-added init_worktree_config() which enables\n    `extensions.worktreeConfig` and takes care of necessary\n    bookkeeping.\n\n    While at it, for backward-compatibility reasons, also stop\n    upgrading the repository format to \"1\" since doing so is\n    (unintentionally) not required to take advantage of\n    `extensions.worktreeConfig`, as explained by 11664196ac (\"Revert\n    \"check_repository_format_gently(): refuse extensions for old\n    repositories\"\", 2020-07-15).\n\n> The main reason to use worktree-specific config for the sparse-checkout\n> builtin was to avoid enabling sparse-checkout patterns in one and\n> causing a loss of files in another. If a worktree does not have a\n> sparse-checkout patterns file, then the sparse-checkout logic will not\n> kick in on that worktree.\n\nPerhaps this paragraph can become the \"[*]\" footnote I referenced in\nthe above rewrite.\n\n> Reported-by: Sean Allred <allred.sean@gmail.com>\n> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n"},{"id":"447837","messageId":"2328375.k9UJiU4Qo1@cayenne","threadId":"57125","inReplyTo":"85779dfaed39220e18129e823aff9c95ade5985b.1643641259.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Jean-Noël AVILA","fromEmail":"jn.avila@free.fr","sentAt":"2022-02-06T10:36:47Z","receivedAt":"2022-02-06T10:37:01Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"On Monday, 31 January 2022 16:00:59 CET Derrick Stolee via GitGitGadget wrote:\n> From: Derrick Stolee <dstolee@microsoft.com>\n> \n> When adding a new worktree, it is reasonable to expect that we want to\n> use the current set of sparse-checkout settings for that new worktree.\n> This is particularly important for repositories where the worktree would\n> become too large to be useful. This is even more important when using\n> partial clone as well, since we want to avoid downloading the missing\n> blobs for files that should not be written to the new worktree.\n> \n> The only way to create such a worktree without this intermediate step of\n> expanding the full worktree is to copy the sparse-checkout patterns and\n> config settings during 'git worktree add'. Each worktree has its own\n> sparse-checkout patterns, and the default behavior when the\n> sparse-checkout file is missing is to include all paths at HEAD. Thus,\n> we need to have patterns from somewhere, they might as well be the\n> current worktree's patterns. These are then modified independently in\n> the future.\n> \n> In addition to the sparse-checkout file, copy the worktree config file\n> if worktree config is enabled and the file exists. This will copy over\n> any important settings to ensure the new worktree behaves the same as\n> the current one. The only exception we must continue to make is that\n> core.bare and core.worktree should become unset in the worktree's config\n> file.\n> \n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n>  builtin/worktree.c                 | 60 ++++++++++++++++++++++++++++++\n>  t/t1091-sparse-checkout-builtin.sh | 31 +++++++++++----\n>  t/t2400-worktree-add.sh            | 46 ++++++++++++++++++++++-\n>  3 files changed, 127 insertions(+), 10 deletions(-)\n> \n> diff --git a/builtin/worktree.c b/builtin/worktree.c\n> index 2838254f7f2..dc9cd6decc8 100644\n> --- a/builtin/worktree.c\n> +++ b/builtin/worktree.c\n> @@ -335,6 +335,66 @@ static int add_worktree(const char *path, const char *refname,\n>  \tstrbuf_addf(&sb, \"%s/commondir\", sb_repo.buf);\n>  \twrite_file(sb.buf, \"../..\");\n>  \n> +\t/*\n> +\t * If the current worktree has sparse-checkout enabled, then copy\n> +\t * the sparse-checkout patterns from the current worktree.\n> +\t */\n> +\tif (core_apply_sparse_checkout) {\n> +\t\tchar *from_file = git_pathdup(\"info/sparse-checkout\");\n> +\t\tchar *to_file = xstrfmt(\"%s/worktrees/%s/info/sparse-checkout\",\n> +\t\t\t\t\trealpath.buf, name);\n> +\n> +\t\tif (file_exists(from_file)) {\n> +\t\t\tif (safe_create_leading_directories(to_file) ||\n> +\t\t\t    copy_file(to_file, from_file, 0666))\n> +\t\t\t\terror(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n> +\t\t\t\t      from_file, to_file);\n> +\t\t}\n> +\n> +\t\tfree(from_file);\n> +\t\tfree(to_file);\n> +\t}\n> +\n> +\t/*\n> +\t * If we are using worktree config, then copy all current config\n> +\t * values from the current worktree into the new one, that way the\n> +\t * new worktree behaves the same as this one.\n> +\t */\n> +\tif (repository_format_worktree_config) {\n> +\t\tchar *from_file = git_pathdup(\"config.worktree\");\n> +\t\tchar *to_file = xstrfmt(\"%s/worktrees/%s/config.worktree\",\n> +\t\t\t\t\trealpath.buf, name);\n> +\n> +\t\tif (file_exists(from_file)) {\n> +\t\t\tstruct config_set cs = { { 0 }};\n> +\t\t\tconst char *str_value;\n> +\t\t\tint bool_value;\n> +\n> +\t\t\tif (safe_create_leading_directories(to_file) ||\n> +\t\t\t    copy_file(to_file, from_file, 0666))\n> +\t\t\t\tdie(_(\"failed to copy worktree config from '%s' to '%s'\"),\n> +\t\t\t\t    from_file, to_file);\n> +\n> +\t\t\tgit_configset_init(&cs);\n> +\t\t\tgit_configset_add_file(&cs, from_file);\n> +\n> +\t\t\tif (!git_configset_get_bool(&cs, \"core.bare\", &bool_value) &&\n> +\t\t\t    bool_value &&\n> +\t\t\t    git_config_set_multivar_in_file_gently(\n> +\t\t\t\t\tto_file, \"core.bare\", NULL, \"true\", 0))\n> +\t\t\t\terror(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n> +\t\t\tif (!git_configset_get_value(&cs, \"core.worktree\", &str_value) &&\n> +\t\t\t    git_config_set_in_file_gently(to_file,\n> +\t\t\t\t\t\t\t  \"core.worktree\", NULL))\n> +\t\t\t\terror(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n> +\n\nIn the first patch of this series, you use _(\"unable to set '%s' in'%s'). Does it make sense to reuse this string here?\n\n\n\n\n"},{"id":"447839","messageId":"CAPig+cQOieO3cmZv42G_8XRiMwkh4v3cO1AYg5VR9SiPNMwxhw@mail.gmail.com","threadId":"57125","inReplyTo":"85779dfaed39220e18129e823aff9c95ade5985b.1643641259.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-02-06T11:30:04Z","receivedAt":"2022-02-06T11:30:20Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jan 31, 2022 at 10:01 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> When adding a new worktree, it is reasonable to expect that we want to\n> use the current set of sparse-checkout settings for that new worktree.\n> This is particularly important for repositories where the worktree would\n> become too large to be useful. This is even more important when using\n> partial clone as well, since we want to avoid downloading the missing\n> blobs for files that should not be written to the new worktree.\n>\n> The only way to create such a worktree without this intermediate step of\n> expanding the full worktree is to copy the sparse-checkout patterns and\n> config settings during 'git worktree add'. Each worktree has its own\n> sparse-checkout patterns, and the default behavior when the\n> sparse-checkout file is missing is to include all paths at HEAD. Thus,\n> we need to have patterns from somewhere, they might as well be the\n> current worktree's patterns. These are then modified independently in\n> the future.\n>\n> In addition to the sparse-checkout file, copy the worktree config file\n> if worktree config is enabled and the file exists. This will copy over\n> any important settings to ensure the new worktree behaves the same as\n> the current one. The only exception we must continue to make is that\n> core.bare and core.worktree should become unset in the worktree's config\n> file.\n>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/builtin/worktree.c b/builtin/worktree.c\n> @@ -335,6 +335,66 @@ static int add_worktree(const char *path, const char *refname,\n> +       /*\n> +        * If the current worktree has sparse-checkout enabled, then copy\n> +        * the sparse-checkout patterns from the current worktree.\n> +        */\n> +       if (core_apply_sparse_checkout) {\n> +               char *from_file = git_pathdup(\"info/sparse-checkout\");\n> +               char *to_file = xstrfmt(\"%s/worktrees/%s/info/sparse-checkout\",\n> +                                       realpath.buf, name);\n\nI think this is too fragile and may easily be broken by an unrelated\nchange since `realpath` is a temporary container which gets reused,\nthus it holds different paths at different times. For instance, it\nfirst holds the realpath of $GIT_DIR but then later holds the realpath\nof $GIT_COMMON_DIR. If someone later comes along and reuses it for\nsome other path, then the code added by this patch may end up\nbreaking. To make this robust, you should instead use `sb_repo` which\nalready has the value \"$GIT_DIR/worktrees/<name>\":\n\n    char *to_file = xstrfmt(%s/info/sparse-checkout\", sb_repo.buf);\n\n> +               if (file_exists(from_file)) {\n> +                       if (safe_create_leading_directories(to_file) ||\n> +                           copy_file(to_file, from_file, 0666))\n> +                               error(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n> +                                     from_file, to_file);\n> +               }\n> +\n> +               free(from_file);\n> +               free(to_file);\n> +       }\n> +\n> +       /*\n> +        * If we are using worktree config, then copy all current config\n> +        * values from the current worktree into the new one, that way the\n> +        * new worktree behaves the same as this one.\n> +        */\n\nAs an aid for future readers, it might be helpful to extend this\ncontent to explain _why_ the new worktree should behave the same as\nthe current one. Reproducing the important bits from the commit\nmessage here in the comment could make it more useful.\n\n> +       if (repository_format_worktree_config) {\n> +               char *from_file = git_pathdup(\"config.worktree\");\n> +               char *to_file = xstrfmt(\"%s/worktrees/%s/config.worktree\",\n> +                                       realpath.buf, name);\n\nSame comment about fragility of using `realpath`. Instead:\n\n    char *to_file = xstrfmt(\"%s/config.worktree\", sb_repo.buf);\n\n> +               if (file_exists(from_file)) {\n> +                       struct config_set cs = { { 0 }};\n\ns/}}/} }/\n\n> +                       const char *str_value;\n> +                       int bool_value;\n> +\n> +                       if (safe_create_leading_directories(to_file) ||\n> +                           copy_file(to_file, from_file, 0666))\n> +                               die(_(\"failed to copy worktree config from '%s' to '%s'\"),\n> +                                   from_file, to_file);\n\nAll the other error conditions handled by this patch use error() but\nthis one uses die(). Does this one really warrant aborting the `git\nworktree add` operation? Perhaps instead just add a label below this\nnew chunk of code and `goto` the label (or indent this code further\nand avoid adding a label).\n\n> +                       git_configset_init(&cs);\n> +                       git_configset_add_file(&cs, from_file);\n> +\n> +                       if (!git_configset_get_bool(&cs, \"core.bare\", &bool_value) &&\n> +                           bool_value &&\n\nNit: This would be slightly easier to understand if you named this\nvariable `bare` (as you did in patch [2/5]) rather than `bool_value`.\n\n> +                           git_config_set_multivar_in_file_gently(\n> +                                       to_file, \"core.bare\", NULL, \"true\", 0))\n> +                               error(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n> +                       if (!git_configset_get_value(&cs, \"core.worktree\", &str_value) &&\n\nIn patch [2/5] you used git_configset_get_string_tmp() to retrieve\nthis setting, but here you're using git_configset_get_value(). Is\nthere a reason for the inconsistency?\n\n> +                           git_config_set_in_file_gently(to_file,\n> +                                                         \"core.worktree\", NULL))\n> +                               error(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n> +\n> +                       git_configset_clear(&cs);\n> +               }\n> +\n> +               free(from_file);\n> +               free(to_file);\n> +       }\n> diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\n> @@ -201,6 +201,21 @@ test_expect_success 'add to sparse-checkout' '\n> +test_expect_success 'worktree: add copies sparse-checkout patterns' '\n> +       cat repo/.git/info/sparse-checkout >old &&\n> +       test_when_finished cp old repo/.git/info/sparse-checkout &&\n> +       test_when_finished git -C repo worktree remove ../worktree &&\n> +       git -C repo sparse-checkout set --no-cone \"/*\" &&\n> +       git -C repo worktree add --quiet ../worktree 2>err &&\n> +       test_must_be_empty err &&\n> +       new=repo/.git/worktrees/worktree/info/sparse-checkout &&\n\nFor robustness, this should be using:\n\n    new=$(git rev-parse --git-path info/sparse-checkout)\n\nto retrieve \".git/worktrees/<id>/info/sparse-checkout\" rather than\nhard-coding \"worktree\" for \"<id>\".\n\n> +       test_path_is_file $new &&\n> +       test_cmp repo/.git/info/sparse-checkout $new &&\n> +       git -C worktree sparse-checkout set --cone &&\n> +       test_cmp_config -C worktree true core.sparseCheckoutCone &&\n> +       test_must_fail git -C repo core.sparseCheckoutCone\n> +'\n> diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\n> index 37ad79470fb..3fb5b21b943 100755\n> --- a/t/t2400-worktree-add.sh\n> +++ b/t/t2400-worktree-add.sh\n> @@ -165,8 +165,50 @@ test_expect_success '\"add\" default branch of a bare repo' '\n>         (\n>                 git clone --bare . bare2 &&\n>                 cd bare2 &&\n> -               git worktree add ../there3 main\n> -       )\n> +               git worktree add ../there3 main &&\n> +               cd ../there3 &&\n> +               git status\n> +       ) &&\n\nIs this some debugging code you forgot to remove or was `git status`\nfailing due to the bug(s) fixed by this patch series? I'm guessing the\nlatter since you also use `git status` in more tests below. Anyhow,\nit's not very clear what the `git-status` is meant to be testing. An\nin-code comment _might_ help. Even better, perhaps, would be to add a\nnew single-purpose test or a well-named function which explicitly\nchecks the conditions you want to test (i.e. that git-config doesn't\nreport core.bare as true or core.worktree as having a value).\n\n> +       cat >expect <<-EOF &&\n> +       init.t\n> +       EOF\n> +       ls there3 >actual &&\n> +       test_cmp expect actual\n> +'\n> +\n> +test_expect_success '\"add\" to bare repo with worktree config' '\n> +       (\n> +               git clone --bare . bare3 &&\n> +               cd bare3 &&\n> +               git config extensions.worktreeconfig true &&\n> +               git config --worktree core.bare true &&\n> +               git config --worktree core.worktree \"$(pwd)\" &&\n\nIt's not clear to the casual reader why these nonsensical keys are\nbeing added. An in-code comment explaining the reason (i.e. you expect\nthe `git worktree add` operation to drop them in the new worktree)\nwould be beneficial for future readers.\n\n> +               git config --worktree bogus.key value &&\n> +               git config --unset core.bare &&\n\nWhy is this being unset? (Genuine question. Am I missing something obvious?)\n\n> +               git worktree add ../there4 main &&\n> +               cd ../there4 &&\n> +               git status &&\n> +               git worktree add --detach ../there5 &&\n> +               cd ../there5 &&\n> +               git status\n> +       ) &&\n\nSame comment about the purpose of git-status not being obvious. A\nwell-named function which checks the specific conditions you're\nlooking for would be more clear than abstruse invocations of\ngit-status.\n\n> +       # the worktree has the arbitrary value copied.\n> +       test_cmp_config -C there4 value bogus.key &&\n> +       test_cmp_config -C there5 value bogus.key &&\n> +\n> +       # however, core.bare and core.worktree were removed.\n> +       test_must_fail git -C there4 config core.bare &&\n> +       test_must_fail git -C there4 config core.worktree &&\n\nThis is the comment I was looking for above. It's certainly okay to\nhave it here, but it was confusing above to not understand the reason\nthose nonsensical keys were being added.\n\n> +       cat >expect <<-EOF &&\n> +       init.t\n> +       EOF\n> +\n> +       ls there4 >actual &&\n> +       test_cmp expect actual &&\n> +       ls there5 >actual &&\n> +       test_cmp expect actual\n>  '\n"},{"id":"447843","messageId":"CAPig+cQf-qxkGfz_inJvxhi-YMKYDBkROyE5T5+86K7Lwh7brA@mail.gmail.com","threadId":"57125","inReplyTo":"CAPig+cQOieO3cmZv42G_8XRiMwkh4v3cO1AYg5VR9SiPNMwxhw@mail.gmail.com","subject":"Re: [PATCH v5 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-02-06T19:36:39Z","receivedAt":"2022-02-06T19:36:56Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Feb 6, 2022 at 6:30 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Mon, Jan 31, 2022 at 10:01 AM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> > +       new=repo/.git/worktrees/worktree/info/sparse-checkout &&\n>\n> For robustness, this should be using:\n>\n>     new=$(git rev-parse --git-path info/sparse-checkout)\n>\n> to retrieve \".git/worktrees/<id>/info/sparse-checkout\" rather than\n> hard-coding \"worktree\" for \"<id>\".\n\nOf course, I mean to type:\n\n    new=$(git -C worktree rev-parse --git-path info/sparse-checkout)\n"},{"id":"447885","messageId":"7d1d3cb5-2bfc-4f82-5230-d4c8d5896649@gmail.com","threadId":"57125","inReplyTo":"2328375.k9UJiU4Qo1@cayenne","subject":"Re: [PATCH v5 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-02-07T14:10:56Z","receivedAt":"2022-02-07T14:29:58Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 2/6/2022 5:36 AM, Jean-Noël AVILA wrote:\n> On Monday, 31 January 2022 16:00:59 CET Derrick Stolee via GitGitGadget wrote:\n\nHi Jean-Noël. Thanks for your attention to the translatable messages\nhere:\n\n>> error(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n>>       from_file, to_file);\n\n>> die(_(\"failed to copy worktree config from '%s' to '%s'\"),\n>>     from_file, to_file);\n\n>> error(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n\n>> error(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n\n> In the first patch of this series, you use _(\"unable to set '%s' in'%s'). Does it make sense to reuse this string here?\n\nI would argue that \"unable to set\" is not appropriate for any of these\nmessages. Perhaps the \"failed to copy\" messages might be able to use\n\"unable to set\", but the information that the config setting is coming\nfrom settings the user controlled is valuable.\n\nThe \"failed to unset\" means \"we are trying to _remove_ this setting\nfrom the config file\", so \"unable to set\" does not seem to work here.\n\nI'm open to revisiting this if you disagree.\n\nThanks,\n-Stolee\n\n"},{"id":"447888","messageId":"93747eca-08d6-0be9-08c7-42c81340b788@gmail.com","threadId":"57125","inReplyTo":"CAPig+cQOieO3cmZv42G_8XRiMwkh4v3cO1AYg5VR9SiPNMwxhw@mail.gmail.com","subject":"Re: [PATCH v5 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-02-07T14:30:35Z","receivedAt":"2022-02-07T14:51:40Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 2/6/2022 6:30 AM, Eric Sunshine wrote:\n> On Mon, Jan 31, 2022 at 10:01 AM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>> +       /*\n>> +        * If we are using worktree config, then copy all current config\n>> +        * values from the current worktree into the new one, that way the\n>> +        * new worktree behaves the same as this one.\n>> +        */\n> \n> As an aid for future readers, it might be helpful to extend this\n> content to explain _why_ the new worktree should behave the same as\n> the current one. Reproducing the important bits from the commit\n> message here in the comment could make it more useful.\n\nHere, I think I disagree. The comment is intentionally short to say\n\"we need to be careful here\" but the reason behind it can be found\nin the commit message from history spelunking.\n\n>> +                           git_config_set_multivar_in_file_gently(\n>> +                                       to_file, \"core.bare\", NULL, \"true\", 0))\n>> +                               error(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n>> +                       if (!git_configset_get_value(&cs, \"core.worktree\", &str_value) &&\n> \n> In patch [2/5] you used git_configset_get_string_tmp() to retrieve\n> this setting, but here you're using git_configset_get_value(). Is\n> there a reason for the inconsistency?\n\nI'm not sure why I chose to use one over the other, but looking at\nthe code, it seems that my use in patch 2 is the only use of\ngit_configset_get_string_tmp() that is not internal to config.c.\n\nI should convert the one in patch 2 to use git_configset_get_value()\nand then we can remove the declaration of git_configset_get_string_tmp()\nin config.h.\n\n>> +               git worktree add ../there3 main &&\n>> +               cd ../there3 &&\n>> +               git status\n>> +       ) &&\n> \n> Is this some debugging code you forgot to remove or was `git status`\n> failing due to the bug(s) fixed by this patch series? I'm guessing the\n> latter since you also use `git status` in more tests below. Anyhow,\n> it's not very clear what the `git-status` is meant to be testing. An\n> in-code comment _might_ help. Even better, perhaps, would be to add a\n> new single-purpose test or a well-named function which explicitly\n> checks the conditions you want to test (i.e. that git-config doesn't\n> report core.bare as true or core.worktree as having a value).\n\nBasically, in the old code any Git command would immediately fail\nbecause of the interpretation of core.bare or core.worktree. So\nthis check is just a check that a basic Git command doesn't fail\n\n>> +               git config --worktree bogus.key value &&\n>> +               git config --unset core.bare &&\n> \n> Why is this being unset? (Genuine question. Am I missing something obvious?)\n\nI'm moving it out of the common config file. Earlier commands\nenabled it in the config.worktree file for this working tree.\n\nThanks,\n-Stolee\n"},{"id":"447907","messageId":"pull.1101.v6.git.1644269583.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v5.git.1643641259.gitgitgadget@gmail.com","subject":"[PATCH v6 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-07T21:32:57Z","receivedAt":"2022-02-07T21:33:10Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"This series is now based on v2.35.0 since that contains all of the necessary\ntopics.\n\nThis patch series includes a fix to the bug reported by Sean Allred [1] and\ndiagnosed by Eric Sunshine [2].\n\nThe root cause is that 'git sparse-checkout init' writes to the worktree\nconfig without checking that core.bare or core.worktree are set in the\ncommon config file. This series fixes this, but also puts in place some\nhelpers to prevent this from happening in the future.\n\nATTENTION: I have significantly redesigned the series since previous\nversions, so most of this cover letter is new.\n\n * Patch 1 updates documentation around extensions.worktreeConfig in a few\n   places to improve discoverability. Several cross links are added to make\n   it easy to find the related areas. (The documentation for the changes to\n   'git sparse-checkout' are delayed to patch 4.)\n\n * Patch 2 introduces the init_worktree_config() helper which follows the\n   documented instructions to enable extensions.worktreeConfig as well as\n   move the core.bare and core.worktree config values. This update does not\n   modify core.repositoryFormatVersion, since this is not needed\n   specifically for extensions.worktreeConfig.\n\n * Patch 3 adds a new repo_config_set_worktree_gently() helper method so we\n   can internally adjust a config value within a worktree, at least if\n   extensions.worktreeConfig is enabled. (It will write to the common config\n   file if the extension is not enabled.)\n\n * Patch 4 modifies the sparse-checkout builtin to use\n   init_worktree_config() and repo_config_set_worktree_gently() in ways that\n   fix the reported bug. The behavior change here is that it will no longer\n   upgrade the repository format version, since that is not needed for\n   extensions.worktreeConfig.\n\n * Patch 5 updates 'git worktree add' to copy the worktree config from the\n   current worktree to the new one (while unsetting core.bare=true and\n   core.worktree=*) along with copying the sparse-checkout patterns file.\n\n[1]\nhttps://lore.kernel.org/git/CABceR4bZmtC4rCwgxZ1BBYZP69VOUca1f_moJoP989vTUZWu9Q@mail.gmail.com/\n[2]\nhttps://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n\n\nUpdates in v6\n=============\n\n * Updated documentation to use \"working tree\" over \"worktree\" and \"\" over\n   \"\"\n * Delay some allocations to avoid leaking memory in error conditions.\n * Use \"main worktree\" over \"base worktree\" in comments.\n * \n\n\nUpdates in v5\n=============\n\nResponded to most of Eric's suggestions. I pushed back on one about a\ncomment including information from the commit message, but everything else\nshould be as Eric suggested.\n\n * Cleaned up documentation as per Elijah's suggestions.\n * Removed unnecessary conflicting change in git-sparse-checkout.txt\n * Fixed an ambiguous comment about moving config values.\n * Removed use of git_configset_get_string_tmp() and added a patch that\n   removes its public declaration.\n * Fragile variables are replaced with better ones.\n * Variable names and code style improved.\n * Several test cleanups in patch 5.\n\n\nUpdates in v4\n=============\n\n * Rebased to v2.35.0\n * Fixed memory leak (was leaking repo_git_path() result)\n * Added additional documentation updates so curious users can discover the\n   intricacies of extensions.worktreeConfig from multiple entry points.\n * Significantly reduced the amount of changes to config.c.\n * 'git sparse-checkout' no longer upgrades the repository format.\n * Dropped the update to upgrade_repository_format(), since it is not\n   needed.\n * Dropped the 'git worktree init-worktree-config' subcommand in favor of a\n   helper method called by 'git sparse-checkout'\n * Many others because of the significant changes required by the above\n   items.\n\nThanks, -Stolee\n\nDerrick Stolee (6):\n  Documentation: add extensions.worktreeConfig details\n  worktree: create init_worktree_config()\n  config: add repo_config_set_worktree_gently()\n  sparse-checkout: set worktree-config correctly\n  worktree: copy sparse-checkout patterns and config on add\n  config: make git_configset_get_string_tmp() private\n\n Documentation/config/extensions.txt   | 31 ++++++++++++\n Documentation/git-config.txt          |  8 ++-\n Documentation/git-sparse-checkout.txt | 16 ++++--\n Documentation/git-worktree.txt        | 11 ++--\n builtin/sparse-checkout.c             | 28 +++++-----\n builtin/worktree.c                    | 63 +++++++++++++++++++++++\n config.c                              | 39 ++++++++++++--\n config.h                              |  9 +++-\n sparse-index.c                        | 10 ++--\n t/t1091-sparse-checkout-builtin.sh    | 35 +++++++++----\n t/t2400-worktree-add.sh               | 58 ++++++++++++++++++++-\n worktree.c                            | 73 +++++++++++++++++++++++++++\n worktree.h                            | 21 ++++++++\n 13 files changed, 353 insertions(+), 49 deletions(-)\n\n\nbase-commit: 89bece5c8c96f0b962cfc89e63f82d603fd60bed\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1101%2Fderrickstolee%2Fsparse-checkout%2Fbare-worktree-bug-v6\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1101/derrickstolee/sparse-checkout/bare-worktree-bug-v6\nPull-Request: https://github.com/gitgitgadget/git/pull/1101\n\nRange-diff vs v5:\n\n 1:  1bd5f26271c ! 1:  0260ff6cac0 Documentation: add extensions.worktreeConfig details\n     @@ Documentation/config/extensions.txt: extensions.objectFormat::\n      +\tIf enabled, then worktrees will load config settings from the\n      +\t`$GIT_DIR/config.worktree` file in addition to the\n      +\t`$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and\n     -+\t`$GIT_DIR` are the same for the main worktree, while other\n     -+\tworktrees have `$GIT_DIR` equal to\n     -+\t`$GIT_COMMON_DIR/worktrees/<worktree-name>/`. The settings in the\n     ++\t`$GIT_DIR` are the same for the main working tree, while other\n     ++\tworking trees have `$GIT_DIR` equal to\n     ++\t`$GIT_COMMON_DIR/worktrees/<id>/`. The settings in the\n      +\t`config.worktree` file will override settings from any other\n      +\tconfig files.\n      ++\n      +When enabling `extensions.worktreeConfig`, you must be careful to move\n     -+certain values from the common config file to the main worktree's\n     ++certain values from the common config file to the main working tree's\n      +`config.worktree` file, if present:\n      ++\n      +* `core.worktree` must be moved from `$GIT_COMMON_DIR/config` to\n     @@ Documentation/git-config.txt: from all available files.\n       \tread from or written to if `extensions.worktreeConfig` is\n      -\tpresent. If not it's the same as `--local`.\n      +\tenabled. If not it's the same as `--local`. Note that `$GIT_DIR`\n     -+\tis equal to `$GIT_COMMON_DIR` for the main worktree, but is of the\n     -+\tform `$GIT_DIR/worktrees/<worktree-name>/` for other worktrees. See\n     ++\tis equal to `$GIT_COMMON_DIR` for the main working tree, but is of\n     ++\tthe form `$GIT_DIR/worktrees/<id>/` for other working trees. See\n      +\tlinkgit:git-worktree[1] to learn how to enable\n      +\t`extensions.worktreeConfig`.\n       \n 2:  2a2c350112e ! 2:  5d0cc242d92 worktree: create init_worktree_config()\n     @@ worktree.c: int should_prune_worktree(const char *id, struct strbuf *reason, cha\n      +\tint bare = 0;\n      +\tstruct config_set cs = { { 0 } };\n      +\tconst char *core_worktree;\n     -+\tchar *common_config_file = xstrfmt(\"%s/config\", r->commondir);\n     -+\tchar *main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n     ++\tchar *common_config_file;\n     ++\tchar *main_worktree_file;\n      +\n      +\t/*\n      +\t * If the extension is already enabled, then we can skip the\n     @@ worktree.c: int should_prune_worktree(const char *id, struct strbuf *reason, cha\n      +\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\")))\n      +\t\treturn error(_(\"failed to set extensions.worktreeConfig setting\"));\n      +\n     ++\tcommon_config_file = xstrfmt(\"%s/config\", r->commondir);\n     ++\tmain_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n     ++\n      +\tgit_configset_init(&cs);\n      +\tgit_configset_add_file(&cs, common_config_file);\n      +\n      +\t/*\n      +\t * If core.bare is true in the common config file, then we need to\n     -+\t * move it to the base worktree's config file or it will break all\n     ++\t * move it to the main worktree's config file or it will break all\n      +\t * worktrees. If it is false, then leave it in place because it\n      +\t * _could_ be negating a global core.bare=true.\n      +\t */\n     @@ worktree.c: int should_prune_worktree(const char *id, struct strbuf *reason, cha\n      +\t\t\tgoto cleanup;\n      +\t}\n      +\t/*\n     -+\t * If core.worktree is set, then the base worktree is located\n     ++\t * If core.worktree is set, then the main worktree is located\n      +\t * somewhere different than the parent of the common Git dir.\n      +\t * Relocate that value to avoid breaking all worktrees with this\n      +\t * upgrade to worktree config.\n      +\t */\n     -+\tif (!git_configset_get_string_tmp(&cs, \"core.worktree\", &core_worktree)) {\n     ++\tif (!git_configset_get_value(&cs, \"core.worktree\", &core_worktree)) {\n      +\t\tif ((res = move_config_setting(\"core.worktree\", core_worktree,\n      +\t\t\t\t\t       common_config_file,\n      +\t\t\t\t\t       main_worktree_file)))\n 3:  802b28a9510 = 3:  cf9e86fe3a4 config: add repo_config_set_worktree_gently()\n 4:  08b89d17ccf ! 4:  5b5924eab49 sparse-checkout: set worktree-config correctly\n     @@ Metadata\n       ## Commit message ##\n          sparse-checkout: set worktree-config correctly\n      \n     -    The previous change added repo_config_set_worktree_gently() to assist\n     -    writing config values into the config.worktree file, if enabled. An\n     -    earlier change added init_worktree_config() as a helper to initialize\n     -    extensions.worktreeConfig if not already enabled.\n     +    `git sparse-checkout set/init` enables worktree-specific\n     +    configuration[*] by setting extensions.worktreeConfig=true, but neglects\n     +    to perform the additional necessary bookkeeping of relocating\n     +    `core.bare=true` and `core.worktree` from $GIT_COMMON_DIR/config to\n     +    $GIT_COMMON_DIR/config.worktree, as documented in git-worktree.txt. As a\n     +    result of this oversight, these settings, which are nonsensical for\n     +    secondary worktrees, can cause Git commands to incorrectly consider a\n     +    worktree bare (in the case of `core.bare`) or operate on the wrong\n     +    worktree (in the case of `core.worktree`). Fix this problem by taking\n     +    advantage of the recently-added init_worktree_config() which enables\n     +    `extensions.worktreeConfig` and takes care of necessary bookkeeping.\n      \n     -    Let the sparse-checkout builtin use these helpers instead of attempting to\n     -    initialize the worktree config on its own. This changes behavior of 'git\n     -    sparse-checkout set' in a few important ways:\n     +    While at it, for backward-compatibility reasons, also stop upgrading the\n     +    repository format to \"1\" since doing so is (unintentionally) not\n     +    required to take advantage of `extensions.worktreeConfig`, as explained\n     +    by 11664196ac (\"Revert \"check_repository_format_gently(): refuse\n     +    extensions for old repositories\"\", 2020-07-15).\n      \n     -     1. Git will no longer upgrade the repository format, since this is not\n     -        a requirement for understanding extensions.worktreeConfig.\n     -\n     -     2. If the main worktree is bare, then this command will not put the\n     -        worktree in a broken state.\n     -\n     -    The main reason to use worktree-specific config for the sparse-checkout\n     -    builtin was to avoid enabling sparse-checkout patterns in one and\n     -    causing a loss of files in another. If a worktree does not have a\n     -    sparse-checkout patterns file, then the sparse-checkout logic will not\n     -    kick in on that worktree.\n     +    [*] The main reason to use worktree-specific config for the\n     +    sparse-checkout builtin was to avoid enabling sparse-checkout patterns\n     +    in one and causing a loss of files in another. If a worktree does not\n     +    have a sparse-checkout patterns file, then the sparse-checkout logic\n     +    will not kick in on that worktree.\n      \n          Reported-by: Sean Allred <allred.sean@gmail.com>\n          Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n 5:  85779dfaed3 ! 5:  c51cb3714e7 worktree: copy sparse-checkout patterns and config on add\n     @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam\n      +\t */\n      +\tif (core_apply_sparse_checkout) {\n      +\t\tchar *from_file = git_pathdup(\"info/sparse-checkout\");\n     -+\t\tchar *to_file = xstrfmt(\"%s/worktrees/%s/info/sparse-checkout\",\n     -+\t\t\t\t\trealpath.buf, name);\n     ++\t\tchar *to_file = xstrfmt(\"%s/info/sparse-checkout\",\n     ++\t\t\t\t\tsb_repo.buf);\n      +\n      +\t\tif (file_exists(from_file)) {\n      +\t\t\tif (safe_create_leading_directories(to_file) ||\n     @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam\n      +\t */\n      +\tif (repository_format_worktree_config) {\n      +\t\tchar *from_file = git_pathdup(\"config.worktree\");\n     -+\t\tchar *to_file = xstrfmt(\"%s/worktrees/%s/config.worktree\",\n     -+\t\t\t\t\trealpath.buf, name);\n     ++\t\tchar *to_file = xstrfmt(\"%s/config.worktree\",\n     ++\t\t\t\t\tsb_repo.buf);\n      +\n      +\t\tif (file_exists(from_file)) {\n     -+\t\t\tstruct config_set cs = { { 0 }};\n     -+\t\t\tconst char *str_value;\n     -+\t\t\tint bool_value;\n     ++\t\t\tstruct config_set cs = { { 0 } };\n     ++\t\t\tconst char *core_worktree;\n     ++\t\t\tint bare;\n      +\n      +\t\t\tif (safe_create_leading_directories(to_file) ||\n     -+\t\t\t    copy_file(to_file, from_file, 0666))\n     -+\t\t\t\tdie(_(\"failed to copy worktree config from '%s' to '%s'\"),\n     -+\t\t\t\t    from_file, to_file);\n     ++\t\t\t    copy_file(to_file, from_file, 0666)) {\n     ++\t\t\t\terror(_(\"failed to copy worktree config from '%s' to '%s'\"),\n     ++\t\t\t\t      from_file, to_file);\n     ++\t\t\t\tgoto worktree_copy_cleanup;\n     ++\t\t\t}\n      +\n      +\t\t\tgit_configset_init(&cs);\n      +\t\t\tgit_configset_add_file(&cs, from_file);\n      +\n     -+\t\t\tif (!git_configset_get_bool(&cs, \"core.bare\", &bool_value) &&\n     -+\t\t\t    bool_value &&\n     ++\t\t\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) &&\n     ++\t\t\t    bare &&\n      +\t\t\t    git_config_set_multivar_in_file_gently(\n      +\t\t\t\t\tto_file, \"core.bare\", NULL, \"true\", 0))\n      +\t\t\t\terror(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n     -+\t\t\tif (!git_configset_get_value(&cs, \"core.worktree\", &str_value) &&\n     ++\t\t\tif (!git_configset_get_value(&cs, \"core.worktree\", &core_worktree) &&\n      +\t\t\t    git_config_set_in_file_gently(to_file,\n      +\t\t\t\t\t\t\t  \"core.worktree\", NULL))\n      +\t\t\t\terror(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n     @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam\n      +\t\t\tgit_configset_clear(&cs);\n      +\t\t}\n      +\n     ++worktree_copy_cleanup:\n      +\t\tfree(from_file);\n      +\t\tfree(to_file);\n      +\t}\n     @@ t/t1091-sparse-checkout-builtin.sh: test_expect_success 'add to sparse-checkout'\n      +\tgit -C repo sparse-checkout set --no-cone \"/*\" &&\n      +\tgit -C repo worktree add --quiet ../worktree 2>err &&\n      +\ttest_must_be_empty err &&\n     -+\tnew=repo/.git/worktrees/worktree/info/sparse-checkout &&\n     -+\ttest_path_is_file $new &&\n     -+\ttest_cmp repo/.git/info/sparse-checkout $new &&\n     ++\tnew=\"$(git -C worktree rev-parse --git-path info/sparse-checkout)\" &&\n     ++\ttest_path_is_file \"$new\" &&\n     ++\ttest_cmp repo/.git/info/sparse-checkout \"$new\" &&\n      +\tgit -C worktree sparse-checkout set --cone &&\n      +\ttest_cmp_config -C worktree true core.sparseCheckoutCone &&\n      +\ttest_must_fail git -C repo core.sparseCheckoutCone\n     @@ t/t2400-worktree-add.sh: test_expect_success '\"add\" default branch of a bare rep\n      -\t)\n      +\t\tgit worktree add ../there3 main &&\n      +\t\tcd ../there3 &&\n     ++\t\t# Simple check that a Git command does not\n     ++\t\t# immediately fail with the current setup\n      +\t\tgit status\n      +\t) &&\n      +\tcat >expect <<-EOF &&\n     @@ t/t2400-worktree-add.sh: test_expect_success '\"add\" default branch of a bare rep\n      +\t\tgit clone --bare . bare3 &&\n      +\t\tcd bare3 &&\n      +\t\tgit config extensions.worktreeconfig true &&\n     ++\n     ++\t\t# Add config values that are erroneous to have in\n     ++\t\t# a config.worktree file outside of the main\n     ++\t\t# working tree, to check that Git filters them out\n     ++\t\t# when copying config during \"git worktree add\".\n      +\t\tgit config --worktree core.bare true &&\n      +\t\tgit config --worktree core.worktree \"$(pwd)\" &&\n     ++\n     ++\t\t# We want to check that bogus.key is copied\n      +\t\tgit config --worktree bogus.key value &&\n      +\t\tgit config --unset core.bare &&\n      +\t\tgit worktree add ../there4 main &&\n      +\t\tcd ../there4 &&\n     ++\n     ++\t\t# Simple check that a Git command does not\n     ++\t\t# immediately fail with the current setup\n      +\t\tgit status &&\n      +\t\tgit worktree add --detach ../there5 &&\n      +\t\tcd ../there5 &&\n -:  ----------- > 6:  f687a0bfa16 config: make git_configset_get_string_tmp() private\n\n-- \ngitgitgadget\n"},{"id":"447908","messageId":"0260ff6cac0c76c6d66187d77defef1edd4c6fb5.1644269583.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v6.git.1644269583.gitgitgadget@gmail.com","subject":"[PATCH v6 1/6] Documentation: add extensions.worktreeConfig details","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-07T21:32:58Z","receivedAt":"2022-02-07T21:33:12Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe extensions.worktreeConfig extension was added in 58b284a (worktree:\nadd per-worktree config files, 2018-10-21) and was somewhat documented\nin Documentation/git-config.txt. However, the extensions.worktreeConfig\nvalue was not specified further in the list of possible config keys. The\nlocation of the config.worktree file is not specified, and there are\nsome precautions that should be mentioned clearly, but are only\nmentioned in git-worktree.txt.\n\nExpand the documentation to help users discover the complexities of\nextensions.worktreeConfig by adding details and cross links in these\nlocations (relative to Documentation/):\n\n- config/extensions.txt\n- git-config.txt\n- git-worktree.txt\n\nThe updates focus on items such as\n\n* $GIT_DIR/config.worktree takes precedence over $GIT_COMMON_DIR/config.\n\n* The core.worktree and core.bare=true settings are incorrect to have in\n  the common config file when extensions.worktreeConfig is enabled.\n\n* The sparse-checkout settings core.sparseCheckout[Cone] are recommended\n  to be set in the worktree config.\n\nAs documented in 11664196ac (\"Revert \"check_repository_format_gently():\nrefuse extensions for old repositories\"\", 2020-07-15), this extension\nmust be considered regardless of the repository format version for\nhistorical reasons.\n\nA future change will update references to extensions.worktreeConfig\nwithin git-sparse-checkout.txt, but a behavior change is needed before\nmaking those updates.\n\nHelped-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/extensions.txt | 31 +++++++++++++++++++++++++++++\n Documentation/git-config.txt        |  8 ++++++--\n Documentation/git-worktree.txt      | 11 +++++++---\n 3 files changed, 45 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt\nindex 4e23d73cdca..bccaec7a963 100644\n--- a/Documentation/config/extensions.txt\n+++ b/Documentation/config/extensions.txt\n@@ -6,3 +6,34 @@ extensions.objectFormat::\n Note that this setting should only be set by linkgit:git-init[1] or\n linkgit:git-clone[1].  Trying to change it after initialization will not\n work and will produce hard-to-diagnose issues.\n+\n+extensions.worktreeConfig::\n+\tIf enabled, then worktrees will load config settings from the\n+\t`$GIT_DIR/config.worktree` file in addition to the\n+\t`$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and\n+\t`$GIT_DIR` are the same for the main working tree, while other\n+\tworking trees have `$GIT_DIR` equal to\n+\t`$GIT_COMMON_DIR/worktrees/<id>/`. The settings in the\n+\t`config.worktree` file will override settings from any other\n+\tconfig files.\n++\n+When enabling `extensions.worktreeConfig`, you must be careful to move\n+certain values from the common config file to the main working tree's\n+`config.worktree` file, if present:\n++\n+* `core.worktree` must be moved from `$GIT_COMMON_DIR/config` to\n+  `$GIT_COMMON_DIR/config.worktree`.\n+* If `core.bare` is true, then it must be moved from `$GIT_COMMON_DIR/config`\n+  to `$GIT_COMMON_DIR/config.worktree`.\n++\n+It may also be beneficial to adjust the locations of `core.sparseCheckout`\n+and `core.sparseCheckoutCone` depending on your desire for customizable\n+sparse-checkout settings for each worktree. By default, the `git\n+sparse-checkout` builtin enables `extensions.worktreeConfig`, assigns\n+these config values on a per-worktree basis, and uses the\n+`$GIT_DIR/info/sparse-checkout` file to specify the sparsity for each\n+worktree independently. See linkgit:git-sparse-checkout[1] for more\n+details.\n++\n+For historical reasons, `extensions.worktreeConfig` is respected\n+regardless of the `core.repositoryFormatVersion` setting.\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 2285effb363..bdcfd94b642 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -141,9 +141,13 @@ from all available files.\n See also <<FILES>>.\n \n --worktree::\n-\tSimilar to `--local` except that `.git/config.worktree` is\n+\tSimilar to `--local` except that `$GIT_DIR/config.worktree` is\n \tread from or written to if `extensions.worktreeConfig` is\n-\tpresent. If not it's the same as `--local`.\n+\tenabled. If not it's the same as `--local`. Note that `$GIT_DIR`\n+\tis equal to `$GIT_COMMON_DIR` for the main working tree, but is of\n+\tthe form `$GIT_DIR/worktrees/<id>/` for other working trees. See\n+\tlinkgit:git-worktree[1] to learn how to enable\n+\t`extensions.worktreeConfig`.\n \n -f <config-file>::\n --file <config-file>::\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex 9e862fbcf79..b8d53c48303 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -286,8 +286,8 @@ CONFIGURATION FILE\n ------------------\n By default, the repository `config` file is shared across all working\n trees. If the config variables `core.bare` or `core.worktree` are\n-already present in the config file, they will be applied to the main\n-working trees only.\n+present in the common config file and `extensions.worktreeConfig` is\n+disabled, then they will be applied to the main working tree only.\n \n In order to have configuration specific to working trees, you can turn\n on the `worktreeConfig` extension, e.g.:\n@@ -307,11 +307,16 @@ them to the `config.worktree` of the main working tree. You may also\n take this opportunity to review and move other configuration that you\n do not want to share to all working trees:\n \n- - `core.worktree` and `core.bare` should never be shared\n+ - `core.worktree` should never be shared.\n+\n+ - `core.bare` should not be shared if the value is `core.bare=true`.\n \n  - `core.sparseCheckout` is recommended per working tree, unless you\n    are sure you always use sparse checkout for all working trees.\n \n+See the documentation of `extensions.worktreeConfig` in\n+linkgit:git-config[1] for more details.\n+\n DETAILS\n -------\n Each linked working tree has a private sub-directory in the repository's\n-- \ngitgitgadget\n\n"},{"id":"447909","messageId":"5d0cc242d92c68bf239f9e17eab9c80ec6b2d469.1644269583.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v6.git.1644269583.gitgitgadget@gmail.com","subject":"[PATCH v6 2/6] worktree: create init_worktree_config()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-07T21:32:59Z","receivedAt":"2022-02-07T21:33:14Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nUpgrading a repository to use extensions.worktreeConfig is non-trivial.\nThere are several steps involved, including moving some config settings\nfrom the common config file to the main worktree's config.worktree file.\nThe previous change updated the documentation with all of these details.\n\nCommands such as 'git sparse-checkout set' upgrade the repository to use\nextensions.worktreeConfig without following these steps, causing some\nuser pain in some special cases.\n\nCreate a helper method, init_worktree_config(), that will be used in a\nlater change to fix this behavior within 'git sparse-checkout set'. The\nmethod is carefully documented in worktree.h.\n\nNote that we do _not_ upgrade the repository format version to 1 during\nthis process. The worktree config extension must be considered by Git\nand third-party tools even if core.repositoryFormatVersion is 0 for\nhistorical reasons documented in 11664196ac (\"Revert\n\"check_repository_format_gently(): refuse extensions for old\nrepositories\"\", 2020-07-15). This is a special case for this extension,\nand newer extensions (such as extensions.objectFormat) still need to\nupgrade the repository format version.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n worktree.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n worktree.h | 21 ++++++++++++++++\n 2 files changed, 94 insertions(+)\n\ndiff --git a/worktree.c b/worktree.c\nindex 6f598dcfcdf..5292c94b3d9 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -5,6 +5,7 @@\n #include \"worktree.h\"\n #include \"dir.h\"\n #include \"wt-status.h\"\n+#include \"config.h\"\n \n void free_worktrees(struct worktree **worktrees)\n {\n@@ -826,3 +827,75 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,\n \t*wtpath = path;\n \treturn 0;\n }\n+\n+static int move_config_setting(const char *key, const char *value,\n+\t\t\t       const char *from_file, const char *to_file)\n+{\n+\tif (git_config_set_in_file_gently(to_file, key, value))\n+\t\treturn error(_(\"unable to set %s in '%s'\"), key, to_file);\n+\tif (git_config_set_in_file_gently(from_file, key, NULL))\n+\t\treturn error(_(\"unable to unset %s in '%s'\"), key, from_file);\n+\treturn 0;\n+}\n+\n+int init_worktree_config(struct repository *r)\n+{\n+\tint res = 0;\n+\tint bare = 0;\n+\tstruct config_set cs = { { 0 } };\n+\tconst char *core_worktree;\n+\tchar *common_config_file;\n+\tchar *main_worktree_file;\n+\n+\t/*\n+\t * If the extension is already enabled, then we can skip the\n+\t * upgrade process.\n+\t */\n+\tif (repository_format_worktree_config)\n+\t\treturn 0;\n+\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\")))\n+\t\treturn error(_(\"failed to set extensions.worktreeConfig setting\"));\n+\n+\tcommon_config_file = xstrfmt(\"%s/config\", r->commondir);\n+\tmain_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n+\n+\tgit_configset_init(&cs);\n+\tgit_configset_add_file(&cs, common_config_file);\n+\n+\t/*\n+\t * If core.bare is true in the common config file, then we need to\n+\t * move it to the main worktree's config file or it will break all\n+\t * worktrees. If it is false, then leave it in place because it\n+\t * _could_ be negating a global core.bare=true.\n+\t */\n+\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n+\t\tif ((res = move_config_setting(\"core.bare\", \"true\",\n+\t\t\t\t\t       common_config_file,\n+\t\t\t\t\t       main_worktree_file)))\n+\t\t\tgoto cleanup;\n+\t}\n+\t/*\n+\t * If core.worktree is set, then the main worktree is located\n+\t * somewhere different than the parent of the common Git dir.\n+\t * Relocate that value to avoid breaking all worktrees with this\n+\t * upgrade to worktree config.\n+\t */\n+\tif (!git_configset_get_value(&cs, \"core.worktree\", &core_worktree)) {\n+\t\tif ((res = move_config_setting(\"core.worktree\", core_worktree,\n+\t\t\t\t\t       common_config_file,\n+\t\t\t\t\t       main_worktree_file)))\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\t/*\n+\t * Ensure that we use worktree config for the remaining lifetime\n+\t * of the current process.\n+\t */\n+\trepository_format_worktree_config = 1;\n+\n+cleanup:\n+\tgit_configset_clear(&cs);\n+\tfree(common_config_file);\n+\tfree(main_worktree_file);\n+\treturn res;\n+}\ndiff --git a/worktree.h b/worktree.h\nindex 9e06fcbdf3d..e9e839926b0 100644\n--- a/worktree.h\n+++ b/worktree.h\n@@ -183,4 +183,25 @@ void strbuf_worktree_ref(const struct worktree *wt,\n \t\t\t struct strbuf *sb,\n \t\t\t const char *refname);\n \n+/**\n+ * Enable worktree config for the first time. This will make the following\n+ * adjustments:\n+ *\n+ * 1. Add extensions.worktreeConfig=true in the common config file.\n+ *\n+ * 2. If the common config file has a core.worktree value, then that value\n+ *    is moved to the main worktree's config.worktree file.\n+ *\n+ * 3. If the common config file has a core.bare enabled, then that value\n+ *    is moved to the main worktree's config.worktree file.\n+ *\n+ * If extensions.worktreeConfig is already true, then this method\n+ * terminates early without any of the above steps. The existing config\n+ * arrangement is assumed to be intentional.\n+ *\n+ * Returns 0 on success. Reports an error message and returns non-zero\n+ * if any of these steps fail.\n+ */\n+int init_worktree_config(struct repository *r);\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"447910","messageId":"cf9e86fe3a403d0ceaff9fdf484a9bf6b07799ac.1644269583.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v6.git.1644269583.gitgitgadget@gmail.com","subject":"[PATCH v6 3/6] config: add repo_config_set_worktree_gently()","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-07T21:33:00Z","receivedAt":"2022-02-07T21:33:18Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nSome config settings, such as those for sparse-checkout, are likely\nintended to only apply to one worktree at a time. To make this write\neasier, add a new config API method, repo_config_set_worktree_gently().\n\nThis method will attempt to write to the worktree-specific config, but\nwill instead write to the common config file if worktree config is not\nenabled.  The next change will introduce a consumer of this method.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n config.c | 35 ++++++++++++++++++++++++++++++++---\n config.h |  8 ++++++++\n 2 files changed, 40 insertions(+), 3 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex 2bffa8d4a01..1a03ced1a54 100644\n--- a/config.c\n+++ b/config.c\n@@ -21,6 +21,7 @@\n #include \"dir.h\"\n #include \"color.h\"\n #include \"refs.h\"\n+#include \"worktree.h\"\n \n struct config_source {\n \tstruct config_source *prev;\n@@ -2884,6 +2885,20 @@ int git_config_set_gently(const char *key, const char *value)\n \treturn git_config_set_multivar_gently(key, value, NULL, 0);\n }\n \n+int repo_config_set_worktree_gently(struct repository *r,\n+\t\t\t\t    const char *key, const char *value)\n+{\n+\t/* Only use worktree-specific config if it is is already enabled. */\n+\tif (repository_format_worktree_config) {\n+\t\tchar *file = repo_git_path(r, \"config.worktree\");\n+\t\tint ret = git_config_set_multivar_in_file_gently(\n+\t\t\t\t\tfile, key, value, NULL, 0);\n+\t\tfree(file);\n+\t\treturn ret;\n+\t}\n+\treturn repo_config_set_multivar_gently(r, key, value, NULL, 0);\n+}\n+\n void git_config_set(const char *key, const char *value)\n {\n \tgit_config_set_multivar(key, value, NULL, 0);\n@@ -3181,14 +3196,28 @@ void git_config_set_multivar_in_file(const char *config_filename,\n int git_config_set_multivar_gently(const char *key, const char *value,\n \t\t\t\t   const char *value_pattern, unsigned flags)\n {\n-\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n-\t\t\t\t\t\t      flags);\n+\treturn repo_config_set_multivar_gently(the_repository, key, value,\n+\t\t\t\t\t       value_pattern, flags);\n+}\n+\n+int repo_config_set_multivar_gently(struct repository *r, const char *key,\n+\t\t\t\t    const char *value,\n+\t\t\t\t    const char *value_pattern, unsigned flags)\n+{\n+\tchar *file = repo_git_path(r, \"config\");\n+\tint res = git_config_set_multivar_in_file_gently(file,\n+\t\t\t\t\t\t\t key, value,\n+\t\t\t\t\t\t\t value_pattern,\n+\t\t\t\t\t\t\t flags);\n+\tfree(file);\n+\treturn res;\n }\n \n void git_config_set_multivar(const char *key, const char *value,\n \t\t\t     const char *value_pattern, unsigned flags)\n {\n-\tgit_config_set_multivar_in_file(NULL, key, value, value_pattern,\n+\tgit_config_set_multivar_in_file(git_path(\"config\"),\n+\t\t\t\t\tkey, value, value_pattern,\n \t\t\t\t\tflags);\n }\n \ndiff --git a/config.h b/config.h\nindex f119de01309..1d98ad269bd 100644\n--- a/config.h\n+++ b/config.h\n@@ -253,6 +253,13 @@ void git_config_set_in_file(const char *, const char *, const char *);\n \n int git_config_set_gently(const char *, const char *);\n \n+/**\n+ * Write a config value that should apply to the current worktree. If\n+ * extensions.worktreeConfig is enabled, then the write will happen in the\n+ * current worktree's config. Otherwise, write to the common config file.\n+ */\n+int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n+\n /**\n  * write config values to `.git/config`, takes a key/value pair as parameter.\n  */\n@@ -281,6 +288,7 @@ int git_config_parse_key(const char *, char **, size_t *);\n \n int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n+int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n \n /**\n-- \ngitgitgadget\n\n"},{"id":"447911","messageId":"5b5924eab498c40542f42989fb31f568cb5371b2.1644269583.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v6.git.1644269583.gitgitgadget@gmail.com","subject":"[PATCH v6 4/6] sparse-checkout: set worktree-config correctly","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-07T21:33:01Z","receivedAt":"2022-02-07T21:33:23Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\n`git sparse-checkout set/init` enables worktree-specific\nconfiguration[*] by setting extensions.worktreeConfig=true, but neglects\nto perform the additional necessary bookkeeping of relocating\n`core.bare=true` and `core.worktree` from $GIT_COMMON_DIR/config to\n$GIT_COMMON_DIR/config.worktree, as documented in git-worktree.txt. As a\nresult of this oversight, these settings, which are nonsensical for\nsecondary worktrees, can cause Git commands to incorrectly consider a\nworktree bare (in the case of `core.bare`) or operate on the wrong\nworktree (in the case of `core.worktree`). Fix this problem by taking\nadvantage of the recently-added init_worktree_config() which enables\n`extensions.worktreeConfig` and takes care of necessary bookkeeping.\n\nWhile at it, for backward-compatibility reasons, also stop upgrading the\nrepository format to \"1\" since doing so is (unintentionally) not\nrequired to take advantage of `extensions.worktreeConfig`, as explained\nby 11664196ac (\"Revert \"check_repository_format_gently(): refuse\nextensions for old repositories\"\", 2020-07-15).\n\n[*] The main reason to use worktree-specific config for the\nsparse-checkout builtin was to avoid enabling sparse-checkout patterns\nin one and causing a loss of files in another. If a worktree does not\nhave a sparse-checkout patterns file, then the sparse-checkout logic\nwill not kick in on that worktree.\n\nReported-by: Sean Allred <allred.sean@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/git-sparse-checkout.txt | 16 +++++++++++----\n builtin/sparse-checkout.c             | 28 +++++++++++++--------------\n sparse-index.c                        | 10 +++-------\n t/t1091-sparse-checkout-builtin.sh    |  4 ++--\n 4 files changed, 30 insertions(+), 28 deletions(-)\n\ndiff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt\nindex b81dbe06543..94dad137b96 100644\n--- a/Documentation/git-sparse-checkout.txt\n+++ b/Documentation/git-sparse-checkout.txt\n@@ -31,13 +31,21 @@ COMMANDS\n \tDescribe the patterns in the sparse-checkout file.\n \n 'set'::\n-\tEnable the necessary config settings\n-\t(extensions.worktreeConfig, core.sparseCheckout,\n-\tcore.sparseCheckoutCone) if they are not already enabled, and\n-\twrite a set of patterns to the sparse-checkout file from the\n+\tEnable the necessary sparse-checkout config settings\n+\t(`core.sparseCheckout`, `core.sparseCheckoutCone`, and\n+\t`index.sparse`) if they are not already set to the desired values,\n+\tand write a set of patterns to the sparse-checkout file from the\n \tlist of arguments following the 'set' subcommand. Update the\n \tworking directory to match the new patterns.\n +\n+To ensure that adjusting the sparse-checkout settings within a worktree\n+does not alter the sparse-checkout settings in other worktrees, the 'set'\n+subcommand will upgrade your repository config to use worktree-specific\n+config if not already present. The sparsity defined by the arguments to\n+the 'set' subcommand are stored in the worktree-specific sparse-checkout\n+file. See linkgit:git-worktree[1] and the documentation of\n+`extensions.worktreeConfig` in linkgit:git-config[1] for more details.\n++\n When the `--stdin` option is provided, the patterns are read from\n standard in as a newline-delimited list instead of from the arguments.\n +\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 679c1070368..314c8d61f80 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -15,6 +15,7 @@\n #include \"wt-status.h\"\n #include \"quote.h\"\n #include \"sparse-index.h\"\n+#include \"worktree.h\"\n \n static const char *empty_base = \"\";\n \n@@ -359,26 +360,23 @@ enum sparse_checkout_mode {\n \n static int set_config(enum sparse_checkout_mode mode)\n {\n-\tconst char *config_path;\n-\n-\tif (upgrade_repository_format(1) < 0)\n-\t\tdie(_(\"unable to upgrade repository format to enable worktreeConfig\"));\n-\tif (git_config_set_gently(\"extensions.worktreeConfig\", \"true\")) {\n-\t\terror(_(\"failed to set extensions.worktreeConfig setting\"));\n+\t/* Update to use worktree config, if not already. */\n+\tif (init_worktree_config(the_repository)) {\n+\t\terror(_(\"failed to initialize worktree config\"));\n \t\treturn 1;\n \t}\n \n-\tconfig_path = git_path(\"config.worktree\");\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckout\",\n-\t\t\t\t      mode ? \"true\" : NULL);\n-\n-\tgit_config_set_in_file_gently(config_path,\n-\t\t\t\t      \"core.sparseCheckoutCone\",\n-\t\t\t\t      mode == MODE_CONE_PATTERNS ? \"true\" : NULL);\n+\tif (repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckout\",\n+\t\t\t\t\t    mode ? \"true\" : \"false\") ||\n+\t    repo_config_set_worktree_gently(the_repository,\n+\t\t\t\t\t    \"core.sparseCheckoutCone\",\n+\t\t\t\t\t    mode == MODE_CONE_PATTERNS ?\n+\t\t\t\t\t\t\"true\" : \"false\"))\n+\t\treturn 1;\n \n \tif (mode == MODE_NO_PATTERNS)\n-\t\tset_sparse_index_config(the_repository, 0);\n+\t\treturn set_sparse_index_config(the_repository, 0);\n \n \treturn 0;\n }\ndiff --git a/sparse-index.c b/sparse-index.c\nindex a1d505d50e9..e93609999e0 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -99,13 +99,9 @@ static int convert_to_sparse_rec(struct index_state *istate,\n \n int set_sparse_index_config(struct repository *repo, int enable)\n {\n-\tint res;\n-\tchar *config_path = repo_git_path(repo, \"config.worktree\");\n-\tres = git_config_set_in_file_gently(config_path,\n-\t\t\t\t\t    \"index.sparse\",\n-\t\t\t\t\t    enable ? \"true\" : NULL);\n-\tfree(config_path);\n-\n+\tint res = repo_config_set_worktree_gently(repo,\n+\t\t\t\t\t\t  \"index.sparse\",\n+\t\t\t\t\t\t  enable ? \"true\" : \"false\");\n \tprepare_repo_settings(repo);\n \trepo->settings.sparse_index = enable;\n \treturn res;\ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex 42776984fe7..be6ea4ffe33 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -117,7 +117,7 @@ test_expect_success 'switching to cone mode with non-cone mode patterns' '\n \t\tcd bad-patterns &&\n \t\tgit sparse-checkout init &&\n \t\tgit sparse-checkout add dir &&\n-\t\tgit config core.sparseCheckoutCone true &&\n+\t\tgit config --worktree core.sparseCheckoutCone true &&\n \t\ttest_must_fail git sparse-checkout add dir 2>err &&\n \t\tgrep \"existing sparse-checkout patterns do not use cone mode\" err\n \t)\n@@ -256,7 +256,7 @@ test_expect_success 'sparse-index enabled and disabled' '\n \t\ttest_cmp expect actual &&\n \n \t\tgit -C repo config --list >config &&\n-\t\t! grep index.sparse config\n+\t\ttest_cmp_config -C repo false index.sparse\n \t)\n '\n \n-- \ngitgitgadget\n\n"},{"id":"447912","messageId":"c51cb3714e7b1d2f8c9370fe87eca9984ff4859f.1644269584.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v6.git.1644269583.gitgitgadget@gmail.com","subject":"[PATCH v6 5/6] worktree: copy sparse-checkout patterns and config on add","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-07T21:33:02Z","receivedAt":"2022-02-07T21:33:24Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWhen adding a new worktree, it is reasonable to expect that we want to\nuse the current set of sparse-checkout settings for that new worktree.\nThis is particularly important for repositories where the worktree would\nbecome too large to be useful. This is even more important when using\npartial clone as well, since we want to avoid downloading the missing\nblobs for files that should not be written to the new worktree.\n\nThe only way to create such a worktree without this intermediate step of\nexpanding the full worktree is to copy the sparse-checkout patterns and\nconfig settings during 'git worktree add'. Each worktree has its own\nsparse-checkout patterns, and the default behavior when the\nsparse-checkout file is missing is to include all paths at HEAD. Thus,\nwe need to have patterns from somewhere, they might as well be the\ncurrent worktree's patterns. These are then modified independently in\nthe future.\n\nIn addition to the sparse-checkout file, copy the worktree config file\nif worktree config is enabled and the file exists. This will copy over\nany important settings to ensure the new worktree behaves the same as\nthe current one. The only exception we must continue to make is that\ncore.bare and core.worktree should become unset in the worktree's config\nfile.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/worktree.c                 | 63 ++++++++++++++++++++++++++++++\n t/t1091-sparse-checkout-builtin.sh | 31 +++++++++++----\n t/t2400-worktree-add.sh            | 58 ++++++++++++++++++++++++++-\n 3 files changed, 142 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 2838254f7f2..c6eb636329a 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -335,6 +335,69 @@ static int add_worktree(const char *path, const char *refname,\n \tstrbuf_addf(&sb, \"%s/commondir\", sb_repo.buf);\n \twrite_file(sb.buf, \"../..\");\n \n+\t/*\n+\t * If the current worktree has sparse-checkout enabled, then copy\n+\t * the sparse-checkout patterns from the current worktree.\n+\t */\n+\tif (core_apply_sparse_checkout) {\n+\t\tchar *from_file = git_pathdup(\"info/sparse-checkout\");\n+\t\tchar *to_file = xstrfmt(\"%s/info/sparse-checkout\",\n+\t\t\t\t\tsb_repo.buf);\n+\n+\t\tif (file_exists(from_file)) {\n+\t\t\tif (safe_create_leading_directories(to_file) ||\n+\t\t\t    copy_file(to_file, from_file, 0666))\n+\t\t\t\terror(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n+\t\t\t\t      from_file, to_file);\n+\t\t}\n+\n+\t\tfree(from_file);\n+\t\tfree(to_file);\n+\t}\n+\n+\t/*\n+\t * If we are using worktree config, then copy all current config\n+\t * values from the current worktree into the new one, that way the\n+\t * new worktree behaves the same as this one.\n+\t */\n+\tif (repository_format_worktree_config) {\n+\t\tchar *from_file = git_pathdup(\"config.worktree\");\n+\t\tchar *to_file = xstrfmt(\"%s/config.worktree\",\n+\t\t\t\t\tsb_repo.buf);\n+\n+\t\tif (file_exists(from_file)) {\n+\t\t\tstruct config_set cs = { { 0 } };\n+\t\t\tconst char *core_worktree;\n+\t\t\tint bare;\n+\n+\t\t\tif (safe_create_leading_directories(to_file) ||\n+\t\t\t    copy_file(to_file, from_file, 0666)) {\n+\t\t\t\terror(_(\"failed to copy worktree config from '%s' to '%s'\"),\n+\t\t\t\t      from_file, to_file);\n+\t\t\t\tgoto worktree_copy_cleanup;\n+\t\t\t}\n+\n+\t\t\tgit_configset_init(&cs);\n+\t\t\tgit_configset_add_file(&cs, from_file);\n+\n+\t\t\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) &&\n+\t\t\t    bare &&\n+\t\t\t    git_config_set_multivar_in_file_gently(\n+\t\t\t\t\tto_file, \"core.bare\", NULL, \"true\", 0))\n+\t\t\t\terror(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n+\t\t\tif (!git_configset_get_value(&cs, \"core.worktree\", &core_worktree) &&\n+\t\t\t    git_config_set_in_file_gently(to_file,\n+\t\t\t\t\t\t\t  \"core.worktree\", NULL))\n+\t\t\t\terror(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n+\n+\t\t\tgit_configset_clear(&cs);\n+\t\t}\n+\n+worktree_copy_cleanup:\n+\t\tfree(from_file);\n+\t\tfree(to_file);\n+\t}\n+\n \tstrvec_pushf(&child_env, \"%s=%s\", GIT_DIR_ENVIRONMENT, sb_git.buf);\n \tstrvec_pushf(&child_env, \"%s=%s\", GIT_WORK_TREE_ENVIRONMENT, path);\n \tcp.git_cmd = 1;\ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex be6ea4ffe33..8a757b43e6c 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -146,9 +146,9 @@ test_expect_success 'interaction with clone --no-checkout (unborn index)' '\n '\n \n test_expect_success 'set enables config' '\n-\tgit init empty-config &&\n+\tgit init worktree-config &&\n \t(\n-\t\tcd empty-config &&\n+\t\tcd worktree-config &&\n \t\ttest_commit test file &&\n \t\ttest_path_is_missing .git/config.worktree &&\n \t\tgit sparse-checkout set nothing &&\n@@ -201,6 +201,21 @@ test_expect_success 'add to sparse-checkout' '\n \tcheck_files repo \"a folder1 folder2\"\n '\n \n+test_expect_success 'worktree: add copies sparse-checkout patterns' '\n+\tcat repo/.git/info/sparse-checkout >old &&\n+\ttest_when_finished cp old repo/.git/info/sparse-checkout &&\n+\ttest_when_finished git -C repo worktree remove ../worktree &&\n+\tgit -C repo sparse-checkout set --no-cone \"/*\" &&\n+\tgit -C repo worktree add --quiet ../worktree 2>err &&\n+\ttest_must_be_empty err &&\n+\tnew=\"$(git -C worktree rev-parse --git-path info/sparse-checkout)\" &&\n+\ttest_path_is_file \"$new\" &&\n+\ttest_cmp repo/.git/info/sparse-checkout \"$new\" &&\n+\tgit -C worktree sparse-checkout set --cone &&\n+\ttest_cmp_config -C worktree true core.sparseCheckoutCone &&\n+\ttest_must_fail git -C repo core.sparseCheckoutCone\n+'\n+\n test_expect_success 'cone mode: match patterns' '\n \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n \trm -rf repo/a repo/folder1 repo/folder2 &&\n@@ -520,13 +535,13 @@ test_expect_success 'interaction with submodules' '\n '\n \n test_expect_success 'different sparse-checkouts with worktrees' '\n+\tgit -C repo sparse-checkout set --cone deep folder1 &&\n \tgit -C repo worktree add --detach ../worktree &&\n-\tcheck_files worktree \"a deep folder1 folder2\" &&\n-\tgit -C worktree sparse-checkout init --cone &&\n-\tgit -C repo sparse-checkout set folder1 &&\n-\tgit -C worktree sparse-checkout set deep/deeper1 &&\n-\tcheck_files repo a folder1 &&\n-\tcheck_files worktree a deep\n+\tcheck_files worktree \"a deep folder1\" &&\n+\tgit -C repo sparse-checkout set --cone folder1 &&\n+\tgit -C worktree sparse-checkout set --cone deep/deeper1 &&\n+\tcheck_files repo \"a folder1\" &&\n+\tcheck_files worktree \"a deep\"\n '\n \n test_expect_success 'set using filename keeps file on-disk' '\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 37ad79470fb..43139af08fc 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -165,8 +165,62 @@ test_expect_success '\"add\" default branch of a bare repo' '\n \t(\n \t\tgit clone --bare . bare2 &&\n \t\tcd bare2 &&\n-\t\tgit worktree add ../there3 main\n-\t)\n+\t\tgit worktree add ../there3 main &&\n+\t\tcd ../there3 &&\n+\t\t# Simple check that a Git command does not\n+\t\t# immediately fail with the current setup\n+\t\tgit status\n+\t) &&\n+\tcat >expect <<-EOF &&\n+\tinit.t\n+\tEOF\n+\tls there3 >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '\"add\" to bare repo with worktree config' '\n+\t(\n+\t\tgit clone --bare . bare3 &&\n+\t\tcd bare3 &&\n+\t\tgit config extensions.worktreeconfig true &&\n+\n+\t\t# Add config values that are erroneous to have in\n+\t\t# a config.worktree file outside of the main\n+\t\t# working tree, to check that Git filters them out\n+\t\t# when copying config during \"git worktree add\".\n+\t\tgit config --worktree core.bare true &&\n+\t\tgit config --worktree core.worktree \"$(pwd)\" &&\n+\n+\t\t# We want to check that bogus.key is copied\n+\t\tgit config --worktree bogus.key value &&\n+\t\tgit config --unset core.bare &&\n+\t\tgit worktree add ../there4 main &&\n+\t\tcd ../there4 &&\n+\n+\t\t# Simple check that a Git command does not\n+\t\t# immediately fail with the current setup\n+\t\tgit status &&\n+\t\tgit worktree add --detach ../there5 &&\n+\t\tcd ../there5 &&\n+\t\tgit status\n+\t) &&\n+\n+\t# the worktree has the arbitrary value copied.\n+\ttest_cmp_config -C there4 value bogus.key &&\n+\ttest_cmp_config -C there5 value bogus.key &&\n+\n+\t# however, core.bare and core.worktree were removed.\n+\ttest_must_fail git -C there4 config core.bare &&\n+\ttest_must_fail git -C there4 config core.worktree &&\n+\n+\tcat >expect <<-EOF &&\n+\tinit.t\n+\tEOF\n+\n+\tls there4 >actual &&\n+\ttest_cmp expect actual &&\n+\tls there5 >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'checkout with grafts' '\n-- \ngitgitgadget\n\n"},{"id":"447913","messageId":"f687a0bfa167aa453a531ad4d2ec9687887a90e7.1644269584.git.gitgitgadget@gmail.com","threadId":"57125","inReplyTo":"pull.1101.v6.git.1644269583.gitgitgadget@gmail.com","subject":"[PATCH v6 6/6] config: make git_configset_get_string_tmp() private","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-07T21:33:03Z","receivedAt":"2022-02-07T21:33:25Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThis method was created in f1de981e8 (config: fix leaks from\ngit_config_get_string_const(), 2020-08-14) but its only use was in the\nrepo_config_get_string_tmp() method, also declared in config.h and\nimplemented in config.c. Since this is otherwise unused and is a very\nsimilar implementation to git_configset_get_value(), let's remove this\ndeclaration.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n config.c | 4 ++--\n config.h | 1 -\n 2 files changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex 1a03ced1a54..870b22dd2ff 100644\n--- a/config.c\n+++ b/config.c\n@@ -2179,8 +2179,8 @@ int git_configset_get_string(struct config_set *cs, const char *key, char **dest\n \t\treturn 1;\n }\n \n-int git_configset_get_string_tmp(struct config_set *cs, const char *key,\n-\t\t\t\t const char **dest)\n+static int git_configset_get_string_tmp(struct config_set *cs, const char *key,\n+\t\t\t\t\tconst char **dest)\n {\n \tconst char *value;\n \tif (!git_configset_get_value(cs, key, &value)) {\ndiff --git a/config.h b/config.h\nindex 1d98ad269bd..184aef1eca4 100644\n--- a/config.h\n+++ b/config.h\n@@ -494,7 +494,6 @@ void git_configset_clear(struct config_set *cs);\n int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);\n \n int git_configset_get_string(struct config_set *cs, const char *key, char **dest);\n-int git_configset_get_string_tmp(struct config_set *cs, const char *key, const char **dest);\n int git_configset_get_int(struct config_set *cs, const char *key, int *dest);\n int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);\n int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);\n-- \ngitgitgadget\n"},{"id":"447939","messageId":"CABPp-BG4m6i+yobjnD6UC5S5n3k-HbB7Y5uUNEvrD79ErOoYHg@mail.gmail.com","threadId":"57125","inReplyTo":"pull.1101.v6.git.1644269583.gitgitgadget@gmail.com","subject":"Re: [PATCH v6 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-02-08T04:14:29Z","receivedAt":"2022-02-08T04:14:55Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Feb 7, 2022 at 1:33 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> This series is now based on v2.35.0 since that contains all of the necessary\n> topics.\n>\n> This patch series includes a fix to the bug reported by Sean Allred [1] and\n> diagnosed by Eric Sunshine [2].\n>\n> The root cause is that 'git sparse-checkout init' writes to the worktree\n> config without checking that core.bare or core.worktree are set in the\n> common config file. This series fixes this, but also puts in place some\n> helpers to prevent this from happening in the future.\n>\n> ATTENTION: I have significantly redesigned the series since previous\n> versions, so most of this cover letter is new.\n>\n>  * Patch 1 updates documentation around extensions.worktreeConfig in a few\n>    places to improve discoverability. Several cross links are added to make\n>    it easy to find the related areas. (The documentation for the changes to\n>    'git sparse-checkout' are delayed to patch 4.)\n>\n>  * Patch 2 introduces the init_worktree_config() helper which follows the\n>    documented instructions to enable extensions.worktreeConfig as well as\n>    move the core.bare and core.worktree config values. This update does not\n>    modify core.repositoryFormatVersion, since this is not needed\n>    specifically for extensions.worktreeConfig.\n>\n>  * Patch 3 adds a new repo_config_set_worktree_gently() helper method so we\n>    can internally adjust a config value within a worktree, at least if\n>    extensions.worktreeConfig is enabled. (It will write to the common config\n>    file if the extension is not enabled.)\n>\n>  * Patch 4 modifies the sparse-checkout builtin to use\n>    init_worktree_config() and repo_config_set_worktree_gently() in ways that\n>    fix the reported bug. The behavior change here is that it will no longer\n>    upgrade the repository format version, since that is not needed for\n>    extensions.worktreeConfig.\n>\n>  * Patch 5 updates 'git worktree add' to copy the worktree config from the\n>    current worktree to the new one (while unsetting core.bare=true and\n>    core.worktree=*) along with copying the sparse-checkout patterns file.\n>\n> [1]\n> https://lore.kernel.org/git/CABceR4bZmtC4rCwgxZ1BBYZP69VOUca1f_moJoP989vTUZWu9Q@mail.gmail.com/\n> [2]\n> https://lore.kernel.org/git/CAPig+cQ6U_yFw-X2OWrizB1rbCvc4bNxuSzKFzmoLNnm0GH8Eg@mail.gmail.com/\n>\n>\n> Updates in v6\n> =============\n>\n>  * Updated documentation to use \"working tree\" over \"worktree\" and \"\" over\n>    \"\"\n\nNot sure what \"\" over \"\" means.\n\n>  * Delay some allocations to avoid leaking memory in error conditions.\n>  * Use \"main worktree\" over \"base worktree\" in comments.\n>  *\n\nWas the empty bullet point meant to cover the new patch 6?  Anyway,\ncomments on the cover letter aside, the patches themselves are:\n\nReviewed-by: Elijah Newren <newren@gmail.com>\n"},{"id":"447942","messageId":"CAPig+cT7GwyhPOBdqDDh8jGm+mWK1x9Gt4Zebcw34dgFyaufkw@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BG4m6i+yobjnD6UC5S5n3k-HbB7Y5uUNEvrD79ErOoYHg@mail.gmail.com","subject":"Re: [PATCH v6 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-02-08T05:02:50Z","receivedAt":"2022-02-08T05:32:53Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Feb 7, 2022 at 11:14 PM Elijah Newren <newren@gmail.com> wrote:\n> On Mon, Feb 7, 2022 at 1:33 PM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> > Updates in v6\n> >  * Updated documentation to use \"working tree\" over \"worktree\" and \"\" over\n> >    \"\"\n>\n> Not sure what \"\" over \"\" means.\n\nProbably related to my review comment[1] about spelling it\n\"$GIT_DIR/worktrees/<id>/\" rather than\n\"$GIT_DIR/worktrees/<worktree-name>/\".\n\n> >  * Delay some allocations to avoid leaking memory in error conditions.\n> >  * Use \"main worktree\" over \"base worktree\" in comments.\n> >  *\n>\n> Was the empty bullet point meant to cover the new patch 6?\n\nThe \"Updates in v6\" section was botched a bit. If you look closely,\nthe remaining bullet points actually ended up in the \"Updates in v5\"\nsection. The complete \"Updates in v6\" section should have been\n(approximately):\n\n * Updated documentation to use \"working tree\" over \"worktree\" and\n   \"<id>\" over \"<worktree-name>\"\n * Delay some allocations to avoid leaking memory in error conditions.\n * Use \"main worktree\" over \"base worktree\" in comments.\n * Removed use of git_configset_get_string_tmp() and added a patch that\n   removes its public declaration.\n * Fragile variables are replaced with better ones.\n * Variable names and code style improved.\n * Several test cleanups in patch 5.\n\n[1]: https://lore.kernel.org/git/pull.1101.v4.git.1643136134.gitgitgadget@gmail.com/T/#m4926177771017bbea82c33e9e03e6a9a004ebf24\n"},{"id":"447943","messageId":"CABPp-BGZoGwpMaqo2px1SqOBk1xLHtwkkBBAaqU5sNfTvKD1og@mail.gmail.com","threadId":"57125","inReplyTo":"CAPig+cT7GwyhPOBdqDDh8jGm+mWK1x9Gt4Zebcw34dgFyaufkw@mail.gmail.com","subject":"Re: [PATCH v6 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-02-08T05:18:19Z","receivedAt":"2022-02-08T05:33:14Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Feb 7, 2022 at 9:03 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Mon, Feb 7, 2022 at 11:14 PM Elijah Newren <newren@gmail.com> wrote:\n> > On Mon, Feb 7, 2022 at 1:33 PM Derrick Stolee via GitGitGadget\n> > <gitgitgadget@gmail.com> wrote:\n> > > Updates in v6\n> > >  * Updated documentation to use \"working tree\" over \"worktree\" and \"\" over\n> > >    \"\"\n> >\n> > Not sure what \"\" over \"\" means.\n>\n> Probably related to my review comment[1] about spelling it\n> \"$GIT_DIR/worktrees/<id>/\" rather than\n> \"$GIT_DIR/worktrees/<worktree-name>/\".\n>\n> > >  * Delay some allocations to avoid leaking memory in error conditions.\n> > >  * Use \"main worktree\" over \"base worktree\" in comments.\n> > >  *\n> >\n> > Was the empty bullet point meant to cover the new patch 6?\n>\n> The \"Updates in v6\" section was botched a bit. If you look closely,\n> the remaining bullet points actually ended up in the \"Updates in v5\"\n> section. The complete \"Updates in v6\" section should have been\n> (approximately):\n>\n>  * Updated documentation to use \"working tree\" over \"worktree\" and\n>    \"<id>\" over \"<worktree-name>\"\n>  * Delay some allocations to avoid leaking memory in error conditions.\n>  * Use \"main worktree\" over \"base worktree\" in comments.\n>  * Removed use of git_configset_get_string_tmp() and added a patch that\n>    removes its public declaration.\n>  * Fragile variables are replaced with better ones.\n>  * Variable names and code style improved.\n>  * Several test cleanups in patch 5.\n>\n> [1]: https://lore.kernel.org/git/pull.1101.v4.git.1643136134.gitgitgadget@gmail.com/T/#m4926177771017bbea82c33e9e03e6a9a004ebf24\n\nSo, you clearly also read the patches in this round.  Do they also\nlook good to you?   :-)\n"},{"id":"447944","messageId":"CAPig+cQWnuG+-eWyih7-Q75PeFjQRHN58SWWhFQWHU2=R6GfyA@mail.gmail.com","threadId":"57125","inReplyTo":"CABPp-BGZoGwpMaqo2px1SqOBk1xLHtwkkBBAaqU5sNfTvKD1og@mail.gmail.com","subject":"Re: [PATCH v6 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-02-08T05:42:50Z","receivedAt":"2022-02-08T05:43:06Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Feb 8, 2022 at 12:18 AM Elijah Newren <newren@gmail.com> wrote:\n> On Mon, Feb 7, 2022 at 9:03 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > On Mon, Feb 7, 2022 at 11:14 PM Elijah Newren <newren@gmail.com> wrote:\n> > > Was the empty bullet point meant to cover the new patch 6?\n> >\n> > The \"Updates in v6\" section was botched a bit. If you look closely,\n> > the remaining bullet points actually ended up in the \"Updates in v5\"\n> > section. The complete \"Updates in v6\" section should have been\n> > (approximately):\n> >\n> >  * Updated documentation to use \"working tree\" over \"worktree\" and\n> >    \"<id>\" over \"<worktree-name>\"\n> >  * Delay some allocations to avoid leaking memory in error conditions.\n> >  * Use \"main worktree\" over \"base worktree\" in comments.\n> >  * Removed use of git_configset_get_string_tmp() and added a patch that\n> >    removes its public declaration.\n> >  * Fragile variables are replaced with better ones.\n> >  * Variable names and code style improved.\n> >  * Several test cleanups in patch 5.\n>\n> So, you clearly also read the patches in this round.  Do they also\n> look good to you?   :-)\n\nI have not yet looked at either the patches or the range-diff, and I\nonly scanned my eye quickly over the cover letter, but the empty\nbullet point made me stop and look a bit more carefully at that part\n(and only that part) of the cover letter. Not sure yet when I'll have\ntime to carefully read this round.\n"},{"id":"447990","messageId":"9c79d950-8d42-829f-7a92-6dbbeabbdf73@gmail.com","threadId":"57125","inReplyTo":"CAPig+cT7GwyhPOBdqDDh8jGm+mWK1x9Gt4Zebcw34dgFyaufkw@mail.gmail.com","subject":"Re: [PATCH v6 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-02-08T14:33:06Z","receivedAt":"2022-02-08T14:33:15Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 2/8/2022 12:02 AM, Eric Sunshine wrote:\n> On Mon, Feb 7, 2022 at 11:14 PM Elijah Newren <newren@gmail.com> wrote:\n>> On Mon, Feb 7, 2022 at 1:33 PM Derrick Stolee via GitGitGadget\n>> <gitgitgadget@gmail.com> wrote:\n>>> Updates in v6\n>>>  * Updated documentation to use \"working tree\" over \"worktree\" and \"\" over\n>>>    \"\"\n>>\n>> Not sure what \"\" over \"\" means.\n> \n> Probably related to my review comment[1] about spelling it\n> \"$GIT_DIR/worktrees/<id>/\" rather than\n> \"$GIT_DIR/worktrees/<worktree-name>/\".\n> \n>>>  * Delay some allocations to avoid leaking memory in error conditions.\n>>>  * Use \"main worktree\" over \"base worktree\" in comments.\n>>>  *\n>>\n>> Was the empty bullet point meant to cover the new patch 6?\n> \n> The \"Updates in v6\" section was botched a bit. If you look closely,\n> the remaining bullet points actually ended up in the \"Updates in v5\"\n> section. The complete \"Updates in v6\" section should have been\n> (approximately):\n\nWhoops on mixing them up. Sorry about that.\n \n>  * Updated documentation to use \"working tree\" over \"worktree\" and\n>    \"<id>\" over \"<worktree-name>\"\n\nthis is the issue for the empty quotes. GGG thought these were HTML\ntags, so made them disappear. I should have used `<id>` and `<worktree-name>`.\n\n>  * Delay some allocations to avoid leaking memory in error conditions.\n>  * Use \"main worktree\" over \"base worktree\" in comments.\n>  * Removed use of git_configset_get_string_tmp() and added a patch that\n>    removes its public declaration.\n>  * Fragile variables are replaced with better ones.\n>  * Variable names and code style improved.\n>  * Several test cleanups in patch 5.\n> \n> [1]: https://lore.kernel.org/git/pull.1101.v4.git.1643136134.gitgitgadget@gmail.com/T/#m4926177771017bbea82c33e9e03e6a9a004ebf24\n\nThanks for cleaning up my cover letter!\n-Stolee\n"},{"id":"447996","messageId":"xmqq1r0dc8om.fsf@gitster.g","threadId":"57125","inReplyTo":"5d0cc242d92c68bf239f9e17eab9c80ec6b2d469.1644269583.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v6 2/6] worktree: create init_worktree_config()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-08T22:09:13Z","receivedAt":"2022-02-08T22:24:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +static int move_config_setting(const char *key, const char *value,\n> +\t\t\t       const char *from_file, const char *to_file)\n> +{\n> +\tif (git_config_set_in_file_gently(to_file, key, value))\n> +\t\treturn error(_(\"unable to set %s in '%s'\"), key, to_file);\n> +\tif (git_config_set_in_file_gently(from_file, key, NULL))\n> +\t\treturn error(_(\"unable to unset %s in '%s'\"), key, from_file);\n> +\treturn 0;\n> +}\n\nInteresting.\n\nThe verb \"move\" in its name made me expect a \"get (and remove)\nwhatever value(s) defined out of the old file, and set them\nidentically in the new file\" sequence, but that is not what is done\nhere.  \"set to this new single value in the new file and unset from\nthe old one\".\n\nI can see the need to say \"move it only when its value is X\",\nso having the caller to extract the value before deciding to call\nthe function (hence not \"moving from old\") does make sense, but then\nthe function is misnamed---it is not \"moving\", it is doing something\nelse.\n\n> +int init_worktree_config(struct repository *r)\n> +{\n> +\tint res = 0;\n> +\tint bare = 0;\n> +\tstruct config_set cs = { { 0 } };\n> +\tconst char *core_worktree;\n> +\tchar *common_config_file;\n> +\tchar *main_worktree_file;\n> +\n> +\t/*\n> +\t * If the extension is already enabled, then we can skip the\n> +\t * upgrade process.\n> +\t */\n> +\tif (repository_format_worktree_config)\n> +\t\treturn 0;\n\nOK.\n\n> +\tif ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\")))\n> +\t\treturn error(_(\"failed to set extensions.worktreeConfig setting\"));\n\nOK.\n\n> +\tcommon_config_file = xstrfmt(\"%s/config\", r->commondir);\n> +\tmain_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n> +\n> +\tgit_configset_init(&cs);\n> +\tgit_configset_add_file(&cs, common_config_file);\n> +\n> +\t/*\n> +\t * If core.bare is true in the common config file, then we need to\n> +\t * move it to the main worktree's config file or it will break all\n> +\t * worktrees. If it is false, then leave it in place because it\n> +\t * _could_ be negating a global core.bare=true.\n> +\t */\n\nIs the assumption that the secondary worktrees are never bare, but\nthe primary one could be (iow, adding worktrees to a bare repository\nwould leave the original bare repository as the primary \"worktree\"\nthat does not have \"working tree\")?  I am trying to see what downsides\nit tries to avoid by not moving the core.bare==false setting.  Shouldn't\ncore.bare be set to false when \"worktree add\" creates a new one anyway,\nif the secondaries are never bare?\n\n> +\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n> +\t\tif ((res = move_config_setting(\"core.bare\", \"true\",\n> +\t\t\t\t\t       common_config_file,\n> +\t\t\t\t\t       main_worktree_file)))\n> +\t\t\tgoto cleanup;\n> +\t}\n\n> +\t/*\n> +\t * If core.worktree is set, then the main worktree is located\n> +\t * somewhere different than the parent of the common Git dir.\n\nOK.  We do not want to share the working tree for the primary worktree\namong secondary worktrees.  For the primary, common and uncommon are\nthe same, so it may not matter, but mention of \"common Git dir\" here\nmay confuse readers?  Unless overridden by the config, the parent of\nthe git dir is the root of the working tree, no?\n\n> +\t * Relocate that value to avoid breaking all worktrees with this\n> +\t * upgrade to worktree config.\n> +\t */\n\nAnd if it is not set, then working tree of each worktree is the\nparent of the per-worktree Git dir, so they will automatically\nbecome separate, which makes sense.\n\n> +\tif (!git_configset_get_value(&cs, \"core.worktree\", &core_worktree)) {\n> +\t\tif ((res = move_config_setting(\"core.worktree\", core_worktree,\n> +\t\t\t\t\t       common_config_file,\n> +\t\t\t\t\t       main_worktree_file)))\n> +\t\t\tgoto cleanup;\n> +\t}\n> +\n> +\t/*\n> +\t * Ensure that we use worktree config for the remaining lifetime\n> +\t * of the current process.\n> +\t */\n> +\trepository_format_worktree_config = 1;\n> +\n> +cleanup:\n> +\tgit_configset_clear(&cs);\n> +\tfree(common_config_file);\n> +\tfree(main_worktree_file);\n> +\treturn res;\n> +}\n> diff --git a/worktree.h b/worktree.h\n> index 9e06fcbdf3d..e9e839926b0 100644\n> --- a/worktree.h\n> +++ b/worktree.h\n> @@ -183,4 +183,25 @@ void strbuf_worktree_ref(const struct worktree *wt,\n>  \t\t\t struct strbuf *sb,\n>  \t\t\t const char *refname);\n>  \n> +/**\n> + * Enable worktree config for the first time. This will make the following\n> + * adjustments:\n> + *\n> + * 1. Add extensions.worktreeConfig=true in the common config file.\n> + *\n> + * 2. If the common config file has a core.worktree value, then that value\n> + *    is moved to the main worktree's config.worktree file.\n> + *\n> + * 3. If the common config file has a core.bare enabled, then that value\n> + *    is moved to the main worktree's config.worktree file.\n> + *\n> + * If extensions.worktreeConfig is already true, then this method\n> + * terminates early without any of the above steps. The existing config\n> + * arrangement is assumed to be intentional.\n> + *\n> + * Returns 0 on success. Reports an error message and returns non-zero\n> + * if any of these steps fail.\n> + */\n> +int init_worktree_config(struct repository *r);\n> +\n>  #endif\n"},{"id":"447998","messageId":"xmqqo83hatm1.fsf@gitster.g","threadId":"57125","inReplyTo":"0260ff6cac0c76c6d66187d77defef1edd4c6fb5.1644269583.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v6 1/6] Documentation: add extensions.worktreeConfig details","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-08T22:20:06Z","receivedAt":"2022-02-08T22:24:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt\n> index 4e23d73cdca..bccaec7a963 100644\n> --- a/Documentation/config/extensions.txt\n> +++ b/Documentation/config/extensions.txt\n> @@ -6,3 +6,34 @@ extensions.objectFormat::\n>  Note that this setting should only be set by linkgit:git-init[1] or\n>  linkgit:git-clone[1].  Trying to change it after initialization will not\n>  work and will produce hard-to-diagnose issues.\n> +\n> +extensions.worktreeConfig::\n> +\tIf enabled, then worktrees will load config settings from the\n> +\t`$GIT_DIR/config.worktree` file in addition to the\n> +\t`$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and\n> +\t`$GIT_DIR` are the same for the main working tree, while other\n> +\tworking trees have `$GIT_DIR` equal to\n> +\t`$GIT_COMMON_DIR/worktrees/<id>/`. The settings in the\n\nThe mixed use of \"worktree\" and \"working tree\" in this paragraph\nmight confuse readers into thinking that the paragraph is being\ncareful to make distinction between the two.  All references to\n\"working tree\" in the above paragraph should actually be \"worktree\",\nI would think.\n\n\tSide note: \"working tree\" is in the glossary-content.txt,\n\tbut \"worktree\", which is one \"working tree\" + repository\n\tmetadata (i.e. \".git/\") that may be partially shared with\n\tother \"worktree\"s of a single repository, is not defined.\n\nThis is a tangent, but I wonder why we chose to use a different\nfilename (i.e. not \"config\" but \"config.worktree\") for this.  If we\nwere redoing multi-worktree support from scratch, we would not reuse\nthe $GIT_DIR used by the primary worktree as $GIT_COMMON_DIR, so\nthat all worktrees would share a single $GIT_COMMON_DIR and\n$GIT_COMMON_DIR/config that has stuff that is shared among all the\nworktrees, while per worktree stuff is in $GIT_DIR/config even for\nthe primary worktree.  But that is all water under the bridge now.\n\nOther than the terminology gotcha, looked sensible.  Migrating\nautomatically and/or noticing a suspicious setting may be needed to\nhelp end users, but that would not be within the scope of this step.\n\nAttached is a \"how about this?\" glossary update suggestion.  Most of\nthe existing mention of \"working tree\" are fine as-is because they\nonly care about what is in the \"working tree\", but some should be\nchanged to \"worktree\" to stress the fact that they care not just the\n\"working tree\" part but also the repository metadata part that is\nassociated with that single \"working tree\".  The first hunk says\nworktree but it is clear that it is interested only in the \"working\ntree\" files.\n\n Documentation/glossary-content.txt | 13 +++++++++++--\n 1 file changed, 11 insertions(+), 2 deletions(-)\n\ndiff --git c/Documentation/glossary-content.txt w/Documentation/glossary-content.txt\nindex c077971335..d816512c6a 100644\n--- c/Documentation/glossary-content.txt\n+++ w/Documentation/glossary-content.txt\n@@ -312,7 +312,7 @@ Pathspecs are used on the command line of \"git ls-files\", \"git\n ls-tree\", \"git add\", \"git grep\", \"git diff\", \"git checkout\",\n and many other commands to\n limit the scope of operations to some subset of the tree or\n-worktree.  See the documentation of each command for whether\n+working tree.  See the documentation of each command for whether\n paths are relative to the current directory or toplevel.  The\n pathspec syntax is as follows:\n +\n@@ -446,7 +446,7 @@ exclude;;\n \tinterface than the <<def_plumbing,plumbing>>.\n \n [[def_per_worktree_ref]]per-worktree ref::\n-\tRefs that are per-<<def_working_tree,worktree>>, rather than\n+\tRefs that are per-<<def_worktree,worktree>>, rather than\n \tglobal.  This is presently only <<def_HEAD,HEAD>> and any refs\n \tthat start with `refs/bisect/`, but might later include other\n \tunusual refs.\n@@ -669,3 +669,12 @@ The most notable example is `HEAD`.\n \tThe tree of actual checked out files.  The working tree normally\n \tcontains the contents of the <<def_HEAD,HEAD>> commit's tree,\n \tplus any local changes that you have made but not yet committed.\n+\n+[[def_work_tree]]worktree::\n+\tA repository can have zero (i.e. bare repository) or one or\n+\tmore worktrees attached to it. One \"worktree\" consists of a\n+\t\"working tree\" and repository metadata, most of which are\n+\tshared among other worktrees of a single repository, and\n+\tsome of which are maintained separately per worktree\n+\t(e.g. the index, HEAD, per-worktree refs and per-worktree\n+\tconfiguration file)\n"},{"id":"448002","messageId":"xmqqv8xpato3.fsf@gitster.g","threadId":"57125","inReplyTo":"cf9e86fe3a403d0ceaff9fdf484a9bf6b07799ac.1644269583.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v6 3/6] config: add repo_config_set_worktree_gently()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-08T22:18:52Z","receivedAt":"2022-02-08T22:25:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Derrick Stolee <dstolee@microsoft.com>\n>\n> Some config settings, such as those for sparse-checkout, are likely\n> intended to only apply to one worktree at a time. To make this write\n> easier, add a new config API method, repo_config_set_worktree_gently().\n>\n> This method will attempt to write to the worktree-specific config, but\n> will instead write to the common config file if worktree config is not\n> enabled.  The next change will introduce a consumer of this method.\n\nMakes sense.\n\n> +int repo_config_set_worktree_gently(struct repository *r,\n> +\t\t\t\t    const char *key, const char *value)\n> +{\n> +\t/* Only use worktree-specific config if it is is already enabled. */\n> +\tif (repository_format_worktree_config) {\n> +\t\tchar *file = repo_git_path(r, \"config.worktree\");\n> +\t\tint ret = git_config_set_multivar_in_file_gently(\n> +\t\t\t\t\tfile, key, value, NULL, 0);\n> +\t\tfree(file);\n> +\t\treturn ret;\n> +\t}\n> +\treturn repo_config_set_multivar_gently(r, key, value, NULL, 0);\n> +}\n\nOK.\n\n> @@ -3181,14 +3196,28 @@ void git_config_set_multivar_in_file(const char *config_filename,\n>  int git_config_set_multivar_gently(const char *key, const char *value,\n>  \t\t\t\t   const char *value_pattern, unsigned flags)\n>  {\n> -\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n> -\t\t\t\t\t\t      flags);\n> +\treturn repo_config_set_multivar_gently(the_repository, key, value,\n> +\t\t\t\t\t       value_pattern, flags);\n> +}\n\nIs this an unrelated \"morally no-op\" change?\n\n> +int repo_config_set_multivar_gently(struct repository *r, const char *key,\n> +\t\t\t\t    const char *value,\n> +\t\t\t\t    const char *value_pattern, unsigned flags)\n> +{\n> +\tchar *file = repo_git_path(r, \"config\");\n> +\tint res = git_config_set_multivar_in_file_gently(file,\n> +\t\t\t\t\t\t\t key, value,\n> +\t\t\t\t\t\t\t value_pattern,\n> +\t\t\t\t\t\t\t flags);\n> +\tfree(file);\n> +\treturn res;\n>  }\n\nOK.\n\n>  void git_config_set_multivar(const char *key, const char *value,\n>  \t\t\t     const char *value_pattern, unsigned flags)\n>  {\n> -\tgit_config_set_multivar_in_file(NULL, key, value, value_pattern,\n> +\tgit_config_set_multivar_in_file(git_path(\"config\"),\n> +\t\t\t\t\tkey, value, value_pattern,\n>  \t\t\t\t\tflags);\n>  }\n\nIs this an unrelated \"morally no-op\" change?\n\nIt might have value to make caller more explicit by reducing the use\nof \"I give NULL, you use 'config' for me\", but that doesn't sound\nrelated to the addition of set_per_worktree_config_gently() helper.\n\n> diff --git a/config.h b/config.h\n> index f119de01309..1d98ad269bd 100644\n> --- a/config.h\n> +++ b/config.h\n> @@ -253,6 +253,13 @@ void git_config_set_in_file(const char *, const char *, const char *);\n>  \n>  int git_config_set_gently(const char *, const char *);\n>  \n> +/**\n> + * Write a config value that should apply to the current worktree. If\n> + * extensions.worktreeConfig is enabled, then the write will happen in the\n> + * current worktree's config. Otherwise, write to the common config file.\n> + */\n> +int repo_config_set_worktree_gently(struct repository *, const char *, const char *);\n> +\n>  /**\n>   * write config values to `.git/config`, takes a key/value pair as parameter.\n>   */\n> @@ -281,6 +288,7 @@ int git_config_parse_key(const char *, char **, size_t *);\n>  \n>  int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n>  void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n> +int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);\n>  int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n>  \n>  /**\n"},{"id":"448021","messageId":"5f7f10df-f048-a663-a98c-536025be0040@gmail.com","threadId":"57125","inReplyTo":"xmqq1r0dc8om.fsf@gitster.g","subject":"Re: [PATCH v6 2/6] worktree: create init_worktree_config()","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-02-09T02:21:17Z","receivedAt":"2022-02-09T02:41:08Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 2/8/2022 5:09 PM, Junio C Hamano wrote:\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n>> +static int move_config_setting(const char *key, const char *value,\n>> +\t\t\t       const char *from_file, const char *to_file)\n>> +{\n>> +\tif (git_config_set_in_file_gently(to_file, key, value))\n>> +\t\treturn error(_(\"unable to set %s in '%s'\"), key, to_file);\n>> +\tif (git_config_set_in_file_gently(from_file, key, NULL))\n>> +\t\treturn error(_(\"unable to unset %s in '%s'\"), key, from_file);\n>> +\treturn 0;\n>> +}\n> \n> Interesting.\n> \n> The verb \"move\" in its name made me expect a \"get (and remove)\n> whatever value(s) defined out of the old file, and set them\n> identically in the new file\" sequence, but that is not what is done\n> here.  \"set to this new single value in the new file and unset from\n> the old one\".\n\nI think this \"copy into the worktree-specific config, then remove\nfrom the common file\" is an important sequence of events in case a\nconcurrent process comes in and reads the two config files in the\nintermediate state and does not see the config value anywhere.\n\nBut perhaps that's not actually what you are concerned about,\nbecause you're saying that the 'value' being provided does not\nactually guarantee that we are moving the setting.\n\n> I can see the need to say \"move it only when its value is X\",\n> so having the caller to extract the value before deciding to call\n> the function (hence not \"moving from old\") does make sense, but then\n> the function is misnamed---it is not \"moving\", it is doing something\n> else.\n\nI think the end state is correct for all uses here, since we only\nrun this after checking to see if the config value exists in the\n'from_file', so 'value' is correct (and this is a static method,\nnot a generally-useful method for config.h).\n\nPerhaps a \"write_in_new_and_remove_from_old()\" would be a better,\nif verbose, name. I struggle to find a less cumbersome name, and\n\"move\" seems to match the intent pretty well in the context of its\nuse.\n\n>> +\tcommon_config_file = xstrfmt(\"%s/config\", r->commondir);\n>> +\tmain_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n>> +\n>> +\tgit_configset_init(&cs);\n>> +\tgit_configset_add_file(&cs, common_config_file);\n>> +\n>> +\t/*\n>> +\t * If core.bare is true in the common config file, then we need to\n>> +\t * move it to the main worktree's config file or it will break all\n>> +\t * worktrees. If it is false, then leave it in place because it\n>> +\t * _could_ be negating a global core.bare=true.\n>> +\t */\n> \n> Is the assumption that the secondary worktrees are never bare, but\n> the primary one could be (iow, adding worktrees to a bare repository\n> would leave the original bare repository as the primary \"worktree\"\n> that does not have \"working tree\")?  I am trying to see what downsides\n> it tries to avoid by not moving the core.bare==false setting.  Shouldn't\n> core.bare be set to false when \"worktree add\" creates a new one anyway,\n> if the secondaries are never bare?\n\nSecondary worktrees cannot be bare. If Git interprets the worktree config\nto have core.bare=true in a secondary worktree, it errors out.\n\nYou seem to be suggesting that we should explicitly write core.bare=false\ninto each of the worktree-specific config files. Is that right? This move\nis effectively the same, since 'false' is the default.\n\n>> +\tif (!git_configset_get_bool(&cs, \"core.bare\", &bare) && bare) {\n>> +\t\tif ((res = move_config_setting(\"core.bare\", \"true\",\n>> +\t\t\t\t\t       common_config_file,\n>> +\t\t\t\t\t       main_worktree_file)))\n>> +\t\t\tgoto cleanup;\n>> +\t}\n> \n>> +\t/*\n>> +\t * If core.worktree is set, then the main worktree is located\n>> +\t * somewhere different than the parent of the common Git dir.\n> \n> OK.  We do not want to share the working tree for the primary worktree\n> among secondary worktrees.  For the primary, common and uncommon are\n> the same, so it may not matter, but mention of \"common Git dir\" here\n> may confuse readers?  Unless overridden by the config, the parent of\n> the git dir is the root of the working tree, no?\n\nHere, the verbal gymnastics are somewhat necessary because secondary\nworktrees have a .git _file_, not a git directory, so using \"common\nGit dir\" is a way to explicitly reference the Git dir. And the\nstrangeness here is exactly that core.worktree can change this working\ntree to be something other than the parent of the (common) Git dir.\n\n>> +\t * Relocate that value to avoid breaking all worktrees with this\n>> +\t * upgrade to worktree config.\n>> +\t */\n> \n> And if it is not set, then working tree of each worktree is the\n> parent of the per-worktree Git dir, so they will automatically\n> become separate, which makes sense.\n\nThanks,\n-Stolee\n"},{"id":"448022","messageId":"ab6021a8-cbb9-3261-8e08-bb4a83287473@gmail.com","threadId":"57125","inReplyTo":"xmqqv8xpato3.fsf@gitster.g","subject":"Re: [PATCH v6 3/6] config: add repo_config_set_worktree_gently()","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-02-09T02:27:16Z","receivedAt":"2022-02-09T02:41:08Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 2/8/2022 5:18 PM, Junio C Hamano wrote:\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n...\n>> @@ -3181,14 +3196,28 @@ void git_config_set_multivar_in_file(const char *config_filename,\n>>  int git_config_set_multivar_gently(const char *key, const char *value,\n>>  \t\t\t\t   const char *value_pattern, unsigned flags)\n>>  {\n>> -\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n>> -\t\t\t\t\t\t      flags);\n>> +\treturn repo_config_set_multivar_gently(the_repository, key, value,\n>> +\t\t\t\t\t       value_pattern, flags);\n>> +}\n> \n> Is this an unrelated \"morally no-op\" change?\n\nThis one is to match the pattern of how \"git_*\" methods should\ndepend on their \"repo_*\" counterparts (with \"the_repository\" inserted\nproperly). So, it's part of the standard process for creating these\n\"repo_*\" variants.\n\n>>  void git_config_set_multivar(const char *key, const char *value,\n>>  \t\t\t     const char *value_pattern, unsigned flags)\n>>  {\n>> -\tgit_config_set_multivar_in_file(NULL, key, value, value_pattern,\n>> +\tgit_config_set_multivar_in_file(git_path(\"config\"),\n>> +\t\t\t\t\tkey, value, value_pattern,\n>>  \t\t\t\t\tflags);\n>>  }\n> \n> Is this an unrelated \"morally no-op\" change?\n> \n> It might have value to make caller more explicit by reducing the use\n> of \"I give NULL, you use 'config' for me\", but that doesn't sound\n> related to the addition of set_per_worktree_config_gently() helper.\n\nHere, you're right. This one should have followed the same pattern\nof having the \"git_*\" equivalent call the \"repo_*\" method, but\ninstead I incorrectly inlined some of the code.\n\nThe proper body should be\n\nvoid git_config_set_multivar(const char *key, const char *value,\n\t\t\t     const char *value_pattern, unsigned flags)\n{\n\trepo_config_set_multivar_gently(the_repository, key, value,\n\t\t\t\t\tvalue_pattern, flags);\n}\n\nto follow convention.\n\nThanks,\n-Stolee\n"},{"id":"448029","messageId":"11bf1541-c46a-fcf5-f8d4-0d144b942d59@gmail.com","threadId":"57125","inReplyTo":"xmqqo83hatm1.fsf@gitster.g","subject":"Re: [PATCH v6 1/6] Documentation: add extensions.worktreeConfig details","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-02-09T02:34:35Z","receivedAt":"2022-02-09T02:41:38Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 2/8/2022 5:20 PM, Junio C Hamano wrote:\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n>> diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt\n>> index 4e23d73cdca..bccaec7a963 100644\n>> --- a/Documentation/config/extensions.txt\n>> +++ b/Documentation/config/extensions.txt\n>> @@ -6,3 +6,34 @@ extensions.objectFormat::\n>>  Note that this setting should only be set by linkgit:git-init[1] or\n>>  linkgit:git-clone[1].  Trying to change it after initialization will not\n>>  work and will produce hard-to-diagnose issues.\n>> +\n>> +extensions.worktreeConfig::\n>> +\tIf enabled, then worktrees will load config settings from the\n>> +\t`$GIT_DIR/config.worktree` file in addition to the\n>> +\t`$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and\n>> +\t`$GIT_DIR` are the same for the main working tree, while other\n>> +\tworking trees have `$GIT_DIR` equal to\n>> +\t`$GIT_COMMON_DIR/worktrees/<id>/`. The settings in the\n> \n> The mixed use of \"worktree\" and \"working tree\" in this paragraph\n> might confuse readers into thinking that the paragraph is being\n> careful to make distinction between the two.  All references to\n> \"working tree\" in the above paragraph should actually be \"worktree\",\n> I would think.\n\nI generally agree. This was changed in the most-recent re-roll\nbased on a request by Eric [1]. I'm happy to take whichever\nversion the two of you settle on.\n\n[1] https://lore.kernel.org/git/CAPig+cS-3CxxyPGcy_vkeN_WYTRo1b-ZhJNdPy8ARZSNKkF1Xg@mail.gmail.com/\n\n> \tSide note: \"working tree\" is in the glossary-content.txt,\n> \tbut \"worktree\", which is one \"working tree\" + repository\n> \tmetadata (i.e. \".git/\") that may be partially shared with\n> \tother \"worktree\"s of a single repository, is not defined.\n> \n> This is a tangent, but I wonder why we chose to use a different\n> filename (i.e. not \"config\" but \"config.worktree\") for this.  If we\n> were redoing multi-worktree support from scratch, we would not reuse\n> the $GIT_DIR used by the primary worktree as $GIT_COMMON_DIR, so\n> that all worktrees would share a single $GIT_COMMON_DIR and\n> $GIT_COMMON_DIR/config that has stuff that is shared among all the\n> worktrees, while per worktree stuff is in $GIT_DIR/config even for\n> the primary worktree.  But that is all water under the bridge now.\n\nRight. I think that since the primary worktree uses $GIT_COMMON_DIR\nas its location for base information (like HEAD) it also means that\nits worktree-specific config file cannot be called \"config\".\n\nPerhaps there could have been a way to split the worktrees so the\nprimary worktree got its own directory within \".git/worktrees/\", but\nmy guess is that the design optimized for backwards compatibility:\nGit clients that don't understand worktrees could still interact with\nthe primary worktree.\n\n> Other than the terminology gotcha, looked sensible.  Migrating\n> automatically and/or noticing a suspicious setting may be needed to\n> help end users, but that would not be within the scope of this step.\n> \n> Attached is a \"how about this?\" glossary update suggestion.  Most of\n> the existing mention of \"working tree\" are fine as-is because they\n> only care about what is in the \"working tree\", but some should be\n> changed to \"worktree\" to stress the fact that they care not just the\n> \"working tree\" part but also the repository metadata part that is\n> associated with that single \"working tree\".  The first hunk says\n> worktree but it is clear that it is interested only in the \"working\n> tree\" files.\n> \n>  Documentation/glossary-content.txt | 13 +++++++++++--\n>  1 file changed, 11 insertions(+), 2 deletions(-)\n> \n> diff --git c/Documentation/glossary-content.txt w/Documentation/glossary-content.txt\n> index c077971335..d816512c6a 100644\n> --- c/Documentation/glossary-content.txt\n> +++ w/Documentation/glossary-content.txt\n> @@ -312,7 +312,7 @@ Pathspecs are used on the command line of \"git ls-files\", \"git\n>  ls-tree\", \"git add\", \"git grep\", \"git diff\", \"git checkout\",\n>  and many other commands to\n>  limit the scope of operations to some subset of the tree or\n> -worktree.  See the documentation of each command for whether\n> +working tree.  See the documentation of each command for whether\n>  paths are relative to the current directory or toplevel.  The\n>  pathspec syntax is as follows:\n>  +\n> @@ -446,7 +446,7 @@ exclude;;\n>  \tinterface than the <<def_plumbing,plumbing>>.\n>  \n>  [[def_per_worktree_ref]]per-worktree ref::\n> -\tRefs that are per-<<def_working_tree,worktree>>, rather than\n> +\tRefs that are per-<<def_worktree,worktree>>, rather than\n>  \tglobal.  This is presently only <<def_HEAD,HEAD>> and any refs\n>  \tthat start with `refs/bisect/`, but might later include other\n>  \tunusual refs.\n> @@ -669,3 +669,12 @@ The most notable example is `HEAD`.\n>  \tThe tree of actual checked out files.  The working tree normally\n>  \tcontains the contents of the <<def_HEAD,HEAD>> commit's tree,\n>  \tplus any local changes that you have made but not yet committed.\n> +\n> +[[def_work_tree]]worktree::\n> +\tA repository can have zero (i.e. bare repository) or one or\n> +\tmore worktrees attached to it. One \"worktree\" consists of a\n> +\t\"working tree\" and repository metadata, most of which are\n> +\tshared among other worktrees of a single repository, and\n> +\tsome of which are maintained separately per worktree\n> +\t(e.g. the index, HEAD, per-worktree refs and per-worktree\n> +\tconfiguration file)\n\nI like this addition, except that I don't understand the \"per-worktree\nrefs\" (other than HEAD). Are there other thins used by features such\nas merge and rebase that would appear as worktree-specific? Of course,\nsome state for these operations is stored per-worktree, I just didn't\nknow if any were actually \"refs\".\n\nOther than that technicality, which could be completely correct, this\nis a good idea to include.\n\nThanks,\n-Stolee\n"},{"id":"448035","messageId":"9656f568-f5a4-0810-90a2-2ae1ced0e74f@free.fr","threadId":"57125","inReplyTo":"7d1d3cb5-2bfc-4f82-5230-d4c8d5896649@gmail.com","subject":"Re: [PATCH v5 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Jean-Noël Avila","fromEmail":"jn.avila@free.fr","sentAt":"2022-02-09T07:53:13Z","receivedAt":"2022-02-09T07:53:29Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"Le 07/02/2022 à 15:10, Derrick Stolee a écrit :\n> On 2/6/2022 5:36 AM, Jean-Noël AVILA wrote:\n>> On Monday, 31 January 2022 16:00:59 CET Derrick Stolee via GitGitGadget wrote:\n> \n> Hi Jean-Noël. Thanks for your attention to the translatable messages\n> here:\n> \n>>> error(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n>>>        from_file, to_file);\n> \n>>> die(_(\"failed to copy worktree config from '%s' to '%s'\"),\n>>>      from_file, to_file);\n> \n>>> error(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n> \n>>> error(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n> \n>> In the first patch of this series, you use _(\"unable to set '%s' in'%s'). Does it make sense to reuse this string here?\n> \n> I would argue that \"unable to set\" is not appropriate for any of these\n> messages. Perhaps the \"failed to copy\" messages might be able to use\n> \"unable to set\", but the information that the config setting is coming\n> from settings the user controlled is valuable.\n> \n> The \"failed to unset\" means \"we are trying to _remove_ this setting\n> from the config file\", so \"unable to set\" does not seem to work here.\n> \n> I'm open to revisiting this if you disagree.\n> \n> Thanks,\n> -Stolee\n> \n\nHi Derrick,\n\nSorry for not being more precise. The first two errors were not the \nsubject of this remark.\n\nFor the last two, this is quite surprising that the same function \nfailing (git_config_set_in_file_gently) can lead to different error \nmessages.\n\nIn any case, I would argue at least for  shifting to :\n\n     error(_(\"failed to unset '%s' in '%s'\"), 'core.bare\", to_file);\nand\n     error(_(\"failed to unset '%s' in '%s'\"), \"core.worktree\", to_file);\n\nin order to factorize the message and get the option name out of the way.\n\nThanks,\n\nJean-Noël\n"},{"id":"448047","messageId":"837ca526-9ecd-6dd8-92b2-b8d05dde2032@gmail.com","threadId":"57125","inReplyTo":"9656f568-f5a4-0810-90a2-2ae1ced0e74f@free.fr","subject":"Re: [PATCH v5 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-02-09T14:45:41Z","receivedAt":"2022-02-09T14:45:50Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 2/9/2022 2:53 AM, Jean-Noël Avila wrote:\n> Le 07/02/2022 à 15:10, Derrick Stolee a écrit :\n>> On 2/6/2022 5:36 AM, Jean-Noël AVILA wrote:\n>>> On Monday, 31 January 2022 16:00:59 CET Derrick Stolee via GitGitGadget wrote:\n>>\n>> Hi Jean-Noël. Thanks for your attention to the translatable messages\n>> here:\n>>\n>>>> error(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n>>>>        from_file, to_file);\n>>\n>>>> die(_(\"failed to copy worktree config from '%s' to '%s'\"),\n>>>>      from_file, to_file);\n>>\n>>>> error(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n>>\n>>>> error(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n>>\n>>> In the first patch of this series, you use _(\"unable to set '%s' in'%s'). Does it make sense to reuse this string here?\n>>\n>> I would argue that \"unable to set\" is not appropriate for any of these\n>> messages. Perhaps the \"failed to copy\" messages might be able to use\n>> \"unable to set\", but the information that the config setting is coming\n>> from settings the user controlled is valuable.\n>>\n>> The \"failed to unset\" means \"we are trying to _remove_ this setting\n>> from the config file\", so \"unable to set\" does not seem to work here.\n>>\n>> I'm open to revisiting this if you disagree.\n>>\n>> Thanks,\n>> -Stolee\n>>\n> \n> Hi Derrick,\n> \n> Sorry for not being more precise. The first two errors were not the subject of this remark.\n> \n> For the last two, this is quite surprising that the same function failing (git_config_set_in_file_gently) can lead to different error messages.\n> \n> In any case, I would argue at least for  shifting to :\n> \n>     error(_(\"failed to unset '%s' in '%s'\"), 'core.bare\", to_file);\n> and\n>     error(_(\"failed to unset '%s' in '%s'\"), \"core.worktree\", to_file);\n> \n> in order to factorize the message and get the option name out of the way.\n\nThank you for the clarification! This makes sense as a way to\nreduce load on translators. Sorry for misunderstanding.\n\nThanks,\n-Stolee\n"},{"id":"448053","messageId":"CABPp-BG0pKWZ=vowE7eGWUzXJ6wpY1udEAzb322fO38v3ZRZ=w@mail.gmail.com","threadId":"57125","inReplyTo":"xmqq1r0dc8om.fsf@gitster.g","subject":"Re: [PATCH v6 2/6] worktree: create init_worktree_config()","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-02-09T16:43:44Z","receivedAt":"2022-02-09T16:44:00Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Feb 8, 2022 at 2:09 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> > +static int move_config_setting(const char *key, const char *value,\n> > +                            const char *from_file, const char *to_file)\n> > +{\n> > +     if (git_config_set_in_file_gently(to_file, key, value))\n> > +             return error(_(\"unable to set %s in '%s'\"), key, to_file);\n> > +     if (git_config_set_in_file_gently(from_file, key, NULL))\n> > +             return error(_(\"unable to unset %s in '%s'\"), key, from_file);\n> > +     return 0;\n> > +}\n>\n> Interesting.\n>\n> The verb \"move\" in its name made me expect a \"get (and remove)\n> whatever value(s) defined out of the old file, and set them\n> identically in the new file\" sequence, but that is not what is done\n> here.  \"set to this new single value in the new file and unset from\n> the old one\".\n>\n> I can see the need to say \"move it only when its value is X\",\n> so having the caller to extract the value before deciding to call\n> the function (hence not \"moving from old\") does make sense, but then\n> the function is misnamed---it is not \"moving\", it is doing something\n> else.\n>\n> > +int init_worktree_config(struct repository *r)\n> > +{\n> > +     int res = 0;\n> > +     int bare = 0;\n> > +     struct config_set cs = { { 0 } };\n> > +     const char *core_worktree;\n> > +     char *common_config_file;\n> > +     char *main_worktree_file;\n> > +\n> > +     /*\n> > +      * If the extension is already enabled, then we can skip the\n> > +      * upgrade process.\n> > +      */\n> > +     if (repository_format_worktree_config)\n> > +             return 0;\n>\n> OK.\n>\n> > +     if ((res = git_config_set_gently(\"extensions.worktreeConfig\", \"true\")))\n> > +             return error(_(\"failed to set extensions.worktreeConfig setting\"));\n>\n> OK.\n>\n> > +     common_config_file = xstrfmt(\"%s/config\", r->commondir);\n> > +     main_worktree_file = xstrfmt(\"%s/config.worktree\", r->commondir);\n> > +\n> > +     git_configset_init(&cs);\n> > +     git_configset_add_file(&cs, common_config_file);\n> > +\n> > +     /*\n> > +      * If core.bare is true in the common config file, then we need to\n> > +      * move it to the main worktree's config file or it will break all\n> > +      * worktrees. If it is false, then leave it in place because it\n> > +      * _could_ be negating a global core.bare=true.\n> > +      */\n>\n> Is the assumption that the secondary worktrees are never bare, but\n> the primary one could be (iow, adding worktrees to a bare repository\n> would leave the original bare repository as the primary \"worktree\"\n> that does not have \"working tree\")?\n\nYes, and in fact that was the case which generated the original bug\nreport -- a bare clone where the affected individual started using\n`git worktree add` to create some non-primary worktrees (and then also\nused sparse-checkout in some of them).\n\n>  I am trying to see what downsides\n> it tries to avoid by not moving the core.bare==false setting.  Shouldn't\n> core.bare be set to false when \"worktree add\" creates a new one anyway,\n> if the secondaries are never bare?\n\nMoving the core.bare==false setting might make sense.  In the previous\ndiscussions, we tried to hypothesize about usage of old git clients\nand non-git clients (jgit, etc.) on the same repos, and didn't know if\nsome of those would break if they couldn't find a `core.bare` setting\nanywhere (since they wouldn't know to look in config.worktree).  We\nneeded to migrate core.bare=true to avoid an incorrect value affecting\nall worktrees (and thus we figured it was worth the risk of breaking\nolder git/non-git clients because having older clients be broken is\nbetter than having all clients including current git be broken), but\nthe same wasn't true for core.bare=false.  That said, we don't\nactively know of any such clients that would be hurt by such a\nmigration.\n"},{"id":"448054","messageId":"xmqqmtj09cuz.fsf@gitster.g","threadId":"57125","inReplyTo":"11bf1541-c46a-fcf5-f8d4-0d144b942d59@gmail.com","subject":"Re: [PATCH v6 1/6] Documentation: add extensions.worktreeConfig details","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-09T17:19:32Z","receivedAt":"2022-02-09T17:19:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n>> +[[def_work_tree]]worktree::\n>> +\tA repository can have zero (i.e. bare repository) or one or\n>> +\tmore worktrees attached to it. One \"worktree\" consists of a\n>> +\t\"working tree\" and repository metadata, most of which are\n>> +\tshared among other worktrees of a single repository, and\n>> +\tsome of which are maintained separately per worktree\n>> +\t(e.g. the index, HEAD, per-worktree refs and per-worktree\n>> +\tconfiguration file)\n>\n> I like this addition, except that I don't understand the \"per-worktree\n> refs\" (other than HEAD). Are there other thins used by features such\n> as merge and rebase that would appear as worktree-specific? Of course,\n> some state for these operations is stored per-worktree, I just didn't\n> know if any were actually \"refs\".\n\n\"per-worktree ref\" is an entry in the glossary.\n\n    [[def_per_worktree_ref]]per-worktree ref::\n            Refs that are per-<<def_working_tree,worktree>>, rather than\n            global.  This is presently only <<def_HEAD,HEAD>> and any refs\n            that start with `refs/bisect/`, but might later include other\n            unusual refs.\n\nAnd those other things are also listed as \"pseudoref\".\n\n    [[def_pseudoref]]pseudoref::\n            Pseudorefs are a class of files under `$GIT_DIR` which behave\n            like refs for the purposes of rev-parse, but which are treated\n            specially by git...\n\nI think the motivation of special casing refs/bisect/ is to allow\nuse of a separate worktree for bisecting without affecting other\ndevelopment or another bisection.  The HEAD is singled out in the\ndescription, but MERGE_HEAD and others (pseudoref) that are declared\nhere to be files under '$GIT_DIR', when we migrate fully to other\nbackend that may not want to have files under '$GIT_DIR' to\nrepresent them, ought to become per-worktree, for the same reason as\nHEAD should be per-worktree, i.e. it allows worktrees to be\nindependent from each other and have their checkout at different\ncommits, growing history of different branches in parallel.\n\n\n"},{"id":"448055","messageId":"a51e39f4-a1a1-de41-d643-6f7c277bfc1a@gmail.com","threadId":"57125","inReplyTo":"xmqqmtj09cuz.fsf@gitster.g","subject":"Re: [PATCH v6 1/6] Documentation: add extensions.worktreeConfig details","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-02-09T17:26:11Z","receivedAt":"2022-02-09T17:26:22Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 2/9/2022 12:19 PM, Junio C Hamano wrote:\n> Derrick Stolee <stolee@gmail.com> writes:\n> \n>>> +[[def_work_tree]]worktree::\n>>> +\tA repository can have zero (i.e. bare repository) or one or\n>>> +\tmore worktrees attached to it. One \"worktree\" consists of a\n>>> +\t\"working tree\" and repository metadata, most of which are\n>>> +\tshared among other worktrees of a single repository, and\n>>> +\tsome of which are maintained separately per worktree\n>>> +\t(e.g. the index, HEAD, per-worktree refs and per-worktree\n>>> +\tconfiguration file)\n>>\n>> I like this addition, except that I don't understand the \"per-worktree\n>> refs\" (other than HEAD). Are there other thins used by features such\n>> as merge and rebase that would appear as worktree-specific? Of course,\n>> some state for these operations is stored per-worktree, I just didn't\n>> know if any were actually \"refs\".\n> \n> \"per-worktree ref\" is an entry in the glossary.\n> \n>     [[def_per_worktree_ref]]per-worktree ref::\n>             Refs that are per-<<def_working_tree,worktree>>, rather than\n>             global.  This is presently only <<def_HEAD,HEAD>> and any refs\n>             that start with `refs/bisect/`, but might later include other\n>             unusual refs.\n> \n> And those other things are also listed as \"pseudoref\".\n> \n>     [[def_pseudoref]]pseudoref::\n>             Pseudorefs are a class of files under `$GIT_DIR` which behave\n>             like refs for the purposes of rev-parse, but which are treated\n>             specially by git...\n> \n> I think the motivation of special casing refs/bisect/ is to allow\n> use of a separate worktree for bisecting without affecting other\n> development or another bisection.  The HEAD is singled out in the\n> description, but MERGE_HEAD and others (pseudoref) that are declared\n> here to be files under '$GIT_DIR', when we migrate fully to other\n> backend that may not want to have files under '$GIT_DIR' to\n> represent them, ought to become per-worktree, for the same reason as\n> HEAD should be per-worktree, i.e. it allows worktrees to be\n> independent from each other and have their checkout at different\n> commits, growing history of different branches in parallel.\n\nThanks for this additional context! It means that I need to look\naround more carefully, not that your patch needs any changes.\n\n-Stolee\n"},{"id":"448056","messageId":"xmqq5ypo9c6g.fsf@gitster.g","threadId":"57125","inReplyTo":"5f7f10df-f048-a663-a98c-536025be0040@gmail.com","subject":"Re: [PATCH v6 2/6] worktree: create init_worktree_config()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-09T17:34:15Z","receivedAt":"2022-02-09T17:34:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> On 2/8/2022 5:09 PM, Junio C Hamano wrote:\n>> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>> \n>>> +static int move_config_setting(const char *key, const char *value,\n>>> +\t\t\t       const char *from_file, const char *to_file)\n>>> +{\n>>> +\tif (git_config_set_in_file_gently(to_file, key, value))\n>>> +\t\treturn error(_(\"unable to set %s in '%s'\"), key, to_file);\n>>> +\tif (git_config_set_in_file_gently(from_file, key, NULL))\n>>> +\t\treturn error(_(\"unable to unset %s in '%s'\"), key, from_file);\n>>> +\treturn 0;\n>>> +}\n>> \n>> Interesting.\n>> \n>> The verb \"move\" in its name made me expect a \"get (and remove)\n>> whatever value(s) defined out of the old file, and set them\n>> identically in the new file\" sequence, but that is not what is done\n>> here.  \"set to this new single value in the new file and unset from\n>> the old one\".\n>\n> I think this \"copy into the worktree-specific config, then remove\n> from the common file\" is an important sequence of events in case a\n> concurrent process comes in and reads the two config files in the\n> intermediate state and does not see the config value anywhere.\n>\n> But perhaps that's not actually what you are concerned about,\n> because you're saying that the 'value' being provided does not\n> actually guarantee that we are moving the setting.\n\nYes.  \"Why are we _ignoring_ what is in the old file when we claim\nto be _moving_?\" was the question I had upon seeing this function.\n\n>> I can see the need to say \"move it only when its value is X\",\n>> so having the caller to extract the value before deciding to call\n>> the function (hence not \"moving from old\") does make sense, but then\n>> the function is misnamed---it is not \"moving\", it is doing something\n>> else.\n\n> I think the end state is correct for all uses here, since we only\n> run this after checking to see if the config value exists in the\n> 'from_file', so 'value' is correct (and this is a static method,\n> not a generally-useful method for config.h).\n\nAs long as this is used on a single-valued \"last one wins\" variable,\nthe callers and this helper taken together will do the right thing.\n\n> Perhaps a \"write_in_new_and_remove_from_old()\" would be a better,\n> if verbose, name. I struggle to find a less cumbersome name, and\n> \"move\" seems to match the intent pretty well in the context of its\n> use.\n\nThe name is fine as long as the requirement for the caller is made\nclear.  A short comment to help the next reader from having to ask\nthe same question before the helper may be sufficient.\n\n>> Is the assumption that the secondary worktrees are never bare, but\n>> the primary one could be (iow, adding worktrees to a bare repository\n>> would leave the original bare repository as the primary \"worktree\"\n>> that does not have \"working tree\")?  I am trying to see what downsides\n>> it tries to avoid by not moving the core.bare==false setting.  Shouldn't\n>> core.bare be set to false when \"worktree add\" creates a new one anyway,\n>> if the secondaries are never bare?\n>\n> Secondary worktrees cannot be bare. If Git interprets the worktree config\n> to have core.bare=true in a secondary worktree, it errors out.\n>\n> You seem to be suggesting that we should explicitly write core.bare=false\n> into each of the worktree-specific config files. Is that right? This move\n> is effectively the same, since 'false' is the default.\n\nUnless there is a lower-precedence configuration file that we have\nto override, yes, not writing core.bare=false upon \"worktree add\" is\nfine.  I simply do not know if we need to do something special in\norder to defeat /etc/gitconfig or $HOME/.gitconfig with the repository\nor the worktree specific configuration file.\n\n> Here, the verbal gymnastics are somewhat necessary because secondary\n> worktrees have a .git _file_, not a git directory, so using \"common\n> Git dir\" is a way to explicitly reference the Git dir. And the\n> strangeness here is exactly that core.worktree can change this working\n> tree to be something other than the parent of the (common) Git dir.\n\nOK.  The .git _file_ is our moral equivalent to a symbolic link, and\nI forgot about that.\n\nI also wonder if we should do something like what we do for refs\n(i.e. the API knows which refs are per-worktree and which are\nglobal, so the callers do not have to care and just can say things\nlike \"update HEAD to this value\", and \"give me the value of\nrefs/bisect/good\") when repo_set_config*() is called, but that is\noutside the scope of this step, which is about one-time migration.\n\nAs the code for migration go, I think I am happy with what it wants\nto do and how it does it.\n\nThanks.\n"},{"id":"448057","messageId":"xmqqzgn07wwf.fsf@gitster.g","threadId":"57125","inReplyTo":"ab6021a8-cbb9-3261-8e08-bb4a83287473@gmail.com","subject":"Re: [PATCH v6 3/6] config: add repo_config_set_worktree_gently()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-09T17:49:36Z","receivedAt":"2022-02-09T17:49:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> On 2/8/2022 5:18 PM, Junio C Hamano wrote:\n>> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> ...\n>>> @@ -3181,14 +3196,28 @@ void git_config_set_multivar_in_file(const char *config_filename,\n>>>  int git_config_set_multivar_gently(const char *key, const char *value,\n>>>  \t\t\t\t   const char *value_pattern, unsigned flags)\n>>>  {\n>>> -\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n>>> -\t\t\t\t\t\t      flags);\n>>> +\treturn repo_config_set_multivar_gently(the_repository, key, value,\n>>> +\t\t\t\t\t       value_pattern, flags);\n>>> +}\n>> \n>> Is this an unrelated \"morally no-op\" change?\n>\n> This one is to match the pattern of how \"git_*\" methods should\n> depend on their \"repo_*\" counterparts (with \"the_repository\" inserted\n> properly). So, it's part of the standard process for creating these\n> \"repo_*\" variants.\n\nIf only one of repo_config_set_multivar_gently() and\ngit_config_set_multivar_gently() existed and we were completing the\npair, then I would understand the explanation, but the title says\nthat it is adding repo_config_set_worktree_gently(), which is not,\nand that is where the \"unrelated\" comes from.\n\nIt needs to be a separate preparatory step to add\nrepo_config_set_multivar_gently() before we add\nrepo_config_set_worktree_gently(), perhaps?\n\nA bit higher level question is if the public part of \"config-set\"\nAPI functions should gain an \"easy\" (in the sense of curl_easy_* set\nof API functions) API to allow the callers to say \"I do not care to\nfind out if per-worktree configuration is in use, or this particular\nvariable is meant to be per-worktree, just set it to this value\".\n\nOn this question, I am of two minds.  As certain variables (like\ncore.sparseCheckout) should always be per-worktree just like certain\nrefs (like HEAD) should always be per-worktree, I can understand the\nviewpoint that the callers _ought_ to know and explicitly say that\nthey want to get/set in the per-worktree configuration file, but at\nthe same time, I would think the callers should not have to care.\nSo, I dunno.\n\nThanks.\n"},{"id":"448058","messageId":"CABPp-BHKr3xPStXE6HXUBB0KR=6Y1-CSjDc1DTDdd63uYvK7uQ@mail.gmail.com","threadId":"57125","inReplyTo":"xmqqmtj09cuz.fsf@gitster.g","subject":"Re: [PATCH v6 1/6] Documentation: add extensions.worktreeConfig details","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-02-09T17:51:46Z","receivedAt":"2022-02-09T17:52:04Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Feb 9, 2022 at 9:19 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Derrick Stolee <stolee@gmail.com> writes:\n>\n> >> +[[def_work_tree]]worktree::\n> >> +    A repository can have zero (i.e. bare repository) or one or\n> >> +    more worktrees attached to it. One \"worktree\" consists of a\n> >> +    \"working tree\" and repository metadata, most of which are\n> >> +    shared among other worktrees of a single repository, and\n> >> +    some of which are maintained separately per worktree\n> >> +    (e.g. the index, HEAD, per-worktree refs and per-worktree\n> >> +    configuration file)\n> >\n> > I like this addition, except that I don't understand the \"per-worktree\n> > refs\" (other than HEAD). Are there other thins used by features such\n> > as merge and rebase that would appear as worktree-specific? Of course,\n> > some state for these operations is stored per-worktree, I just didn't\n> > know if any were actually \"refs\".\n>\n> \"per-worktree ref\" is an entry in the glossary.\n>\n>     [[def_per_worktree_ref]]per-worktree ref::\n>             Refs that are per-<<def_working_tree,worktree>>, rather than\n>             global.  This is presently only <<def_HEAD,HEAD>> and any refs\n>             that start with `refs/bisect/`, but might later include other\n>             unusual refs.\n>\n> And those other things are also listed as \"pseudoref\".\n>\n>     [[def_pseudoref]]pseudoref::\n>             Pseudorefs are a class of files under `$GIT_DIR` which behave\n>             like refs for the purposes of rev-parse, but which are treated\n>             specially by git...\n>\n> I think the motivation of special casing refs/bisect/ is to allow\n> use of a separate worktree for bisecting without affecting other\n> development or another bisection.  The HEAD is singled out in the\n> description, but MERGE_HEAD and others (pseudoref) that are declared\n> here to be files under '$GIT_DIR', when we migrate fully to other\n> backend that may not want to have files under '$GIT_DIR' to\n> represent them, ought to become per-worktree, for the same reason as\n> HEAD should be per-worktree, i.e. it allows worktrees to be\n> independent from each other and have their checkout at different\n> commits, growing history of different branches in parallel.\n\nYou had me worried for a second; things would be really broken if\nthese pseudorefs weren't per-worktree.\n\nBut testing just now, I think the pseudorefs are already per-worktree.\nI just did a merge in a secondary worktree, and then observed from the\nprimary worktree that a .git/worktrees/<id>/MERGE_HEAD was created,\nnot a .git/MERGE_HEAD.  (Maybe the glossary could just spell out that\nthese are under $GIT_DIR and _not_ $GIT_COMMON_DIR to avoid potential\nconfusion?)\n"},{"id":"448060","messageId":"CABPp-BGj7DZY8D-Ps3fhcRik_gSGoDyoFokuUMRN_Q7Z2cbEfw@mail.gmail.com","threadId":"57125","inReplyTo":"xmqqo83hatm1.fsf@gitster.g","subject":"Re: [PATCH v6 1/6] Documentation: add extensions.worktreeConfig details","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-02-09T18:04:07Z","receivedAt":"2022-02-09T18:04:29Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Feb 8, 2022 at 2:20 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> > diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt\n> > index 4e23d73cdca..bccaec7a963 100644\n> > --- a/Documentation/config/extensions.txt\n> > +++ b/Documentation/config/extensions.txt\n> > @@ -6,3 +6,34 @@ extensions.objectFormat::\n> >  Note that this setting should only be set by linkgit:git-init[1] or\n> >  linkgit:git-clone[1].  Trying to change it after initialization will not\n> >  work and will produce hard-to-diagnose issues.\n> > +\n> > +extensions.worktreeConfig::\n> > +     If enabled, then worktrees will load config settings from the\n> > +     `$GIT_DIR/config.worktree` file in addition to the\n> > +     `$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and\n> > +     `$GIT_DIR` are the same for the main working tree, while other\n> > +     working trees have `$GIT_DIR` equal to\n> > +     `$GIT_COMMON_DIR/worktrees/<id>/`. The settings in the\n>\n> The mixed use of \"worktree\" and \"working tree\" in this paragraph\n> might confuse readers into thinking that the paragraph is being\n> careful to make distinction between the two.  All references to\n> \"working tree\" in the above paragraph should actually be \"worktree\",\n> I would think.\n>\n>         Side note: \"working tree\" is in the glossary-content.txt,\n>         but \"worktree\", which is one \"working tree\" + repository\n>         metadata (i.e. \".git/\") that may be partially shared with\n>         other \"worktree\"s of a single repository, is not defined.\n>\n> This is a tangent, but I wonder why we chose to use a different\n> filename (i.e. not \"config\" but \"config.worktree\") for this.  If we\n> were redoing multi-worktree support from scratch, we would not reuse\n> the $GIT_DIR used by the primary worktree as $GIT_COMMON_DIR, so\n> that all worktrees would share a single $GIT_COMMON_DIR and\n> $GIT_COMMON_DIR/config that has stuff that is shared among all the\n> worktrees, while per worktree stuff is in $GIT_DIR/config even for\n> the primary worktree.  But that is all water under the bridge now.\n>\n> Other than the terminology gotcha, looked sensible.  Migrating\n> automatically and/or noticing a suspicious setting may be needed to\n> help end users, but that would not be within the scope of this step.\n>\n> Attached is a \"how about this?\" glossary update suggestion.  Most of\n> the existing mention of \"working tree\" are fine as-is because they\n> only care about what is in the \"working tree\", but some should be\n> changed to \"worktree\" to stress the fact that they care not just the\n> \"working tree\" part but also the repository metadata part that is\n> associated with that single \"working tree\".  The first hunk says\n> worktree but it is clear that it is interested only in the \"working\n> tree\" files.\n>\n>  Documentation/glossary-content.txt | 13 +++++++++++--\n>  1 file changed, 11 insertions(+), 2 deletions(-)\n>\n> diff --git c/Documentation/glossary-content.txt w/Documentation/glossary-content.txt\n> index c077971335..d816512c6a 100644\n> --- c/Documentation/glossary-content.txt\n> +++ w/Documentation/glossary-content.txt\n> @@ -312,7 +312,7 @@ Pathspecs are used on the command line of \"git ls-files\", \"git\n>  ls-tree\", \"git add\", \"git grep\", \"git diff\", \"git checkout\",\n>  and many other commands to\n>  limit the scope of operations to some subset of the tree or\n> -worktree.  See the documentation of each command for whether\n> +working tree.  See the documentation of each command for whether\n>  paths are relative to the current directory or toplevel.  The\n>  pathspec syntax is as follows:\n>  +\n> @@ -446,7 +446,7 @@ exclude;;\n>         interface than the <<def_plumbing,plumbing>>.\n>\n>  [[def_per_worktree_ref]]per-worktree ref::\n> -       Refs that are per-<<def_working_tree,worktree>>, rather than\n> +       Refs that are per-<<def_worktree,worktree>>, rather than\n>         global.  This is presently only <<def_HEAD,HEAD>> and any refs\n>         that start with `refs/bisect/`, but might later include other\n>         unusual refs.\n> @@ -669,3 +669,12 @@ The most notable example is `HEAD`.\n>         The tree of actual checked out files.  The working tree normally\n>         contains the contents of the <<def_HEAD,HEAD>> commit's tree,\n>         plus any local changes that you have made but not yet committed.\n> +\n> +[[def_work_tree]]worktree::\n> +       A repository can have zero (i.e. bare repository) or one or\n> +       more worktrees attached to it. One \"worktree\" consists of a\n> +       \"working tree\" and repository metadata, most of which are\n> +       shared among other worktrees of a single repository, and\n> +       some of which are maintained separately per worktree\n> +       (e.g. the index, HEAD, per-worktree refs and per-worktree\n> +       configuration file)\n\nWe could also add pseudorefs to the list of things maintained\nseparately in the final parenthetical comment, but otherwise looks\ngood.\n"},{"id":"448066","messageId":"xmqqv8xn7ukc.fsf@gitster.g","threadId":"57125","inReplyTo":"CABPp-BHKr3xPStXE6HXUBB0KR=6Y1-CSjDc1DTDdd63uYvK7uQ@mail.gmail.com","subject":"Re: [PATCH v6 1/6] Documentation: add extensions.worktreeConfig details","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-09T18:40:03Z","receivedAt":"2022-02-09T18:41:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> But testing just now, I think the pseudorefs are already per-worktree.\n> I just did a merge in a secondary worktree, and then observed from the\n> primary worktree that a .git/worktrees/<id>/MERGE_HEAD was created,\n> not a .git/MERGE_HEAD.  (Maybe the glossary could just spell out that\n> these are under $GIT_DIR and _not_ $GIT_COMMON_DIR to avoid potential\n> confusion?)\n\nI actually think the longer-term direction is to describe that these\nare always per-worktree, without referring to $GIT_DIR or giving any\nhints that these may be represented as a file in the filesystem.\nThat would leave the door open for the reftable backend to take them\nover as well as the normal refs.\n"},{"id":"448067","messageId":"xmqqr18b7uhd.fsf@gitster.g","threadId":"57125","inReplyTo":"CABPp-BGj7DZY8D-Ps3fhcRik_gSGoDyoFokuUMRN_Q7Z2cbEfw@mail.gmail.com","subject":"Re: [PATCH v6 1/6] Documentation: add extensions.worktreeConfig details","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-09T18:41:50Z","receivedAt":"2022-02-09T18:43:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n>>  [[def_per_worktree_ref]]per-worktree ref::\n>> -       Refs that are per-<<def_working_tree,worktree>>, rather than\n>> +       Refs that are per-<<def_worktree,worktree>>, rather than\n>>         global.  This is presently only <<def_HEAD,HEAD>> and any refs\n>>         that start with `refs/bisect/`, but might later include other\n>>         unusual refs.\n>> @@ -669,3 +669,12 @@ The most notable example is `HEAD`.\n>>         The tree of actual checked out files.  The working tree normally\n>>         contains the contents of the <<def_HEAD,HEAD>> commit's tree,\n>>         plus any local changes that you have made but not yet committed.\n>> +\n>> +[[def_work_tree]]worktree::\n>> +       A repository can have zero (i.e. bare repository) or one or\n>> +       more worktrees attached to it. One \"worktree\" consists of a\n>> +       \"working tree\" and repository metadata, most of which are\n>> +       shared among other worktrees of a single repository, and\n>> +       some of which are maintained separately per worktree\n>> +       (e.g. the index, HEAD, per-worktree refs and per-worktree\n>> +       configuration file)\n>\n> We could also add pseudorefs to the list of things maintained\n> separately in the final parenthetical comment, but otherwise looks\n> good.\n\nI think what needs updating is the per_worktree_ref section.  Before\nwe say \"later include other unusual refs\", not so unusual pseudorefs\ncan be mentioned there.\n"},{"id":"448138","messageId":"2b88fa25-ec40-6416-05c8-81789a415844@gmail.com","threadId":"57125","inReplyTo":"xmqqzgn07wwf.fsf@gitster.g","subject":"Re: [PATCH v6 3/6] config: add repo_config_set_worktree_gently()","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2022-02-10T14:48:22Z","receivedAt":"2022-02-10T14:48:27Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 2/9/2022 12:49 PM, Junio C Hamano wrote:\n> Derrick Stolee <stolee@gmail.com> writes:\n> \n>> On 2/8/2022 5:18 PM, Junio C Hamano wrote:\n>>> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>> ...\n>>>> @@ -3181,14 +3196,28 @@ void git_config_set_multivar_in_file(const char *config_filename,\n>>>>  int git_config_set_multivar_gently(const char *key, const char *value,\n>>>>  \t\t\t\t   const char *value_pattern, unsigned flags)\n>>>>  {\n>>>> -\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n>>>> -\t\t\t\t\t\t      flags);\n>>>> +\treturn repo_config_set_multivar_gently(the_repository, key, value,\n>>>> +\t\t\t\t\t       value_pattern, flags);\n>>>> +}\n>>>\n>>> Is this an unrelated \"morally no-op\" change?\n>>\n>> This one is to match the pattern of how \"git_*\" methods should\n>> depend on their \"repo_*\" counterparts (with \"the_repository\" inserted\n>> properly). So, it's part of the standard process for creating these\n>> \"repo_*\" variants.\n> \n> If only one of repo_config_set_multivar_gently() and\n> git_config_set_multivar_gently() existed and we were completing the\n> pair, then I would understand the explanation, but the title says\n> that it is adding repo_config_set_worktree_gently(), which is not,\n> and that is where the \"unrelated\" comes from.\n> \n> It needs to be a separate preparatory step to add\n> repo_config_set_multivar_gently() before we add\n> repo_config_set_worktree_gently(), perhaps?\n\nTrue, they could be split. The reason to create the _multivar_\nversion is for the case that worktree config is not specified,\nso that is the only caller at the moment.\n\n> A bit higher level question is if the public part of \"config-set\"\n> API functions should gain an \"easy\" (in the sense of curl_easy_* set\n> of API functions) API to allow the callers to say \"I do not care to\n> find out if per-worktree configuration is in use, or this particular\n> variable is meant to be per-worktree, just set it to this value\".\n> \n> On this question, I am of two minds.  As certain variables (like\n> core.sparseCheckout) should always be per-worktree just like certain\n> refs (like HEAD) should always be per-worktree, I can understand the\n> viewpoint that the callers _ought_ to know and explicitly say that\n> they want to get/set in the per-worktree configuration file, but at\n> the same time, I would think the callers should not have to care.\n> So, I dunno.\n\nThis is an interesting idea. This would require creating a list\nof \"should be per-worktree\" config keys that are checked within\nthe different *_config_set_* methods.\n\nThe biggest technical hurdle is that the multivar versions might\nneed to send a subset of the given config into the worktree\nconfig and the rest to the common config.\n\nLet's see how this progresses in the future, and whether we\nhave more config that we believe _must_ be stored on a per-\nworktree basis. At that point, we can see whether the current\n\"distributed responsibility\" model is too cumbersome.\n\nThanks,\n-Stolee\n"},{"id":"448141","messageId":"xmqqczjuwtz8.fsf@gitster.g","threadId":"57125","inReplyTo":"2b88fa25-ec40-6416-05c8-81789a415844@gmail.com","subject":"Re: [PATCH v6 3/6] config: add repo_config_set_worktree_gently()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-10T16:45:47Z","receivedAt":"2022-02-10T16:45:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> True, they could be split. The reason to create the _multivar_\n> version is for the case that worktree config is not specified,\n> so that is the only caller at the moment.\n\nSorry, but I am not following this part.\n\n> This is an interesting idea. This would require creating a list\n> of \"should be per-worktree\" config keys that are checked within\n> the different *_config_set_* methods.\n\nYes.\n\n> The biggest technical hurdle is that the multivar versions might\n> need to send a subset of the given config into the worktree\n> config and the rest to the common config.\n\nYes, instead of having the caller do it.\n\n> Let's see how this progresses in the future, and whether we\n> have more config that we believe _must_ be stored on a per-\n> worktree basis. At that point, we can see whether the current\n> \"distributed responsibility\" model is too cumbersome.\n\nIt is not too distributed, which is a saving grace.  The callers\nknow they are setting core.sparseCheckout* and they are responsible\nto call the per-worktree version.  It would be like having in ref\nAPI an update_HEAD() helper for modifying HEAD, instead of having a\nmore generic update_ref() that can modify any ref and pass \"HEAD\" as\nan argument to the latter.  The caller needs to know a bit more\ndetails about what needs to happen when dealing with a special thing,\nbut the special case knowledge is fairly concentrated.\n\n"},{"id":"448474","messageId":"CAPig+cSY7E_XQC1gHzDJxoDGGmgWDmNz9Ys=CwbhLkCt+DQf-A@mail.gmail.com","threadId":"57125","inReplyTo":"11bf1541-c46a-fcf5-f8d4-0d144b942d59@gmail.com","subject":"Re: [PATCH v6 1/6] Documentation: add extensions.worktreeConfig details","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-02-15T20:37:59Z","receivedAt":"2022-02-15T20:38:14Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Feb 8, 2022 at 9:34 PM Derrick Stolee <stolee@gmail.com> wrote:\n> On 2/8/2022 5:20 PM, Junio C Hamano wrote:\n> > \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> >> +extensions.worktreeConfig::\n> >> +    If enabled, then worktrees will load config settings from the\n> >> +    `$GIT_DIR/config.worktree` file in addition to the\n> >> +    `$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and\n> >> +    `$GIT_DIR` are the same for the main working tree, while other\n> >> +    working trees have `$GIT_DIR` equal to\n> >> +    `$GIT_COMMON_DIR/worktrees/<id>/`. The settings in the\n> >\n> > The mixed use of \"worktree\" and \"working tree\" in this paragraph\n> > might confuse readers into thinking that the paragraph is being\n> > careful to make distinction between the two.  All references to\n> > \"working tree\" in the above paragraph should actually be \"worktree\",\n> > I would think.\n>\n> I generally agree. This was changed in the most-recent re-roll\n> based on a request by Eric [1]. I'm happy to take whichever\n> version the two of you settle on.\n>\n> [1] https://lore.kernel.org/git/CAPig+cS-3CxxyPGcy_vkeN_WYTRo1b-ZhJNdPy8ARZSNKkF1Xg@mail.gmail.com/\n\n\"request\" is perhaps too strong a word considering that I led in with:\n\n    A few minor comments, which can be addressed later or not\n    at all, and likely are not worth holding up the series...\n\nI mentioned \"worktree vs. working tree\" only to point out the\nterminology inconsistency being introduced by the new patch; the same\nsort of inconsistency which had bothered Michael Haggerty enough to do\nsomething about it in bc483285b7 (Documentation/git-worktree:\nconsistently use term \"linked working tree\", 2015-07-20).\n\nI, personally, prefer the term \"worktree\" for both convenience and\nbecause it better encapsulates the overall \"thing\" which is\nmanipulated by the git-worktree command unlike the term \"working tree\"\nwhich, as Junio points out, has (perhaps) a more narrow meaning. As\nsuch, I would not be opposed to a patch series which changes \"working\ntree\" to \"worktree\" in documentation where appropriate, but that's\noutside the scope of this series.\n"},{"id":"448480","messageId":"CAPig+cS6Of=1OOw-6MOCBJVW6_FxtGw_ciUR=RTYakdy=1Z_hA@mail.gmail.com","threadId":"57125","inReplyTo":"93747eca-08d6-0be9-08c7-42c81340b788@gmail.com","subject":"Re: [PATCH v5 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-02-15T22:01:33Z","receivedAt":"2022-02-15T22:01:46Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Feb 7, 2022 at 9:30 AM Derrick Stolee <stolee@gmail.com> wrote:\n> On 2/6/2022 6:30 AM, Eric Sunshine wrote:\n> > On Mon, Jan 31, 2022 at 10:01 AM Derrick Stolee via GitGitGadget\n> > <gitgitgadget@gmail.com> wrote:\n> >> +               git config --worktree bogus.key value &&\n> >> +               git config --unset core.bare &&\n> >\n> > Why is this being unset? (Genuine question. Am I missing something obvious?)\n>\n> I'm moving it out of the common config file. Earlier commands\n> enabled it in the config.worktree file for this working tree.\n\nBut won't the `git worktree add` commands which immediately follow\nthis bit automatically drop `core.bare=true` from the common config\nfile? Or am I misthinking on this? Or are you just trying to be\nexplicit here with the manual removal?\n"},{"id":"448492","messageId":"CAPig+cS+q-X1KLRp6fV5Lvd80wX8WZ1993OXisQUk2ra_kJ1wA@mail.gmail.com","threadId":"57125","inReplyTo":"c51cb3714e7b1d2f8c9370fe87eca9984ff4859f.1644269584.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v6 5/6] worktree: copy sparse-checkout patterns and config on add","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-02-15T22:23:57Z","receivedAt":"2022-02-15T22:24:12Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Feb 7, 2022 at 4:33 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> When adding a new worktree, it is reasonable to expect that we want to\n> use the current set of sparse-checkout settings for that new worktree.\n> This is particularly important for repositories where the worktree would\n> become too large to be useful. This is even more important when using\n> partial clone as well, since we want to avoid downloading the missing\n> blobs for files that should not be written to the new worktree.\n> [...]\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/builtin/worktree.c b/builtin/worktree.c\n> @@ -335,6 +335,69 @@ static int add_worktree(const char *path, const char *refname,\n> +       /*\n> +        * If the current worktree has sparse-checkout enabled, then copy\n> +        * the sparse-checkout patterns from the current worktree.\n> +        */\n> +       if (core_apply_sparse_checkout) {\n> +               char *from_file = git_pathdup(\"info/sparse-checkout\");\n> +               char *to_file = xstrfmt(\"%s/info/sparse-checkout\",\n> +                                       sb_repo.buf);\n> +\n> +               if (file_exists(from_file)) {\n> +                       if (safe_create_leading_directories(to_file) ||\n> +                           copy_file(to_file, from_file, 0666))\n> +                               error(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n> +                                     from_file, to_file);\n> +               }\n> +\n> +               free(from_file);\n> +               free(to_file);\n> +       }\n> +\n> +       /*\n> +        * If we are using worktree config, then copy all current config\n> +        * values from the current worktree into the new one, that way the\n> +        * new worktree behaves the same as this one.\n> +        */\n> +       if (repository_format_worktree_config) {\n> +               char *from_file = git_pathdup(\"config.worktree\");\n> +               char *to_file = xstrfmt(\"%s/config.worktree\",\n> +                                       sb_repo.buf);\n> +\n> +               if (file_exists(from_file)) {\n> +                       struct config_set cs = { { 0 } };\n> +                       const char *core_worktree;\n> +                       int bare;\n> +\n> +                       if (safe_create_leading_directories(to_file) ||\n> +                           copy_file(to_file, from_file, 0666)) {\n> +                               error(_(\"failed to copy worktree config from '%s' to '%s'\"),\n> +                                     from_file, to_file);\n> +                               goto worktree_copy_cleanup;\n> +                       }\n> +\n> +                       git_configset_init(&cs);\n> +                       git_configset_add_file(&cs, from_file);\n> +\n> +                       if (!git_configset_get_bool(&cs, \"core.bare\", &bare) &&\n> +                           bare &&\n> +                           git_config_set_multivar_in_file_gently(\n> +                                       to_file, \"core.bare\", NULL, \"true\", 0))\n> +                               error(_(\"failed to unset 'core.bare' in '%s'\"), to_file);\n> +                       if (!git_configset_get_value(&cs, \"core.worktree\", &core_worktree) &&\n> +                           git_config_set_in_file_gently(to_file,\n> +                                                         \"core.worktree\", NULL))\n> +                               error(_(\"failed to unset 'core.worktree' in '%s'\"), to_file);\n> +\n> +                       git_configset_clear(&cs);\n> +               }\n> +\n> +worktree_copy_cleanup:\n> +               free(from_file);\n> +               free(to_file);\n> +       }\n\nGiven that add_worktree() is already overly long, a good future\ncleanup would be to move these two new hunks of functionality into\nseparate functions -- copy_sparsity() and copy_config(), perhaps -- in\nbuiltin/worktree.c. This should be simple enough since (I think) the\nonly state that needs to be passed to them is `sb_repo`. But such\ncleanup needn't hold up this series.\n"},{"id":"448519","messageId":"CAPig+cQTRGuuspz2g5LZr4Oh8bQCr4FUDV0tirs+ZPwhtAaJVw@mail.gmail.com","threadId":"57125","inReplyTo":"CAPig+cQWnuG+-eWyih7-Q75PeFjQRHN58SWWhFQWHU2=R6GfyA@mail.gmail.com","subject":"Re: [PATCH v6 0/6] Sparse checkout: fix bug with worktree of bare repo","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-02-16T00:56:58Z","receivedAt":"2022-02-16T00:57:13Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Feb 8, 2022 at 12:42 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Tue, Feb 8, 2022 at 12:18 AM Elijah Newren <newren@gmail.com> wrote:\n> > So, you clearly also read the patches in this round.  Do they also\n> > look good to you?   :-)\n>\n> I have not yet looked at either the patches or the range-diff, and I\n> only scanned my eye quickly over the cover letter, but the empty\n> bullet point made me stop and look a bit more carefully at that part\n> (and only that part) of the cover letter. Not sure yet when I'll have\n> time to carefully read this round.\n\nI've now read through the series and, I think, left only one or two\nminor not-actionable comments.\n"},{"id":"448525","messageId":"xmqq4k4zy3x3.fsf@gitster.g","threadId":"57125","inReplyTo":"CAPig+cSY7E_XQC1gHzDJxoDGGmgWDmNz9Ys=CwbhLkCt+DQf-A@mail.gmail.com","subject":"Re: [PATCH v6 1/6] Documentation: add extensions.worktreeConfig details","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-16T01:51:36Z","receivedAt":"2022-02-16T01:51:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n>> I generally agree. This was changed in the most-recent re-roll\n>> based on a request by Eric [1]. I'm happy to take whichever\n>> version the two of you settle on.\n>>\n>> [1] https://lore.kernel.org/git/CAPig+cS-3CxxyPGcy_vkeN_WYTRo1b-ZhJNdPy8ARZSNKkF1Xg@mail.gmail.com/\n>\n> \"request\" is perhaps too strong a word considering that I led in with:\n>\n>     A few minor comments, which can be addressed later or not\n>     at all, and likely are not worth holding up the series...\n>\n> I mentioned \"worktree vs. working tree\" only to point out the\n> terminology inconsistency being introduced by the new patch; the same\n> sort of inconsistency which had bothered Michael Haggerty enough to do\n> something about it in bc483285b7 (Documentation/git-worktree:\n> consistently use term \"linked working tree\", 2015-07-20).\n\nYup, it seems both of us found mixed use of these two terms\ndisturbing.  Michael's old commit was mostly about \"worktree\" vs\n\"working tree\", even though 2 changes among 13 changes to the file\nwere about updating \"working directory\" to \"working tree\", and to me\nit seems to made the terminology straightened up.  E.g. a hunk from\nthe change uses \"a linked working tree and its administrative files\"\n\n    -- `remove` to remove a linked worktree and its administrative files (and\n    -  warn if the worktree is dirty)\n    -- `mv` to move or rename a worktree and update its administrative files\n    -- `list` to list linked worktrees\n    +- `remove` to remove a linked working tree and its administrative files (and\n    +  warn if the working tree is dirty)\n    +- `mv` to move or rename a working tree and update its administrative files\n    +- `list` to list linked working trees\n     - `lock` to prevent automatic pruning of administrative files (for instance,\n    -  for a worktree on a portable device)\n    +  for a working tree on a portable device)\n\nwhich clearly refers to things above .git as \"working tree\".\n\n> I, personally, prefer the term \"worktree\" for both convenience and\n> because it better encapsulates the overall \"thing\" which is\n> manipulated by the git-worktree command unlike the term \"working tree\"\n> which, as Junio points out, has (perhaps) a more narrow meaning. As\n> such, I would not be opposed to a patch series which changes \"working\n> tree\" to \"worktree\" in documentation where appropriate, but that's\n> outside the scope of this series.\n\nI would welcome such changes where appropriate in both directions (I\nthink I updated a few places in the glossary).  I agree that this\ntopic is not a place to do so.\n"},{"id":"448593","messageId":"3769f426-6263-527b-be46-986ad0904cb4@github.com","threadId":"57125","inReplyTo":"CAPig+cS6Of=1OOw-6MOCBJVW6_FxtGw_ciUR=RTYakdy=1Z_hA@mail.gmail.com","subject":"Re: [PATCH v5 5/5] worktree: copy sparse-checkout patterns and config on add","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-02-16T13:58:57Z","receivedAt":"2022-02-16T13:59:04Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 2/15/2022 5:01 PM, Eric Sunshine wrote:\n> On Mon, Feb 7, 2022 at 9:30 AM Derrick Stolee <stolee@gmail.com> wrote:\n>> On 2/6/2022 6:30 AM, Eric Sunshine wrote:\n>>> On Mon, Jan 31, 2022 at 10:01 AM Derrick Stolee via GitGitGadget\n>>> <gitgitgadget@gmail.com> wrote:\n>>>> +               git config --worktree bogus.key value &&\n>>>> +               git config --unset core.bare &&\n>>>\n>>> Why is this being unset? (Genuine question. Am I missing something obvious?)\n>>\n>> I'm moving it out of the common config file. Earlier commands\n>> enabled it in the config.worktree file for this working tree.\n> \n> But won't the `git worktree add` commands which immediately follow\n> this bit automatically drop `core.bare=true` from the common config\n> file? Or am I misthinking on this? Or are you just trying to be\n> explicit here with the manual removal?\n\nAh. Here we are testing that bogus.key gets copied from the\nconfig.worktree file, but core.bare and core.worktree do _not_.\n\nThis is kind of like the case where we run two 'git worktree add'\ncommands in a row. The first one moves core.bare and core.worktree\ninto the config.worktree file. The second one attempts to copy the\nconfig.worktree file into the new worktree (but must filter out\nthese config keys).\n\nThanks,\n-Stolee\n"}]}