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
return result, nil
}

View file

@ -303,7 +303,7 @@ func TestGetCommits(t *testing.T) {
builder := &CommitLoader{
Common: common,
cmd: cmd,
getWorkingTreeState: func() models.WorkingTreeState { return models.WORKING_TREE_STATE_NONE },
getWorkingTreeState: func() models.WorkingTreeState { return models.WorkingTreeState{} },
dotGitDir: ".git",
readFile: func(filename string) ([]byte, error) {
return []byte(""), nil
@ -486,7 +486,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
builder := &CommitLoader{
Common: common,
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",
readFile: func(filename string) ([]byte, error) {
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 self.status.WorkingTreeState() == models.WORKING_TREE_STATE_REBASING {
if self.status.WorkingTreeState().Rebasing {
_ = self.rebase.AbortRebase()
}
return err
@ -252,7 +252,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
self.rebase.onSuccessfulContinue = func() error {
// add patches to index
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()
}
return err

View file

@ -21,15 +21,12 @@ func NewStatusCommands(
}
func (self *StatusCommands) WorkingTreeState() models.WorkingTreeState {
isInRebase, _ := self.IsInRebase()
if isInRebase {
return models.WORKING_TREE_STATE_REBASING
}
merging, _ := self.IsInMergeState()
if merging {
return models.WORKING_TREE_STATE_MERGING
}
return models.WORKING_TREE_STATE_NONE
result := models.WorkingTreeState{}
result.Rebasing, _ = self.IsInRebase()
result.Merging, _ = self.IsInMergeState()
result.CherryPicking, _ = self.IsInCherryPick()
result.Reverting, _ = self.IsInRevert()
return result
}
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"))
}
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
// being rebased, or empty string when we're not in a rebase
func (self *StatusCommands) BranchBeingRebased() string {

View file

@ -2,62 +2,111 @@ package models
import "github.com/jesseduffield/lazygit/pkg/i18n"
type WorkingTreeState int
const (
// this means we're neither rebasing nor merging
WORKING_TREE_STATE_NONE WorkingTreeState = iota
WORKING_TREE_STATE_REBASING
WORKING_TREE_STATE_MERGING
)
func (self WorkingTreeState) IsMerging() bool {
return self == WORKING_TREE_STATE_MERGING
// 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) IsRebasing() bool {
return self == WORKING_TREE_STATE_REBASING
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 {
switch self {
case WORKING_TREE_STATE_REBASING:
return tr.RebasingStatus
case WORKING_TREE_STATE_MERGING:
return tr.MergingStatus
default:
// should never actually display this
return "none"
}
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 {
switch self {
case WORKING_TREE_STATE_REBASING:
return tr.LowercaseRebasingStatus
case WORKING_TREE_STATE_MERGING:
return tr.LowercaseMergingStatus
default:
// should never actually display this
return "none"
}
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 {
if self == WORKING_TREE_STATE_MERGING {
return tr.MergeOptionsTitle
}
return tr.RebaseOptionsTitle
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 {
switch self {
case WORKING_TREE_STATE_MERGING:
return "merge"
case WORKING_TREE_STATE_REBASING:
return "rebase"
default:
// shouldn't be possible to land here
return ""
}
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

@ -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()
return presentation.GetCommitListDisplayStrings(

View file

@ -5,7 +5,6 @@ import (
"fmt"
"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/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{
{
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) {
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 true, nil

View file

@ -50,7 +50,7 @@ func (self *MergeAndRebaseHelper) CreateRebaseOptionsMenu() error {
{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{
option: REBASE_OPTION_SKIP, key: 's',
})
@ -77,12 +77,13 @@ func (self *MergeAndRebaseHelper) ContinueRebase() error {
func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error {
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)
}
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(
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'
// 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
// 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 {
// TODO: see if we should be calling more of the code from self.Git.Rebase.GenericMergeOrRebaseAction

View file

@ -4,7 +4,6 @@ import (
"fmt"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/samber/lo"
@ -115,7 +114,7 @@ func (self *ModeHelper) Statuses() []ModeStatus {
},
{
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 {
workingTreeState := self.c.Git().Status.WorkingTreeState()

View file

@ -3,7 +3,6 @@ package helpers
import (
"errors"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/patch"
"github.com/jesseduffield/lazygit/pkg/gui/patch_exploring"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@ -22,7 +21,7 @@ func NewPatchBuildingHelper(
}
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 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() })
}

View file

@ -681,7 +681,7 @@ func (self *LocalCommitsController) rewordEnabled(commit *models.Commit) *types.
}
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 {
@ -975,7 +975,7 @@ func (self *LocalCommitsController) moveFixupCommitToOwnerStackedBranch(targetCo
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
return nil
}

View file

@ -7,7 +7,6 @@ import (
"time"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/constants"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"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()))
repoName := self.c.Git().RepoPaths.RepoName()
workingTreeState := self.c.Git().Status.WorkingTreeState()
switch workingTreeState {
case models.WORKING_TREE_STATE_REBASING, models.WORKING_TREE_STATE_MERGING:
if workingTreeState.Any() {
workingTreeStatus := fmt.Sprintf("(%s)", workingTreeState.LowerCaseTitle(self.c.Tr))
if cursorInSubstring(opts.X, upstreamStatus+" ", workingTreeStatus) {
return self.c.Helpers().MergeAndRebase.CreateRebaseOptionsMenu()
@ -118,10 +116,8 @@ func (self *StatusController) onClick(opts gocui.ViewMouseBindingOpts) error {
if cursorInSubstring(opts.X, upstreamStatus+" "+workingTreeStatus+" ", repoName) {
return self.c.Helpers().Repos.CreateRecentReposMenu()
}
default:
if cursorInSubstring(opts.X, upstreamStatus+" ", repoName) {
return self.c.Helpers().Repos.CreateRecentReposMenu()
}
} else if cursorInSubstring(opts.X, upstreamStatus+" ", repoName) {
return self.c.Helpers().Repos.CreateRecentReposMenu()
}
return nil

View file

@ -5,7 +5,6 @@ import (
"fmt"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -78,7 +77,7 @@ func (self *UndoController) reflogUndo() error {
undoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit undo]"}
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)
}
@ -142,7 +141,7 @@ func (self *UndoController) reflogRedo() error {
redoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit redo]"}
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)
}

View file

@ -82,16 +82,10 @@ func (self *OptionsMapMgr) renderContextOptionsMap() {
}
// Mode-specific global keybindings
if self.c.Model().WorkingTreeStateAtLastCommitRefresh.IsRebasing() {
if state := self.c.Model().WorkingTreeStateAtLastCommitRefresh; state.Any() {
optionsMap = utils.Prepend(optionsMap, bindingInfo{
key: keybindings.Label(self.c.KeybindingsOpts().Config.Universal.CreateRebaseOptionsMenu),
description: self.c.Tr.ViewRebaseOptions,
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,
description: state.OptionsMapTitle(self.c.Tr),
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))
}

View file

@ -306,11 +306,15 @@ type TranslationSet struct {
ViewMergeRebaseOptionsTooltip string
ViewMergeOptions string
ViewRebaseOptions string
ViewCherryPickOptions string
ViewRevertOptions string
NotMergingOrRebasing string
AlreadyRebasing string
RecentRepos string
MergeOptionsTitle string
RebaseOptionsTitle string
CherryPickOptionsTitle string
RevertOptionsTitle string
CommitSummaryTitle string
CommitDescriptionTitle string
CommitDescriptionSubTitle string
@ -392,6 +396,8 @@ type TranslationSet struct {
MergingStatus string
LowercaseRebasingStatus string
LowercaseMergingStatus string
LowercaseCherryPickingStatus string
LowercaseRevertingStatus string
AmendingStatus string
CherryPickingStatus string
UndoingStatus string
@ -1359,11 +1365,15 @@ func EnglishTranslationSet() *TranslationSet {
ViewMergeRebaseOptionsTooltip: "View options to abort/continue/skip the current merge/rebase.",
ViewMergeOptions: "View merge options",
ViewRebaseOptions: "View rebase options",
ViewCherryPickOptions: "View cherry-pick options",
ViewRevertOptions: "View revert options",
NotMergingOrRebasing: "You are currently neither rebasing nor merging",
AlreadyRebasing: "Can't perform this action during a rebase",
RecentRepos: "Recent repositories",
MergeOptionsTitle: "Merge options",
RebaseOptionsTitle: "Rebase options",
CherryPickOptionsTitle: "Cherry-pick options",
RevertOptionsTitle: "Revert options",
CommitSummaryTitle: "Commit summary",
CommitDescriptionTitle: "Commit description",
CommitDescriptionSubTitle: "Press {{.togglePanelKeyBinding}} to toggle focus, {{.commitMenuKeybinding}} to open menu",
@ -1451,8 +1461,10 @@ func EnglishTranslationSet() *TranslationSet {
MovingStatus: "Moving",
RebasingStatus: "Rebasing",
MergingStatus: "Merging",
LowercaseRebasingStatus: "rebasing", // lowercase because it shows up in parentheses
LowercaseMergingStatus: "merging", // 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
LowercaseCherryPickingStatus: "cherry-picking", // lowercase because it shows up in parentheses
LowercaseRevertingStatus: "reverting", // lowercase because it shows up in parentheses
AmendingStatus: "Amending",
CherryPickingStatus: "Cherry-picking",
UndoingStatus: "Undoing",