1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-02-09 13:47:11 +02:00

Allow deleting remote tags/branches from local tag/branch views (#2738)

This commit is contained in:
Federico 2023-08-10 09:39:26 +02:00 committed by GitHub
parent c43830b027
commit 0df5cb1286
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 487 additions and 158 deletions

View File

@ -148,7 +148,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;c-y&gt;</kbd>: Copy pull request URL to clipboard
<kbd>c</kbd>: Checkout by name
<kbd>F</kbd>: Force checkout
<kbd>d</kbd>: Delete branch
<kbd>d</kbd>: View delete options
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>M</kbd>: Merge into currently checked out branch
<kbd>f</kbd>: Fast-forward this branch from its upstream
@ -255,7 +255,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>n</kbd>: New branch
<kbd>M</kbd>: Merge into currently checked out branch
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>d</kbd>: Delete branch
<kbd>d</kbd>: Delete remote tag
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: View reset options
<kbd>w</kbd>: View worktree options
@ -332,7 +332,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;space&gt;</kbd>: Checkout
<kbd>d</kbd>: Delete tag
<kbd>d</kbd>: View delete options
<kbd>P</kbd>: Push tag
<kbd>n</kbd>: Create tag
<kbd>g</kbd>: View reset options

View File

@ -172,7 +172,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;space&gt;</kbd>: チェックアウト
<kbd>d</kbd>: タグを削除
<kbd>d</kbd>: View delete options
<kbd>P</kbd>: タグをpush
<kbd>n</kbd>: タグを作成
<kbd>g</kbd>: View reset options
@ -220,7 +220,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;c-y&gt;</kbd>: Pull RequestのURLをクリップボードにコピー
<kbd>c</kbd>: Checkout by name
<kbd>F</kbd>: Force checkout
<kbd>d</kbd>: ブランチを削除
<kbd>d</kbd>: View delete options
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>M</kbd>: 現在のブランチにマージ
<kbd>f</kbd>: Fast-forward this branch from its upstream
@ -320,7 +320,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>n</kbd>: 新しいブランチを作成
<kbd>M</kbd>: 現在のブランチにマージ
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>d</kbd>: ブランチを削除
<kbd>d</kbd>: Delete remote tag
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: View reset options
<kbd>w</kbd>: View worktree options

View File

@ -183,7 +183,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;c-y&gt;</kbd>: 풀 리퀘스트 URL을 클립보드에 복사
<kbd>c</kbd>: 이름으로 체크아웃
<kbd>F</kbd>: 강제 체크아웃
<kbd>d</kbd>: 브랜치 삭제
<kbd>d</kbd>: View delete options
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
<kbd>M</kbd>: 현재 브랜치에 병합
<kbd>f</kbd>: Fast-forward this branch from its upstream
@ -239,7 +239,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>n</kbd>: 새 브랜치 생성
<kbd>M</kbd>: 현재 브랜치에 병합
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
<kbd>d</kbd>: 브랜치 삭제
<kbd>d</kbd>: Delete remote tag
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: View reset options
<kbd>w</kbd>: View worktree options
@ -309,7 +309,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;space&gt;</kbd>: 체크아웃
<kbd>d</kbd>: 태그 삭제
<kbd>d</kbd>: View delete options
<kbd>P</kbd>: 태그를 push
<kbd>n</kbd>: 태그를 생성
<kbd>g</kbd>: View reset options

View File

@ -90,7 +90,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;c-y&gt;</kbd>: Kopieer de URL van het pull-verzoek naar het klembord
<kbd>c</kbd>: Uitchecken bij naam
<kbd>F</kbd>: Forceer checkout
<kbd>d</kbd>: Verwijder branch
<kbd>d</kbd>: View delete options
<kbd>r</kbd>: Rebase branch
<kbd>M</kbd>: Merge in met huidige checked out branch
<kbd>f</kbd>: Fast-forward deze branch vanaf zijn upstream
@ -233,7 +233,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>n</kbd>: Nieuwe branch
<kbd>M</kbd>: Merge in met huidige checked out branch
<kbd>r</kbd>: Rebase branch
<kbd>d</kbd>: Verwijder branch
<kbd>d</kbd>: Delete remote tag
<kbd>u</kbd>: Stel in als upstream van uitgecheckte branch
<kbd>g</kbd>: Bekijk reset opties
<kbd>w</kbd>: View worktree options
@ -332,7 +332,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;space&gt;</kbd>: Uitchecken
<kbd>d</kbd>: Verwijder tag
<kbd>d</kbd>: View delete options
<kbd>P</kbd>: Push tag
<kbd>n</kbd>: Creëer tag
<kbd>g</kbd>: Bekijk reset opties

View File

@ -106,7 +106,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;c-y&gt;</kbd>: Skopiuj adres URL żądania pobrania do schowka
<kbd>c</kbd>: Przełącz używając nazwy
<kbd>F</kbd>: Wymuś przełączenie
<kbd>d</kbd>: Usuń gałąź
<kbd>d</kbd>: View delete options
<kbd>r</kbd>: Zmiana bazy gałęzi
<kbd>M</kbd>: Scal do obecnej gałęzi
<kbd>f</kbd>: Fast-forward this branch from its upstream
@ -232,7 +232,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>n</kbd>: Nowa gałąź
<kbd>M</kbd>: Scal do obecnej gałęzi
<kbd>r</kbd>: Zmiana bazy gałęzi
<kbd>d</kbd>: Usuń gałąź
<kbd>d</kbd>: Delete remote tag
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>w</kbd>: View worktree options
@ -325,7 +325,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;space&gt;</kbd>: Przełącz
<kbd>d</kbd>: Delete tag
<kbd>d</kbd>: View delete options
<kbd>P</kbd>: Push tag
<kbd>n</kbd>: Create tag
<kbd>g</kbd>: Wyświetl opcje resetu

View File

@ -181,7 +181,7 @@ _Связки клавиш_
<kbd>&lt;c-y&gt;</kbd>: Скопировать URL запроса на принятие изменений в буфер обмена
<kbd>c</kbd>: Переключить по названию
<kbd>F</kbd>: Принудительное переключение
<kbd>d</kbd>: Удалить ветку
<kbd>d</kbd>: View delete options
<kbd>r</kbd>: Перебазировать переключённую ветку на эту ветку
<kbd>M</kbd>: Слияние с текущей переключённой веткой
<kbd>f</kbd>: Перемотать эту ветку вперёд из её upstream-ветки
@ -277,7 +277,7 @@ _Связки клавиш_
<pre>
<kbd>&lt;space&gt;</kbd>: Переключить
<kbd>d</kbd>: Удалить тег
<kbd>d</kbd>: View delete options
<kbd>P</kbd>: Отправить тег
<kbd>n</kbd>: Создать тег
<kbd>g</kbd>: Просмотреть параметры сброса
@ -294,7 +294,7 @@ _Связки клавиш_
<kbd>n</kbd>: Новая ветка
<kbd>M</kbd>: Слияние с текущей переключённой веткой
<kbd>r</kbd>: Перебазировать переключённую ветку на эту ветку
<kbd>d</kbd>: Удалить ветку
<kbd>d</kbd>: Delete remote tag
<kbd>u</kbd>: Установить как upstream-ветку переключённую ветку
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>w</kbd>: View worktree options

View File

@ -84,7 +84,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;c-y&gt;</kbd>: 将抓取请求 URL 复制到剪贴板
<kbd>c</kbd>: 按名称检出
<kbd>F</kbd>: 强制检出
<kbd>d</kbd>: 删除分支
<kbd>d</kbd>: View delete options
<kbd>r</kbd>: 将已检出的分支变基到该分支
<kbd>M</kbd>: 合并到当前检出的分支
<kbd>f</kbd>: 从上游快进此分支
@ -234,7 +234,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;space&gt;</kbd>: 检出
<kbd>d</kbd>: 删除标签
<kbd>d</kbd>: View delete options
<kbd>P</kbd>: 推送标签
<kbd>n</kbd>: 创建标签
<kbd>g</kbd>: 查看重置选项
@ -334,7 +334,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>n</kbd>: 新分支
<kbd>M</kbd>: 合并到当前检出的分支
<kbd>r</kbd>: 将已检出的分支变基到该分支
<kbd>d</kbd>: 删除分支
<kbd>d</kbd>: Delete remote tag
<kbd>u</kbd>: 设置为检出分支的上游
<kbd>g</kbd>: 查看重置选项
<kbd>w</kbd>: View worktree options

View File

@ -256,7 +256,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
<kbd>&lt;c-y&gt;</kbd>: 複製拉取請求的 URL 到剪貼板
<kbd>c</kbd>: 根據名稱檢出
<kbd>F</kbd>: 強制檢出
<kbd>d</kbd>: 刪除分支
<kbd>d</kbd>: View delete options
<kbd>r</kbd>: 將已檢出的分支變基至此分支
<kbd>M</kbd>: 合併到當前檢出的分支
<kbd>f</kbd>: 從上游快進此分支
@ -273,7 +273,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
<pre>
<kbd>&lt;space&gt;</kbd>: 檢出
<kbd>d</kbd>: 刪除標籤
<kbd>d</kbd>: View delete options
<kbd>P</kbd>: 推送標籤
<kbd>n</kbd>: 建立標籤
<kbd>g</kbd>: 檢視重設選項
@ -344,7 +344,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
<kbd>n</kbd>: 新分支
<kbd>M</kbd>: 合併到當前檢出的分支
<kbd>r</kbd>: 將已檢出的分支變基至此分支
<kbd>d</kbd>: 刪除分支
<kbd>d</kbd>: Delete remote tag
<kbd>u</kbd>: 將此分支設為當前分支之上游
<kbd>g</kbd>: 檢視重設選項
<kbd>w</kbd>: View worktree options

View File

@ -85,8 +85,8 @@ func (self *BranchCommands) CurrentBranchName() (string, error) {
return "", err
}
// Delete delete branch
func (self *BranchCommands) Delete(branch string, force bool) error {
// LocalDelete delete branch locally
func (self *BranchCommands) LocalDelete(branch string, force bool) error {
cmdArgs := NewGitCmd("branch").
ArgIfElse(force, "-D", "-d").
Arg(branch).

View File

@ -93,7 +93,7 @@ func TestBranchDeleteBranch(t *testing.T) {
t.Run(s.testName, func(t *testing.T) {
instance := buildBranchCommands(commonDeps{runner: s.runner})
s.test(instance.Delete("test", s.force))
s.test(instance.LocalDelete("test", s.force))
s.runner.CheckForMissingCalls()
})
}

View File

@ -56,6 +56,14 @@ func (self *RemoteCommands) DeleteRemoteBranch(task gocui.Task, remoteName strin
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
}
func (self *RemoteCommands) DeleteRemoteTag(task gocui.Task, remoteName string, tagName string) error {
cmdArgs := NewGitCmd("push").
Arg(remoteName, "--delete", tagName).
ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
}
// CheckRemoteBranchExists Returns remote branch
func (self *RemoteCommands) CheckRemoteBranchExists(branchName string) bool {
cmdArgs := NewGitCmd("show-ref").

View File

@ -41,7 +41,7 @@ func (self *TagCommands) HasTag(tagName string) bool {
return self.cmd.New(cmdArgs).Run() == nil
}
func (self *TagCommands) Delete(tagName string) error {
func (self *TagCommands) LocalDelete(tagName string) error {
cmdArgs := NewGitCmd("tag").Arg("-d", tagName).
ToArgv()

View File

@ -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,

View File

@ -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)

View File

@ -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}})
})
},
})
}

View File

@ -22,6 +22,7 @@ type Helpers struct {
Suggestions *SuggestionsHelper
Files *FilesHelper
WorkingTree *WorkingTreeHelper
BranchesHelper *BranchesHelper
Tags *TagsHelper
MergeAndRebase *MergeAndRebaseHelper
MergeConflicts *MergeConflictsHelper

View File

@ -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 {

View File

@ -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,27 +89,93 @@ func (self *TagsController) checkout(tag *models.Tag) error {
return self.c.PushContext(self.c.Contexts().Branches)
}
func (self *TagsController) delete(tag *models.Tag) error {
prompt := utils.ResolvePlaceholderString(
self.c.Tr.DeleteTagPrompt,
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.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}})
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 {
menuTitle := utils.ResolvePlaceholderString(
self.c.Tr.DeleteTagTitle,
map[string]string{
"tagName": tag.Name,
},
)
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,
})
}
func (self *TagsController) push(tag *models.Tag) error {
title := utils.ResolvePlaceholderString(
self.c.Tr.PushTagTitle,

View File

@ -82,8 +82,6 @@ func chineseTranslationSet() TranslationSet {
BranchName: "分支名称",
NewBranchNameBranchOff: "新分支名称(基于 {{.branchName}})",
CantDeleteCheckOutBranch: "您不能删除已检出的分支!",
DeleteBranch: "删除分支",
DeleteBranchMessage: "您确定要删除分支 {{.selectedBranchName}} 吗?",
ForceDeleteBranchMessage: "{{.selectedBranchName}} 还没有被完全合并。您确定要删除它吗?",
RebaseBranch: "将已检出的分支变基到该分支",
CantRebaseOntoSelf: "您不能将分支变基到其自身",
@ -309,9 +307,6 @@ func chineseTranslationSet() TranslationSet {
TagMessageTitle: "标签消息",
AnnotatedTag: "附注标签",
LightweightTag: "轻量标签",
DeleteTag: "删除标签",
DeleteTagTitle: "删除标签",
DeleteTagPrompt: "您确定要删除标签 {{.tagName}} 吗?",
PushTagTitle: "将 {{.tagName}} 推送到远程仓库:",
PushTag: "推送标签",
CreateTag: "创建标签",
@ -457,7 +452,6 @@ func chineseTranslationSet() TranslationSet {
CheckoutTag: "检出标签",
CheckoutBranch: "检出分支",
ForceCheckoutBranch: "强制检出分支",
DeleteBranch: "删除分支",
Merge: "合并",
RebaseBranch: "变基分支",
RenameBranch: "重命名分支",
@ -521,7 +515,6 @@ func chineseTranslationSet() TranslationSet {
BulkUpdateSubmodules: "批量更新子模块",
BulkDeinitialiseSubmodules: "批量取消初始化子模块",
UpdateSubmodule: "更新子模块",
DeleteTag: "删除标签",
PushTag: "推送标签",
NukeWorkingTree: "Nuke 工作树",
DiscardUnstagedFileChanges: "放弃未暂存的文件更改",

View File

@ -48,8 +48,6 @@ func dutchTranslationSet() TranslationSet {
BranchName: "Branch naam",
NewBranchNameBranchOff: "Nieuw branch naam (Branch is afgeleid van '{{.branchName}}')",
CantDeleteCheckOutBranch: "Je kan een uitgecheckte branch niet verwijderen!",
DeleteBranch: "Verwijder branch",
DeleteBranchMessage: "Weet je zeker dat je branch '{{.selectedBranchName}}' wilt verwijderen?",
ForceDeleteBranchMessage: "Weet je zeker dat je branch '{{.selectedBranchName}}' geforceerd wil verwijderen?",
RebaseBranch: "Rebase branch",
CantRebaseOntoSelf: "Je kan niet een branch rebasen op zichzelf",
@ -263,10 +261,7 @@ func dutchTranslationSet() TranslationSet {
SetUpstreamMessage: "Weet je zeker dat je de upstream branch van '{{.checkedOut}}' naar '{{.selected}}' wilt zetten",
EditRemote: "Wijzig remote",
TagCommit: "Tag commit",
TagNameTitle: "Tag naam",
DeleteTag: "Verwijder tag",
DeleteTagTitle: "Verwijder tag",
DeleteTagPrompt: "Weet je zeker dat je '{{.tagName}}' wil verwijderen?",
TagNameTitle: "Tag naam:",
PushTagTitle: "Remote om tag '{{.tagName}}' te pushen naar:",
PushTag: "Push tag",
CreateTag: "Creëer tag",

View File

@ -68,8 +68,11 @@ type TranslationSet struct {
BranchName string
NewBranchNameBranchOff string
CantDeleteCheckOutBranch string
DeleteBranch string
DeleteBranchMessage string
DeleteBranchTitle string
DeleteLocalBranch string
DeleteRemoteBranchOption string
DeleteRemoteBranchPrompt string
ForceDeleteBranchTitle string
ForceDeleteBranchMessage string
RebaseBranch string
CantRebaseOntoSelf string
@ -292,6 +295,7 @@ type TranslationSet struct {
DiscardUntrackedFiles string
DiscardStagedChanges string
HardReset string
ViewDeleteOptions string
ViewResetOptions string
CreateFixupCommit string
CreateFixupCommitDescription string
@ -352,9 +356,12 @@ type TranslationSet struct {
TagMessageTitle string
LightweightTag string
AnnotatedTag string
DeleteTag string
DeleteTagTitle string
DeleteTagPrompt string
DeleteLocalTag string
DeleteRemoteTag string
SelectRemoteTagUpstream string
DeleteRemoteTagPrompt string
RemoteTagDeletedMessage string
PushTagTitle string
PushTag string
CreateTag string
@ -633,6 +640,7 @@ type Actions struct {
CheckoutTag string
CheckoutBranch string
ForceCheckoutBranch string
DeleteLocalBranch string
DeleteBranch string
Merge string
RebaseBranch string
@ -715,7 +723,8 @@ type Actions struct {
UpdateSubmodule string
CreateLightweightTag string
CreateAnnotatedTag string
DeleteTag string
DeleteLocalTag string
DeleteRemoteTag string
PushTag string
NukeWorkingTree string
DiscardUnstagedFileChanges string
@ -829,8 +838,11 @@ func EnglishTranslationSet() TranslationSet {
BranchName: "Branch name",
NewBranchNameBranchOff: "New branch name (branch is off of '{{.branchName}}')",
CantDeleteCheckOutBranch: "You cannot delete the checked out branch!",
DeleteBranch: "Delete branch",
DeleteBranchMessage: "Are you sure you want to delete the branch '{{.selectedBranchName}}'?",
DeleteBranchTitle: "Delete branch '{{.selectedBranchName}}'?",
DeleteLocalBranch: "Delete local branch",
DeleteRemoteBranchOption: "Delete remote branch",
DeleteRemoteBranchPrompt: "Are you sure you want to delete the remote branch '{{.selectedBranchName}}' from '{{.upstream}}'?",
ForceDeleteBranchTitle: "Force delete branch",
ForceDeleteBranchMessage: "'{{.selectedBranchName}}' is not fully merged. Are you sure you want to delete it?",
RebaseBranch: "Rebase checked-out branch onto this branch",
CantRebaseOntoSelf: "You cannot rebase a branch onto itself",
@ -1056,6 +1068,7 @@ func EnglishTranslationSet() TranslationSet {
DiscardUntrackedFiles: "Discard untracked files",
DiscardStagedChanges: "Discard staged changes",
HardReset: "Hard reset",
ViewDeleteOptions: "View delete options",
ViewResetOptions: `View reset options`,
CreateFixupCommitDescription: `Create fixup commit for this commit`,
SquashAboveCommits: `Squash all 'fixup!' commits above selected commit (autosquash)`,
@ -1102,7 +1115,7 @@ func EnglishTranslationSet() TranslationSet {
EditRemoteUrl: `Enter updated remote url for {{.remoteName}}:`,
RemoveRemote: `Remove remote`,
RemoveRemotePrompt: "Are you sure you want to remove remote",
DeleteRemoteBranch: "Delete Remote Branch",
DeleteRemoteBranch: "Delete remote branch",
DeleteRemoteBranchMessage: "Are you sure you want to delete remote branch",
SetAsUpstream: "Set as upstream of checked-out branch",
SetUpstream: "Set upstream of selected branch",
@ -1116,9 +1129,12 @@ func EnglishTranslationSet() TranslationSet {
TagMessageTitle: "Tag description",
AnnotatedTag: "Annotated tag",
LightweightTag: "Lightweight tag",
DeleteTag: "Delete tag",
DeleteTagTitle: "Delete tag",
DeleteTagPrompt: "Are you sure you want to delete tag '{{.tagName}}'?",
DeleteTagTitle: "Delete tag '{{.tagName}}'?",
DeleteLocalTag: "Delete local tag",
DeleteRemoteTag: "Delete remote tag",
RemoteTagDeletedMessage: "Remote tag deleted",
SelectRemoteTagUpstream: "Remote from which to remove tag '{{.tagName}}':",
DeleteRemoteTagPrompt: "Are you sure you want to delete the remote tag '{{.tagName}}' from '{{.upstream}}'?",
PushTagTitle: "Remote to push tag '{{.tagName}}' to:",
PushTag: "Push tag",
CreateTag: "Create tag",
@ -1355,6 +1371,7 @@ func EnglishTranslationSet() TranslationSet {
CheckoutTag: "Checkout tag",
CheckoutBranch: "Checkout branch",
ForceCheckoutBranch: "Force checkout branch",
DeleteLocalBranch: "Delete local branch",
DeleteBranch: "Delete branch",
Merge: "Merge",
RebaseBranch: "Rebase branch",
@ -1436,7 +1453,8 @@ func EnglishTranslationSet() TranslationSet {
BulkUpdateSubmodules: "Bulk update submodules",
BulkDeinitialiseSubmodules: "Bulk deinitialise submodules",
UpdateSubmodule: "Update submodule",
DeleteTag: "Delete tag",
DeleteLocalTag: "Delete local tag",
DeleteRemoteTag: "Delete remote tag",
PushTag: "Push tag",
NukeWorkingTree: "Nuke working tree",
DiscardUnstagedFileChanges: "Discard unstaged file changes",

View File

@ -73,9 +73,6 @@ func japaneseTranslationSet() TranslationSet {
BranchName: "ブランチ名",
NewBranchNameBranchOff: "新規ブランチ名 ('{{.branchName}}' に作成)",
CantDeleteCheckOutBranch: "チェックアウト中のブランチは削除できません!",
DeleteBranch: "ブランチを削除",
DeleteBranchMessage: "ブランチ '{{.selectedBranchName}}' を削除します。よろしいですか?",
ForceDeleteBranchMessage: "'{{.selectedBranchName}}' はマージされていません。本当に削除しますか?",
// LcRebaseBranch: "Rebase checked-out branch onto this branch",
CantRebaseOntoSelf: "ブランチを自分自身にリベースすることはできません。",
CantMergeBranchIntoItself: "ブランチを自分自身にマージすることはできません。",
@ -319,9 +316,6 @@ func japaneseTranslationSet() TranslationSet {
TagMessageTitle: "タグメッセージ",
AnnotatedTag: "注釈付きタグ",
LightweightTag: "軽量タグ",
DeleteTag: "タグを削除",
DeleteTagTitle: "タグを削除",
DeleteTagPrompt: "タグ '{{.tagName}}' を削除します。よろしいですか?",
PushTagTitle: "リモートにタグ '{{.tagName}}' をpush",
PushTag: "タグをpush",
CreateTag: "タグを作成",
@ -554,7 +548,6 @@ func japaneseTranslationSet() TranslationSet {
BulkUpdateSubmodules: "サブモジュールを一括更新",
// BulkDeinitialiseSubmodules: "Bulk deinitialise submodules",
UpdateSubmodule: "サブモジュールを更新",
DeleteTag: "タグを削除",
PushTag: "タグをpush",
// NukeWorkingTree: "Nuke working tree",
// DiscardUnstagedFileChanges: "Discard unstaged file changes",

View File

@ -72,8 +72,6 @@ func koreanTranslationSet() TranslationSet {
BranchName: "브랜치 이름",
NewBranchNameBranchOff: "새 브랜치 이름 (branch is off of '{{.branchName}}')",
CantDeleteCheckOutBranch: "체크아웃하는 브랜치는 삭제할 수 없습니다!",
DeleteBranch: "브랜치 삭제",
DeleteBranchMessage: "정말로 브랜치 '{{.selectedBranchName}}' 를 삭제하시겠습니까?",
ForceDeleteBranchMessage: "'{{.selectedBranchName}}'는 완전히 병합되지 않았습니다. 정말 삭제하시겠습니까?",
RebaseBranch: "체크아웃된 브랜치를 이 브랜치에 리베이스",
CantRebaseOntoSelf: "브랜치를 자기 자신에게 리베이스할 수는 없습니다.",
@ -314,9 +312,6 @@ func koreanTranslationSet() TranslationSet {
TagMessageTitle: "태그 메시지",
AnnotatedTag: "Annotated tag",
LightweightTag: "Lightweight tag",
DeleteTag: "태그 삭제",
DeleteTagTitle: "태그 삭제",
DeleteTagPrompt: "정말로 태그 '{{.tagName}}' 를 삭제하시겠습니까?",
PushTagTitle: "원격에 태그 '{{.tagName}}' 를 푸시",
PushTag: "태그를 push",
CreateTag: "태그를 생성",
@ -478,7 +473,6 @@ func koreanTranslationSet() TranslationSet {
CheckoutTag: "태그 체크아웃",
CheckoutBranch: "브랜치 체크아웃",
ForceCheckoutBranch: "브랜치 Force 체크아웃",
DeleteBranch: "브랜치 삭제",
Merge: "병합",
RebaseBranch: "브랜치 리베이스",
RenameBranch: "브랜치 이름 변경",
@ -552,7 +546,6 @@ func koreanTranslationSet() TranslationSet {
BulkUpdateSubmodules: "Bulk update submodules",
BulkDeinitialiseSubmodules: "Bulk deinitialise submodules",
UpdateSubmodule: "서브모듈 업데이트",
DeleteTag: "태그 삭제",
PushTag: "태그 푸시g",
NukeWorkingTree: "Nuke working tree",
DiscardUnstagedFileChanges: "Unstaged 파일 변경사항 버리기",

View File

@ -43,8 +43,6 @@ func polishTranslationSet() TranslationSet {
BranchName: "Nazwa gałęzi",
NewBranchNameBranchOff: "Nazwa nowej gałęzi (gałąź na bazie '{{.branchName}}')",
CantDeleteCheckOutBranch: "Nie możesz usunąć obecnie przełączonej gałęzi!",
DeleteBranch: "Usuń gałąź",
DeleteBranchMessage: "Jesteś pewien, że chcesz usunąć gałąź '{{.selectedBranchName}}' ?",
ForceDeleteBranchMessage: "Na pewno wymusić usunięcie gałęzi '{{.selectedBranchName}}'?",
RebaseBranch: "Zmiana bazy gałęzi",
CantRebaseOntoSelf: "Nie możesz zmienić bazy gałęzi na nią samą",

View File

@ -91,8 +91,6 @@ func RussianTranslationSet() TranslationSet {
BranchName: "Название ветки",
NewBranchNameBranchOff: "Название новой ветки (Ветка с '{{.branchName}}')",
CantDeleteCheckOutBranch: "Невозможно удалить переключённую ветку!",
DeleteBranch: "Удалить ветку",
DeleteBranchMessage: "Вы уверены, что хотите удалить ветку '{{.selectedBranchName}}'?",
ForceDeleteBranchMessage: "'{{.selectedBranchName}}' не полностью слилась. Вы уверены, что хотите удалить его?",
RebaseBranch: "Перебазировать переключённую ветку на эту ветку",
CantRebaseOntoSelf: "Невозможно перебазировать ветку на себя",
@ -375,9 +373,7 @@ func RussianTranslationSet() TranslationSet {
TagMessageTitle: "Сообщения тега",
AnnotatedTag: "Аннотированный тег",
LightweightTag: "Легковесный тег",
DeleteTag: "Удалить тег",
DeleteTagTitle: "Удалить тег",
DeleteTagPrompt: "Вы уверены, что хотите удалить тег '{{.tagName}}'?",
PushTagTitle: "Удалённый репозитории для отправки тега '{{.tagName}}' в:",
PushTag: "Отправить тег",
CreateTag: "Создать тег",
@ -647,7 +643,6 @@ func RussianTranslationSet() TranslationSet {
BulkUpdateSubmodules: "Массовое обновление подмодулей",
BulkDeinitialiseSubmodules: "Массовая деинициализация подмодулей",
UpdateSubmodule: "Обновить подмодуль",
DeleteTag: "Удалить тег",
PushTag: "Отправить тег",
NukeWorkingTree: "Уничтожить рабочее дерево",
DiscardUnstagedFileChanges: "Отменить непроиндексированные изменения файла",

View File

@ -124,8 +124,6 @@ func traditionalChineseTranslationSet() TranslationSet {
BranchName: "分支名稱",
NewBranchNameBranchOff: "新的分支名稱 (根據 '{{.branchName}}' 分支創建)",
CantDeleteCheckOutBranch: "你不能刪除已檢出的分支!",
DeleteBranch: "刪除分支",
DeleteBranchMessage: "你確定要刪除 '{{.selectedBranchName}}' 分支嗎?",
ForceDeleteBranchMessage: "'{{.selectedBranchName}}' 分支尚未完全合併。你確定要刪除嗎?",
RebaseBranch: "將已檢出的分支變基至此分支",
CantRebaseOntoSelf: "你不能將分支變基至自己",
@ -402,9 +400,6 @@ func traditionalChineseTranslationSet() TranslationSet {
TagMessageTitle: "標籤訊息",
AnnotatedTag: "附註標籤",
LightweightTag: "輕量標籤",
DeleteTag: "刪除標籤",
DeleteTagTitle: "刪除標籤",
DeleteTagPrompt: "你確定要刪除 '{{.tagName}}' 標籤嗎?",
PushTagTitle: "推送標籤 '{{.tagName}}' 至遠端:",
PushTag: "推送標籤",
CreateTag: "建立標籤",
@ -674,7 +669,6 @@ func traditionalChineseTranslationSet() TranslationSet {
BulkUpdateSubmodules: "批量更新子模塊",
BulkDeinitialiseSubmodules: "批量取消初始化子模塊",
UpdateSubmodule: "更新子模塊",
DeleteTag: "刪除標籤",
PushTag: "推送標籤",
NukeWorkingTree: "清空工作樹",
DiscardUnstagedFileChanges: "放棄未預存的檔案更改",

View File

@ -18,14 +18,28 @@ func (self *Git) TagNamesAt(ref string, expectedNames []string) *Git {
return self.assert([]string{"git", "tag", "--sort=v:refname", "--points-at", ref}, strings.Join(expectedNames, "\n"))
}
func (self *Git) RemoteTagDeleted(ref string, tagName string) *Git {
return self.expect([]string{"git", "ls-remote", ref, fmt.Sprintf("refs/tags/%s", tagName)}, func(s string) (bool, string) {
return len(s) == 0, fmt.Sprintf("Expected tag %s to have been removed from %s", tagName, ref)
})
}
func (self *Git) assert(cmdArgs []string, expected string) *Git {
self.expect(cmdArgs, func(output string) (bool, string) {
return output == expected, fmt.Sprintf("Expected current branch name to be '%s', but got '%s'", expected, output)
})
return self
}
func (self *Git) expect(cmdArgs []string, condition func(string) (bool, string)) *Git {
self.assertWithRetries(func() (bool, string) {
output, err := self.shell.runCommandWithOutput(cmdArgs)
if err != nil {
return false, fmt.Sprintf("Unexpected error running command: `%v`. Error: %s", cmdArgs, err.Error())
}
actual := strings.TrimSpace(output)
return actual == expected, fmt.Sprintf("Expected current branch name to be '%s', but got '%s'", expected, actual)
return condition(actual)
})
return self

View File

@ -66,6 +66,12 @@ func (self *MenuDriver) Wait(milliseconds int) *MenuDriver {
return self
}
func (self *MenuDriver) Tooltip(option *TextMatcher) *MenuDriver {
self.t.Views().Tooltip().Content(option)
return self
}
func (self *MenuDriver) checkNecessaryChecksCompleted() {
if !self.hasCheckedTitle {
self.t.Fail("You must check the title of a menu popup by calling Title() before calling Confirm()/Cancel().")

View File

@ -162,6 +162,10 @@ func (self *Shell) CreateAnnotatedTag(name string, message string, ref string) *
return self.RunCommand([]string{"git", "tag", "-a", name, "-m", message, ref})
}
func (self *Shell) PushBranch(upstream, branch string) *Shell {
return self.RunCommand([]string{"git", "push", "--set-upstream", upstream, branch})
}
// convenience method for creating a file and adding it
func (self *Shell) CreateFileAndAdd(fileName string, fileContents string) *Shell {
return self.

View File

@ -222,3 +222,7 @@ func (self *Views) Suggestions() *ViewDriver {
func (self *Views) Search() *ViewDriver {
return self.regularView("search")
}
func (self *Views) Tooltip() *ViewDriver {
return self.regularView("tooltip")
}

View File

@ -6,38 +6,110 @@ import (
)
var Delete = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Try to delete the checked out branch first (to no avail), and then delete another branch.",
Description: "Try all combination of local and remote branch deletions",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.
CloneIntoRemote("origin").
EmptyCommit("blah").
NewBranch("branch-one").
NewBranch("branch-two")
PushBranch("origin", "branch-one").
NewBranch("branch-two").
PushBranch("origin", "branch-two").
EmptyCommit("deletion blocker").
NewBranch("branch-three")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Branches().
Focus().
Lines(
MatchesRegexp(`\*.*branch-two`).IsSelected(),
MatchesRegexp(`\*.*branch-three`).IsSelected(),
MatchesRegexp(`branch-two`),
MatchesRegexp(`branch-one`),
MatchesRegexp(`master`),
).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().Alert().Title(Equals("Error")).Content(Contains("You cannot delete the checked out branch!")).Confirm()
t.ExpectPopup().
Menu().
Tooltip(Contains("You cannot delete the checked out branch!")).
Title(Equals("Delete branch 'branch-three'?")).
Select(Contains("Delete local branch")).
Confirm()
t.ExpectPopup().
Alert().
Title(Equals("Error")).
Content(Contains("You cannot delete the checked out branch!")).
Confirm()
}).
SelectNextItem().
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().Confirmation().
Title(Equals("Delete branch")).
Content(Contains("Are you sure you want to delete the branch 'branch-one'?")).
t.ExpectPopup().
Menu().
Title(Equals("Delete branch 'branch-two'?")).
Select(Contains("Delete local branch")).
Confirm()
}).
Tap(func() {
t.ExpectPopup().
Confirmation().
Title(Equals("Force delete branch")).
Content(Equals("'branch-two' is not fully merged. Are you sure you want to delete it?")).
Confirm()
}).
Lines(
MatchesRegexp(`\*.*branch-two`),
MatchesRegexp(`\*.*branch-three`),
MatchesRegexp(`branch-one`).IsSelected(),
MatchesRegexp(`master`),
).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().
Menu().
Title(Equals("Delete branch 'branch-one'?")).
Select(Contains("Delete remote branch")).
Confirm()
}).
Tap(func() {
t.ExpectPopup().
Confirmation().
Title(Equals("Delete branch 'branch-one'?")).
Content(Equals("Are you sure you want to delete the remote branch 'branch-one' from 'origin'?")).
Confirm()
}).
Tap(func() {
t.Views().Remotes().
Focus().
Lines(Contains("origin")).
PressEnter()
t.Views().
RemoteBranches().
Lines(Equals("branch-two")).
Press(keys.Universal.Return)
t.Views().
Branches().
Focus()
}).
Lines(
MatchesRegexp(`\*.*branch-three`),
MatchesRegexp(`branch-one \(upstream gone\)`).IsSelected(),
MatchesRegexp(`master`),
).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().
Menu().
Title(Equals("Delete branch 'branch-one'?")).
Select(Contains("Delete local branch")).
Confirm()
}).
Lines(
MatchesRegexp(`\*.*branch-three`),
MatchesRegexp(`master`).IsSelected(),
)
},

View File

@ -12,6 +12,7 @@ var CrudAnnotated = NewIntegrationTest(NewIntegrationTestArgs{
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.EmptyCommit("initial commit")
shell.CloneIntoRemote("origin")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Tags().
@ -31,11 +32,53 @@ var CrudAnnotated = NewIntegrationTest(NewIntegrationTestArgs{
Lines(
MatchesRegexp(`new-tag.*message`).IsSelected(),
).
Press(keys.Universal.Push).
Tap(func() {
t.ExpectPopup().Prompt().
Title(Equals("Remote to push tag 'new-tag' to:")).
InitialText(Equals("origin")).
SuggestionLines(
Contains("origin"),
).
Confirm()
}).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().Confirmation().
Title(Equals("Delete tag")).
Content(Equals("Are you sure you want to delete tag 'new-tag'?")).
t.ExpectPopup().
Menu().
Title(Equals("Delete tag 'new-tag'?")).
Select(Contains("Delete remote tag")).
Confirm()
}).
Tap(func() {
t.ExpectPopup().Prompt().
Title(Equals("Remote from which to remove tag 'new-tag':")).
InitialText(Equals("origin")).
SuggestionLines(
Contains("origin"),
).
Confirm()
}).
Tap(func() {
t.ExpectPopup().
Confirmation().
Title(Equals("Delete tag 'new-tag'?")).
Content(Equals("Are you sure you want to delete the remote tag 'new-tag' from 'origin'?")).
Confirm()
}).
Lines(
MatchesRegexp(`new-tag.*message`).IsSelected(),
).
Tap(func() {
t.Git().
RemoteTagDeleted("origin", "new-tag")
}).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().
Menu().
Title(Equals("Delete tag 'new-tag'?")).
Select(Contains("Delete local tag")).
Confirm()
}).
IsEmpty().

View File

@ -12,6 +12,7 @@ var CrudLightweight = NewIntegrationTest(NewIntegrationTestArgs{
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.EmptyCommit("initial commit")
shell.CloneIntoRemote("origin")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Tags().
@ -36,11 +37,53 @@ var CrudLightweight = NewIntegrationTest(NewIntegrationTestArgs{
).
PressEscape()
}).
Press(keys.Universal.Push).
Tap(func() {
t.ExpectPopup().Prompt().
Title(Equals("Remote to push tag 'new-tag' to:")).
InitialText(Equals("origin")).
SuggestionLines(
Contains("origin"),
).
Confirm()
}).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().Confirmation().
Title(Equals("Delete tag")).
Content(Equals("Are you sure you want to delete tag 'new-tag'?")).
t.ExpectPopup().
Menu().
Title(Equals("Delete tag 'new-tag'?")).
Select(Contains("Delete remote tag")).
Confirm()
}).
Tap(func() {
t.ExpectPopup().Prompt().
Title(Equals("Remote from which to remove tag 'new-tag':")).
InitialText(Equals("origin")).
SuggestionLines(
Contains("origin"),
).
Confirm()
}).
Tap(func() {
t.ExpectPopup().
Confirmation().
Title(Equals("Delete tag 'new-tag'?")).
Content(Equals("Are you sure you want to delete the remote tag 'new-tag' from 'origin'?")).
Confirm()
}).
Lines(
MatchesRegexp(`new-tag.*initial commit`).IsSelected(),
).
Tap(func() {
t.Git().
RemoteTagDeleted("origin", "new-tag")
}).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().
Menu().
Title(Equals("Delete tag 'new-tag'?")).
Select(Contains("Delete local tag")).
Confirm()
}).
IsEmpty()

View File

@ -27,6 +27,13 @@ var DetachWorktreeFromBranch = NewIntegrationTest(NewIntegrationTestArgs{
).
NavigateToLine(Contains("newbranch")).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().
Menu().
Title(Equals("Delete branch 'newbranch'?")).
Select(Contains("Delete local branch")).
Confirm()
}).
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("Branch newbranch is checked out by worktree linked-worktree")).

View File

@ -28,6 +28,13 @@ var RemoveWorktreeFromBranch = NewIntegrationTest(NewIntegrationTestArgs{
).
NavigateToLine(Contains("newbranch")).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().
Menu().
Title(Equals("Delete branch 'newbranch'?")).
Select(Contains("Delete local branch")).
Confirm()
}).
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("Branch newbranch is checked out by worktree linked-worktree")).