Continue/abort a conflicted cherry-pick or revert (#4441)

- **PR Description**

This is part one of a four part series of PRs that improve the
cherry-pick and revert experience.

With this first PR we make it possible to continue or abort a
cherry-pick or revert operation, in the same way you can continue or
abort a rebase or merge. Currently this is only relevant for revert,
because lazygit doesn't use git cherry-pick for copying/pasting commits.
This will change in a later PR in this series though, so here we are
already preparing for that.

Fixes #1807
This commit is contained in:
Stefan Haller 2025-04-20 15:55:11 +02:00 committed by GitHub
commit 3dc045b69c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 421 additions and 255 deletions

View file

@ -136,7 +136,7 @@ func NewGitCommandAux(
branchLoader := git_commands.NewBranchLoader(cmn, gitCommon, cmd, branchCommands.CurrentBranchInfo, configCommands) branchLoader := git_commands.NewBranchLoader(cmn, gitCommon, cmd, branchCommands.CurrentBranchInfo, configCommands)
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd) commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
commitLoader := git_commands.NewCommitLoader(cmn, cmd, statusCommands.RebaseMode, gitCommon) commitLoader := git_commands.NewCommitLoader(cmn, cmd, statusCommands.WorkingTreeState, gitCommon)
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd) reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
remoteLoader := git_commands.NewRemoteLoader(cmn, cmd, repo.Remotes) remoteLoader := git_commands.NewRemoteLoader(cmn, cmd, repo.Remotes)
worktreeLoader := git_commands.NewWorktreeLoader(gitCommon) worktreeLoader := git_commands.NewWorktreeLoader(gitCommon)

View file

@ -13,7 +13,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/common" "github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo" "github.com/samber/lo"
@ -31,7 +30,7 @@ type CommitLoader struct {
*common.Common *common.Common
cmd oscommands.ICmdObjBuilder cmd oscommands.ICmdObjBuilder
getRebaseMode func() (enums.RebaseMode, error) getWorkingTreeState func() models.WorkingTreeState
readFile func(filename string) ([]byte, error) readFile func(filename string) ([]byte, error)
walkFiles func(root string, fn filepath.WalkFunc) error walkFiles func(root string, fn filepath.WalkFunc) error
dotGitDir string dotGitDir string
@ -42,13 +41,13 @@ type CommitLoader struct {
func NewCommitLoader( func NewCommitLoader(
cmn *common.Common, cmn *common.Common,
cmd oscommands.ICmdObjBuilder, cmd oscommands.ICmdObjBuilder,
getRebaseMode func() (enums.RebaseMode, error), getWorkingTreeState func() models.WorkingTreeState,
gitCommon *GitCommon, gitCommon *GitCommon,
) *CommitLoader { ) *CommitLoader {
return &CommitLoader{ return &CommitLoader{
Common: cmn, Common: cmn,
cmd: cmd, cmd: cmd,
getRebaseMode: getRebaseMode, getWorkingTreeState: getWorkingTreeState,
readFile: os.ReadFile, readFile: os.ReadFile,
walkFiles: filepath.Walk, walkFiles: filepath.Walk,
GitCommon: gitCommon, GitCommon: gitCommon,
@ -172,17 +171,12 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
} }
} }
rebaseMode, err := self.getRebaseMode() if !self.getWorkingTreeState().Rebasing {
if err != nil {
return nil, err
}
if rebaseMode == enums.REBASE_MODE_NONE {
// not in rebase mode so return original commits // not in rebase mode so return original commits
return result, nil return result, nil
} }
rebasingCommits, err := self.getHydratedRebasingCommits(rebaseMode) rebasingCommits, err := self.getHydratedRebasingCommits()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -248,8 +242,8 @@ func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool
} }
} }
func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode) ([]*models.Commit, error) { func (self *CommitLoader) getHydratedRebasingCommits() ([]*models.Commit, error) {
commits := self.getRebasingCommits(rebaseMode) commits := self.getRebasingCommits()
if len(commits) == 0 { if len(commits) == 0 {
return nil, nil return nil, nil
@ -310,11 +304,7 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
// git-rebase-todo example: // git-rebase-todo example:
// pick ac446ae94ee560bdb8d1d057278657b251aaef17 ac446ae // pick ac446ae94ee560bdb8d1d057278657b251aaef17 ac446ae
// pick afb893148791a2fbd8091aeb81deba4930c73031 afb8931 // pick afb893148791a2fbd8091aeb81deba4930c73031 afb8931
func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) []*models.Commit { func (self *CommitLoader) getRebasingCommits() []*models.Commit {
if rebaseMode != enums.REBASE_MODE_INTERACTIVE {
return nil
}
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")) bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"))
if err != nil { if err != nil {
self.Log.Error(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error())) self.Log.Error(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error()))

View file

@ -8,7 +8,6 @@ import (
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
"github.com/stefanhaller/git-todo-parser/todo" "github.com/stefanhaller/git-todo-parser/todo"
@ -33,7 +32,6 @@ func TestGetCommits(t *testing.T) {
expectedCommits []*models.Commit expectedCommits []*models.Commit
expectedError error expectedError error
logOrder string logOrder string
rebaseMode enums.RebaseMode
opts GetCommitsOptions opts GetCommitsOptions
mainBranches []string mainBranches []string
} }
@ -42,7 +40,6 @@ func TestGetCommits(t *testing.T) {
{ {
testName: "should return no commits if there are none", testName: "should return no commits if there are none",
logOrder: "topo-order", logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false}, opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t). runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil). ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
@ -54,7 +51,6 @@ func TestGetCommits(t *testing.T) {
{ {
testName: "should use proper upstream name for branch", testName: "should use proper upstream name for branch",
logOrder: "topo-order", logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", RefForPushedStatus: "refs/heads/mybranch", IncludeRebaseCommits: false}, opts: GetCommitsOptions{RefName: "refs/heads/mybranch", RefForPushedStatus: "refs/heads/mybranch", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t). runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil). ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
@ -66,7 +62,6 @@ func TestGetCommits(t *testing.T) {
{ {
testName: "should return commits if they are present", testName: "should return commits if they are present",
logOrder: "topo-order", logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false}, opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
mainBranches: []string{"master", "main", "develop"}, mainBranches: []string{"master", "main", "develop"},
runner: oscommands.NewFakeRunner(t). runner: oscommands.NewFakeRunner(t).
@ -203,7 +198,6 @@ func TestGetCommits(t *testing.T) {
{ {
testName: "should not call merge-base for mainBranches if none exist", testName: "should not call merge-base for mainBranches if none exist",
logOrder: "topo-order", logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false}, opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
mainBranches: []string{"master", "main"}, mainBranches: []string{"master", "main"},
runner: oscommands.NewFakeRunner(t). runner: oscommands.NewFakeRunner(t).
@ -240,7 +234,6 @@ func TestGetCommits(t *testing.T) {
{ {
testName: "should call merge-base for all main branches that exist", testName: "should call merge-base for all main branches that exist",
logOrder: "topo-order", logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false}, opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
mainBranches: []string{"master", "main", "develop", "1.0-hotfixes"}, mainBranches: []string{"master", "main", "develop", "1.0-hotfixes"},
runner: oscommands.NewFakeRunner(t). runner: oscommands.NewFakeRunner(t).
@ -279,7 +272,6 @@ func TestGetCommits(t *testing.T) {
{ {
testName: "should not specify order if `log.order` is `default`", testName: "should not specify order if `log.order` is `default`",
logOrder: "default", logOrder: "default",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false}, opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t). runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil). ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
@ -291,7 +283,6 @@ func TestGetCommits(t *testing.T) {
{ {
testName: "should set filter path", testName: "should set filter path",
logOrder: "default", logOrder: "default",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", FilterPath: "src"}, opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", FilterPath: "src"},
runner: oscommands.NewFakeRunner(t). runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil). ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
@ -312,7 +303,7 @@ func TestGetCommits(t *testing.T) {
builder := &CommitLoader{ builder := &CommitLoader{
Common: common, Common: common,
cmd: cmd, cmd: cmd,
getRebaseMode: func() (enums.RebaseMode, error) { return scenario.rebaseMode, nil }, getWorkingTreeState: func() models.WorkingTreeState { return models.WorkingTreeState{} },
dotGitDir: ".git", dotGitDir: ".git",
readFile: func(filename string) ([]byte, error) { readFile: func(filename string) ([]byte, error) {
return []byte(""), nil return []byte(""), nil
@ -495,7 +486,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
builder := &CommitLoader{ builder := &CommitLoader{
Common: common, Common: common,
cmd: oscommands.NewDummyCmdObjBuilder(oscommands.NewFakeRunner(t)), cmd: oscommands.NewDummyCmdObjBuilder(oscommands.NewFakeRunner(t)),
getRebaseMode: func() (enums.RebaseMode, error) { return enums.REBASE_MODE_INTERACTIVE, nil }, getWorkingTreeState: func() models.WorkingTreeState { return models.WorkingTreeState{Rebasing: true} },
dotGitDir: ".git", dotGitDir: ".git",
readFile: func(filename string) ([]byte, error) { readFile: func(filename string) ([]byte, error) {
return []byte(""), nil return []byte(""), nil

View file

@ -8,7 +8,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/app/daemon" "github.com/jesseduffield/lazygit/pkg/app/daemon"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/patch" "github.com/jesseduffield/lazygit/pkg/commands/patch"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/stefanhaller/git-todo-parser/todo" "github.com/stefanhaller/git-todo-parser/todo"
) )
@ -229,7 +228,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
} }
if err := self.ApplyCustomPatch(true, true); err != nil { if err := self.ApplyCustomPatch(true, true); err != nil {
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING { if self.status.WorkingTreeState().Rebasing {
_ = self.rebase.AbortRebase() _ = self.rebase.AbortRebase()
} }
return err return err
@ -253,7 +252,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
self.rebase.onSuccessfulContinue = func() error { self.rebase.onSuccessfulContinue = func() error {
// add patches to index // add patches to index
if err := self.ApplyPatch(patch, ApplyPatchOpts{Index: true, ThreeWay: true}); err != nil { if err := self.ApplyPatch(patch, ApplyPatchOpts{Index: true, ThreeWay: true}); err != nil {
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING { if self.status.WorkingTreeState().Rebasing {
_ = self.rebase.AbortRebase() _ = self.rebase.AbortRebase()
} }
return err return err

View file

@ -5,7 +5,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums" "github.com/jesseduffield/lazygit/pkg/commands/models"
) )
type StatusCommands struct { type StatusCommands struct {
@ -20,43 +20,25 @@ func NewStatusCommands(
} }
} }
// RebaseMode returns "" for non-rebase mode, "normal" for normal rebase func (self *StatusCommands) WorkingTreeState() models.WorkingTreeState {
// and "interactive" for interactive rebase result := models.WorkingTreeState{}
func (self *StatusCommands) RebaseMode() (enums.RebaseMode, error) { result.Rebasing, _ = self.IsInRebase()
ok, err := self.IsInNormalRebase() result.Merging, _ = self.IsInMergeState()
if err == nil && ok { result.CherryPicking, _ = self.IsInCherryPick()
return enums.REBASE_MODE_NORMAL, nil result.Reverting, _ = self.IsInRevert()
} return result
ok, err = self.IsInInteractiveRebase()
if err == nil && ok {
return enums.REBASE_MODE_INTERACTIVE, err
}
return enums.REBASE_MODE_NONE, err
}
func (self *StatusCommands) WorkingTreeState() enums.RebaseMode {
rebaseMode, _ := self.RebaseMode()
if rebaseMode != enums.REBASE_MODE_NONE {
return enums.REBASE_MODE_REBASING
}
merging, _ := self.IsInMergeState()
if merging {
return enums.REBASE_MODE_MERGING
}
return enums.REBASE_MODE_NONE
} }
func (self *StatusCommands) IsBareRepo() bool { func (self *StatusCommands) IsBareRepo() bool {
return self.repoPaths.isBareRepo return self.repoPaths.isBareRepo
} }
func (self *StatusCommands) IsInNormalRebase() (bool, error) { func (self *StatusCommands) IsInRebase() (bool, error) {
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply")) exists, err := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge"))
if err == nil && exists {
return true, nil
} }
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply"))
func (self *StatusCommands) IsInInteractiveRebase() (bool, error) {
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge"))
} }
// IsInMergeState states whether we are still mid-merge // IsInMergeState states whether we are still mid-merge
@ -64,6 +46,42 @@ func (self *StatusCommands) IsInMergeState() (bool, error) {
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "MERGE_HEAD")) return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "MERGE_HEAD"))
} }
func (self *StatusCommands) IsInCherryPick() (bool, error) {
exists, err := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "CHERRY_PICK_HEAD"))
if err != nil || !exists {
return exists, err
}
// Sometimes, CHERRY_PICK_HEAD is present during rebases even if no
// cherry-pick is in progress. I suppose this is because rebase used to be
// implemented as a series of cherry-picks, so this could be remnants of
// code that is shared between cherry-pick and rebase, or something. The way
// to tell if this is the case is to check for the presence of the
// stopped-sha file, which records the sha of the last pick that was
// executed before the rebase stopped, and seeing if the sha in that file is
// the same as the one in CHERRY_PICK_HEAD.
cherryPickHead, err := os.ReadFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "CHERRY_PICK_HEAD"))
if err != nil {
return false, err
}
stoppedSha, err := os.ReadFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge", "stopped-sha"))
if err != nil {
// If we get an error we assume the file doesn't exist
return true, nil
}
cherryPickHeadStr := strings.TrimSpace(string(cherryPickHead))
stoppedShaStr := strings.TrimSpace(string(stoppedSha))
// Need to use HasPrefix here because the cherry-pick HEAD is a full sha1,
// but stopped-sha is an abbreviated sha1
if strings.HasPrefix(cherryPickHeadStr, stoppedShaStr) {
return false, nil
}
return true, nil
}
func (self *StatusCommands) IsInRevert() (bool, error) {
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "REVERT_HEAD"))
}
// Full ref (e.g. "refs/heads/mybranch") of the branch that is currently // Full ref (e.g. "refs/heads/mybranch") of the branch that is currently
// being rebased, or empty string when we're not in a rebase // being rebased, or empty string when we're not in a rebase
func (self *StatusCommands) BranchBeingRebased() string { func (self *StatusCommands) BranchBeingRebased() string {

View file

@ -0,0 +1,112 @@
package models
import "github.com/jesseduffield/lazygit/pkg/i18n"
// The state of the working tree. Several of these can be true at once.
// In particular, the concrete multi-state combinations that can occur in
// practice are Rebasing+CherryPicking, and Rebasing+Reverting. Theoretically, I
// guess Rebasing+Merging could also happen, but it probably won't in practice.
type WorkingTreeState struct {
Rebasing bool
Merging bool
CherryPicking bool
Reverting bool
}
func (self WorkingTreeState) Any() bool {
return self.Rebasing || self.Merging || self.CherryPicking || self.Reverting
}
func (self WorkingTreeState) None() bool {
return !self.Any()
}
type EffectiveWorkingTreeState int
const (
// this means we're neither rebasing nor merging, cherry-picking, or reverting
WORKING_TREE_STATE_NONE EffectiveWorkingTreeState = iota
WORKING_TREE_STATE_REBASING
WORKING_TREE_STATE_MERGING
WORKING_TREE_STATE_CHERRY_PICKING
WORKING_TREE_STATE_REVERTING
)
// Effective returns the "current" state; if several states are true at once,
// this is the one that should be displayed in status views, and it's the one
// that the user can continue or abort.
//
// As an example, if you are stopped in an interactive rebase, and then you
// perform a cherry-pick, and the cherry-pick conflicts, then both
// WorkingTreeState.Rebasing and WorkingTreeState.CherryPicking are true.
// The effective state is cherry-picking, because that's the one you can
// continue or abort. It is not possible to continue the rebase without first
// aborting the cherry-pick.
func (self WorkingTreeState) Effective() EffectiveWorkingTreeState {
if self.Reverting {
return WORKING_TREE_STATE_REVERTING
}
if self.CherryPicking {
return WORKING_TREE_STATE_CHERRY_PICKING
}
if self.Merging {
return WORKING_TREE_STATE_MERGING
}
if self.Rebasing {
return WORKING_TREE_STATE_REBASING
}
return WORKING_TREE_STATE_NONE
}
func (self WorkingTreeState) Title(tr *i18n.TranslationSet) string {
return map[EffectiveWorkingTreeState]string{
WORKING_TREE_STATE_REBASING: tr.RebasingStatus,
WORKING_TREE_STATE_MERGING: tr.MergingStatus,
WORKING_TREE_STATE_CHERRY_PICKING: tr.CherryPickingStatus,
WORKING_TREE_STATE_REVERTING: tr.RevertingStatus,
}[self.Effective()]
}
func (self WorkingTreeState) LowerCaseTitle(tr *i18n.TranslationSet) string {
return map[EffectiveWorkingTreeState]string{
WORKING_TREE_STATE_REBASING: tr.LowercaseRebasingStatus,
WORKING_TREE_STATE_MERGING: tr.LowercaseMergingStatus,
WORKING_TREE_STATE_CHERRY_PICKING: tr.LowercaseCherryPickingStatus,
WORKING_TREE_STATE_REVERTING: tr.LowercaseRevertingStatus,
}[self.Effective()]
}
func (self WorkingTreeState) OptionsMenuTitle(tr *i18n.TranslationSet) string {
return map[EffectiveWorkingTreeState]string{
WORKING_TREE_STATE_REBASING: tr.RebaseOptionsTitle,
WORKING_TREE_STATE_MERGING: tr.MergeOptionsTitle,
WORKING_TREE_STATE_CHERRY_PICKING: tr.CherryPickOptionsTitle,
WORKING_TREE_STATE_REVERTING: tr.RevertOptionsTitle,
}[self.Effective()]
}
func (self WorkingTreeState) OptionsMapTitle(tr *i18n.TranslationSet) string {
return map[EffectiveWorkingTreeState]string{
WORKING_TREE_STATE_REBASING: tr.ViewRebaseOptions,
WORKING_TREE_STATE_MERGING: tr.ViewMergeOptions,
WORKING_TREE_STATE_CHERRY_PICKING: tr.ViewCherryPickOptions,
WORKING_TREE_STATE_REVERTING: tr.ViewRevertOptions,
}[self.Effective()]
}
func (self WorkingTreeState) CommandName() string {
return map[EffectiveWorkingTreeState]string{
WORKING_TREE_STATE_REBASING: "rebase",
WORKING_TREE_STATE_MERGING: "merge",
WORKING_TREE_STATE_CHERRY_PICKING: "cherry-pick",
WORKING_TREE_STATE_REVERTING: "revert",
}[self.Effective()]
}
func (self WorkingTreeState) CanShowTodos() bool {
return self.Rebasing || self.CherryPicking || self.Reverting
}
func (self WorkingTreeState) CanSkip() bool {
return self.Rebasing || self.CherryPicking || self.Reverting
}

View file

@ -1,22 +0,0 @@
package enums
type RebaseMode int
const (
// this means we're neither rebasing nor merging
REBASE_MODE_NONE RebaseMode = iota
// this means normal rebase as opposed to interactive rebase
REBASE_MODE_NORMAL
REBASE_MODE_INTERACTIVE
// REBASE_MODE_REBASING is a general state that captures both REBASE_MODE_NORMAL and REBASE_MODE_INTERACTIVE
REBASE_MODE_REBASING
REBASE_MODE_MERGING
)
func (self RebaseMode) IsMerging() bool {
return self == REBASE_MODE_MERGING
}
func (self RebaseMode) IsRebasing() bool {
return self == REBASE_MODE_INTERACTIVE || self == REBASE_MODE_NORMAL || self == REBASE_MODE_REBASING
}

View file

@ -7,7 +7,6 @@ import (
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/presentation" "github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/samber/lo" "github.com/samber/lo"
@ -41,7 +40,7 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
} }
} }
showYouAreHereLabel := c.Model().WorkingTreeStateAtLastCommitRefresh == enums.REBASE_MODE_REBASING showYouAreHereLabel := c.Model().WorkingTreeStateAtLastCommitRefresh.CanShowTodos()
hasRebaseUpdateRefsConfig := c.Git().Config.GetRebaseUpdateRefs() hasRebaseUpdateRefsConfig := c.Git().Config.GetRebaseUpdateRefs()
return presentation.GetCommitListDisplayStrings( return presentation.GetCommitListDisplayStrings(

View file

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers" "github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
) )
@ -44,7 +43,7 @@ func (self *CustomPatchOptionsMenuAction) Call() error {
}, },
} }
if self.c.Git().Patch.PatchBuilder.CanRebase && self.c.Git().Status.WorkingTreeState() == enums.REBASE_MODE_NONE { if self.c.Git().Patch.PatchBuilder.CanRebase && self.c.Git().Status.WorkingTreeState().None() {
menuItems = append(menuItems, []*types.MenuItem{ menuItems = append(menuItems, []*types.MenuItem{
{ {
Label: fmt.Sprintf(self.c.Tr.RemovePatchFromOriginalCommit, self.c.Git().Patch.PatchBuilder.To), Label: fmt.Sprintf(self.c.Tr.RemovePatchFromOriginalCommit, self.c.Git().Patch.PatchBuilder.To),
@ -115,7 +114,7 @@ func (self *CustomPatchOptionsMenuAction) getPatchCommitIndex() int {
} }
func (self *CustomPatchOptionsMenuAction) validateNormalWorkingTreeState() (bool, error) { func (self *CustomPatchOptionsMenuAction) validateNormalWorkingTreeState() (bool, error) {
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE { if self.c.Git().Status.WorkingTreeState().Any() {
return false, errors.New(self.c.Tr.CantPatchWhileRebasingError) return false, errors.New(self.c.Tr.CantPatchWhileRebasingError)
} }
return true, nil return true, nil

View file

@ -40,6 +40,7 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
Description: self.c.Tr.ViewMergeRebaseOptions, Description: self.c.Tr.ViewMergeRebaseOptions,
Tooltip: self.c.Tr.ViewMergeRebaseOptionsTooltip, Tooltip: self.c.Tr.ViewMergeRebaseOptionsTooltip,
OpensMenu: true, OpensMenu: true,
GetDisabledReason: self.canShowRebaseOptions,
}, },
{ {
Key: opts.GetKey(opts.Config.Universal.Refresh), Key: opts.GetKey(opts.Config.Universal.Refresh),
@ -191,3 +192,12 @@ func (self *GlobalController) escape() error {
func (self *GlobalController) toggleWhitespace() error { func (self *GlobalController) toggleWhitespace() error {
return (&ToggleWhitespaceAction{c: self.c}).Call() return (&ToggleWhitespaceAction{c: self.c}).Call()
} }
func (self *GlobalController) canShowRebaseOptions() *types.DisabledReason {
if self.c.Model().WorkingTreeStateAtLastCommitRefresh.None() {
return &types.DisabledReason{
Text: self.c.Tr.NotMergingOrRebasing,
}
}
return nil
}

View file

@ -77,7 +77,7 @@ func (self *CherryPickHelper) Paste() error {
"numCommits": strconv.Itoa(len(self.getData().CherryPickedCommits)), "numCommits": strconv.Itoa(len(self.getData().CherryPickedCommits)),
}), }),
HandleConfirm: func() error { HandleConfirm: func() error {
isInRebase, err := self.c.Git().Status.IsInInteractiveRebase() isInRebase, err := self.c.Git().Status.IsInRebase()
if err != nil { if err != nil {
return err return err
} }
@ -107,7 +107,7 @@ func (self *CherryPickHelper) Paste() error {
// be because there were conflicts. Don't clear the copied // be because there were conflicts. Don't clear the copied
// commits in this case, since we might want to abort and // commits in this case, since we might want to abort and
// try pasting them again. // try pasting them again.
isInRebase, err = self.c.Git().Status.IsInInteractiveRebase() isInRebase, err = self.c.Git().Status.IsInRebase()
if err != nil { if err != nil {
return err return err
} }

View file

@ -10,7 +10,6 @@ import (
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo" "github.com/samber/lo"
@ -51,7 +50,7 @@ func (self *MergeAndRebaseHelper) CreateRebaseOptionsMenu() error {
{option: REBASE_OPTION_ABORT, key: 'a'}, {option: REBASE_OPTION_ABORT, key: 'a'},
} }
if self.c.Git().Status.WorkingTreeState() == enums.REBASE_MODE_REBASING { if self.c.Git().Status.WorkingTreeState().CanSkip() {
options = append(options, optionAndKey{ options = append(options, optionAndKey{
option: REBASE_OPTION_SKIP, key: 's', option: REBASE_OPTION_SKIP, key: 's',
}) })
@ -67,13 +66,7 @@ func (self *MergeAndRebaseHelper) CreateRebaseOptionsMenu() error {
} }
}) })
var title string title := self.c.Git().Status.WorkingTreeState().OptionsMenuTitle(self.c.Tr)
if self.c.Git().Status.WorkingTreeState() == enums.REBASE_MODE_MERGING {
title = self.c.Tr.MergeOptionsTitle
} else {
title = self.c.Tr.RebaseOptionsTitle
}
return self.c.Menu(types.CreateMenuOptions{Title: title, Items: menuItems}) return self.c.Menu(types.CreateMenuOptions{Title: title, Items: menuItems})
} }
@ -84,12 +77,13 @@ func (self *MergeAndRebaseHelper) ContinueRebase() error {
func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error { func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error {
status := self.c.Git().Status.WorkingTreeState() status := self.c.Git().Status.WorkingTreeState()
if status != enums.REBASE_MODE_MERGING && status != enums.REBASE_MODE_REBASING { if status.None() {
return errors.New(self.c.Tr.NotMergingOrRebasing) return errors.New(self.c.Tr.NotMergingOrRebasing)
} }
self.c.LogAction(fmt.Sprintf("Merge/Rebase: %s", command)) self.c.LogAction(fmt.Sprintf("Merge/Rebase: %s", command))
if status == enums.REBASE_MODE_REBASING { effectiveStatus := status.Effective()
if effectiveStatus == models.WORKING_TREE_STATE_REBASING {
todoFile, err := os.ReadFile( todoFile, err := os.ReadFile(
filepath.Join(self.c.Git().RepoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"), filepath.Join(self.c.Git().RepoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"),
) )
@ -103,23 +97,15 @@ func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error {
} }
} }
commandType := "" commandType := status.CommandName()
switch status {
case enums.REBASE_MODE_MERGING:
commandType = "merge"
case enums.REBASE_MODE_REBASING:
commandType = "rebase"
default:
// shouldn't be possible to land here
}
// we should end up with a command like 'git merge --continue' // we should end up with a command like 'git merge --continue'
// it's impossible for a rebase to require a commit so we'll use a subprocess only if it's a merge // it's impossible for a rebase to require a commit so we'll use a subprocess only if it's a merge
needsSubprocess := (status == enums.REBASE_MODE_MERGING && command != REBASE_OPTION_ABORT && self.c.UserConfig().Git.Merging.ManualCommit) || needsSubprocess := (effectiveStatus == models.WORKING_TREE_STATE_MERGING && command != REBASE_OPTION_ABORT && self.c.UserConfig().Git.Merging.ManualCommit) ||
// but we'll also use a subprocess if we have exec todos; those are likely to be lengthy build // but we'll also use a subprocess if we have exec todos; those are likely to be lengthy build
// tasks whose output the user will want to see in the terminal // tasks whose output the user will want to see in the terminal
(status == enums.REBASE_MODE_REBASING && command != REBASE_OPTION_ABORT && self.hasExecTodos()) (effectiveStatus == models.WORKING_TREE_STATE_REBASING && command != REBASE_OPTION_ABORT && self.hasExecTodos())
if needsSubprocess { if needsSubprocess {
// TODO: see if we should be calling more of the code from self.Git.Rebase.GenericMergeOrRebaseAction // TODO: see if we should be calling more of the code from self.Git.Rebase.GenericMergeOrRebaseAction
@ -152,6 +138,8 @@ var conflictStrings = []string{
"fix conflicts", "fix conflicts",
"Resolve all conflicts manually", "Resolve all conflicts manually",
"Merge conflict in file", "Merge conflict in file",
"hint: after resolving the conflicts",
"CONFLICT (content):",
} }
func isMergeConflictErr(errStr string) bool { func isMergeConflictErr(errStr string) bool {
@ -199,7 +187,7 @@ func (self *MergeAndRebaseHelper) CheckForConflicts(result error) error {
} }
func (self *MergeAndRebaseHelper) PromptForConflictHandling() error { func (self *MergeAndRebaseHelper) PromptForConflictHandling() error {
mode := self.workingTreeStateNoun() mode := self.c.Git().Status.WorkingTreeState().CommandName()
return self.c.Menu(types.CreateMenuOptions{ return self.c.Menu(types.CreateMenuOptions{
Title: self.c.Tr.FoundConflictsTitle, Title: self.c.Tr.FoundConflictsTitle,
Items: []*types.MenuItem{ Items: []*types.MenuItem{
@ -224,7 +212,7 @@ func (self *MergeAndRebaseHelper) PromptForConflictHandling() error {
func (self *MergeAndRebaseHelper) AbortMergeOrRebaseWithConfirm() error { func (self *MergeAndRebaseHelper) AbortMergeOrRebaseWithConfirm() error {
// prompt user to confirm that they want to abort, then do it // prompt user to confirm that they want to abort, then do it
mode := self.workingTreeStateNoun() mode := self.c.Git().Status.WorkingTreeState().CommandName()
self.c.Confirm(types.ConfirmOpts{ self.c.Confirm(types.ConfirmOpts{
Title: fmt.Sprintf(self.c.Tr.AbortTitle, mode), Title: fmt.Sprintf(self.c.Tr.AbortTitle, mode),
Prompt: fmt.Sprintf(self.c.Tr.AbortPrompt, mode), Prompt: fmt.Sprintf(self.c.Tr.AbortPrompt, mode),
@ -236,23 +224,11 @@ func (self *MergeAndRebaseHelper) AbortMergeOrRebaseWithConfirm() error {
return nil return nil
} }
func (self *MergeAndRebaseHelper) workingTreeStateNoun() string {
workingTreeState := self.c.Git().Status.WorkingTreeState()
switch workingTreeState {
case enums.REBASE_MODE_NONE:
return ""
case enums.REBASE_MODE_MERGING:
return "merge"
default:
return "rebase"
}
}
// PromptToContinueRebase asks the user if they want to continue the rebase/merge that's in progress // PromptToContinueRebase asks the user if they want to continue the rebase/merge that's in progress
func (self *MergeAndRebaseHelper) PromptToContinueRebase() error { func (self *MergeAndRebaseHelper) PromptToContinueRebase() error {
self.c.Confirm(types.ConfirmOpts{ self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.Continue, Title: self.c.Tr.Continue,
Prompt: self.c.Tr.ConflictsResolved, Prompt: fmt.Sprintf(self.c.Tr.ConflictsResolved, self.c.Git().Status.WorkingTreeState().CommandName()),
HandleConfirm: func() error { HandleConfirm: func() error {
// By the time we get here, we might have unstaged changes again, // By the time we get here, we might have unstaged changes again,
// e.g. if the user had to fix build errors after resolving the // e.g. if the user had to fix build errors after resolving the

View file

@ -4,8 +4,6 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/samber/lo" "github.com/samber/lo"
@ -116,12 +114,12 @@ func (self *ModeHelper) Statuses() []ModeStatus {
}, },
{ {
IsActive: func() bool { IsActive: func() bool {
return !self.suppressRebasingMode && self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE return !self.suppressRebasingMode && self.c.Git().Status.WorkingTreeState().Any()
}, },
Description: func() string { Description: func() string {
workingTreeState := self.c.Git().Status.WorkingTreeState() workingTreeState := self.c.Git().Status.WorkingTreeState()
return self.withResetButton( return self.withResetButton(
presentation.FormatWorkingTreeStateTitle(self.c.Tr, workingTreeState), style.FgYellow, workingTreeState.Title(self.c.Tr), style.FgYellow,
) )
}, },
Reset: self.mergeAndRebaseHelper.AbortMergeOrRebaseWithConfirm, Reset: self.mergeAndRebaseHelper.AbortMergeOrRebaseWithConfirm,

View file

@ -4,7 +4,6 @@ import (
"errors" "errors"
"github.com/jesseduffield/lazygit/pkg/commands/patch" "github.com/jesseduffield/lazygit/pkg/commands/patch"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/patch_exploring" "github.com/jesseduffield/lazygit/pkg/gui/patch_exploring"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
) )
@ -22,7 +21,7 @@ func NewPatchBuildingHelper(
} }
func (self *PatchBuildingHelper) ValidateNormalWorkingTreeState() (bool, error) { func (self *PatchBuildingHelper) ValidateNormalWorkingTreeState() (bool, error) {
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE { if self.c.Git().Status.WorkingTreeState().Any() {
return false, errors.New(self.c.Tr.CantPatchWhileRebasingError) return false, errors.New(self.c.Tr.CantPatchWhileRebasingError)
} }
return true, nil return true, nil

View file

@ -9,7 +9,6 @@ import (
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/filetree" "github.com/jesseduffield/lazygit/pkg/gui/filetree"
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts" "github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
@ -583,7 +582,7 @@ func (self *RefreshHelper) refreshStateFiles() error {
} }
} }
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE && conflictFileCount == 0 && prevConflictFileCount > 0 { if self.c.Git().Status.WorkingTreeState().Any() && conflictFileCount == 0 && prevConflictFileCount > 0 {
self.c.OnUIThread(func() error { return self.mergeAndRebaseHelper.PromptToContinueRebase() }) self.c.OnUIThread(func() error { return self.mergeAndRebaseHelper.PromptToContinueRebase() })
} }

View file

@ -8,7 +8,6 @@ import (
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits" "github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers" "github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
@ -682,7 +681,7 @@ func (self *LocalCommitsController) rewordEnabled(commit *models.Commit) *types.
} }
func (self *LocalCommitsController) isRebasing() bool { func (self *LocalCommitsController) isRebasing() bool {
return self.c.Model().WorkingTreeStateAtLastCommitRefresh != enums.REBASE_MODE_NONE return self.c.Model().WorkingTreeStateAtLastCommitRefresh.Any()
} }
func (self *LocalCommitsController) moveDown(selectedCommits []*models.Commit, startIdx int, endIdx int) error { func (self *LocalCommitsController) moveDown(selectedCommits []*models.Commit, startIdx int, endIdx int) error {
@ -869,7 +868,8 @@ func (self *LocalCommitsController) revert(commit *models.Commit) error {
HandleConfirm: func() error { HandleConfirm: func() error {
self.c.LogAction(self.c.Tr.Actions.RevertCommit) self.c.LogAction(self.c.Tr.Actions.RevertCommit)
return self.c.WithWaitingStatusSync(self.c.Tr.RevertingStatus, func() error { return self.c.WithWaitingStatusSync(self.c.Tr.RevertingStatus, func() error {
if err := self.c.Git().Commit.Revert(commit.Hash); err != nil { result := self.c.Git().Commit.Revert(commit.Hash)
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(result); err != nil {
return err return err
} }
return self.afterRevertCommit() return self.afterRevertCommit()
@ -976,7 +976,7 @@ func (self *LocalCommitsController) moveFixupCommitToOwnerStackedBranch(targetCo
return nil return nil
} }
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE { if self.c.Git().Status.WorkingTreeState().Any() {
// Can't move commits while rebasing // Can't move commits while rebasing
return nil return nil
} }

View file

@ -7,7 +7,6 @@ import (
"time" "time"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/constants" "github.com/jesseduffield/lazygit/pkg/constants"
"github.com/jesseduffield/lazygit/pkg/gui/presentation" "github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
@ -109,20 +108,17 @@ func (self *StatusController) onClick(opts gocui.ViewMouseBindingOpts) error {
upstreamStatus := utils.Decolorise(presentation.BranchStatus(currentBranch, types.ItemOperationNone, self.c.Tr, time.Now(), self.c.UserConfig())) upstreamStatus := utils.Decolorise(presentation.BranchStatus(currentBranch, types.ItemOperationNone, self.c.Tr, time.Now(), self.c.UserConfig()))
repoName := self.c.Git().RepoPaths.RepoName() repoName := self.c.Git().RepoPaths.RepoName()
workingTreeState := self.c.Git().Status.WorkingTreeState() workingTreeState := self.c.Git().Status.WorkingTreeState()
switch workingTreeState { if workingTreeState.Any() {
case enums.REBASE_MODE_REBASING, enums.REBASE_MODE_MERGING: workingTreeStatus := fmt.Sprintf("(%s)", workingTreeState.LowerCaseTitle(self.c.Tr))
workingTreeStatus := fmt.Sprintf("(%s)", presentation.FormatWorkingTreeStateLower(self.c.Tr, workingTreeState))
if cursorInSubstring(opts.X, upstreamStatus+" ", workingTreeStatus) { if cursorInSubstring(opts.X, upstreamStatus+" ", workingTreeStatus) {
return self.c.Helpers().MergeAndRebase.CreateRebaseOptionsMenu() return self.c.Helpers().MergeAndRebase.CreateRebaseOptionsMenu()
} }
if cursorInSubstring(opts.X, upstreamStatus+" "+workingTreeStatus+" ", repoName) { if cursorInSubstring(opts.X, upstreamStatus+" "+workingTreeStatus+" ", repoName) {
return self.c.Helpers().Repos.CreateRecentReposMenu() return self.c.Helpers().Repos.CreateRecentReposMenu()
} }
default: } else if cursorInSubstring(opts.X, upstreamStatus+" ", repoName) {
if cursorInSubstring(opts.X, upstreamStatus+" ", repoName) {
return self.c.Helpers().Repos.CreateRecentReposMenu() return self.c.Helpers().Repos.CreateRecentReposMenu()
} }
}
return nil return nil
} }

View file

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
@ -78,7 +77,7 @@ func (self *UndoController) reflogUndo() error {
undoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit undo]"} undoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit undo]"}
undoingStatus := self.c.Tr.UndoingStatus undoingStatus := self.c.Tr.UndoingStatus
if self.c.Git().Status.WorkingTreeState() == enums.REBASE_MODE_REBASING { if self.c.Git().Status.WorkingTreeState().Any() {
return errors.New(self.c.Tr.CantUndoWhileRebasing) return errors.New(self.c.Tr.CantUndoWhileRebasing)
} }
@ -142,7 +141,7 @@ func (self *UndoController) reflogRedo() error {
redoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit redo]"} redoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit redo]"}
redoingStatus := self.c.Tr.RedoingStatus redoingStatus := self.c.Tr.RedoingStatus
if self.c.Git().Status.WorkingTreeState() == enums.REBASE_MODE_REBASING { if self.c.Git().Status.WorkingTreeState().Any() {
return errors.New(self.c.Tr.CantRedoWhileRebasing) return errors.New(self.c.Tr.CantRedoWhileRebasing)
} }

View file

@ -82,16 +82,10 @@ func (self *OptionsMapMgr) renderContextOptionsMap() {
} }
// Mode-specific global keybindings // Mode-specific global keybindings
if self.c.Model().WorkingTreeStateAtLastCommitRefresh.IsRebasing() { if state := self.c.Model().WorkingTreeStateAtLastCommitRefresh; state.Any() {
optionsMap = utils.Prepend(optionsMap, bindingInfo{ optionsMap = utils.Prepend(optionsMap, bindingInfo{
key: keybindings.Label(self.c.KeybindingsOpts().Config.Universal.CreateRebaseOptionsMenu), key: keybindings.Label(self.c.KeybindingsOpts().Config.Universal.CreateRebaseOptionsMenu),
description: self.c.Tr.ViewRebaseOptions, description: state.OptionsMapTitle(self.c.Tr),
style: style.FgYellow,
})
} else if self.c.Model().WorkingTreeStateAtLastCommitRefresh.IsMerging() {
optionsMap = utils.Prepend(optionsMap, bindingInfo{
key: keybindings.Label(self.c.KeybindingsOpts().Config.Universal.CreateRebaseOptionsMenu),
description: self.c.Tr.ViewMergeOptions,
style: style.FgYellow, style: style.FgYellow,
}) })
} }

View file

@ -5,7 +5,6 @@ import (
"time" "time"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons" "github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
@ -18,7 +17,7 @@ func FormatStatus(
currentBranch *models.Branch, currentBranch *models.Branch,
itemOperation types.ItemOperation, itemOperation types.ItemOperation,
linkedWorktreeName string, linkedWorktreeName string,
workingTreeState enums.RebaseMode, workingTreeState models.WorkingTreeState,
tr *i18n.TranslationSet, tr *i18n.TranslationSet,
userConfig *config.UserConfig, userConfig *config.UserConfig,
) string { ) string {
@ -31,8 +30,8 @@ func FormatStatus(
} }
} }
if workingTreeState != enums.REBASE_MODE_NONE { if workingTreeState.Any() {
status += style.FgYellow.Sprintf("(%s) ", FormatWorkingTreeStateLower(tr, workingTreeState)) status += style.FgYellow.Sprintf("(%s) ", workingTreeState.LowerCaseTitle(tr))
} }
name := GetBranchTextStyle(currentBranch.Name).Sprint(currentBranch.Name) name := GetBranchTextStyle(currentBranch.Name).Sprint(currentBranch.Name)

View file

@ -1,30 +0,0 @@
package presentation
import (
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/i18n"
)
func FormatWorkingTreeStateTitle(tr *i18n.TranslationSet, rebaseMode enums.RebaseMode) string {
switch rebaseMode {
case enums.REBASE_MODE_REBASING:
return tr.RebasingStatus
case enums.REBASE_MODE_MERGING:
return tr.MergingStatus
default:
// should never actually display this
return "none"
}
}
func FormatWorkingTreeStateLower(tr *i18n.TranslationSet, rebaseMode enums.RebaseMode) string {
switch rebaseMode {
case enums.REBASE_MODE_REBASING:
return tr.LowercaseRebasingStatus
case enums.REBASE_MODE_MERGING:
return tr.LowercaseMergingStatus
default:
// should never actually display this
return "none"
}
}

View file

@ -6,7 +6,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/common" "github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
@ -288,7 +287,7 @@ type Model struct {
ReflogCommits []*models.Commit ReflogCommits []*models.Commit
BisectInfo *git_commands.BisectInfo BisectInfo *git_commands.BisectInfo
WorkingTreeStateAtLastCommitRefresh enums.RebaseMode WorkingTreeStateAtLastCommitRefresh models.WorkingTreeState
RemoteBranches []*models.RemoteBranch RemoteBranches []*models.RemoteBranch
Tags []*models.Tag Tags []*models.Tag

View file

@ -306,11 +306,15 @@ type TranslationSet struct {
ViewMergeRebaseOptionsTooltip string ViewMergeRebaseOptionsTooltip string
ViewMergeOptions string ViewMergeOptions string
ViewRebaseOptions string ViewRebaseOptions string
ViewCherryPickOptions string
ViewRevertOptions string
NotMergingOrRebasing string NotMergingOrRebasing string
AlreadyRebasing string AlreadyRebasing string
RecentRepos string RecentRepos string
MergeOptionsTitle string MergeOptionsTitle string
RebaseOptionsTitle string RebaseOptionsTitle string
CherryPickOptionsTitle string
RevertOptionsTitle string
CommitSummaryTitle string CommitSummaryTitle string
CommitDescriptionTitle string CommitDescriptionTitle string
CommitDescriptionSubTitle string CommitDescriptionSubTitle string
@ -392,6 +396,8 @@ type TranslationSet struct {
MergingStatus string MergingStatus string
LowercaseRebasingStatus string LowercaseRebasingStatus string
LowercaseMergingStatus string LowercaseMergingStatus string
LowercaseCherryPickingStatus string
LowercaseRevertingStatus string
AmendingStatus string AmendingStatus string
CherryPickingStatus string CherryPickingStatus string
UndoingStatus string UndoingStatus string
@ -1359,11 +1365,15 @@ func EnglishTranslationSet() *TranslationSet {
ViewMergeRebaseOptionsTooltip: "View options to abort/continue/skip the current merge/rebase.", ViewMergeRebaseOptionsTooltip: "View options to abort/continue/skip the current merge/rebase.",
ViewMergeOptions: "View merge options", ViewMergeOptions: "View merge options",
ViewRebaseOptions: "View rebase options", ViewRebaseOptions: "View rebase options",
ViewCherryPickOptions: "View cherry-pick options",
ViewRevertOptions: "View revert options",
NotMergingOrRebasing: "You are currently neither rebasing nor merging", NotMergingOrRebasing: "You are currently neither rebasing nor merging",
AlreadyRebasing: "Can't perform this action during a rebase", AlreadyRebasing: "Can't perform this action during a rebase",
RecentRepos: "Recent repositories", RecentRepos: "Recent repositories",
MergeOptionsTitle: "Merge options", MergeOptionsTitle: "Merge options",
RebaseOptionsTitle: "Rebase options", RebaseOptionsTitle: "Rebase options",
CherryPickOptionsTitle: "Cherry-pick options",
RevertOptionsTitle: "Revert options",
CommitSummaryTitle: "Commit summary", CommitSummaryTitle: "Commit summary",
CommitDescriptionTitle: "Commit description", CommitDescriptionTitle: "Commit description",
CommitDescriptionSubTitle: "Press {{.togglePanelKeyBinding}} to toggle focus, {{.commitMenuKeybinding}} to open menu", CommitDescriptionSubTitle: "Press {{.togglePanelKeyBinding}} to toggle focus, {{.commitMenuKeybinding}} to open menu",
@ -1381,7 +1391,7 @@ func EnglishTranslationSet() *TranslationSet {
SecondaryTitle: "Secondary", SecondaryTitle: "Secondary",
ReflogCommitsTitle: "Reflog", ReflogCommitsTitle: "Reflog",
GlobalTitle: "Global keybindings", GlobalTitle: "Global keybindings",
ConflictsResolved: "All merge conflicts resolved. Continue?", ConflictsResolved: "All merge conflicts resolved. Continue the %s?",
Continue: "Continue", Continue: "Continue",
UnstagedFilesAfterConflictsResolved: "Files have been modified since conflicts were resolved. Auto-stage them and continue?", UnstagedFilesAfterConflictsResolved: "Files have been modified since conflicts were resolved. Auto-stage them and continue?",
Keybindings: "Keybindings", Keybindings: "Keybindings",
@ -1453,6 +1463,8 @@ func EnglishTranslationSet() *TranslationSet {
MergingStatus: "Merging", MergingStatus: "Merging",
LowercaseRebasingStatus: "rebasing", // lowercase because it shows up in parentheses LowercaseRebasingStatus: "rebasing", // lowercase because it shows up in parentheses
LowercaseMergingStatus: "merging", // lowercase because it shows up in parentheses LowercaseMergingStatus: "merging", // lowercase because it shows up in parentheses
LowercaseCherryPickingStatus: "cherry-picking", // lowercase because it shows up in parentheses
LowercaseRevertingStatus: "reverting", // lowercase because it shows up in parentheses
AmendingStatus: "Amending", AmendingStatus: "Amending",
CherryPickingStatus: "Cherry-picking", CherryPickingStatus: "Cherry-picking",
UndoingStatus: "Undoing", UndoingStatus: "Undoing",

View file

@ -31,7 +31,7 @@ func main() {
integrationTest := getIntegrationTest() integrationTest := getIntegrationTest()
if os.Getenv(components.WAIT_FOR_DEBUGGER_ENV_VAR) != "" { if os.Getenv(components.WAIT_FOR_DEBUGGER_ENV_VAR) != "" && !daemon.InDaemonMode() {
println("Waiting for debugger to attach...") println("Waiting for debugger to attach...")
for !isDebuggerAttached() { for !isDebuggerAttached() {
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)

View file

@ -1,5 +1,7 @@
package components package components
import "fmt"
// for running common actions // for running common actions
type Common struct { type Common struct {
t *TestDriver t *TestDriver
@ -43,10 +45,10 @@ func (self *Common) AcknowledgeConflicts() {
Confirm() Confirm()
} }
func (self *Common) ContinueOnConflictsResolved() { func (self *Common) ContinueOnConflictsResolved(command string) {
self.t.ExpectPopup().Confirmation(). self.t.ExpectPopup().Confirmation().
Title(Equals("Continue")). Title(Equals("Continue")).
Content(Contains("All merge conflicts resolved. Continue?")). Content(Contains(fmt.Sprintf("All merge conflicts resolved. Continue the %s?", command))).
Confirm() Confirm()
} }

View file

@ -48,7 +48,7 @@ var Rebase = NewIntegrationTest(NewIntegrationTestArgs{
t.Views().Information().Content(Contains("Rebasing")) t.Views().Information().Content(Contains("Rebasing"))
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.Views().Information().Content(DoesNotContain("Rebasing")) t.Views().Information().Content(DoesNotContain("Rebasing"))

View file

@ -77,7 +77,7 @@ var RebaseAndDrop = NewIntegrationTest(NewIntegrationTestArgs{
IsFocused(). IsFocused().
PressPrimaryAction() PressPrimaryAction()
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.Views().Information().Content(DoesNotContain("Rebasing")) t.Views().Information().Content(DoesNotContain("Rebasing"))

View file

@ -51,7 +51,7 @@ var RebaseConflictsFixBuildErrors = NewIntegrationTest(NewIntegrationTestArgs{
popup := t.ExpectPopup().Confirmation(). popup := t.ExpectPopup().Confirmation().
Title(Equals("Continue")). Title(Equals("Continue")).
Content(Contains("All merge conflicts resolved. Continue?")) Content(Contains("All merge conflicts resolved. Continue the rebase?"))
// While the popup is showing, fix some build errors // While the popup is showing, fix some build errors
t.Shell().UpdateFile("file", "make it compile again") t.Shell().UpdateFile("file", "make it compile again")

View file

@ -69,7 +69,7 @@ var CherryPickConflicts = NewIntegrationTest(NewIntegrationTestArgs{
SelectNextItem(). SelectNextItem().
PressPrimaryAction() PressPrimaryAction()
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.Views().Files().IsEmpty() t.Views().Files().IsEmpty()

View file

@ -0,0 +1,80 @@
package commit
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var RevertWithConflictSingleCommit = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Reverts a commit that conflicts",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.CreateFileAndAdd("myfile", "")
shell.Commit("add empty file")
shell.CreateFileAndAdd("myfile", "first line\n")
shell.Commit("add first line")
shell.UpdateFileAndAdd("myfile", "first line\nsecond line\n")
shell.Commit("add second line")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().
Focus().
Lines(
Contains("CI ◯ add second line").IsSelected(),
Contains("CI ◯ add first line"),
Contains("CI ◯ add empty file"),
).
SelectNextItem().
Press(keys.Commits.RevertCommit).
Tap(func() {
t.ExpectPopup().Confirmation().
Title(Equals("Revert commit")).
Content(MatchesRegexp(`Are you sure you want to revert \w+?`)).
Confirm()
t.ExpectPopup().Menu().
Title(Equals("Conflicts!")).
Select(Contains("View conflicts")).
Confirm()
}).
Lines(
/* EXPECTED:
Proper display of revert commits is not implemented yet; we'll do this in the next PR
Contains("revert").Contains("CI <-- CONFLICT --- add first line"),
Contains("CI ◯ add second line"),
Contains("CI ◯ add first line"),
Contains("CI ◯ add empty file"),
ACTUAL: */
Contains("CI ◯ <-- YOU ARE HERE --- add second line"),
Contains("CI ◯ add first line"),
Contains("CI ◯ add empty file"),
)
t.Views().Options().Content(Contains("View revert options: m"))
t.Views().Information().Content(Contains("Reverting (Reset)"))
t.Views().Files().IsFocused().
Lines(
Contains("UU myfile").IsSelected(),
).
PressEnter()
t.Views().MergeConflicts().IsFocused().
SelectNextItem().
PressPrimaryAction()
t.ExpectPopup().Alert().
Title(Equals("Continue")).
Content(Contains("All merge conflicts resolved. Continue the revert?")).
Confirm()
t.Views().Commits().
Lines(
Contains(`CI ◯ Revert "add first line"`),
Contains("CI ◯ add second line"),
Contains("CI ◯ add first line"),
Contains("CI ◯ add empty file"),
)
},
})

View file

@ -61,7 +61,7 @@ func doTheRebaseForAmendTests(t *TestDriver, keys config.KeybindingConfig) {
t.ExpectPopup().Confirmation(). t.ExpectPopup().Confirmation().
Title(Equals("Continue")). Title(Equals("Continue")).
Content(Contains("All merge conflicts resolved. Continue?")). Content(Contains("All merge conflicts resolved. Continue the rebase?")).
Cancel() Cancel()
} }

View file

@ -25,7 +25,7 @@ var ResolveExternally = NewIntegrationTest(NewIntegrationTestArgs{
}). }).
Press(keys.Universal.Refresh) Press(keys.Universal.Refresh)
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("merge")
t.Views().Files(). t.Views().Files().
IsEmpty() IsEmpty()

View file

@ -51,6 +51,6 @@ var ResolveMultipleFiles = NewIntegrationTest(NewIntegrationTestArgs{
). ).
PressPrimaryAction() PressPrimaryAction()
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("merge")
}, },
}) })

View file

@ -43,7 +43,7 @@ var ResolveWithoutTrailingLf = NewIntegrationTest(NewIntegrationTestArgs{
t.ExpectPopup().Alert(). t.ExpectPopup().Alert().
Title(Equals("Continue")). Title(Equals("Continue")).
Content(Contains("All merge conflicts resolved. Continue?")). Content(Contains("All merge conflicts resolved. Continue the merge?")).
Cancel() Cancel()
t.Views().Files(). t.Views().Files().

View file

@ -93,7 +93,7 @@ var DiscardAllDirChanges = NewIntegrationTest(NewIntegrationTestArgs{
Confirm() Confirm()
}). }).
Tap(func() { Tap(func() {
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("merge")
t.ExpectPopup().Confirmation(). t.ExpectPopup().Confirmation().
Title(Equals("Continue")). Title(Equals("Continue")).
Content(Contains("Files have been modified since conflicts were resolved. Auto-stage them and continue?")). Content(Contains("Files have been modified since conflicts were resolved. Auto-stage them and continue?")).

View file

@ -53,7 +53,7 @@ var DiscardVariousChanges = NewIntegrationTest(NewIntegrationTestArgs{
t.ExpectPopup().Confirmation(). t.ExpectPopup().Confirmation().
Title(Equals("Continue")). Title(Equals("Continue")).
Content(Contains("All merge conflicts resolved. Continue?")). Content(Contains("All merge conflicts resolved. Continue the merge?")).
Cancel() Cancel()
discardOneByOne([]statusFile{ discardOneByOne([]statusFile{

View file

@ -40,7 +40,7 @@ var DiscardVariousChangesRangeSelect = NewIntegrationTest(NewIntegrationTestArgs
t.ExpectPopup().Confirmation(). t.ExpectPopup().Confirmation().
Title(Equals("Continue")). Title(Equals("Continue")).
Content(Contains("All merge conflicts resolved. Continue?")). Content(Contains("All merge conflicts resolved. Continue the merge?")).
Cancel() Cancel()
}). }).
Lines( Lines(

View file

@ -60,7 +60,7 @@ var AmendCommitWithConflict = NewIntegrationTest(NewIntegrationTestArgs{
SelectNextItem(). SelectNextItem().
PressPrimaryAction() // pick "4" PressPrimaryAction() // pick "4"
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.Common().AcknowledgeConflicts() t.Common().AcknowledgeConflicts()

View file

@ -0,0 +1,45 @@
package interactive_rebase
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var RebaseWithCommitThatBecomesEmpty = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Performs a rebase involving a commit that becomes empty during the rebase, and gets dropped.",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.EmptyCommit("initial commit")
// It is important that we create two separate commits for the two
// changes to the file, but only one commit for the same changes on our
// branch; otherwise, the commit would be discarded at the start of the
// rebase already.
shell.CreateFileAndAdd("file", "change 1\n")
shell.Commit("master change 1")
shell.UpdateFileAndAdd("file", "change 1\nchange 2\n")
shell.Commit("master change 2")
shell.NewBranchFrom("branch", "HEAD^^")
shell.CreateFileAndAdd("file", "change 1\nchange 2\n")
shell.Commit("branch change")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Branches().
Focus().
NavigateToLine(Contains("master")).
Press(keys.Branches.RebaseBranch)
t.ExpectPopup().Menu().
Title(Equals("Rebase 'branch'")).
Select(Contains("Simple rebase")).
Confirm()
t.Views().Commits().
Lines(
Contains("master change 2"),
Contains("master change 1"),
Contains("initial commit"),
)
},
})

View file

@ -33,7 +33,7 @@ func handleConflictsFromSwap(t *TestDriver) {
SelectNextItem(). SelectNextItem().
PressPrimaryAction() // pick "three" PressPrimaryAction() // pick "three"
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.Common().AcknowledgeConflicts() t.Common().AcknowledgeConflicts()
@ -56,7 +56,7 @@ func handleConflictsFromSwap(t *TestDriver) {
SelectNextItem(). SelectNextItem().
PressPrimaryAction() // pick "two" PressPrimaryAction() // pick "two"
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.Views().Commits(). t.Views().Commits().
Focus(). Focus().

View file

@ -71,7 +71,7 @@ var MoveToEarlierCommitFromAddedFile = NewIntegrationTest(NewIntegrationTestArgs
SelectNextItem(). SelectNextItem().
PressPrimaryAction() // choose the version with all three lines PressPrimaryAction() // choose the version with all three lines
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.Views().Commits(). t.Views().Commits().
Focus(). Focus().

View file

@ -67,7 +67,7 @@ var MoveToIndexFromAddedFileWithConflict = NewIntegrationTest(NewIntegrationTest
SelectNextItem(). SelectNextItem().
PressPrimaryAction() PressPrimaryAction()
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.ExpectPopup().Alert(). t.ExpectPopup().Alert().
Title(Equals("Error")). Title(Equals("Error")).

View file

@ -62,7 +62,7 @@ var MoveToIndexWithConflict = NewIntegrationTest(NewIntegrationTestArgs{
). ).
PressPrimaryAction() PressPrimaryAction()
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.ExpectPopup().Alert(). t.ExpectPopup().Alert().
Title(Equals("Error")). Title(Equals("Error")).

View file

@ -60,7 +60,7 @@ var PullMergeConflict = NewIntegrationTest(NewIntegrationTestArgs{
). ).
PressPrimaryAction() // choose 'content4' PressPrimaryAction() // choose 'content4'
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("merge")
t.Views().Status().Content(Equals("↑2 repo → master")) t.Views().Status().Content(Equals("↑2 repo → master"))

View file

@ -61,7 +61,7 @@ var PullRebaseConflict = NewIntegrationTest(NewIntegrationTestArgs{
SelectNextItem(). SelectNextItem().
PressPrimaryAction() // choose 'content4' PressPrimaryAction() // choose 'content4'
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.Views().Status().Content(Equals("↑1 repo → master")) t.Views().Status().Content(Equals("↑1 repo → master"))

View file

@ -74,7 +74,7 @@ var PullRebaseInteractiveConflict = NewIntegrationTest(NewIntegrationTestArgs{
SelectNextItem(). SelectNextItem().
PressPrimaryAction() // choose 'content4' PressPrimaryAction() // choose 'content4'
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.Views().Status().Content(Equals("↑2 repo → master")) t.Views().Status().Content(Equals("↑2 repo → master"))

View file

@ -83,7 +83,7 @@ var PullRebaseInteractiveConflictDrop = NewIntegrationTest(NewIntegrationTestArg
SelectNextItem(). SelectNextItem().
PressPrimaryAction() // choose 'content4' PressPrimaryAction() // choose 'content4'
t.Common().ContinueOnConflictsResolved() t.Common().ContinueOnConflictsResolved("rebase")
t.Views().Status().Content(Equals("↑1 repo → master")) t.Views().Status().Content(Equals("↑1 repo → master"))

View file

@ -128,6 +128,7 @@ var tests = []*components.IntegrationTest{
commit.ResetAuthorRange, commit.ResetAuthorRange,
commit.Revert, commit.Revert,
commit.RevertMerge, commit.RevertMerge,
commit.RevertWithConflictSingleCommit,
commit.Reword, commit.Reword,
commit.Search, commit.Search,
commit.SetAuthor, commit.SetAuthor,
@ -260,6 +261,7 @@ var tests = []*components.IntegrationTest{
interactive_rebase.QuickStartKeepSelection, interactive_rebase.QuickStartKeepSelection,
interactive_rebase.QuickStartKeepSelectionRange, interactive_rebase.QuickStartKeepSelectionRange,
interactive_rebase.Rebase, interactive_rebase.Rebase,
interactive_rebase.RebaseWithCommitThatBecomesEmpty,
interactive_rebase.RewordCommitWithEditorAndFail, interactive_rebase.RewordCommitWithEditorAndFail,
interactive_rebase.RewordFirstCommit, interactive_rebase.RewordFirstCommit,
interactive_rebase.RewordLastCommit, interactive_rebase.RewordLastCommit,