Use the work tree as the repo path when it is the main worktree

RepoPath() is meant to be the same as WorktreePath() when we're in the
main worktree, but we derived it from the git dir's location instead.
That is only the same thing when the git dir lives inside the work tree.
With core.worktree, --work-tree, or a .git file pointing at a repo dir
that isn't called .git, it lands on a directory that isn't a worktree at
all, and the repo name we show follows it there.

A worktree that has the repo's common git dir to itself is the main
worktree, so use its path. That subsumes the submodule case, whose git
dir lives under the superproject's .git/modules but is still the
submodule's own common dir; --show-superproject-working-tree is now only
needed for a linked worktree of a submodule.

The existing bare repo test asserted a git output that can't occur (a
work tree and --is-bare-repository=true at once), but the rest of it is
the shape of a repo opened with --git-dir/--work-tree, where the new
repo path is the correct one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller 2026-08-08 09:29:20 +02:00
parent 180039e78c
commit e17ed2484c
2 changed files with 25 additions and 22 deletions

View file

@ -95,15 +95,30 @@ func GetRepoPathsForDir(
repoGitDirPath := gitDirResults[2]
isBareRepo := gitDirResults[3] == "true"
// If we're in a submodule, --show-superproject-working-tree will return
// a value, meaning gitDirResults will be length 5. In that case
// return the worktree path as the repoPath. Otherwise we're in a
// normal repo or a worktree so return the parent of the git common
// dir (repoGitDirPath)
// A worktree that has the repo's common git dir to itself is the repo's main
// worktree, so it is the repoPath. That holds for a submodule as well: its
// git dir lives under the superproject's .git/modules, but it is still the
// submodule's own common dir.
isMainWorktree := worktreeGitDirPath == repoGitDirPath
// If we're in a submodule, --show-superproject-working-tree will return a
// value, meaning gitDirResults will be length 5. That only tells us anything
// new for a linked worktree of a submodule, which isMainWorktree misses.
isSubmodule := len(gitDirResults) == 5
// Otherwise we're in a linked worktree, and the repoPath is the repo's main
// worktree. git won't tell us where that is: `git worktree list` reports it
// as the common git dir with a trailing "/.git" removed, which is this same
// derivation. So take the directory holding the common git dir. That is the
// main worktree of an ordinary repo, and of a bare one it is the directory
// its worktrees live in. It is not the main worktree of a repo that moved
// that elsewhere with core.worktree; there we end up naming the git dir's
// directory, which means that the repo name we display in the status panel
// isn't correct, and we start looking for .lazygit.yml in the wrong place.
// Both of those are not severe enough to justify the extra git call to get
// the real main worktree, so we accept this for this rather niche use case.
var repoPath string
if isSubmodule {
if isMainWorktree || isSubmodule {
repoPath = worktreePath
} else {
repoPath = filepath.Dir(repoGitDirPath)

View file

@ -109,16 +109,16 @@ func TestGetRepoPaths(t *testing.T) {
Expected: lo.Ternary(runtime.GOOS == "windows", &RepoPaths{
worktreePath: `C:\path\to\repo`,
worktreeGitDirPath: `C:\path\to\bare_repo\bare.git`,
repoPath: `C:\path\to\bare_repo`,
repoPath: `C:\path\to\repo`,
repoGitDirPath: `C:\path\to\bare_repo\bare.git`,
repoName: `bare_repo`,
repoName: `repo`,
isBareRepo: true,
}, &RepoPaths{
worktreePath: "/path/to/repo",
worktreeGitDirPath: "/path/to/bare_repo/bare.git",
repoPath: "/path/to/bare_repo",
repoPath: "/path/to/repo",
repoGitDirPath: "/path/to/bare_repo/bare.git",
repoName: "bare_repo",
repoName: "repo",
isBareRepo: true,
}),
Err: nil,
@ -159,28 +159,16 @@ func TestGetRepoPaths(t *testing.T) {
Expected: lo.Ternary(runtime.GOOS == "windows", &RepoPaths{
worktreePath: `C:\path\to\worktree`,
worktreeGitDirPath: `C:\path\to\repo\.git`,
/* EXPECTED:
repoPath: `C:\path\to\worktree`,
ACTUAL: */
repoPath: `C:\path\to\repo`,
repoGitDirPath: `C:\path\to\repo\.git`,
/* EXPECTED:
repoName: `worktree`,
ACTUAL: */
repoName: `repo`,
isBareRepo: false,
}, &RepoPaths{
worktreePath: "/path/to/worktree",
worktreeGitDirPath: "/path/to/repo/.git",
/* EXPECTED:
repoPath: "/path/to/worktree",
ACTUAL: */
repoPath: "/path/to/repo",
repoGitDirPath: "/path/to/repo/.git",
/* EXPECTED:
repoName: "worktree",
ACTUAL: */
repoName: "repo",
isBareRepo: false,
}),
Err: nil,