Demonstrate that the recent repos menu can't read a submodule's branch

The menu shows "Branch unknown" for every submodule, whatever it has
checked out. A submodule has no .git directory; its .git is a file
naming the directory, and for a submodule git writes that name relative
to the submodule. We hand it to os.ReadFile unchanged, so it resolves
against lazygit's own working directory and the read fails. A worktree
created with --relative-paths (or with worktree.useRelativePaths set)
gets a relative name too, and fails the same way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller 2026-09-14 10:20:22 +02:00
parent 9e542ff6e0
commit 00c0c4c061
2 changed files with 38 additions and 1 deletions

View file

@ -46,6 +46,34 @@ func TestReadHeadInfo(t *testing.T) {
expected: headInfo{branch: "mybranch"},
expectedOk: true,
},
{
name: "worktree whose .git file names the git dir relatively",
files: map[string]string{
"repo/.git/worktrees/wt/HEAD": "ref: refs/heads/mybranch\n",
"wt/.git": "gitdir: ../repo/.git/worktrees/wt\n",
},
/* EXPECTED:
repoPath: "wt",
expected: headInfo{branch: "mybranch"},
expectedOk: true,
ACTUAL: */
repoPath: "wt",
expectedOk: false,
},
{
name: "submodule, whose .git file always names the git dir relatively",
files: map[string]string{
"repo/.git/modules/sub/HEAD": "ref: refs/heads/mybranch\n",
"repo/sub/.git": "gitdir: ../.git/modules/sub\n",
},
/* EXPECTED:
repoPath: "repo/sub",
expected: headInfo{branch: "mybranch"},
expectedOk: true,
ACTUAL: */
repoPath: "repo/sub",
expectedOk: false,
},
{
name: "directory without a .git entry",
repoPath: "notarepo",

View file

@ -19,13 +19,18 @@ var RecentReposBranchColumn = NewIntegrationTest(NewIntegrationTestArgs{
current, _ := filepath.Abs(".")
onBranch, _ := filepath.Abs("../on-branch")
detached, _ := filepath.Abs("../detached")
cfg.GetAppState().RecentRepos = []string{current, onBranch, detached}
submodule, _ := filepath.Abs("sub")
cfg.GetAppState().RecentRepos = []string{current, onBranch, detached, submodule}
},
SetupRepo: func(shell *Shell) {
shell.EmptyCommit("one")
shell.CloneNonBare("on-branch")
shell.CloneNonBare("detached")
shell.RunCommand([]string{"git", "-C", "../detached", "checkout", "--detach"})
shell.CloneIntoSubmodule("submodule", "sub")
shell.GitAddAll()
shell.Commit("add submodule")
shell.RunCommand([]string{"git", "-C", "sub", "checkout", "--detach"})
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.ExpectPopup().Menu().
@ -33,6 +38,10 @@ var RecentReposBranchColumn = NewIntegrationTest(NewIntegrationTestArgs{
Lines(
Contains("on-branch").Contains("master").IsSelected(),
Contains("detached").MatchesRegexp(`HEAD detached at [0-9a-f]{8}`),
/* EXPECTED:
Contains("sub").MatchesRegexp(`HEAD detached at [0-9a-f]{8}`),
ACTUAL: */
Contains("sub").Contains("Branch unknown"),
Contains("Cancel"),
).
Cancel()