From ebee95b8474ffeb5fe3c1f6e53f5a4e36ddb3737 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Tue, 13 Oct 2026 09:03:05 +0200 Subject: [PATCH] Fix crash when two worktrees have the same path If a linked worktree's directory is deleted and another worktree is moved to the same path, `git worktree list` reports both worktrees with that path. lazygit then crashes at startup with a stack overflow, and the terminal is left in a broken state. To tell the paths in a group apart, getUniqueNamesFromPathsAux keeps looking at one more parent directory, but identical paths never come apart, so it recurses forever. A stack overflow is a fatal error rather than a panic. No deferred function runs, so the terminal is not restored either. Name identical paths like a single path instead of recursing. Co-Authored-By: Claude Opus 5.5 (1M context) --- pkg/commands/git_commands/worktree_loader.go | 13 ++++++++--- .../git_commands/worktree_loader_test.go | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/pkg/commands/git_commands/worktree_loader.go b/pkg/commands/git_commands/worktree_loader.go index 418ca5c06..d9b8c07c4 100644 --- a/pkg/commands/git_commands/worktree_loader.go +++ b/pkg/commands/git_commands/worktree_loader.go @@ -236,9 +236,16 @@ func getUniqueNamesFromPathsAux(paths []pathWithIndexT, depth int) []nameWithInd result := []nameWithIndexT{} for _, group := range groups { - if len(group) == 1 { - path := group[0] - result = append(result, nameWithIndexT{index: path.index, name: sliceAtDepth(path.path, depth)}) + // Identical paths can't be told apart at any depth, so they are named + // like a single path. Git reports two worktrees with the same path when + // a worktree directory was deleted and another worktree was moved to + // its place. + if lo.EveryBy(group, func(path pathWithIndexT) bool { + return trimSlashes(path.path) == trimSlashes(group[0].path) + }) { + for _, path := range group { + result = append(result, nameWithIndexT{index: path.index, name: sliceAtDepth(path.path, depth)}) + } } else { result = append(result, getUniqueNamesFromPathsAux(group, depth+1)...) } diff --git a/pkg/commands/git_commands/worktree_loader_test.go b/pkg/commands/git_commands/worktree_loader_test.go index c537c6be4..2f715a7f0 100644 --- a/pkg/commands/git_commands/worktree_loader_test.go +++ b/pkg/commands/git_commands/worktree_loader_test.go @@ -353,6 +353,28 @@ func TestGetUniqueNamesFromPaths(t *testing.T) { "e/c/d", }, }, + { + input: []string{ + "/a/b/c", + "/a/b/c", + }, + expected: []string{ + "c", + "c", + }, + }, + { + input: []string{ + "/a/b/c", + "/a/b/c/", + "/a/d/c", + }, + expected: []string{ + "b/c", + "b/c", + "d/c", + }, + }, } { actual := getUniqueNamesFromPaths(scenario.input) assert.EqualValues(t, scenario.expected, actual)