mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-11-24 08:52:21 +02:00
Support range select in rebase actions
This commit is contained in:
parent
44e2542e4a
commit
f0de880136
@ -34,8 +34,8 @@ const (
|
||||
|
||||
DaemonKindExitImmediately
|
||||
DaemonKindCherryPick
|
||||
DaemonKindMoveTodoUp
|
||||
DaemonKindMoveTodoDown
|
||||
DaemonKindMoveTodosUp
|
||||
DaemonKindMoveTodosDown
|
||||
DaemonKindInsertBreak
|
||||
DaemonKindChangeTodoActions
|
||||
DaemonKindMoveFixupCommitDown
|
||||
@ -56,8 +56,8 @@ func getInstruction() Instruction {
|
||||
DaemonKindCherryPick: deserializeInstruction[*CherryPickCommitsInstruction],
|
||||
DaemonKindChangeTodoActions: deserializeInstruction[*ChangeTodoActionsInstruction],
|
||||
DaemonKindMoveFixupCommitDown: deserializeInstruction[*MoveFixupCommitDownInstruction],
|
||||
DaemonKindMoveTodoUp: deserializeInstruction[*MoveTodoUpInstruction],
|
||||
DaemonKindMoveTodoDown: deserializeInstruction[*MoveTodoDownInstruction],
|
||||
DaemonKindMoveTodosUp: deserializeInstruction[*MoveTodosUpInstruction],
|
||||
DaemonKindMoveTodosDown: deserializeInstruction[*MoveTodosDownInstruction],
|
||||
DaemonKindInsertBreak: deserializeInstruction[*InsertBreakInstruction],
|
||||
}
|
||||
|
||||
@ -208,13 +208,15 @@ func (self *ChangeTodoActionsInstruction) SerializedInstructions() string {
|
||||
|
||||
func (self *ChangeTodoActionsInstruction) run(common *common.Common) error {
|
||||
return handleInteractiveRebase(common, func(path string) error {
|
||||
for _, c := range self.Changes {
|
||||
if err := utils.EditRebaseTodo(path, c.Sha, todo.Pick, c.NewAction, getCommentChar()); err != nil {
|
||||
return err
|
||||
changes := lo.Map(self.Changes, func(c ChangeTodoAction, _ int) utils.TodoChange {
|
||||
return utils.TodoChange{
|
||||
Sha: c.Sha,
|
||||
OldAction: todo.Pick,
|
||||
NewAction: c.NewAction,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
return utils.EditRebaseTodo(path, changes, getCommentChar())
|
||||
})
|
||||
}
|
||||
|
||||
@ -247,51 +249,65 @@ func (self *MoveFixupCommitDownInstruction) run(common *common.Common) error {
|
||||
})
|
||||
}
|
||||
|
||||
type MoveTodoUpInstruction struct {
|
||||
Sha string
|
||||
type MoveTodosUpInstruction struct {
|
||||
Shas []string
|
||||
}
|
||||
|
||||
func NewMoveTodoUpInstruction(sha string) Instruction {
|
||||
return &MoveTodoUpInstruction{
|
||||
Sha: sha,
|
||||
func NewMoveTodosUpInstruction(shas []string) Instruction {
|
||||
return &MoveTodosUpInstruction{
|
||||
Shas: shas,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *MoveTodoUpInstruction) Kind() DaemonKind {
|
||||
return DaemonKindMoveTodoUp
|
||||
func (self *MoveTodosUpInstruction) Kind() DaemonKind {
|
||||
return DaemonKindMoveTodosUp
|
||||
}
|
||||
|
||||
func (self *MoveTodoUpInstruction) SerializedInstructions() string {
|
||||
func (self *MoveTodosUpInstruction) SerializedInstructions() string {
|
||||
return serializeInstruction(self)
|
||||
}
|
||||
|
||||
func (self *MoveTodoUpInstruction) run(common *common.Common) error {
|
||||
func (self *MoveTodosUpInstruction) run(common *common.Common) error {
|
||||
todosToMove := lo.Map(self.Shas, func(sha string, _ int) utils.Todo {
|
||||
return utils.Todo{
|
||||
Sha: sha,
|
||||
Action: todo.Pick,
|
||||
}
|
||||
})
|
||||
|
||||
return handleInteractiveRebase(common, func(path string) error {
|
||||
return utils.MoveTodoUp(path, self.Sha, todo.Pick, getCommentChar())
|
||||
return utils.MoveTodosUp(path, todosToMove, getCommentChar())
|
||||
})
|
||||
}
|
||||
|
||||
type MoveTodoDownInstruction struct {
|
||||
Sha string
|
||||
type MoveTodosDownInstruction struct {
|
||||
Shas []string
|
||||
}
|
||||
|
||||
func NewMoveTodoDownInstruction(sha string) Instruction {
|
||||
return &MoveTodoDownInstruction{
|
||||
Sha: sha,
|
||||
func NewMoveTodosDownInstruction(shas []string) Instruction {
|
||||
return &MoveTodosDownInstruction{
|
||||
Shas: shas,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *MoveTodoDownInstruction) Kind() DaemonKind {
|
||||
return DaemonKindMoveTodoDown
|
||||
func (self *MoveTodosDownInstruction) Kind() DaemonKind {
|
||||
return DaemonKindMoveTodosDown
|
||||
}
|
||||
|
||||
func (self *MoveTodoDownInstruction) SerializedInstructions() string {
|
||||
func (self *MoveTodosDownInstruction) SerializedInstructions() string {
|
||||
return serializeInstruction(self)
|
||||
}
|
||||
|
||||
func (self *MoveTodoDownInstruction) run(common *common.Common) error {
|
||||
func (self *MoveTodosDownInstruction) run(common *common.Common) error {
|
||||
todosToMove := lo.Map(self.Shas, func(sha string, _ int) utils.Todo {
|
||||
return utils.Todo{
|
||||
Sha: sha,
|
||||
Action: todo.Pick,
|
||||
}
|
||||
})
|
||||
|
||||
return handleInteractiveRebase(common, func(path string) error {
|
||||
return utils.MoveTodoDown(path, self.Sha, todo.Pick, getCommentChar())
|
||||
return utils.MoveTodosDown(path, todosToMove, getCommentChar())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -105,58 +105,49 @@ func (self *RebaseCommands) GenericAmend(commits []*models.Commit, index int, f
|
||||
return self.ContinueRebase()
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) MoveCommitDown(commits []*models.Commit, index int) error {
|
||||
baseShaOrRoot := getBaseShaOrRoot(commits, index+2)
|
||||
func (self *RebaseCommands) MoveCommitsDown(commits []*models.Commit, startIdx int, endIdx int) error {
|
||||
baseShaOrRoot := getBaseShaOrRoot(commits, endIdx+2)
|
||||
|
||||
sha := commits[index].Sha
|
||||
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.MoveCommitDown,
|
||||
map[string]string{
|
||||
"shortSha": utils.ShortSha(sha),
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
shas := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) string {
|
||||
return commit.Sha
|
||||
})
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: baseShaOrRoot,
|
||||
instruction: daemon.NewMoveTodoDownInstruction(sha),
|
||||
instruction: daemon.NewMoveTodosDownInstruction(shas),
|
||||
overrideEditor: true,
|
||||
}).Run()
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) MoveCommitUp(commits []*models.Commit, index int) error {
|
||||
baseShaOrRoot := getBaseShaOrRoot(commits, index+1)
|
||||
func (self *RebaseCommands) MoveCommitsUp(commits []*models.Commit, startIdx int, endIdx int) error {
|
||||
baseShaOrRoot := getBaseShaOrRoot(commits, endIdx+1)
|
||||
|
||||
sha := commits[index].Sha
|
||||
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.MoveCommitUp,
|
||||
map[string]string{
|
||||
"shortSha": utils.ShortSha(sha),
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
shas := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) string {
|
||||
return commit.Sha
|
||||
})
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: baseShaOrRoot,
|
||||
instruction: daemon.NewMoveTodoUpInstruction(sha),
|
||||
instruction: daemon.NewMoveTodosUpInstruction(shas),
|
||||
overrideEditor: true,
|
||||
}).Run()
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, index int, action todo.TodoCommand) error {
|
||||
baseIndex := index + 1
|
||||
func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, startIdx int, endIdx int, action todo.TodoCommand) error {
|
||||
baseIndex := endIdx + 1
|
||||
if action == todo.Squash || action == todo.Fixup {
|
||||
baseIndex++
|
||||
}
|
||||
|
||||
baseShaOrRoot := getBaseShaOrRoot(commits, baseIndex)
|
||||
|
||||
changes := []daemon.ChangeTodoAction{{
|
||||
Sha: commits[index].Sha,
|
||||
NewAction: action,
|
||||
}}
|
||||
changes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) daemon.ChangeTodoAction {
|
||||
return daemon.ChangeTodoAction{
|
||||
Sha: commit.Sha,
|
||||
NewAction: action,
|
||||
}
|
||||
})
|
||||
|
||||
self.os.LogCommand(logTodoChanges(changes), false)
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
@ -200,7 +191,7 @@ func logTodoChanges(changes []daemon.ChangeTodoAction) string {
|
||||
changeTodoStr := strings.Join(lo.Map(changes, func(c daemon.ChangeTodoAction, _ int) string {
|
||||
return fmt.Sprintf("%s:%s", c.Sha, c.NewAction)
|
||||
}), "\n")
|
||||
return fmt.Sprintf("Changing TODO actions: %s", changeTodoStr)
|
||||
return fmt.Sprintf("Changing TODO actions:\n%s", changeTodoStr)
|
||||
}
|
||||
|
||||
type PrepareInteractiveRebaseCommandOpts struct {
|
||||
@ -281,22 +272,45 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e
|
||||
}).Run()
|
||||
}
|
||||
|
||||
// EditRebaseTodo sets the action for a given rebase commit in the git-rebase-todo file
|
||||
func (self *RebaseCommands) EditRebaseTodo(commit *models.Commit, action todo.TodoCommand) error {
|
||||
// Sets the action for the given commits in the git-rebase-todo file
|
||||
func (self *RebaseCommands) EditRebaseTodo(commits []*models.Commit, action todo.TodoCommand) error {
|
||||
commitsWithAction := lo.Map(commits, func(commit *models.Commit, _ int) utils.TodoChange {
|
||||
return utils.TodoChange{
|
||||
Sha: commit.Sha,
|
||||
OldAction: commit.Action,
|
||||
NewAction: action,
|
||||
}
|
||||
})
|
||||
|
||||
return utils.EditRebaseTodo(
|
||||
filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"), commit.Sha, commit.Action, action, self.config.GetCoreCommentChar())
|
||||
filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"),
|
||||
commitsWithAction,
|
||||
self.config.GetCoreCommentChar(),
|
||||
)
|
||||
}
|
||||
|
||||
// MoveTodoDown moves a rebase todo item down by one position
|
||||
func (self *RebaseCommands) MoveTodoDown(commit *models.Commit) error {
|
||||
func (self *RebaseCommands) MoveTodosDown(commits []*models.Commit) error {
|
||||
fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")
|
||||
return utils.MoveTodoDown(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar())
|
||||
todosToMove := lo.Map(commits, func(commit *models.Commit, _ int) utils.Todo {
|
||||
return utils.Todo{
|
||||
Sha: commit.Sha,
|
||||
Action: commit.Action,
|
||||
}
|
||||
})
|
||||
|
||||
return utils.MoveTodosDown(fileName, todosToMove, self.config.GetCoreCommentChar())
|
||||
}
|
||||
|
||||
// MoveTodoDown moves a rebase todo item down by one position
|
||||
func (self *RebaseCommands) MoveTodoUp(commit *models.Commit) error {
|
||||
func (self *RebaseCommands) MoveTodosUp(commits []*models.Commit) error {
|
||||
fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")
|
||||
return utils.MoveTodoUp(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar())
|
||||
todosToMove := lo.Map(commits, func(commit *models.Commit, _ int) utils.Todo {
|
||||
return utils.Todo{
|
||||
Sha: commit.Sha,
|
||||
Action: commit.Action,
|
||||
}
|
||||
})
|
||||
|
||||
return utils.MoveTodosUp(fileName, todosToMove, self.config.GetCoreCommentChar())
|
||||
}
|
||||
|
||||
// SquashAllAboveFixupCommits squashes all fixup! commits above the given one
|
||||
|
@ -2,6 +2,8 @@ package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
@ -80,6 +82,19 @@ func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error {
|
||||
}
|
||||
|
||||
self.c.LogAction(fmt.Sprintf("Merge/Rebase: %s", command))
|
||||
if status == enums.REBASE_MODE_REBASING {
|
||||
todoFile, err := os.ReadFile(
|
||||
filepath.Join(self.c.Git().RepoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
self.c.LogCommand(string(todoFile), false)
|
||||
}
|
||||
}
|
||||
|
||||
commandType := ""
|
||||
switch status {
|
||||
|
@ -45,6 +45,7 @@ func NewLocalCommitsController(
|
||||
c,
|
||||
c.Contexts().LocalCommits,
|
||||
c.Contexts().LocalCommits.GetSelected,
|
||||
c.Contexts().LocalCommits.GetSelectedItems,
|
||||
),
|
||||
}
|
||||
}
|
||||
@ -55,17 +56,23 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
||||
outsideFilterModeBindings := []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.SquashDown),
|
||||
Handler: self.withItem(self.squashDown),
|
||||
Handler: self.withItemsRange(self.squashDown),
|
||||
GetDisabledReason: self.require(
|
||||
self.singleItemSelected(self.getDisabledReasonForSquashDown),
|
||||
self.itemRangeSelected(
|
||||
self.midRebaseCommandEnabled,
|
||||
self.canSquashOrFixup,
|
||||
),
|
||||
),
|
||||
Description: self.c.Tr.SquashDown,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.MarkCommitAsFixup),
|
||||
Handler: self.withItem(self.fixup),
|
||||
Handler: self.withItemsRange(self.fixup),
|
||||
GetDisabledReason: self.require(
|
||||
self.singleItemSelected(self.getDisabledReasonForFixup),
|
||||
self.itemRangeSelected(
|
||||
self.midRebaseCommandEnabled,
|
||||
self.canSquashOrFixup,
|
||||
),
|
||||
),
|
||||
Description: self.c.Tr.FixupCommit,
|
||||
},
|
||||
@ -73,7 +80,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
||||
Key: opts.GetKey(opts.Config.Commits.RenameCommit),
|
||||
Handler: self.withItem(self.reword),
|
||||
GetDisabledReason: self.require(
|
||||
self.singleItemSelected(self.rebaseCommandEnabled(todo.Reword)),
|
||||
self.singleItemSelected(self.rewordEnabled),
|
||||
),
|
||||
Description: self.c.Tr.RewordCommit,
|
||||
},
|
||||
@ -81,23 +88,26 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
||||
Key: opts.GetKey(opts.Config.Commits.RenameCommitWithEditor),
|
||||
Handler: self.withItem(self.rewordEditor),
|
||||
GetDisabledReason: self.require(
|
||||
self.singleItemSelected(self.rebaseCommandEnabled(todo.Reword)),
|
||||
self.singleItemSelected(self.rewordEnabled),
|
||||
),
|
||||
Description: self.c.Tr.RenameCommitEditor,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.withItem(self.drop),
|
||||
Handler: self.withItemsRange(self.drop),
|
||||
GetDisabledReason: self.require(
|
||||
self.singleItemSelected(self.rebaseCommandEnabled(todo.Drop)),
|
||||
self.itemRangeSelected(
|
||||
self.midRebaseCommandEnabled,
|
||||
),
|
||||
),
|
||||
Description: self.c.Tr.DeleteCommit,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(editCommitKey),
|
||||
Handler: self.withItem(self.edit),
|
||||
Handler: self.withItems(self.edit),
|
||||
// TODO: have disabled reason ensure that if we're not rebasing, we only select one commit
|
||||
GetDisabledReason: self.require(
|
||||
self.singleItemSelected(self.rebaseCommandEnabled(todo.Edit)),
|
||||
self.itemRangeSelected(self.midRebaseCommandEnabled),
|
||||
),
|
||||
Description: self.c.Tr.EditCommit,
|
||||
},
|
||||
@ -107,7 +117,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
||||
// when you manually select the base commit.
|
||||
Key: opts.GetKey(opts.Config.Commits.StartInteractiveRebase),
|
||||
Handler: self.withItem(self.quickStartInteractiveRebase),
|
||||
GetDisabledReason: self.require(self.notMidRebase, self.canFindCommitForQuickStart),
|
||||
GetDisabledReason: self.require(self.notMidRebase(self.c.Tr.AlreadyRebasing), self.canFindCommitForQuickStart),
|
||||
Description: self.c.Tr.QuickStartInteractiveRebase,
|
||||
Tooltip: utils.ResolvePlaceholderString(self.c.Tr.QuickStartInteractiveRebaseTooltip, map[string]string{
|
||||
"editKey": keybindings.Label(editCommitKey),
|
||||
@ -115,9 +125,9 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.PickCommit),
|
||||
Handler: self.withItem(self.pick),
|
||||
Handler: self.withItems(self.pick),
|
||||
GetDisabledReason: self.require(
|
||||
self.singleItemSelected(self.rebaseCommandEnabled(todo.Pick)),
|
||||
self.itemRangeSelected(self.pickEnabled),
|
||||
),
|
||||
Description: self.c.Tr.PickCommit,
|
||||
},
|
||||
@ -131,22 +141,28 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
||||
Key: opts.GetKey(opts.Config.Commits.SquashAboveCommits),
|
||||
Handler: self.withItem(self.squashAllAboveFixupCommits),
|
||||
GetDisabledReason: self.require(
|
||||
self.notMidRebase,
|
||||
self.notMidRebase(self.c.Tr.AlreadyRebasing),
|
||||
self.singleItemSelected(),
|
||||
),
|
||||
Description: self.c.Tr.SquashAboveCommits,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.MoveDownCommit),
|
||||
Handler: self.withItem(self.moveDown),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.MoveDownCommit,
|
||||
Key: opts.GetKey(opts.Config.Commits.MoveDownCommit),
|
||||
Handler: self.withItemsRange(self.moveDown),
|
||||
GetDisabledReason: self.require(self.itemRangeSelected(
|
||||
self.midRebaseCommandEnabled,
|
||||
self.canMoveDown,
|
||||
)),
|
||||
Description: self.c.Tr.MoveDownCommit,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.MoveUpCommit),
|
||||
Handler: self.withItem(self.moveUp),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.MoveUpCommit,
|
||||
Key: opts.GetKey(opts.Config.Commits.MoveUpCommit),
|
||||
Handler: self.withItemsRange(self.moveUp),
|
||||
GetDisabledReason: self.require(self.itemRangeSelected(
|
||||
self.midRebaseCommandEnabled,
|
||||
self.canMoveUp,
|
||||
)),
|
||||
Description: self.c.Tr.MoveUpCommit,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.PasteCommits),
|
||||
@ -263,13 +279,9 @@ func secondaryPatchPanelUpdateOpts(c *ControllerCommon) *types.ViewUpdateOpts {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) squashDown(commit *models.Commit) error {
|
||||
applied, err := self.handleMidRebaseCommand(todo.Squash, commit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if applied {
|
||||
return nil
|
||||
func (self *LocalCommitsController) squashDown(selectedCommits []*models.Commit, startIdx int, endIdx int) error {
|
||||
if self.isRebasing() {
|
||||
return self.updateTodos(todo.Squash, selectedCommits)
|
||||
}
|
||||
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
@ -278,27 +290,15 @@ func (self *LocalCommitsController) squashDown(commit *models.Commit) error {
|
||||
HandleConfirm: func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func(gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.SquashCommitDown)
|
||||
return self.interactiveRebase(todo.Squash)
|
||||
return self.interactiveRebase(todo.Squash, startIdx, endIdx)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) getDisabledReasonForSquashDown(commit *models.Commit) *types.DisabledReason {
|
||||
if self.context().GetSelectedLineIdx() >= len(self.c.Model().Commits)-1 {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CannotSquashOrFixupFirstCommit}
|
||||
}
|
||||
|
||||
return self.rebaseCommandEnabled(todo.Squash)(commit)
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) fixup(commit *models.Commit) error {
|
||||
applied, err := self.handleMidRebaseCommand(todo.Fixup, commit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if applied {
|
||||
return nil
|
||||
func (self *LocalCommitsController) fixup(selectedCommits []*models.Commit, startIdx int, endIdx int) error {
|
||||
if self.isRebasing() {
|
||||
return self.updateTodos(todo.Fixup, selectedCommits)
|
||||
}
|
||||
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
@ -307,29 +307,13 @@ func (self *LocalCommitsController) fixup(commit *models.Commit) error {
|
||||
HandleConfirm: func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.FixingStatus, func(gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.FixupCommit)
|
||||
return self.interactiveRebase(todo.Fixup)
|
||||
return self.interactiveRebase(todo.Fixup, startIdx, endIdx)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) getDisabledReasonForFixup(commit *models.Commit) *types.DisabledReason {
|
||||
if self.context().GetSelectedLineIdx() >= len(self.c.Model().Commits)-1 {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CannotSquashOrFixupFirstCommit}
|
||||
}
|
||||
|
||||
return self.rebaseCommandEnabled(todo.Squash)(commit)
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) reword(commit *models.Commit) error {
|
||||
applied, err := self.handleMidRebaseCommand(todo.Reword, commit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if applied {
|
||||
return nil
|
||||
}
|
||||
|
||||
commitMessage, err := self.c.Git().Commit.GetCommitMessage(commit.Sha)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
@ -404,14 +388,6 @@ func (self *LocalCommitsController) doRewordEditor() error {
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) rewordEditor(commit *models.Commit) error {
|
||||
midRebase, err := self.handleMidRebaseCommand(todo.Reword, commit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if midRebase {
|
||||
return nil
|
||||
}
|
||||
|
||||
if self.c.UserConfig.Gui.SkipRewordInEditorWarning {
|
||||
return self.doRewordEditor()
|
||||
} else {
|
||||
@ -423,37 +399,37 @@ func (self *LocalCommitsController) rewordEditor(commit *models.Commit) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) drop(commit *models.Commit) error {
|
||||
applied, err := self.handleMidRebaseCommand(todo.Drop, commit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if applied {
|
||||
return nil
|
||||
func (self *LocalCommitsController) drop(selectedCommits []*models.Commit, startIdx int, endIdx int) error {
|
||||
if self.isRebasing() {
|
||||
return self.updateTodos(todo.Drop, selectedCommits)
|
||||
}
|
||||
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.DeleteCommitTitle,
|
||||
Prompt: self.c.Tr.DeleteCommitPrompt,
|
||||
Title: self.c.Tr.DropCommitTitle,
|
||||
Prompt: self.c.Tr.DropCommitPrompt,
|
||||
HandleConfirm: func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func(gocui.Task) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.DroppingStatus, func(gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.DropCommit)
|
||||
return self.interactiveRebase(todo.Drop)
|
||||
return self.interactiveRebase(todo.Drop, startIdx, endIdx)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) edit(commit *models.Commit) error {
|
||||
applied, err := self.handleMidRebaseCommand(todo.Edit, commit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if applied {
|
||||
return nil
|
||||
func (self *LocalCommitsController) edit(selectedCommits []*models.Commit) error {
|
||||
if self.isRebasing() {
|
||||
return self.updateTodos(todo.Edit, selectedCommits)
|
||||
}
|
||||
|
||||
return self.startInteractiveRebaseWithEdit(commit, commit)
|
||||
// TODO: support range select here (start a rebase and set the selected commits
|
||||
// to 'edit' in the todo file)
|
||||
if len(selectedCommits) > 1 {
|
||||
return self.c.ErrorMsg(self.c.Tr.RangeSelectNotSupported)
|
||||
}
|
||||
|
||||
selectedCommit := selectedCommits[0]
|
||||
|
||||
return self.startInteractiveRebaseWithEdit(selectedCommit, selectedCommit)
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) quickStartInteractiveRebase(selectedCommit *models.Commit) error {
|
||||
@ -504,13 +480,9 @@ func (self *LocalCommitsController) findCommitForQuickStartInteractiveRebase() (
|
||||
return commit, nil
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) pick(commit *models.Commit) error {
|
||||
applied, err := self.handleMidRebaseCommand(todo.Pick, commit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if applied {
|
||||
return nil
|
||||
func (self *LocalCommitsController) pick(selectedCommits []*models.Commit) error {
|
||||
if self.isRebasing() {
|
||||
return self.updateTodos(todo.Pick, selectedCommits)
|
||||
}
|
||||
|
||||
// at this point we aren't actually rebasing so we will interpret this as an
|
||||
@ -518,159 +490,93 @@ func (self *LocalCommitsController) pick(commit *models.Commit) error {
|
||||
return self.pullFiles()
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) interactiveRebase(action todo.TodoCommand) error {
|
||||
err := self.c.Git().Rebase.InteractiveRebase(self.c.Model().Commits, self.context().GetSelectedLineIdx(), action)
|
||||
func (self *LocalCommitsController) interactiveRebase(action todo.TodoCommand, startIdx int, endIdx int) error {
|
||||
// When performing an action that will remove the selected commits, we need to select the
|
||||
// next commit down (which will end up at the start index after the action is performed)
|
||||
if action == todo.Drop || action == todo.Fixup || action == todo.Squash {
|
||||
self.context().SetSelection(startIdx)
|
||||
}
|
||||
|
||||
err := self.c.Git().Rebase.InteractiveRebase(self.c.Model().Commits, startIdx, endIdx, action)
|
||||
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||
}
|
||||
|
||||
// handleMidRebaseCommand sees if the selected commit is in fact a rebasing
|
||||
// updateTodos sees if the selected commit is in fact a rebasing
|
||||
// commit meaning you are trying to edit the todo file rather than actually
|
||||
// begin a rebase. It then updates the todo file with that action
|
||||
func (self *LocalCommitsController) handleMidRebaseCommand(action todo.TodoCommand, commit *models.Commit) (bool, error) {
|
||||
if !commit.IsTODO() {
|
||||
return false, nil
|
||||
func (self *LocalCommitsController) updateTodos(action todo.TodoCommand, selectedCommits []*models.Commit) error {
|
||||
if err := self.c.Git().Rebase.EditRebaseTodo(selectedCommits, action); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.LogAction("Update rebase TODO")
|
||||
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.c.Tr.Log.HandleMidRebaseCommand,
|
||||
map[string]string{
|
||||
"shortSha": commit.ShortSha(),
|
||||
"action": action.String(),
|
||||
},
|
||||
)
|
||||
self.c.LogCommand(msg, false)
|
||||
|
||||
if err := self.c.Git().Rebase.EditRebaseTodo(commit, action); err != nil {
|
||||
return false, self.c.Error(err)
|
||||
}
|
||||
|
||||
return true, self.c.Refresh(types.RefreshOptions{
|
||||
return self.c.Refresh(types.RefreshOptions{
|
||||
Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) rebaseCommandEnabled(action todo.TodoCommand) func(*models.Commit) *types.DisabledReason {
|
||||
return func(commit *models.Commit) *types.DisabledReason {
|
||||
if commit.Action == models.ActionConflict {
|
||||
return &types.DisabledReason{Text: self.c.Tr.ChangingThisActionIsNotAllowed}
|
||||
}
|
||||
|
||||
if !commit.IsTODO() {
|
||||
if self.c.Model().WorkingTreeStateAtLastCommitRefresh != enums.REBASE_MODE_NONE {
|
||||
// If we are in a rebase, the only action that is allowed for
|
||||
// non-todo commits is rewording the current head commit
|
||||
if !(action == todo.Reword && self.isHeadCommit()) {
|
||||
return &types.DisabledReason{Text: self.c.Tr.AlreadyRebasing}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// for now we do not support setting 'reword' because it requires an editor
|
||||
// and that means we either unconditionally wait around for the subprocess to ask for
|
||||
// our input or we set a lazygit client as the EDITOR env variable and have it
|
||||
// request us to edit the commit message when prompted.
|
||||
if action == todo.Reword {
|
||||
return &types.DisabledReason{Text: self.c.Tr.RewordNotSupported}
|
||||
}
|
||||
|
||||
if allowed := isChangeOfRebaseTodoAllowed(action); !allowed {
|
||||
return &types.DisabledReason{Text: self.c.Tr.ChangingThisActionIsNotAllowed}
|
||||
}
|
||||
|
||||
return nil
|
||||
func (self *LocalCommitsController) rewordEnabled(commit *models.Commit) *types.DisabledReason {
|
||||
// for now we do not support setting 'reword' on TODO commits because it requires an editor
|
||||
// and that means we either unconditionally wait around for the subprocess to ask for
|
||||
// our input or we set a lazygit client as the EDITOR env variable and have it
|
||||
// request us to edit the commit message when prompted.
|
||||
if commit.IsTODO() {
|
||||
return &types.DisabledReason{Text: self.c.Tr.RewordNotSupported}
|
||||
}
|
||||
|
||||
// If we are in a rebase, the only action that is allowed for
|
||||
// non-todo commits is rewording the current head commit
|
||||
if self.isRebasing() && !self.isHeadCommit() {
|
||||
return &types.DisabledReason{Text: self.c.Tr.AlreadyRebasing}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) moveDown(commit *models.Commit) error {
|
||||
index := self.context().GetSelectedLineIdx()
|
||||
commits := self.c.Model().Commits
|
||||
func (self *LocalCommitsController) isRebasing() bool {
|
||||
return self.c.Model().WorkingTreeStateAtLastCommitRefresh != enums.REBASE_MODE_NONE
|
||||
}
|
||||
|
||||
// can't move past the initial commit
|
||||
if index >= len(commits)-1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if commit.IsTODO() {
|
||||
if !commits[index+1].IsTODO() || commits[index+1].Action == models.ActionConflict {
|
||||
return nil
|
||||
}
|
||||
|
||||
// logging directly here because MoveTodoDown doesn't have enough information
|
||||
// to provide a useful log
|
||||
self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
|
||||
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.c.Tr.Log.MovingCommitDown,
|
||||
map[string]string{
|
||||
"shortSha": commit.ShortSha(),
|
||||
},
|
||||
)
|
||||
self.c.LogCommand(msg, false)
|
||||
|
||||
if err := self.c.Git().Rebase.MoveTodoDown(commit); err != nil {
|
||||
func (self *LocalCommitsController) moveDown(selectedCommits []*models.Commit, startIdx int, endIdx int) error {
|
||||
if self.isRebasing() {
|
||||
if err := self.c.Git().Rebase.MoveTodosDown(selectedCommits); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
self.context().MoveSelectedLine(1)
|
||||
self.context().MoveSelection(1)
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{
|
||||
Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS},
|
||||
})
|
||||
}
|
||||
|
||||
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
||||
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
||||
}
|
||||
|
||||
return self.c.WithWaitingStatusSync(self.c.Tr.MovingStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
|
||||
err := self.c.Git().Rebase.MoveCommitDown(self.c.Model().Commits, index)
|
||||
err := self.c.Git().Rebase.MoveCommitsDown(self.c.Model().Commits, startIdx, endIdx)
|
||||
if err == nil {
|
||||
self.context().MoveSelectedLine(1)
|
||||
self.context().MoveSelection(1)
|
||||
}
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(
|
||||
err, types.RefreshOptions{Mode: types.SYNC})
|
||||
})
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) moveUp(commit *models.Commit) error {
|
||||
index := self.context().GetSelectedLineIdx()
|
||||
if index == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if commit.IsTODO() {
|
||||
// logging directly here because MoveTodoDown doesn't have enough information
|
||||
// to provide a useful log
|
||||
self.c.LogAction(self.c.Tr.Actions.MoveCommitUp)
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.c.Tr.Log.MovingCommitUp,
|
||||
map[string]string{
|
||||
"shortSha": commit.ShortSha(),
|
||||
},
|
||||
)
|
||||
self.c.LogCommand(msg, false)
|
||||
|
||||
if err := self.c.Git().Rebase.MoveTodoUp(self.c.Model().Commits[index]); err != nil {
|
||||
func (self *LocalCommitsController) moveUp(selectedCommits []*models.Commit, startIdx int, endIdx int) error {
|
||||
if self.isRebasing() {
|
||||
if err := self.c.Git().Rebase.MoveTodosUp(selectedCommits); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
self.context().MoveSelectedLine(-1)
|
||||
self.context().MoveSelection(-1)
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{
|
||||
Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS},
|
||||
})
|
||||
}
|
||||
|
||||
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
||||
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
||||
}
|
||||
|
||||
return self.c.WithWaitingStatusSync(self.c.Tr.MovingStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.MoveCommitUp)
|
||||
err := self.c.Git().Rebase.MoveCommitUp(self.c.Model().Commits, index)
|
||||
err := self.c.Git().Rebase.MoveCommitsUp(self.c.Model().Commits, startIdx, endIdx)
|
||||
if err == nil {
|
||||
self.context().MoveSelectedLine(-1)
|
||||
self.context().MoveSelection(-1)
|
||||
}
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(
|
||||
err, types.RefreshOptions{Mode: types.SYNC})
|
||||
@ -693,10 +599,6 @@ func (self *LocalCommitsController) amendTo(commit *models.Commit) error {
|
||||
})
|
||||
}
|
||||
|
||||
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
||||
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
||||
}
|
||||
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.AmendCommitTitle,
|
||||
Prompt: self.c.Tr.AmendCommitPrompt,
|
||||
@ -713,7 +615,7 @@ func (self *LocalCommitsController) amendTo(commit *models.Commit) error {
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) canAmend(commit *models.Commit) *types.DisabledReason {
|
||||
if !self.isHeadCommit() && self.c.Model().WorkingTreeStateAtLastCommitRefresh != enums.REBASE_MODE_NONE {
|
||||
if !self.isHeadCommit() && self.isRebasing() {
|
||||
return &types.DisabledReason{Text: self.c.Tr.AlreadyRebasing}
|
||||
}
|
||||
|
||||
@ -721,10 +623,6 @@ func (self *LocalCommitsController) canAmend(commit *models.Commit) *types.Disab
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) amendAttribute(commit *models.Commit) error {
|
||||
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE && !self.isHeadCommit() {
|
||||
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
||||
}
|
||||
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
Title: "Amend commit attribute",
|
||||
Items: []*types.MenuItem{
|
||||
@ -846,7 +744,7 @@ func (self *LocalCommitsController) createRevertMergeCommitMenu(commit *models.C
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) afterRevertCommit() error {
|
||||
self.context().MoveSelectedLine(1)
|
||||
self.context().MoveSelection(1)
|
||||
return self.c.Refresh(types.RefreshOptions{
|
||||
Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS, types.BRANCHES},
|
||||
})
|
||||
@ -895,24 +793,6 @@ func (self *LocalCommitsController) squashAllAboveFixupCommits(commit *models.Co
|
||||
})
|
||||
}
|
||||
|
||||
// For getting disabled reason
|
||||
func (self *LocalCommitsController) notMidRebase() *types.DisabledReason {
|
||||
if self.c.Model().WorkingTreeStateAtLastCommitRefresh != enums.REBASE_MODE_NONE {
|
||||
return &types.DisabledReason{Text: self.c.Tr.AlreadyRebasing}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// For getting disabled reason
|
||||
func (self *LocalCommitsController) canFindCommitForQuickStart() *types.DisabledReason {
|
||||
if _, err := self.findCommitForQuickStartInteractiveRebase(); err != nil {
|
||||
return &types.DisabledReason{Text: err.Error(), ShowErrorInPanel: true}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) createTag(commit *models.Commit) error {
|
||||
return self.c.Helpers().Tags.OpenCreateTagPrompt(commit.Sha, func() {})
|
||||
}
|
||||
@ -1079,15 +959,88 @@ func (self *LocalCommitsController) isHeadCommit() bool {
|
||||
return models.IsHeadCommit(self.c.Model().Commits, self.context().GetSelectedLineIdx())
|
||||
}
|
||||
|
||||
func isChangeOfRebaseTodoAllowed(action todo.TodoCommand) bool {
|
||||
allowedActions := []todo.TodoCommand{
|
||||
todo.Pick,
|
||||
todo.Drop,
|
||||
todo.Edit,
|
||||
todo.Fixup,
|
||||
todo.Squash,
|
||||
todo.Reword,
|
||||
func (self *LocalCommitsController) notMidRebase(message string) func() *types.DisabledReason {
|
||||
return func() *types.DisabledReason {
|
||||
if self.isRebasing() {
|
||||
return &types.DisabledReason{Text: message}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) canFindCommitForQuickStart() *types.DisabledReason {
|
||||
if _, err := self.findCommitForQuickStartInteractiveRebase(); err != nil {
|
||||
return &types.DisabledReason{Text: err.Error(), ShowErrorInPanel: true}
|
||||
}
|
||||
|
||||
return lo.Contains(allowedActions, action)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) canSquashOrFixup(_selectedCommits []*models.Commit, startIdx int, endIdx int) *types.DisabledReason {
|
||||
if endIdx >= len(self.c.Model().Commits)-1 {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CannotSquashOrFixupFirstCommit}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) canMoveDown(selectedCommits []*models.Commit, startIdx int, endIdx int) *types.DisabledReason {
|
||||
if endIdx >= len(self.c.Model().Commits)-1 {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CannotMoveAnyFurther}
|
||||
}
|
||||
|
||||
if self.isRebasing() {
|
||||
commits := self.c.Model().Commits
|
||||
|
||||
if !commits[endIdx+1].IsTODO() || commits[endIdx+1].Action == models.ActionConflict {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CannotMoveAnyFurther}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) canMoveUp(selectedCommits []*models.Commit, startIdx int, endIdx int) *types.DisabledReason {
|
||||
if startIdx == 0 {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CannotMoveAnyFurther}
|
||||
}
|
||||
|
||||
if self.isRebasing() {
|
||||
commits := self.c.Model().Commits
|
||||
|
||||
if !commits[startIdx-1].IsTODO() || commits[startIdx-1].Action == models.ActionConflict {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CannotMoveAnyFurther}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensures that if we are mid-rebase, we're only selecting valid commits (non-conflict TODO commits)
|
||||
func (self *LocalCommitsController) midRebaseCommandEnabled(selectedCommits []*models.Commit, startIdx int, endIdx int) *types.DisabledReason {
|
||||
if !self.isRebasing() {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, commit := range selectedCommits {
|
||||
if !commit.IsTODO() {
|
||||
return &types.DisabledReason{Text: self.c.Tr.MustSelectTodoCommits}
|
||||
}
|
||||
|
||||
if commit.Action == models.ActionConflict {
|
||||
return &types.DisabledReason{Text: self.c.Tr.ChangingThisActionIsNotAllowed}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) pickEnabled(selectedCommits []*models.Commit, startIdx int, endIdx int) *types.DisabledReason {
|
||||
if !self.isRebasing() {
|
||||
// if not rebasing, we're going to do a pull so we don't care about the selection
|
||||
return nil
|
||||
}
|
||||
|
||||
return self.midRebaseCommandEnabled(selectedCommits, startIdx, endIdx)
|
||||
}
|
||||
|
@ -217,8 +217,8 @@ func chineseTranslationSet() TranslationSet {
|
||||
ScrollDownMainPanel: "向下滚动主面板",
|
||||
AmendCommitTitle: "修改提交",
|
||||
AmendCommitPrompt: "您确定要使用暂存文件来修改此提交吗?",
|
||||
DeleteCommitTitle: "删除提交",
|
||||
DeleteCommitPrompt: "您确定要删除此提交吗?",
|
||||
DropCommitTitle: "删除提交",
|
||||
DropCommitPrompt: "您确定要删除此提交吗?",
|
||||
PullingStatus: "正在拉取",
|
||||
PushingStatus: "正在推送",
|
||||
FetchingStatus: "正在抓取",
|
||||
|
@ -181,8 +181,8 @@ func dutchTranslationSet() TranslationSet {
|
||||
ScrollDownMainPanel: "Scroll naar beneden vanaf hoofdpaneel",
|
||||
AmendCommitTitle: "Commit wijzigen",
|
||||
AmendCommitPrompt: "Weet je zeker dat je deze commit wil wijzigen met de vorige staged bestanden?",
|
||||
DeleteCommitTitle: "Verwijder commit",
|
||||
DeleteCommitPrompt: "Weet je zeker dat je deze commit wil verwijderen?",
|
||||
DropCommitTitle: "Verwijder commit",
|
||||
DropCommitPrompt: "Weet je zeker dat je deze commit wil verwijderen?",
|
||||
PullingStatus: "Pullen",
|
||||
PushingStatus: "Pushen",
|
||||
FetchingStatus: "Fetchen",
|
||||
|
@ -119,6 +119,7 @@ type TranslationSet struct {
|
||||
DeleteCommit string
|
||||
MoveDownCommit string
|
||||
MoveUpCommit string
|
||||
CannotMoveAnyFurther string
|
||||
EditCommit string
|
||||
AmendToCommit string
|
||||
ResetAuthor string
|
||||
@ -239,6 +240,7 @@ type TranslationSet struct {
|
||||
SimpleRebase string
|
||||
InteractiveRebase string
|
||||
InteractiveRebaseTooltip string
|
||||
MustSelectTodoCommits string
|
||||
ConfirmMerge string
|
||||
FwdNoUpstream string
|
||||
FwdNoLocalUpstream string
|
||||
@ -270,14 +272,15 @@ type TranslationSet struct {
|
||||
ScrollDownMainPanel string
|
||||
AmendCommitTitle string
|
||||
AmendCommitPrompt string
|
||||
DeleteCommitTitle string
|
||||
DeleteCommitPrompt string
|
||||
DropCommitTitle string
|
||||
DropCommitPrompt string
|
||||
PullingStatus string
|
||||
PushingStatus string
|
||||
FetchingStatus string
|
||||
SquashingStatus string
|
||||
FixingStatus string
|
||||
DeletingStatus string
|
||||
DroppingStatus string
|
||||
MovingStatus string
|
||||
RebasingStatus string
|
||||
MergingStatus string
|
||||
@ -686,8 +689,6 @@ type Log struct {
|
||||
CherryPickCommits string
|
||||
HandleUndo string
|
||||
HandleMidRebaseCommand string
|
||||
MovingCommitUp string
|
||||
MovingCommitDown string
|
||||
RemoveFile string
|
||||
CopyToClipboard string
|
||||
Remove string
|
||||
@ -945,8 +946,8 @@ func EnglishTranslationSet() TranslationSet {
|
||||
UpdateRefHere: "Update branch '{{.ref}}' here",
|
||||
CannotSquashOrFixupFirstCommit: "There's no commit below to squash into",
|
||||
Fixup: "Fixup",
|
||||
SureFixupThisCommit: "Are you sure you want to 'fixup' this commit? It will be merged into the commit below",
|
||||
SureSquashThisCommit: "Are you sure you want to squash this commit into the commit below?",
|
||||
SureFixupThisCommit: "Are you sure you want to 'fixup' the selected commit(s) into the commit below?",
|
||||
SureSquashThisCommit: "Are you sure you want to squash the selected commit(s) into the commit below?",
|
||||
Squash: "Squash",
|
||||
PickCommit: "Pick commit (when mid-rebase)",
|
||||
RevertCommit: "Revert commit",
|
||||
@ -954,6 +955,7 @@ func EnglishTranslationSet() TranslationSet {
|
||||
DeleteCommit: "Delete commit",
|
||||
MoveDownCommit: "Move commit down one",
|
||||
MoveUpCommit: "Move commit up one",
|
||||
CannotMoveAnyFurther: "Cannot move any further",
|
||||
EditCommit: "Edit commit",
|
||||
AmendToCommit: "Amend commit with staged changes",
|
||||
ResetAuthor: "Reset author",
|
||||
@ -1079,6 +1081,7 @@ func EnglishTranslationSet() TranslationSet {
|
||||
SimpleRebase: "Simple rebase",
|
||||
InteractiveRebase: "Interactive rebase",
|
||||
InteractiveRebaseTooltip: "Begin an interactive rebase with a break at the start, so you can update the TODO commits before continuing",
|
||||
MustSelectTodoCommits: "When rebasing, this action only works on a selection of TODO commits.",
|
||||
ConfirmMerge: "Are you sure you want to merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}'?",
|
||||
FwdNoUpstream: "Cannot fast-forward a branch with no upstream",
|
||||
FwdNoLocalUpstream: "Cannot fast-forward a branch whose remote is not registered locally",
|
||||
@ -1110,14 +1113,15 @@ func EnglishTranslationSet() TranslationSet {
|
||||
ScrollDownMainPanel: "Scroll down main panel",
|
||||
AmendCommitTitle: "Amend commit",
|
||||
AmendCommitPrompt: "Are you sure you want to amend this commit with your staged files?",
|
||||
DeleteCommitTitle: "Delete commit",
|
||||
DeleteCommitPrompt: "Are you sure you want to delete this commit?",
|
||||
DropCommitTitle: "Drop commit",
|
||||
DropCommitPrompt: "Are you sure you want to drop the selected commit(s)?",
|
||||
PullingStatus: "Pulling",
|
||||
PushingStatus: "Pushing",
|
||||
FetchingStatus: "Fetching",
|
||||
SquashingStatus: "Squashing",
|
||||
FixingStatus: "Fixing up",
|
||||
DeletingStatus: "Deleting",
|
||||
DroppingStatus: "Dropping",
|
||||
MovingStatus: "Moving",
|
||||
RebasingStatus: "Rebasing",
|
||||
MergingStatus: "Merging",
|
||||
@ -1626,8 +1630,6 @@ func EnglishTranslationSet() TranslationSet {
|
||||
CherryPickCommits: "Cherry-picking commits:\n'{{.commitLines}}'",
|
||||
HandleUndo: "Undoing last conflict resolution",
|
||||
HandleMidRebaseCommand: "Updating rebase action of commit {{.shortSha}} to '{{.action}}'",
|
||||
MovingCommitUp: "Moving commit {{.shortSha}} up",
|
||||
MovingCommitDown: "Moving commit {{.shortSha}} down",
|
||||
RemoveFile: "Deleting path '{{.path}}'",
|
||||
CopyToClipboard: "Copying '{{.str}}' to clipboard",
|
||||
Remove: "Removing '{{.filename}}'",
|
||||
|
@ -221,8 +221,8 @@ func japaneseTranslationSet() TranslationSet {
|
||||
ScrollDownMainPanel: "メインパネルを下にスクロール",
|
||||
AmendCommitTitle: "Amendコミット",
|
||||
AmendCommitPrompt: "ステージされたファイルで現在のコミットをamendします。よろしいですか?",
|
||||
DeleteCommitTitle: "コミットを削除",
|
||||
DeleteCommitPrompt: "選択されたコミットを削除します。よろしいですか?",
|
||||
DropCommitTitle: "コミットを削除",
|
||||
DropCommitPrompt: "選択されたコミットを削除します。よろしいですか?",
|
||||
PullingStatus: "Pull中",
|
||||
PushingStatus: "Push中",
|
||||
FetchingStatus: "Fetch中",
|
||||
|
@ -218,8 +218,8 @@ func koreanTranslationSet() TranslationSet {
|
||||
ScrollDownMainPanel: "메인 패널을 아래로로 스크롤",
|
||||
AmendCommitTitle: "Amend commit",
|
||||
AmendCommitPrompt: "Are you sure you want to amend this commit with your staged files?",
|
||||
DeleteCommitTitle: "커밋 삭제",
|
||||
DeleteCommitPrompt: "정말로 선택한 커밋을 삭제하시겠습니까?",
|
||||
DropCommitTitle: "커밋 삭제",
|
||||
DropCommitPrompt: "정말로 선택한 커밋을 삭제하시겠습니까?",
|
||||
PullingStatus: "업데이트 중",
|
||||
PushingStatus: "푸시 중",
|
||||
FetchingStatus: "패치 중",
|
||||
|
@ -147,8 +147,8 @@ func polishTranslationSet() TranslationSet {
|
||||
ScrollUp: "Przewiń w górę",
|
||||
AmendCommitTitle: "Popraw commit",
|
||||
AmendCommitPrompt: "Czy na pewno chcesz poprawić ten commit plikami z poczekalni?",
|
||||
DeleteCommitTitle: "Usuń commit",
|
||||
DeleteCommitPrompt: "Czy na pewno usunąć ten commit?",
|
||||
DropCommitTitle: "Usuń commit",
|
||||
DropCommitPrompt: "Czy na pewno usunąć ten commit?",
|
||||
PullingStatus: "Pobieranie zmian",
|
||||
PushingStatus: "Wysyłanie zmian",
|
||||
FetchingStatus: "Pobieram",
|
||||
|
@ -262,8 +262,8 @@ func RussianTranslationSet() TranslationSet {
|
||||
ScrollDownMainPanel: "Прокрутить вниз главную панель",
|
||||
AmendCommitTitle: "Править коммит (amend)",
|
||||
AmendCommitPrompt: "Вы уверены, что хотите править этот коммит проиндексированными файлами?",
|
||||
DeleteCommitTitle: "Удалить коммит",
|
||||
DeleteCommitPrompt: "Вы уверены, что хотите удалить этот коммит?",
|
||||
DropCommitTitle: "Удалить коммит",
|
||||
DropCommitPrompt: "Вы уверены, что хотите удалить этот коммит?",
|
||||
PullingStatus: "Получение и слияние изменении",
|
||||
PushingStatus: "Отправка изменении",
|
||||
FetchingStatus: "Получение изменении",
|
||||
|
@ -293,8 +293,8 @@ func traditionalChineseTranslationSet() TranslationSet {
|
||||
ScrollDownMainPanel: "向下捲動主面板",
|
||||
AmendCommitTitle: "修正提交",
|
||||
AmendCommitPrompt: "你確定要使用預存的檔案修正此提交嗎?",
|
||||
DeleteCommitTitle: "刪除提交",
|
||||
DeleteCommitPrompt: "你確定要刪除此提交嗎?",
|
||||
DropCommitTitle: "刪除提交",
|
||||
DropCommitPrompt: "你確定要刪除此提交嗎?",
|
||||
PullingStatus: "拉取",
|
||||
PushingStatus: "推送",
|
||||
FetchingStatus: "擷取",
|
||||
|
@ -22,8 +22,8 @@ var Undo = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
confirmCommitDrop := func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Delete commit")).
|
||||
Content(Equals("Are you sure you want to delete this commit?")).
|
||||
Title(Equals("Drop commit")).
|
||||
Content(Equals("Are you sure you want to drop the selected commit(s)?")).
|
||||
Wait(500).
|
||||
Confirm()
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ var DropWithCustomCommentChar = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Delete commit")).
|
||||
Content(Equals("Are you sure you want to delete this commit?")).
|
||||
Title(Equals("Drop commit")).
|
||||
Content(Equals("Are you sure you want to drop the selected commit(s)?")).
|
||||
Confirm()
|
||||
}).
|
||||
Lines(
|
||||
|
@ -29,6 +29,6 @@ var EditNonTodoCommitDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
NavigateToLine(Contains("commit 01")).
|
||||
Press(keys.Universal.Edit)
|
||||
|
||||
t.ExpectToast(Contains("Can't perform this action during a rebase"))
|
||||
t.ExpectToast(Contains("Disabled: When rebasing, this action only works on a selection of TODO commits."))
|
||||
},
|
||||
})
|
||||
|
@ -39,6 +39,6 @@ var EditTheConflCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
NavigateToLine(Contains("<-- YOU ARE HERE --- commit three")).
|
||||
Press(keys.Commits.RenameCommit)
|
||||
|
||||
t.ExpectToast(Contains("Changing this kind of rebase todo entry is not allowed"))
|
||||
t.ExpectToast(Contains("Disabled: Rewording commits while interactively rebasing is not currently supported"))
|
||||
},
|
||||
})
|
||||
|
@ -29,7 +29,7 @@ var FixupSecondCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Fixup")).
|
||||
Content(Equals("Are you sure you want to 'fixup' this commit? It will be merged into the commit below")).
|
||||
Content(Equals("Are you sure you want to 'fixup' the selected commit(s) into the commit below?")).
|
||||
Confirm()
|
||||
}).
|
||||
Lines(
|
||||
|
@ -0,0 +1,205 @@
|
||||
package interactive_rebase
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var MidRebaseRangeSelect = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Do various things with range selection in the commits view when mid-rebase",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
CreateNCommits(10)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
TopLines(
|
||||
Contains("commit 10").IsSelected(),
|
||||
).
|
||||
NavigateToLine(Contains("commit 07")).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
TopLines(
|
||||
Contains("commit 10"),
|
||||
Contains("commit 09"),
|
||||
Contains("commit 08"),
|
||||
Contains("commit 07").IsSelected(),
|
||||
Contains("commit 06").IsSelected(),
|
||||
Contains("commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
// Verify we can't perform an edit on multiple commits (it's not supported
|
||||
// yet)
|
||||
Press(keys.Universal.Edit).
|
||||
Tap(func() {
|
||||
// This ought to be a toast but I'm too lazy to implement that right now.
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Equals("Error")).
|
||||
Content(Contains("Action does not support range selection, please select a single item")).
|
||||
Confirm()
|
||||
}).
|
||||
NavigateToLine(Contains("commit 05")).
|
||||
// Start a rebase
|
||||
Press(keys.Universal.Edit).
|
||||
TopLines(
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08"),
|
||||
Contains("pick").Contains("commit 07"),
|
||||
Contains("pick").Contains("commit 06"),
|
||||
Contains("<-- YOU ARE HERE --- commit 05").IsSelected(),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
SelectPreviousItem().
|
||||
// perform various actions on a range of commits
|
||||
Press(keys.Universal.RangeSelectUp).
|
||||
TopLines(
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08"),
|
||||
Contains("pick").Contains("commit 07").IsSelected(),
|
||||
Contains("pick").Contains("commit 06").IsSelected(),
|
||||
Contains("<-- YOU ARE HERE --- commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
Press(keys.Commits.MarkCommitAsFixup).
|
||||
TopLines(
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08"),
|
||||
Contains("fixup").Contains("commit 07").IsSelected(),
|
||||
Contains("fixup").Contains("commit 06").IsSelected(),
|
||||
Contains("<-- YOU ARE HERE --- commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
Press(keys.Commits.PickCommit).
|
||||
TopLines(
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08"),
|
||||
Contains("pick").Contains("commit 07").IsSelected(),
|
||||
Contains("pick").Contains("commit 06").IsSelected(),
|
||||
Contains("<-- YOU ARE HERE --- commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
Press(keys.Universal.Edit).
|
||||
TopLines(
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08"),
|
||||
Contains("edit").Contains("commit 07").IsSelected(),
|
||||
Contains("edit").Contains("commit 06").IsSelected(),
|
||||
Contains("<-- YOU ARE HERE --- commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
Press(keys.Commits.SquashDown).
|
||||
TopLines(
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08"),
|
||||
Contains("squash").Contains("commit 07").IsSelected(),
|
||||
Contains("squash").Contains("commit 06").IsSelected(),
|
||||
Contains("<-- YOU ARE HERE --- commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
Press(keys.Commits.MoveDownCommit).
|
||||
TopLines(
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08"),
|
||||
Contains("squash").Contains("commit 07").IsSelected(),
|
||||
Contains("squash").Contains("commit 06").IsSelected(),
|
||||
Contains("<-- YOU ARE HERE --- commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
Tap(func() {
|
||||
t.ExpectToast(Contains("Disabled: Cannot move any further"))
|
||||
}).
|
||||
Press(keys.Commits.MoveUpCommit).
|
||||
TopLines(
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("squash").Contains("commit 07").IsSelected(),
|
||||
Contains("squash").Contains("commit 06").IsSelected(),
|
||||
Contains("pick").Contains("commit 08"),
|
||||
Contains("<-- YOU ARE HERE --- commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
Press(keys.Commits.MoveUpCommit).
|
||||
TopLines(
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("squash").Contains("commit 07").IsSelected(),
|
||||
Contains("squash").Contains("commit 06").IsSelected(),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08"),
|
||||
Contains("<-- YOU ARE HERE --- commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
Press(keys.Commits.MoveUpCommit).
|
||||
TopLines(
|
||||
Contains("squash").Contains("commit 07").IsSelected(),
|
||||
Contains("squash").Contains("commit 06").IsSelected(),
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08"),
|
||||
Contains("<-- YOU ARE HERE --- commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
Press(keys.Commits.MoveUpCommit).
|
||||
Tap(func() {
|
||||
t.ExpectToast(Contains("Disabled: Cannot move any further"))
|
||||
}).
|
||||
TopLines(
|
||||
Contains("squash").Contains("commit 07").IsSelected(),
|
||||
Contains("squash").Contains("commit 06").IsSelected(),
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08"),
|
||||
Contains("<-- YOU ARE HERE --- commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
// Verify we can't perform an action on a range that includes both
|
||||
// TODO and non-TODO commits
|
||||
NavigateToLine(Contains("commit 08")).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
TopLines(
|
||||
Contains("squash").Contains("commit 07"),
|
||||
Contains("squash").Contains("commit 06"),
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08").IsSelected(),
|
||||
Contains("<-- YOU ARE HERE --- commit 05").IsSelected(),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
Press(keys.Commits.MarkCommitAsFixup).
|
||||
Tap(func() {
|
||||
t.ExpectToast(Contains("Disabled: When rebasing, this action only works on a selection of TODO commits."))
|
||||
}).
|
||||
TopLines(
|
||||
Contains("squash").Contains("commit 07"),
|
||||
Contains("squash").Contains("commit 06"),
|
||||
Contains("pick").Contains("commit 10"),
|
||||
Contains("pick").Contains("commit 09"),
|
||||
Contains("pick").Contains("commit 08").IsSelected(),
|
||||
Contains("<-- YOU ARE HERE --- commit 05").IsSelected(),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
// continue the rebase
|
||||
Tap(func() {
|
||||
t.Common().ContinueRebase()
|
||||
}).
|
||||
TopLines(
|
||||
Contains("commit 10"),
|
||||
Contains("commit 09"),
|
||||
Contains("commit 08"),
|
||||
Contains("commit 05"),
|
||||
// selected indexes are retained, though we may want to clear it
|
||||
// in future (not sure what the best behaviour is right now)
|
||||
Contains("commit 04").IsSelected(),
|
||||
Contains("commit 03").IsSelected(),
|
||||
)
|
||||
},
|
||||
})
|
@ -45,6 +45,9 @@ var Move = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
// assert nothing happens upon trying to move beyond the last commit
|
||||
Press(keys.Commits.MoveDownCommit).
|
||||
Tap(func() {
|
||||
t.ExpectToast(Contains("Disabled: Cannot move any further"))
|
||||
}).
|
||||
Lines(
|
||||
Contains("commit 03"),
|
||||
Contains("commit 02"),
|
||||
@ -74,6 +77,9 @@ var Move = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
// assert nothing happens upon trying to move beyond the first commit
|
||||
Press(keys.Commits.MoveUpCommit).
|
||||
Tap(func() {
|
||||
t.ExpectToast(Contains("Disabled: Cannot move any further"))
|
||||
}).
|
||||
Lines(
|
||||
Contains("commit 04").IsSelected(),
|
||||
Contains("commit 03"),
|
||||
|
@ -45,8 +45,11 @@ var MoveInRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("commit 03"),
|
||||
Contains("YOU ARE HERE").Contains("commit 01"),
|
||||
).
|
||||
Press(keys.Commits.MoveUpCommit).
|
||||
// assert we can't move past the top
|
||||
Press(keys.Commits.MoveUpCommit).
|
||||
Tap(func() {
|
||||
t.ExpectToast(Contains("Disabled: Cannot move any further"))
|
||||
}).
|
||||
Lines(
|
||||
Contains("commit 02").IsSelected(),
|
||||
Contains("commit 04"),
|
||||
@ -69,6 +72,9 @@ var MoveInRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
// assert we can't move past the bottom
|
||||
Press(keys.Commits.MoveDownCommit).
|
||||
Tap(func() {
|
||||
t.ExpectToast(Contains("Disabled: Cannot move any further"))
|
||||
}).
|
||||
Lines(
|
||||
Contains("commit 04"),
|
||||
Contains("commit 03"),
|
||||
|
@ -0,0 +1,155 @@
|
||||
package interactive_rebase
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var OutsideRebaseRangeSelect = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Do various things with range selection in the commits view when outside rebase",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
CreateNCommits(10)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
TopLines(
|
||||
Contains("commit 10").IsSelected(),
|
||||
).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
TopLines(
|
||||
Contains("commit 10").IsSelected(),
|
||||
Contains("commit 09").IsSelected(),
|
||||
Contains("commit 08"),
|
||||
).
|
||||
// Drop commits
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Drop commit")).
|
||||
Content(Contains("Are you sure you want to drop the selected commit(s)?")).
|
||||
Confirm()
|
||||
}).
|
||||
TopLines(
|
||||
Contains("commit 08").IsSelected(),
|
||||
Contains("commit 07"),
|
||||
).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
TopLines(
|
||||
Contains("commit 08").IsSelected(),
|
||||
Contains("commit 07").IsSelected(),
|
||||
Contains("commit 06"),
|
||||
).
|
||||
// Squash commits
|
||||
Press(keys.Commits.SquashDown).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Squash")).
|
||||
Content(Contains("Are you sure you want to squash the selected commit(s) into the commit below?")).
|
||||
Confirm()
|
||||
}).
|
||||
TopLines(
|
||||
Contains("commit 06").IsSelected(),
|
||||
Contains("commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
// Verify commit messages are concatenated
|
||||
Tap(func() {
|
||||
t.Views().Main().
|
||||
ContainsLines(
|
||||
Contains("commit 06"),
|
||||
AnyString(),
|
||||
Contains("commit 07"),
|
||||
AnyString(),
|
||||
Contains("commit 08"),
|
||||
)
|
||||
}).
|
||||
// Fixup commits
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
TopLines(
|
||||
Contains("commit 06").IsSelected(),
|
||||
Contains("commit 05").IsSelected(),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
Press(keys.Commits.MarkCommitAsFixup).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Fixup")).
|
||||
Content(Contains("Are you sure you want to 'fixup' the selected commit(s) into the commit below?")).
|
||||
Confirm()
|
||||
}).
|
||||
TopLines(
|
||||
Contains("commit 04").IsSelected(),
|
||||
Contains("commit 03"),
|
||||
Contains("commit 02"),
|
||||
).
|
||||
// Verify commit messages are dropped
|
||||
Tap(func() {
|
||||
t.Views().Main().
|
||||
Content(
|
||||
Contains("commit 04").
|
||||
DoesNotContain("commit 06").
|
||||
DoesNotContain("commit 05"),
|
||||
)
|
||||
}).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
TopLines(
|
||||
Contains("commit 04").IsSelected(),
|
||||
Contains("commit 03").IsSelected(),
|
||||
Contains("commit 02"),
|
||||
).
|
||||
// Move commits
|
||||
Press(keys.Commits.MoveDownCommit).
|
||||
TopLines(
|
||||
Contains("commit 02"),
|
||||
Contains("commit 04").IsSelected(),
|
||||
Contains("commit 03").IsSelected(),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
Press(keys.Commits.MoveDownCommit).
|
||||
TopLines(
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
Contains("commit 04").IsSelected(),
|
||||
Contains("commit 03").IsSelected(),
|
||||
).
|
||||
Press(keys.Commits.MoveDownCommit).
|
||||
TopLines(
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
Contains("commit 04").IsSelected(),
|
||||
Contains("commit 03").IsSelected(),
|
||||
).
|
||||
Tap(func() {
|
||||
t.ExpectToast(Contains("Disabled: Cannot move any further"))
|
||||
}).
|
||||
Press(keys.Commits.MoveUpCommit).
|
||||
TopLines(
|
||||
Contains("commit 02"),
|
||||
Contains("commit 04").IsSelected(),
|
||||
Contains("commit 03").IsSelected(),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
Press(keys.Commits.MoveUpCommit).
|
||||
TopLines(
|
||||
Contains("commit 04").IsSelected(),
|
||||
Contains("commit 03").IsSelected(),
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
Press(keys.Commits.MoveUpCommit).
|
||||
Tap(func() {
|
||||
t.ExpectToast(Contains("Disabled: Cannot move any further"))
|
||||
}).
|
||||
TopLines(
|
||||
Contains("commit 04").IsSelected(),
|
||||
Contains("commit 03").IsSelected(),
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
)
|
||||
},
|
||||
})
|
@ -27,7 +27,7 @@ var SquashDownSecondCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Squash")).
|
||||
Content(Equals("Are you sure you want to squash this commit into the commit below?")).
|
||||
Content(Equals("Are you sure you want to squash the selected commit(s) into the commit below?")).
|
||||
Confirm()
|
||||
}).
|
||||
Lines(
|
||||
|
@ -165,6 +165,7 @@ var tests = []*components.IntegrationTest{
|
||||
interactive_rebase.Move,
|
||||
interactive_rebase.MoveInRebase,
|
||||
interactive_rebase.MoveWithCustomCommentChar,
|
||||
interactive_rebase.OutsideRebaseRangeSelect,
|
||||
interactive_rebase.PickRescheduled,
|
||||
interactive_rebase.QuickStart,
|
||||
interactive_rebase.Rebase,
|
||||
|
@ -24,8 +24,8 @@ var UndoCheckoutAndDrop = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
confirmCommitDrop := func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Delete commit")).
|
||||
Content(Equals("Are you sure you want to delete this commit?")).
|
||||
Title(Equals("Drop commit")).
|
||||
Content(Equals("Are you sure you want to drop the selected commit(s)?")).
|
||||
Confirm()
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,8 @@ var UndoDrop = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
confirmCommitDrop := func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Delete commit")).
|
||||
Content(Equals("Are you sure you want to delete this commit?")).
|
||||
Title(Equals("Drop commit")).
|
||||
Content(Equals("Are you sure you want to drop the selected commit(s)?")).
|
||||
Confirm()
|
||||
}
|
||||
|
||||
|
@ -9,27 +9,46 @@ import (
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// Read a git-rebase-todo file, change the action for the given sha to
|
||||
// newAction, and write it back
|
||||
func EditRebaseTodo(filePath string, sha string, oldAction todo.TodoCommand, newAction todo.TodoCommand, commentChar byte) error {
|
||||
type Todo struct {
|
||||
Sha string
|
||||
Action todo.TodoCommand
|
||||
}
|
||||
|
||||
// In order to change a TODO in git-rebase-todo, we need to specify the old action,
|
||||
// because sometimes the same sha appears multiple times in the file (e.g. in a pick
|
||||
// and later in a merge)
|
||||
type TodoChange struct {
|
||||
Sha string
|
||||
OldAction todo.TodoCommand
|
||||
NewAction todo.TodoCommand
|
||||
}
|
||||
|
||||
// Read a git-rebase-todo file, change the actions for the given commits,
|
||||
// and write it back
|
||||
func EditRebaseTodo(filePath string, changes []TodoChange, commentChar byte) error {
|
||||
todos, err := ReadRebaseTodoFile(filePath, commentChar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
matchCount := 0
|
||||
for i := range todos {
|
||||
t := &todos[i]
|
||||
// Comparing just the sha is not enough; we need to compare both the
|
||||
// action and the sha, as the sha could appear multiple times (e.g. in a
|
||||
// pick and later in a merge)
|
||||
if t.Command == oldAction && equalShas(t.Commit, sha) {
|
||||
t.Command = newAction
|
||||
return WriteRebaseTodoFile(filePath, todos, commentChar)
|
||||
// This is a nested loop, but it's ok because the number of todos should be small
|
||||
for _, change := range changes {
|
||||
if t.Command == change.OldAction && equalShas(t.Commit, change.Sha) {
|
||||
matchCount++
|
||||
t.Command = change.NewAction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Should never get here
|
||||
return fmt.Errorf("Todo %s not found in git-rebase-todo", sha)
|
||||
if matchCount < len(changes) {
|
||||
// Should never get here
|
||||
return fmt.Errorf("Some todos not found in git-rebase-todo")
|
||||
}
|
||||
|
||||
return WriteRebaseTodoFile(filePath, todos, commentChar)
|
||||
}
|
||||
|
||||
func equalShas(a, b string) bool {
|
||||
@ -73,24 +92,24 @@ func PrependStrToTodoFile(filePath string, linesToPrepend []byte) error {
|
||||
return os.WriteFile(filePath, linesToPrepend, 0o644)
|
||||
}
|
||||
|
||||
func MoveTodoDown(fileName string, sha string, action todo.TodoCommand, commentChar byte) error {
|
||||
func MoveTodosDown(fileName string, todosToMove []Todo, commentChar byte) error {
|
||||
todos, err := ReadRebaseTodoFile(fileName, commentChar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rearrangedTodos, err := moveTodoDown(todos, sha, action)
|
||||
rearrangedTodos, err := moveTodosDown(todos, todosToMove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return WriteRebaseTodoFile(fileName, rearrangedTodos, commentChar)
|
||||
}
|
||||
|
||||
func MoveTodoUp(fileName string, sha string, action todo.TodoCommand, commentChar byte) error {
|
||||
func MoveTodosUp(fileName string, todosToMove []Todo, commentChar byte) error {
|
||||
todos, err := ReadRebaseTodoFile(fileName, commentChar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rearrangedTodos, err := moveTodoUp(todos, sha, action)
|
||||
rearrangedTodos, err := moveTodosUp(todos, todosToMove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -102,6 +121,11 @@ func moveTodoDown(todos []todo.Todo, sha string, action todo.TodoCommand) ([]tod
|
||||
return lo.Reverse(rearrangedTodos), err
|
||||
}
|
||||
|
||||
func moveTodosDown(todos []todo.Todo, todosToMove []Todo) ([]todo.Todo, error) {
|
||||
rearrangedTodos, err := moveTodosUp(lo.Reverse(todos), lo.Reverse(todosToMove))
|
||||
return lo.Reverse(rearrangedTodos), err
|
||||
}
|
||||
|
||||
func moveTodoUp(todos []todo.Todo, sha string, action todo.TodoCommand) ([]todo.Todo, error) {
|
||||
_, sourceIdx, ok := lo.FindIndexOf(todos, func(t todo.Todo) bool {
|
||||
// Comparing just the sha is not enough; we need to compare both the
|
||||
@ -134,6 +158,19 @@ func moveTodoUp(todos []todo.Todo, sha string, action todo.TodoCommand) ([]todo.
|
||||
return rearrangedTodos, nil
|
||||
}
|
||||
|
||||
func moveTodosUp(todos []todo.Todo, todosToMove []Todo) ([]todo.Todo, error) {
|
||||
for _, todoToMove := range todosToMove {
|
||||
var newTodos []todo.Todo
|
||||
newTodos, err := moveTodoUp(todos, todoToMove.Sha, todoToMove.Action)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
todos = newTodos
|
||||
}
|
||||
|
||||
return todos, nil
|
||||
}
|
||||
|
||||
func MoveFixupCommitDown(fileName string, originalSha string, fixupSha string, commentChar byte) error {
|
||||
todos, err := ReadRebaseTodoFile(fileName, commentChar)
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user