Make WorkingTreeState a struct, and add cherry-picking and reverting states

This commit is contained in:
Stefan Haller 2024-06-10 17:54:04 +02:00
parent 8af8f7754b
commit 542525743c
17 changed files with 176 additions and 95 deletions

View file

@ -171,7 +171,7 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
} }
} }
if !self.getWorkingTreeState().IsRebasing() { if !self.getWorkingTreeState().Rebasing {
// not in rebase mode so return original commits // not in rebase mode so return original commits
return result, nil return result, nil
} }

View file

@ -303,7 +303,7 @@ func TestGetCommits(t *testing.T) {
builder := &CommitLoader{ builder := &CommitLoader{
Common: common, Common: common,
cmd: cmd, cmd: cmd,
getWorkingTreeState: func() models.WorkingTreeState { return models.WORKING_TREE_STATE_NONE }, 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
@ -486,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)),
getWorkingTreeState: func() models.WorkingTreeState { return models.WORKING_TREE_STATE_REBASING }, 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

@ -228,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() == models.WORKING_TREE_STATE_REBASING { if self.status.WorkingTreeState().Rebasing {
_ = self.rebase.AbortRebase() _ = self.rebase.AbortRebase()
} }
return err return err
@ -252,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() == models.WORKING_TREE_STATE_REBASING { if self.status.WorkingTreeState().Rebasing {
_ = self.rebase.AbortRebase() _ = self.rebase.AbortRebase()
} }
return err return err

View file

@ -21,15 +21,12 @@ func NewStatusCommands(
} }
func (self *StatusCommands) WorkingTreeState() models.WorkingTreeState { func (self *StatusCommands) WorkingTreeState() models.WorkingTreeState {
isInRebase, _ := self.IsInRebase() result := models.WorkingTreeState{}
if isInRebase { result.Rebasing, _ = self.IsInRebase()
return models.WORKING_TREE_STATE_REBASING result.Merging, _ = self.IsInMergeState()
} result.CherryPicking, _ = self.IsInCherryPick()
merging, _ := self.IsInMergeState() result.Reverting, _ = self.IsInRevert()
if merging { return result
return models.WORKING_TREE_STATE_MERGING
}
return models.WORKING_TREE_STATE_NONE
} }
func (self *StatusCommands) IsBareRepo() bool { func (self *StatusCommands) IsBareRepo() bool {
@ -49,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

@ -2,62 +2,111 @@ package models
import "github.com/jesseduffield/lazygit/pkg/i18n" import "github.com/jesseduffield/lazygit/pkg/i18n"
type WorkingTreeState int // 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
const ( // practice are Rebasing+CherryPicking, and Rebasing+Reverting. Theoretically, I
// this means we're neither rebasing nor merging // guess Rebasing+Merging could also happen, but it probably won't in practice.
WORKING_TREE_STATE_NONE WorkingTreeState = iota type WorkingTreeState struct {
WORKING_TREE_STATE_REBASING Rebasing bool
WORKING_TREE_STATE_MERGING Merging bool
) CherryPicking bool
Reverting bool
func (self WorkingTreeState) IsMerging() bool {
return self == WORKING_TREE_STATE_MERGING
} }
func (self WorkingTreeState) IsRebasing() bool { func (self WorkingTreeState) Any() bool {
return self == WORKING_TREE_STATE_REBASING 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 { func (self WorkingTreeState) Title(tr *i18n.TranslationSet) string {
switch self { return map[EffectiveWorkingTreeState]string{
case WORKING_TREE_STATE_REBASING: WORKING_TREE_STATE_REBASING: tr.RebasingStatus,
return tr.RebasingStatus WORKING_TREE_STATE_MERGING: tr.MergingStatus,
case WORKING_TREE_STATE_MERGING: WORKING_TREE_STATE_CHERRY_PICKING: tr.CherryPickingStatus,
return tr.MergingStatus WORKING_TREE_STATE_REVERTING: tr.RevertingStatus,
default: }[self.Effective()]
// should never actually display this
return "none"
}
} }
func (self WorkingTreeState) LowerCaseTitle(tr *i18n.TranslationSet) string { func (self WorkingTreeState) LowerCaseTitle(tr *i18n.TranslationSet) string {
switch self { return map[EffectiveWorkingTreeState]string{
case WORKING_TREE_STATE_REBASING: WORKING_TREE_STATE_REBASING: tr.LowercaseRebasingStatus,
return tr.LowercaseRebasingStatus WORKING_TREE_STATE_MERGING: tr.LowercaseMergingStatus,
case WORKING_TREE_STATE_MERGING: WORKING_TREE_STATE_CHERRY_PICKING: tr.LowercaseCherryPickingStatus,
return tr.LowercaseMergingStatus WORKING_TREE_STATE_REVERTING: tr.LowercaseRevertingStatus,
default: }[self.Effective()]
// should never actually display this
return "none"
}
} }
func (self WorkingTreeState) OptionsMenuTitle(tr *i18n.TranslationSet) string { func (self WorkingTreeState) OptionsMenuTitle(tr *i18n.TranslationSet) string {
if self == WORKING_TREE_STATE_MERGING { return map[EffectiveWorkingTreeState]string{
return tr.MergeOptionsTitle WORKING_TREE_STATE_REBASING: tr.RebaseOptionsTitle,
} WORKING_TREE_STATE_MERGING: tr.MergeOptionsTitle,
return tr.RebaseOptionsTitle 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 { func (self WorkingTreeState) CommandName() string {
switch self { return map[EffectiveWorkingTreeState]string{
case WORKING_TREE_STATE_MERGING: WORKING_TREE_STATE_REBASING: "rebase",
return "merge" WORKING_TREE_STATE_MERGING: "merge",
case WORKING_TREE_STATE_REBASING: WORKING_TREE_STATE_CHERRY_PICKING: "cherry-pick",
return "rebase" WORKING_TREE_STATE_REVERTING: "revert",
default: }[self.Effective()]
// shouldn't be possible to land here }
return ""
} 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

@ -40,7 +40,7 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
} }
} }
showYouAreHereLabel := c.Model().WorkingTreeStateAtLastCommitRefresh == models.WORKING_TREE_STATE_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/models"
"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() == models.WORKING_TREE_STATE_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() != models.WORKING_TREE_STATE_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

@ -50,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() == models.WORKING_TREE_STATE_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',
}) })
@ -77,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 != models.WORKING_TREE_STATE_MERGING && status != models.WORKING_TREE_STATE_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 == models.WORKING_TREE_STATE_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"),
) )
@ -101,10 +102,10 @@ func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error {
// 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 == models.WORKING_TREE_STATE_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 == models.WORKING_TREE_STATE_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

View file

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"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"
@ -115,7 +114,7 @@ func (self *ModeHelper) Statuses() []ModeStatus {
}, },
{ {
IsActive: func() bool { IsActive: func() bool {
return !self.suppressRebasingMode && self.c.Git().Status.WorkingTreeState() != models.WORKING_TREE_STATE_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()

View file

@ -3,7 +3,6 @@ package helpers
import ( import (
"errors" "errors"
"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/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() != models.WORKING_TREE_STATE_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

@ -582,7 +582,7 @@ func (self *RefreshHelper) refreshStateFiles() error {
} }
} }
if self.c.Git().Status.WorkingTreeState() != models.WORKING_TREE_STATE_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

@ -681,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 != models.WORKING_TREE_STATE_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 {
@ -975,7 +975,7 @@ func (self *LocalCommitsController) moveFixupCommitToOwnerStackedBranch(targetCo
return nil return nil
} }
if self.c.Git().Status.WorkingTreeState() != models.WORKING_TREE_STATE_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/models"
"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,8 +108,7 @@ 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 models.WORKING_TREE_STATE_REBASING, models.WORKING_TREE_STATE_MERGING:
workingTreeStatus := fmt.Sprintf("(%s)", workingTreeState.LowerCaseTitle(self.c.Tr)) workingTreeStatus := fmt.Sprintf("(%s)", workingTreeState.LowerCaseTitle(self.c.Tr))
if cursorInSubstring(opts.X, upstreamStatus+" ", workingTreeStatus) { if cursorInSubstring(opts.X, upstreamStatus+" ", workingTreeStatus) {
return self.c.Helpers().MergeAndRebase.CreateRebaseOptionsMenu() return self.c.Helpers().MergeAndRebase.CreateRebaseOptionsMenu()
@ -118,11 +116,9 @@ func (self *StatusController) onClick(opts gocui.ViewMouseBindingOpts) error {
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/models"
"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() == models.WORKING_TREE_STATE_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() == models.WORKING_TREE_STATE_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

@ -30,7 +30,7 @@ func FormatStatus(
} }
} }
if workingTreeState != models.WORKING_TREE_STATE_NONE { if workingTreeState.Any() {
status += style.FgYellow.Sprintf("(%s) ", workingTreeState.LowerCaseTitle(tr)) status += style.FgYellow.Sprintf("(%s) ", workingTreeState.LowerCaseTitle(tr))
} }

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