Git fork

worktree: detect from secondary worktree if main worktree is bare

When extensions.worktreeConfig is true and the main worktree is
bare -- that is, its config.worktree file contains core.bare=true
-- commands run from secondary worktrees incorrectly see the main
worktree as not bare. As such, those commands incorrectly think
that the repository's default branch (typically "main" or
"master") is checked out in the bare repository even though it's
not. This makes it impossible, for instance, to checkout or delete
the default branch from a secondary worktree, among other
shortcomings.

This problem occurs because, when extensions.worktreeConfig is
true, commands run in secondary worktrees only consult
$commondir/config and $commondir/worktrees/<id>/config.worktree,
thus they never see the main worktree's core.bare=true setting in
$commondir/config.worktree.

Fix this problem by consulting the main worktree's config.worktree
file when checking whether it is bare. (This extra work is
performed only when running from a secondary worktree.)

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Olga Pilipenco <olga.pilipenco@shopify.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Olga Pilipenco and committed by
Junio C Hamano
78a95e0d 58b5801a

+46 -9
+14
t/t3200-branch.sh
··· 410 git -C secondary branch -D main 411 ' 412 413 test_expect_success 'git branch --list -v with --abbrev' ' 414 test_when_finished "git branch -D t" && 415 git branch t &&
··· 410 git -C secondary branch -D main 411 ' 412 413 + test_expect_success 'secondary worktrees recognize core.bare=true in main config.worktree' ' 414 + test_when_finished "rm -rf bare_repo non_bare_repo secondary_worktree" && 415 + git init -b main non_bare_repo && 416 + test_commit -C non_bare_repo x && 417 + 418 + git clone --bare non_bare_repo bare_repo && 419 + git -C bare_repo config extensions.worktreeConfig true && 420 + git -C bare_repo config unset core.bare && 421 + git -C bare_repo config --worktree core.bare true && 422 + 423 + git -C bare_repo worktree add ../secondary_worktree && 424 + git -C secondary_worktree checkout main 425 + ' 426 + 427 test_expect_success 'git branch --list -v with --abbrev' ' 428 test_when_finished "git branch -D t" && 429 git branch t &&
+32 -9
worktree.c
··· 65 return is_current; 66 } 67 68 /** 69 * get the main worktree 70 */ ··· 79 CALLOC_ARRAY(worktree, 1); 80 worktree->repo = the_repository; 81 worktree->path = strbuf_detach(&worktree_path, NULL); 82 - /* 83 - * NEEDSWORK: If this function is called from a secondary worktree and 84 - * config.worktree is present, is_bare_repository_cfg will reflect the 85 - * contents of config.worktree, not the contents of the main worktree. 86 - * This means that worktree->is_bare may be set to 0 even if the main 87 - * worktree is configured to be bare. 88 - */ 89 worktree->is_bare = (is_bare_repository_cfg == 1) || 90 - is_bare_repository(); 91 - worktree->is_current = is_current_worktree(worktree); 92 if (!skip_reading_head) 93 add_head_info(worktree); 94 return worktree;
··· 65 return is_current; 66 } 67 68 + /* 69 + * When in a secondary worktree, and when extensions.worktreeConfig 70 + * is true, only $commondir/config and $commondir/worktrees/<id>/ 71 + * config.worktree are consulted, hence any core.bare=true setting in 72 + * $commondir/config.worktree gets overlooked. Thus, check it manually 73 + * to determine if the repository is bare. 74 + */ 75 + static int is_main_worktree_bare(struct repository *repo) 76 + { 77 + int bare = 0; 78 + struct config_set cs = {0}; 79 + char *worktree_config = xstrfmt("%s/config.worktree", repo_get_common_dir(repo)); 80 + 81 + git_configset_init(&cs); 82 + git_configset_add_file(&cs, worktree_config); 83 + git_configset_get_bool(&cs, "core.bare", &bare); 84 + 85 + git_configset_clear(&cs); 86 + free(worktree_config); 87 + return bare; 88 + } 89 + 90 /** 91 * get the main worktree 92 */ ··· 101 CALLOC_ARRAY(worktree, 1); 102 worktree->repo = the_repository; 103 worktree->path = strbuf_detach(&worktree_path, NULL); 104 + worktree->is_current = is_current_worktree(worktree); 105 worktree->is_bare = (is_bare_repository_cfg == 1) || 106 + is_bare_repository() || 107 + /* 108 + * When in a secondary worktree we have to also verify if the main 109 + * worktree is bare in $commondir/config.worktree. 110 + * This check is unnecessary if we're currently in the main worktree, 111 + * as prior checks already consulted all configs of the current worktree. 112 + */ 113 + (!worktree->is_current && is_main_worktree_bare(the_repository)); 114 + 115 if (!skip_reading_head) 116 add_head_info(worktree); 117 return worktree;