summaryrefslogtreecommitdiffstats
path: root/pkg/gui
diff options
context:
space:
mode:
authorFederico <federicogerardi94@gmail.com>2023-08-10 09:39:26 +0200
committerGitHub <noreply@github.com>2023-08-10 17:39:26 +1000
commit0df5cb1286a1f6fb442789ea8afacf7cdcd53735 (patch)
treead63ebac448f657320a1dcfb2b6adf8bc93d7aa2 /pkg/gui
parentc43830b0271374ab42e83771479bf757ba34839c (diff)
Allow deleting remote tags/branches from local tag/branch views (#2738)
Diffstat (limited to 'pkg/gui')
-rw-r--r--pkg/gui/controllers.go1
-rw-r--r--pkg/gui/controllers/branches_controller.go105
-rw-r--r--pkg/gui/controllers/helpers/branches_helper.go46
-rw-r--r--pkg/gui/controllers/helpers/helpers.go1
-rw-r--r--pkg/gui/controllers/remote_branches_controller.go22
-rw-r--r--pkg/gui/controllers/tags_controller.go91
6 files changed, 204 insertions, 62 deletions
diff --git a/pkg/gui/controllers.go b/pkg/gui/controllers.go
index cc45d833a..1430ad239 100644
--- a/pkg/gui/controllers.go
+++ b/pkg/gui/controllers.go
@@ -86,6 +86,7 @@ func (gui *Gui) resetHelpersAndControllers() {
Files: helpers.NewFilesHelper(helperCommon),
WorkingTree: helpers.NewWorkingTreeHelper(helperCommon, refsHelper, commitsHelper, gpgHelper),
Tags: helpers.NewTagsHelper(helperCommon, commitsHelper),
+ BranchesHelper: helpers.NewBranchesHelper(helperCommon),
GPG: helpers.NewGpgHelper(helperCommon),
MergeAndRebase: rebaseHelper,
MergeConflicts: mergeConflictsHelper,
diff --git a/pkg/gui/controllers/branches_controller.go b/pkg/gui/controllers/branches_controller.go
index e7023959f..84d9e838e 100644
--- a/pkg/gui/controllers/branches_controller.go
+++ b/pkg/gui/controllers/branches_controller.go
@@ -70,7 +70,8 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty
{
Key: opts.GetKey(opts.Config.Universal.Remove),
Handler: self.checkSelectedAndReal(self.delete),
- Description: self.c.Tr.DeleteBranch,
+ Description: self.c.Tr.ViewDeleteOptions,
+ OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Branches.RebaseBranch),
@@ -316,19 +317,6 @@ func (self *BranchesController) createNewBranchWithName(newBranchName string) er
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
}
-func (self *BranchesController) delete(branch *models.Branch) error {
- checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef()
- if checkedOutBranch.Name == branch.Name {
- return self.c.ErrorMsg(self.c.Tr.CantDeleteCheckOutBranch)
- }
-
- if self.checkedOutByOtherWorktree(branch) {
- return self.promptWorktreeBranchDelete(branch)
- }
-
- return self.deleteWithForce(branch, false)
-}
-
func (self *BranchesController) checkedOutByOtherWorktree(branch *models.Branch) bool {
return git_commands.CheckedOutByOtherWorktree(branch, self.c.Model().Worktrees)
}
@@ -371,18 +359,34 @@ func (self *BranchesController) promptWorktreeBranchDelete(selectedBranch *model
})
}
-func (self *BranchesController) deleteWithForce(selectedBranch *models.Branch, force bool) error {
- title := self.c.Tr.DeleteBranch
- var templateStr string
- if force {
- templateStr = self.c.Tr.ForceDeleteBranchMessage
- } else {
- templateStr = self.c.Tr.DeleteBranchMessage
+func (self *BranchesController) localDelete(branch *models.Branch) error {
+ if self.checkedOutByOtherWorktree(branch) {
+ return self.promptWorktreeBranchDelete(branch)
}
+
+ return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func(_ gocui.Task) error {
+ self.c.LogAction(self.c.Tr.Actions.DeleteLocalBranch)
+ err := self.c.Git().Branch.LocalDelete(branch.Name, false)
+ if err != nil && strings.Contains(err.Error(), "git branch -D ") {
+ return self.forceDelete(branch)
+ }
+ if err != nil {
+ return self.c.Error(err)
+ }
+ return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.BRANCHES}})
+ })
+}
+
+func (self *BranchesController) remoteDelete(branch *models.Branch) error {
+ return self.c.Helpers().BranchesHelper.ConfirmDeleteRemote(branch.UpstreamRemote, branch.Name)
+}
+
+func (self *BranchesController) forceDelete(branch *models.Branch) error {
+ title := self.c.Tr.ForceDeleteBranchTitle
message := utils.ResolvePlaceholderString(
- templateStr,
+ self.c.Tr.ForceDeleteBranchMessage,
map[string]string{
- "selectedBranchName": selectedBranch.Name,
+ "selectedBranchName": branch.Name,
},
)
@@ -390,19 +394,60 @@ func (self *BranchesController) deleteWithForce(selectedBranch *models.Branch, f
Title: title,
Prompt: message,
HandleConfirm: func() error {
- self.c.LogAction(self.c.Tr.Actions.DeleteBranch)
- if err := self.c.Git().Branch.Delete(selectedBranch.Name, force); err != nil {
- errMessage := err.Error()
- if !force && strings.Contains(errMessage, "git branch -D ") {
- return self.deleteWithForce(selectedBranch, true)
- }
- return self.c.ErrorMsg(errMessage)
+ if err := self.c.Git().Branch.LocalDelete(branch.Name, true); err != nil {
+ return self.c.ErrorMsg(err.Error())
}
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.BRANCHES}})
},
})
}
+func (self *BranchesController) delete(branch *models.Branch) error {
+ menuItems := []*types.MenuItem{}
+ checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef()
+
+ localDeleteItem := &types.MenuItem{
+ Label: self.c.Tr.DeleteLocalBranch,
+ Key: 'c',
+ OnPress: func() error {
+ return self.localDelete(branch)
+ },
+ }
+ if checkedOutBranch.Name == branch.Name {
+ localDeleteItem = &types.MenuItem{
+ Label: self.c.Tr.DeleteLocalBranch,
+ Key: 'c',
+ Tooltip: self.c.Tr.CantDeleteCheckOutBranch,
+ OnPress: func() error {
+ return self.c.ErrorMsg(self.c.Tr.CantDeleteCheckOutBranch)
+ },
+ }
+ }
+ menuItems = append(menuItems, localDeleteItem)
+
+ if branch.IsTrackingRemote() && !branch.UpstreamGone {
+ menuItems = append(menuItems, &types.MenuItem{
+ Label: self.c.Tr.DeleteRemoteBranch,
+ Key: 'r',
+ OnPress: func() error {
+ return self.remoteDelete(branch)
+ },
+ })
+ }
+
+ menuTitle := utils.ResolvePlaceholderString(
+ self.c.Tr.DeleteBranchTitle,
+ map[string]string{
+ "selectedBranchName": branch.Name,
+ },
+ )
+
+ return self.c.Menu(types.CreateMenuOptions{
+ Title: menuTitle,
+ Items: menuItems,
+ })
+}
+
func (self *BranchesController) merge() error {
selectedBranchName := self.context().GetSelected().Name
return self.c.Helpers().MergeAndRebase.MergeRefIntoCheckedOutBranch(selectedBranchName)
diff --git a/pkg/gui/controllers/helpers/branches_helper.go b/pkg/gui/controllers/helpers/branches_helper.go
new file mode 100644
index 000000000..6bc336e8e
--- /dev/null
+++ b/pkg/gui/controllers/helpers/branches_helper.go
@@ -0,0 +1,46 @@
+package helpers
+
+import (
+ "github.com/jesseduffield/gocui"
+ "github.com/jesseduffield/lazygit/pkg/gui/types"
+ "github.com/jesseduffield/lazygit/pkg/utils"
+)
+
+type BranchesHelper struct {
+ c *HelperCommon
+}
+
+func NewBranchesHelper(c *HelperCommon) *BranchesHelper {
+ return &BranchesHelper{
+ c: c,
+ }
+}
+
+func (self *BranchesHelper) ConfirmDeleteRemote(remoteName string, branchName string) error {
+ title := utils.ResolvePlaceholderString(
+ self.c.Tr.DeleteBranchTitle,
+ map[string]string{
+ "selectedBranchName": branchName,
+ },
+ )
+ prompt := utils.ResolvePlaceholderString(
+ self.c.Tr.DeleteRemoteBranchPrompt,
+ map[string]string{
+ "selectedBranchName": branchName,
+ "upstream": remoteName,
+ },
+ )
+ return self.c.Confirm(types.ConfirmOpts{
+ Title: title,
+ Prompt: prompt,
+ HandleConfirm: func() error {
+ return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func(task gocui.Task) error {
+ self.c.LogAction(self.c.Tr.Actions.DeleteRemoteBranch)
+ if err := self.c.Git().Remote.DeleteRemoteBranch(task, remoteName, branchName); err != nil {
+ return self.c.Error(err)
+ }
+ return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.BRANCHES, types.REMOTES}})
+ })
+ },
+ })
+}
diff --git a/pkg/gui/controllers/helpers/helpers.go b/pkg/gui/controllers/helpers/helpers.go
index e87b57eb0..22a7ea91b 100644
--- a/pkg/gui/controllers/helpers/helpers.go
+++ b/pkg/gui/controllers/helpers/helpers.go
@@ -22,6 +22,7 @@ type Helpers struct {
Suggestions *SuggestionsHelper
Files *FilesHelper
WorkingTree *WorkingTreeHelper
+ BranchesHelper *BranchesHelper
Tags *TagsHelper
MergeAndRebase *MergeAndRebaseHelper
MergeConflicts *MergeConflictsHelper
diff --git a/pkg/gui/controllers/remote_branches_controller.go b/pkg/gui/controllers/remote_branches_controller.go
index 529b00a90..ffb55d5ca 100644
--- a/pkg/gui/controllers/remote_branches_controller.go
+++ b/pkg/gui/controllers/remote_branches_controller.go
@@ -1,10 +1,8 @@
package controllers
import (
- "fmt"
"strings"
- "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@@ -53,7 +51,7 @@ func (self *RemoteBranchesController) GetKeybindings(opts types.KeybindingsOpts)
{
Key: opts.GetKey(opts.Config.Universal.Remove),
Handler: self.checkSelected(self.delete),
- Description: self.c.Tr.DeleteBranch,
+ Description: self.c.Tr.DeleteRemoteTag,
},
{
Key: opts.GetKey(opts.Config.Branches.SetUpstream),
@@ -112,23 +110,7 @@ func (self *RemoteBranchesController) checkSelected(callback func(*models.Remote
}
func (self *RemoteBranchesController) delete(selectedBranch *models.RemoteBranch) error {
- message := fmt.Sprintf("%s '%s'?", self.c.Tr.DeleteRemoteBranchMessage, selectedBranch.FullName())
-
- return self.c.Confirm(types.ConfirmOpts{
- Title: self.c.Tr.DeleteRemoteBranch,
- Prompt: message,
- HandleConfirm: func() error {
- return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func(task gocui.Task) error {
- self.c.LogAction(self.c.Tr.Actions.DeleteRemoteBranch)
- err := self.c.Git().Remote.DeleteRemoteBranch(task, selectedBranch.RemoteName, selectedBranch.Name)
- if err != nil {
- _ = self.c.Error(err)
- }
-
- return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.REMOTES}})
- })
- },
- })
+ return self.c.Helpers().BranchesHelper.ConfirmDeleteRemote(selectedBranch.RemoteName, selectedBranch.Name)
}
func (self *RemoteBranchesController) merge(selectedBranch *models.RemoteBranch) error {
diff --git a/pkg/gui/controllers/tags_controller.go b/pkg/gui/controllers/tags_controller.go
index 80248391e..91d590c32 100644
--- a/pkg/gui/controllers/tags_controller.go
+++ b/pkg/gui/controllers/tags_controller.go
@@ -34,7 +34,8 @@ func (self *TagsController) GetKeybindings(opts types.KeybindingsOpts) []*types.
{
Key: opts.GetKey(opts.Config.Universal.Remove),
Handler: self.withSelectedTag(self.delete),
- Description: self.c.Tr.DeleteTag,
+ Description: self.c.Tr.ViewDeleteOptions,
+ OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Branches.PushTag),
@@ -88,24 +89,90 @@ func (self *TagsController) checkout(tag *models.Tag) error {
return self.c.PushContext(self.c.Contexts().Branches)
}
+func (self *TagsController) localDelete(tag *models.Tag) error {
+ return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func(gocui.Task) error {
+ self.c.LogAction(self.c.Tr.Actions.DeleteLocalTag)
+ if err := self.c.Git().Tag.LocalDelete(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 *TagsController) remoteDelete(tag *models.Tag) error {
+ title := utils.ResolvePlaceholderString(
+ self.c.Tr.SelectRemoteTagUpstream,
+ map[string]string{
+ "tagName": tag.Name,
+ },
+ )
+
+ return self.c.Prompt(types.PromptOpts{
+ Title: title,
+ InitialContent: "origin",
+ FindSuggestionsFunc: self.c.Helpers().Suggestions.GetRemoteSuggestionsFunc(),
+ HandleConfirm: func(upstream string) error {
+ confirmTitle := utils.ResolvePlaceholderString(
+ self.c.Tr.DeleteTagTitle,
+ map[string]string{
+ "tagName": tag.Name,
+ },
+ )
+ confirmPrompt := utils.ResolvePlaceholderString(
+ self.c.Tr.DeleteRemoteTagPrompt,
+ map[string]string{
+ "tagName": tag.Name,
+ "upstream": upstream,
+ },
+ )
+
+ return self.c.Confirm(types.ConfirmOpts{
+ Title: confirmTitle,
+ Prompt: confirmPrompt,
+ HandleConfirm: func() error {
+ return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func(t gocui.Task) error {
+ self.c.LogAction(self.c.Tr.Actions.DeleteRemoteTag)
+ if err := self.c.Git().Remote.DeleteRemoteTag(t, upstream, tag.Name); err != nil {
+ return self.c.Error(err)
+ }
+ self.c.Toast(self.c.Tr.RemoteTagDeletedMessage)
+ return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.COMMITS, types.TAGS}})
+ })
+ },
+ })
+ },
+ })
+}
+
func (self *TagsController) delete(tag *models.Tag) error {
- prompt := utils.ResolvePlaceholderString(
- self.c.Tr.DeleteTagPrompt,
+ menuTitle := utils.ResolvePlaceholderString(
+ self.c.Tr.DeleteTagTitle,
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.c.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}})
+ menuItems := []*types.MenuItem{
+ {
+ Label: self.c.Tr.DeleteLocalTag,
+ Key: 'c',
+ OnPress: func() error {
+ return self.localDelete(tag)
+ },
},
+ {
+ Label: self.c.Tr.DeleteRemoteTag,
+ Key: 'r',
+ OpensMenu: true,
+ OnPress: func() error {
+ return self.remoteDelete(tag)
+ },
+ },
+ }
+
+ return self.c.Menu(types.CreateMenuOptions{
+ Title: menuTitle,
+ Items: menuItems,
})
}