From afc4aedd4f20c0b6fb021e95bf45e1d867315560 Mon Sep 17 00:00:00 2001 From: Joel Baranick Date: Thu, 1 Sep 2022 17:58:36 -0700 Subject: [PATCH] Support for deleting a worktree --- pkg/commands/git.go | 3 + pkg/commands/git_commands/worktree.go | 17 ++++ pkg/gui/controllers/worktrees_controller.go | 86 ++++++++++++--------- pkg/i18n/english.go | 14 ++++ 4 files changed, 84 insertions(+), 36 deletions(-) create mode 100644 pkg/commands/git_commands/worktree.go diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 6e74daa52..4c2215820 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -37,6 +37,7 @@ type GitCommand struct { Tag *git_commands.TagCommands WorkingTree *git_commands.WorkingTreeCommands Bisect *git_commands.BisectCommands + Worktree *git_commands.WorktreeCommands Loaders Loaders } @@ -128,6 +129,7 @@ func NewGitCommandAux( }) patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder) bisectCommands := git_commands.NewBisectCommands(gitCommon) + worktreeCommands := git_commands.NewWorktreeCommands(gitCommon) branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands) commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd) @@ -156,6 +158,7 @@ func NewGitCommandAux( Tag: tagCommands, Bisect: bisectCommands, WorkingTree: workingTreeCommands, + Worktree: worktreeCommands, Loaders: Loaders{ BranchLoader: branchLoader, CommitFileLoader: commitFileLoader, diff --git a/pkg/commands/git_commands/worktree.go b/pkg/commands/git_commands/worktree.go new file mode 100644 index 000000000..84931f0cd --- /dev/null +++ b/pkg/commands/git_commands/worktree.go @@ -0,0 +1,17 @@ +package git_commands + +type WorktreeCommands struct { + *GitCommon +} + +func NewWorktreeCommands(gitCommon *GitCommon) *WorktreeCommands { + return &WorktreeCommands{ + GitCommon: gitCommon, + } +} + +func (self *WorktreeCommands) Delete(worktreePath string, force bool) error { + cmdArgs := NewGitCmd("worktree").Arg("remove").ArgIf(force, "-f").Arg(worktreePath).ToArgv() + + return self.cmd.New(cmdArgs).Run() +} diff --git a/pkg/gui/controllers/worktrees_controller.go b/pkg/gui/controllers/worktrees_controller.go index f3b5ed60b..9cbbb6faa 100644 --- a/pkg/gui/controllers/worktrees_controller.go +++ b/pkg/gui/controllers/worktrees_controller.go @@ -8,6 +8,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/jesseduffield/lazygit/pkg/utils" ) type WorktreesController struct { @@ -33,27 +34,16 @@ func (self *WorktreesController) GetKeybindings(opts types.KeybindingsOpts) []*t Handler: self.checkSelected(self.enter), Description: self.c.Tr.EnterWorktree, }, - //{ - // Key: opts.GetKey(opts.Config.Universal.Remove), - // Handler: self.withSelectedTag(self.delete), - // Description: self.c.Tr.LcDeleteTag, - //}, - //{ - // Key: opts.GetKey(opts.Config.Branches.PushTag), - // Handler: self.withSelectedTag(self.push), - // Description: self.c.Tr.LcPushTag, - //}, + { + Key: opts.GetKey(opts.Config.Universal.Remove), + Handler: self.checkSelected(self.delete), + Description: self.c.Tr.DeleteWorktree, + }, //{ // Key: opts.GetKey(opts.Config.Universal.New), // Handler: self.create, // Description: self.c.Tr.LcCreateTag, //}, - //{ - // Key: opts.GetKey(opts.Config.Commits.ViewResetOptions), - // Handler: self.withSelectedTag(self.createResetMenu), - // Description: self.c.Tr.LcViewResetOptions, - // OpensMenu: true, - //}, } return bindings @@ -95,26 +85,50 @@ func (self *WorktreesController) GetOnRenderToMain() func() error { // return gui.dispatchSwitchToRepo(submodule.Path, true) //} -// func (self *WorktreesController) delete(tag *models.Tag) error { -// prompt := utils.ResolvePlaceholderString( -// self.c.Tr.DeleteTagPrompt, -// map[string]string{ -// "tagName": tag.Name, -// }, -// ) -// -// return self.c.Confirm(types.ConfirmOpts{ -// Title: self.c.Tr.DeleteTagTitle, -// Prompt: prompt, -// HandleConfirm: func() error { -// self.c.LogAction(self.c.Tr.Actions.DeleteTag) -// if err := self.git.Tag.Delete(tag.Name); err != nil { -// return self.c.Error(err) -// } -// return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.COMMITS, types.TAGS}}) -// }, -// }) -// } +func (self *WorktreesController) delete(worktree *models.Worktree) error { + if worktree.Main { + return self.c.ErrorMsg(self.c.Tr.CantDeleteMainWorktree) + } + + if worktree.Current { + return self.c.ErrorMsg(self.c.Tr.CantDeleteCurrentWorktree) + } + + return self.deleteWithForce(worktree, false) +} + +func (self *WorktreesController) deleteWithForce(worktree *models.Worktree, force bool) error { + title := self.c.Tr.DeleteWorktreeTitle + var templateStr string + if force { + templateStr = self.c.Tr.ForceDeleteWorktreePrompt + } else { + templateStr = self.c.Tr.DeleteWorktreePrompt + } + message := utils.ResolvePlaceholderString( + templateStr, + map[string]string{ + "worktreeName": worktree.Name, + }, + ) + + return self.c.Confirm(types.ConfirmOpts{ + Title: title, + Prompt: message, + HandleConfirm: func() error { + self.c.LogAction(self.c.Tr.Actions.DeleteWorktree) + if err := self.c.Git().Worktree.Delete(worktree.Path, force); err != nil { + errMessage := err.Error() + if !force { + return self.deleteWithForce(worktree, true) + } + return self.c.ErrorMsg(errMessage) + } + return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.WORKTREES}}) + }, + }) +} + // // func (self *WorktreesController) push(tag *models.Tag) error { // title := utils.ResolvePlaceholderString( diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index a47056b55..27288e078 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -543,6 +543,12 @@ type TranslationSet struct { ExitSearchMode string ExitTextFilterMode string EnterWorktree string + DeleteWorktree string + DeleteWorktreeTitle string + DeleteWorktreePrompt string + ForceDeleteWorktreePrompt string + CantDeleteCurrentWorktree string + CantDeleteMainWorktree string Actions Actions Bisect Bisect } @@ -673,6 +679,7 @@ type Actions struct { ResetBisect string BisectSkip string BisectMark string + DeleteWorktree string } const englishIntroPopupMessage = ` @@ -1243,6 +1250,12 @@ func EnglishTranslationSet() TranslationSet { SearchPrefix: "Search: ", FilterPrefix: "Filter: ", EnterWorktree: "Enter worktree", + DeleteWorktree: "Delete worktree", + DeleteWorktreeTitle: "Delete worktree", + DeleteWorktreePrompt: "Are you sure you want to delete worktree '{{.worktreeName}}'?", + ForceDeleteWorktreePrompt: "'{{.worktreeName}}' is not fully merged. Are you sure you want to delete it?", + CantDeleteCurrentWorktree: "You cannot delete the current worktree!", + CantDeleteMainWorktree: "You cannot delete the main worktree!", Actions: Actions{ // TODO: combine this with the original keybinding descriptions (those are all in lowercase atm) CheckoutCommit: "Checkout commit", @@ -1350,6 +1363,7 @@ func EnglishTranslationSet() TranslationSet { ResetBisect: "Reset bisect", BisectSkip: "Bisect skip", BisectMark: "Bisect mark", + DeleteWorktree: "Delete worktree", }, Bisect: Bisect{ Mark: "Mark current commit (%s) as %s",