From 875641557b3593ade7035b3fadd6f655c6b7700d Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Mon, 12 Oct 2026 18:27:16 +0200 Subject: [PATCH] Move the menu for removing a worktree to BranchesHelper The files panel is going to show it too. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../controllers/helpers/branches_helper.go | 60 +++++++++++++++++++ pkg/gui/controllers/worktrees_controller.go | 58 +----------------- 2 files changed, 61 insertions(+), 57 deletions(-) diff --git a/pkg/gui/controllers/helpers/branches_helper.go b/pkg/gui/controllers/helpers/branches_helper.go index 5f26c8099..849226e03 100644 --- a/pkg/gui/controllers/helpers/branches_helper.go +++ b/pkg/gui/controllers/helpers/branches_helper.go @@ -231,6 +231,66 @@ func (self *BranchesHelper) promptWorktreeBranchDelete( }) } +// RemoveWorktreeMenu shows the menu for removing a worktree, optionally along +// with the branch that it has checked out. +func (self *BranchesHelper) RemoveWorktreeMenu(worktree *models.Worktree) error { + if worktree.IsMain { + return errors.New(self.c.Tr.CantDeleteMainWorktree) + } + + if worktree.IsCurrent { + return errors.New(self.c.Tr.CantDeleteCurrentWorktree) + } + + removeWorktreeItem := &types.MenuItem{ + Label: self.c.Tr.RemoveWorktree, + Keys: menuKey('w'), + OnPress: func() error { + return self.worktreeHelper.Remove(worktree, nil) + }, + } + + branch, branchFound := lo.Find(self.c.Model().Branches, func(branch *models.Branch) bool { + return branch.Name == worktree.Branch + }) + // A worktree with a detached HEAD has no branch to delete + detachedReason := &types.DisabledReason{Text: self.c.Tr.WorktreeNotCheckedOutOnBranch} + + removeWorktreeAndBranchItem := &types.MenuItem{ + Label: self.c.Tr.RemoveWorktreeAndDeleteBranch, + Keys: menuKey('b'), + OnPress: func() error { + return self.RemoveWorktreeAndDeleteBranch(worktree, branch) + }, + } + if !branchFound { + removeWorktreeAndBranchItem.DisabledReason = detachedReason + } + + removeWorktreeAndBothBranchesItem := &types.MenuItem{ + Label: self.c.Tr.RemoveWorktreeAndDeleteBothBranches, + Keys: menuKey('r'), + OnPress: func() error { + return self.RemoveWorktreeAndDeleteBothBranches(worktree, branch) + }, + } + if !branchFound { + removeWorktreeAndBothBranchesItem.DisabledReason = detachedReason + } else if !branch.IsTrackingRemote() || branch.UpstreamGone { + removeWorktreeAndBothBranchesItem.DisabledReason = &types.DisabledReason{ + Text: self.c.Tr.UpstreamNotSetError, + } + } + + return self.c.Menu(types.CreateMenuOptions{ + Title: utils.ResolvePlaceholderString( + self.c.Tr.RemoveWorktreeMenuTitle, + map[string]string{"worktreeName": worktree.Name}, + ), + Items: []*types.MenuItem{removeWorktreeItem, removeWorktreeAndBranchItem, removeWorktreeAndBothBranchesItem}, + }) +} + // RemoveWorktreeAndDeleteBranch removes the worktree and deletes the local branch // it has checked out, force-warning first if the branch isn't fully merged. It's // the worktrees-panel counterpart to deleting a worktree-checked-out branch from diff --git a/pkg/gui/controllers/worktrees_controller.go b/pkg/gui/controllers/worktrees_controller.go index 02d20b3f2..886cf9efd 100644 --- a/pkg/gui/controllers/worktrees_controller.go +++ b/pkg/gui/controllers/worktrees_controller.go @@ -1,7 +1,6 @@ package controllers import ( - "errors" "fmt" "strings" "text/tabwriter" @@ -11,7 +10,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" - "github.com/samber/lo" ) type WorktreesController struct { @@ -123,61 +121,7 @@ func (self *WorktreesController) add() error { } func (self *WorktreesController) remove(worktree *models.Worktree) error { - if worktree.IsMain { - return errors.New(self.c.Tr.CantDeleteMainWorktree) - } - - if worktree.IsCurrent { - return errors.New(self.c.Tr.CantDeleteCurrentWorktree) - } - - removeWorktreeItem := &types.MenuItem{ - Label: self.c.Tr.RemoveWorktree, - Keys: menuKey('w'), - OnPress: func() error { - return self.c.Helpers().Worktree.Remove(worktree, nil) - }, - } - - branch, branchFound := lo.Find(self.c.Model().Branches, func(branch *models.Branch) bool { - return branch.Name == worktree.Branch - }) - // A worktree with a detached HEAD has no branch to delete - detachedReason := &types.DisabledReason{Text: self.c.Tr.WorktreeNotCheckedOutOnBranch} - - removeWorktreeAndBranchItem := &types.MenuItem{ - Label: self.c.Tr.RemoveWorktreeAndDeleteBranch, - Keys: menuKey('b'), - OnPress: func() error { - return self.c.Helpers().BranchesHelper.RemoveWorktreeAndDeleteBranch(worktree, branch) - }, - } - if !branchFound { - removeWorktreeAndBranchItem.DisabledReason = detachedReason - } - - removeWorktreeAndBothBranchesItem := &types.MenuItem{ - Label: self.c.Tr.RemoveWorktreeAndDeleteBothBranches, - Keys: menuKey('r'), - OnPress: func() error { - return self.c.Helpers().BranchesHelper.RemoveWorktreeAndDeleteBothBranches(worktree, branch) - }, - } - if !branchFound { - removeWorktreeAndBothBranchesItem.DisabledReason = detachedReason - } else if !branch.IsTrackingRemote() || branch.UpstreamGone { - removeWorktreeAndBothBranchesItem.DisabledReason = &types.DisabledReason{ - Text: self.c.Tr.UpstreamNotSetError, - } - } - - return self.c.Menu(types.CreateMenuOptions{ - Title: utils.ResolvePlaceholderString( - self.c.Tr.RemoveWorktreeMenuTitle, - map[string]string{"worktreeName": worktree.Name}, - ), - Items: []*types.MenuItem{removeWorktreeItem, removeWorktreeAndBranchItem, removeWorktreeAndBothBranchesItem}, - }) + return self.c.Helpers().BranchesHelper.RemoveWorktreeMenu(worktree) } func (self *WorktreesController) GetOnDoubleClick() func() error {