Skip to content

Commit

Permalink
worktree: detect from secondary worktree if main worktree is bare
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Olga Pilipenco <[email protected]>
  • Loading branch information
olga-mcbfe committed Feb 4, 2025
1 parent f93ff17 commit a34459b
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 9 deletions.
14 changes: 14 additions & 0 deletions t/t3200-branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,20 @@ test_expect_success 'bare main worktree has HEAD at branch deleted by secondary
git -C secondary branch -D main
'

test_expect_success 'secondary worktrees recognize core.bare=true in main config.worktree' '
test_when_finished "rm -rf bare_repo non_bare_repo secondary_worktree" &&
git init -b main non_bare_repo &&
test_commit -C non_bare_repo x &&
git clone --bare non_bare_repo bare_repo &&
git -C bare_repo config extensions.worktreeConfig true &&
git -C bare_repo config unset core.bare &&
git -C bare_repo config --worktree core.bare true &&
git -C bare_repo worktree add ../secondary_worktree &&
git -C secondary_worktree checkout main
'

test_expect_success 'git branch --list -v with --abbrev' '
test_when_finished "git branch -D t" &&
git branch t &&
Expand Down
41 changes: 32 additions & 9 deletions worktree.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,28 @@ static int is_current_worktree(struct worktree *wt)
return is_current;
}

/*
* When in a secondary worktree, and when extensions.worktreeConfig
* is true, only $commondir/config and $commondir/worktrees/<id>/
* config.worktree are consulted, hence any core.bare=true setting in
* $commondir/config.worktree gets overlooked. Thus, check it manually
* to determine if the repository is bare.
*/
static int is_main_worktree_bare(struct repository *repo)
{
int bare = 0;
struct config_set cs = {0};
char *worktree_config = xstrfmt("%s/config.worktree", repo_get_common_dir(repo));

git_configset_init(&cs);
git_configset_add_file(&cs, worktree_config);
git_configset_get_bool(&cs, "core.bare", &bare);

git_configset_clear(&cs);
free(worktree_config);
return bare;
}

/**
* get the main worktree
*/
Expand All @@ -79,16 +101,17 @@ static struct worktree *get_main_worktree(int skip_reading_head)
CALLOC_ARRAY(worktree, 1);
worktree->repo = the_repository;
worktree->path = strbuf_detach(&worktree_path, NULL);
/*
* NEEDSWORK: If this function is called from a secondary worktree and
* config.worktree is present, is_bare_repository_cfg will reflect the
* contents of config.worktree, not the contents of the main worktree.
* This means that worktree->is_bare may be set to 0 even if the main
* worktree is configured to be bare.
*/
worktree->is_bare = (is_bare_repository_cfg == 1) ||
is_bare_repository();
worktree->is_current = is_current_worktree(worktree);
worktree->is_bare = (is_bare_repository_cfg == 1) ||
is_bare_repository() ||
/*
* When in a secondary worktree we have to also verify if the main
* worktree is bare in $commondir/config.worktree.
* This check is unnecessary if we're currently in the main worktree,
* as prior checks already consulted all configs of the current worktree.
*/
(!worktree->is_current && is_main_worktree_bare(the_repository));

if (!skip_reading_head)
add_head_info(worktree);
return worktree;
Expand Down

0 comments on commit a34459b

Please sign in to comment.