sparse-checkout: set worktree-config correctly

`git sparse-checkout set/init` enables worktree-specific configuration[*] by setting extensions.worktreeConfig=true, but neglects to perform the additional necessary bookkeeping of relocating `core.bare=true` and `core.worktree` from $GIT_COMMON_DIR/config to $GIT_COMMON_DIR/config.worktree, as documented in git-worktree.txt. As a result of this oversight, these settings, which are nonsensical for secondary worktrees, can cause Git commands to incorrectly consider a worktree bare (in the case of `core.bare`) or operate on the wrong worktree (in the case of `core.worktree`). Fix this problem by taking advantage of the recently-added init_worktree_config() which enables `extensions.worktreeConfig` and takes care of necessary bookkeeping. While at it, for backward-compatibility reasons, also stop upgrading the repository format to "1" since doing so is (unintentionally) not required to take advantage of `extensions.worktreeConfig`, as explained by 11664196ac ("Revert "check_repository_format_gently(): refuse extensions for old repositories"", 2020-07-15). [*] The main reason to use worktree-specific config for the sparse-checkout builtin was to avoid enabling sparse-checkout patterns in one and causing a loss of files in another. If a worktree does not have a sparse-checkout patterns file, then the sparse-checkout logic will not kick in on that worktree. Reported-by: Sean Allred <allred.sean@gmail.com> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Reviewed-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Feb 7, 2022 at 21:33 UTC 7316dc5f6f2c8297d32e47d5859933ffacb6c00e
4 files changed +30 -28
Documentation/git-sparse-checkout.txt
+12 -4
@@ -31,13 +31,21 @@ COMMANDS
31 Describe the patterns in the sparse-checkout file.
32
33 'set'::
34 - Enable the necessary config settings
35 - (extensions.worktreeConfig, core.sparseCheckout,
36 - core.sparseCheckoutCone) if they are not already enabled, and
37 - write a set of patterns to the sparse-checkout file from the
34 + Enable the necessary sparse-checkout config settings
35 + (`core.sparseCheckout`, `core.sparseCheckoutCone`, and
36 + `index.sparse`) if they are not already set to the desired values,
37 + and write a set of patterns to the sparse-checkout file from the
38 list of arguments following the 'set' subcommand. Update the
39 working directory to match the new patterns.
40 +
41 +To ensure that adjusting the sparse-checkout settings within a worktree
42 +does not alter the sparse-checkout settings in other worktrees, the 'set'
43 +subcommand will upgrade your repository config to use worktree-specific
44 +config if not already present. The sparsity defined by the arguments to
45 +the 'set' subcommand are stored in the worktree-specific sparse-checkout
46 +file. See linkgit:git-worktree[1] and the documentation of
47 +`extensions.worktreeConfig` in linkgit:git-config[1] for more details.
48 ++
49 When the `--stdin` option is provided, the patterns are read from
50 standard in as a newline-delimited list instead of from the arguments.
51 +
builtin/sparse-checkout.c
+13 -15
@@ -15,6 +15,7 @@
15 #include "wt-status.h"
16 #include "quote.h"
17 #include "sparse-index.h"
18 +#include "worktree.h"
19
20 static const char *empty_base = "";
21
@@ -359,26 +360,23 @@ enum sparse_checkout_mode {
360
361 static int set_config(enum sparse_checkout_mode mode)
362 {
362 - const char *config_path;
363 -
364 - if (upgrade_repository_format(1) < 0)
365 - die(_("unable to upgrade repository format to enable worktreeConfig"));
366 - if (git_config_set_gently("extensions.worktreeConfig", "true")) {
367 - error(_("failed to set extensions.worktreeConfig setting"));
363 + /* Update to use worktree config, if not already. */
364 + if (init_worktree_config(the_repository)) {
365 + error(_("failed to initialize worktree config"));
366 return 1;
367 }
368
371 - config_path = git_path("config.worktree");
372 - git_config_set_in_file_gently(config_path,
373 - "core.sparseCheckout",
374 - mode ? "true" : NULL);
375 -
376 - git_config_set_in_file_gently(config_path,
377 - "core.sparseCheckoutCone",
378 - mode == MODE_CONE_PATTERNS ? "true" : NULL);
369 + if (repo_config_set_worktree_gently(the_repository,
370 + "core.sparseCheckout",
371 + mode ? "true" : "false") ||
372 + repo_config_set_worktree_gently(the_repository,
373 + "core.sparseCheckoutCone",
374 + mode == MODE_CONE_PATTERNS ?
375 + "true" : "false"))
376 + return 1;
377
378 if (mode == MODE_NO_PATTERNS)
381 - set_sparse_index_config(the_repository, 0);
379 + return set_sparse_index_config(the_repository, 0);
380
381 return 0;
382 }
sparse-index.c
+3 -7
@@ -99,13 +99,9 @@ static int convert_to_sparse_rec(struct index_state *istate,
99
100 int set_sparse_index_config(struct repository *repo, int enable)
101 {
102 - int res;
103 - char *config_path = repo_git_path(repo, "config.worktree");
104 - res = git_config_set_in_file_gently(config_path,
105 - "index.sparse",
106 - enable ? "true" : NULL);
107 - free(config_path);
108 -
102 + int res = repo_config_set_worktree_gently(repo,
103 + "index.sparse",
104 + enable ? "true" : "false");
105 prepare_repo_settings(repo);
106 repo->settings.sparse_index = enable;
107 return res;
t/t1091-sparse-checkout-builtin.sh
+2 -2
@@ -117,7 +117,7 @@ test_expect_success 'switching to cone mode with non-cone mode patterns' '
117 cd bad-patterns &&
118 git sparse-checkout init &&
119 git sparse-checkout add dir &&
120 - git config core.sparseCheckoutCone true &&
120 + git config --worktree core.sparseCheckoutCone true &&
121 test_must_fail git sparse-checkout add dir 2>err &&
122 grep "existing sparse-checkout patterns do not use cone mode" err
123 )
@@ -256,7 +256,7 @@ test_expect_success 'sparse-index enabled and disabled' '
256 test_cmp expect actual &&
257
258 git -C repo config --list >config &&
259 - ! grep index.sparse config
259 + test_cmp_config -C repo false index.sparse
260 )
261 '
262