mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Move dragged commits in one rebase
Let the todo-move primitives take a distance instead of hardcoding a single row, by iterating the one-row move in memory. Dropping a commit several rows away thus rewrites the todo file once and, outside of an interactive rebase, runs a single rebase rather than one per row.
This commit is contained in:
parent
b85483ecc0
commit
e756511042
|
|
@ -263,12 +263,14 @@ func (self *MoveFixupCommitDownInstruction) run(common *common.Common) error {
|
|||
}
|
||||
|
||||
type MoveTodosUpInstruction struct {
|
||||
Hashes []string
|
||||
Hashes []string
|
||||
Distance int
|
||||
}
|
||||
|
||||
func NewMoveTodosUpInstruction(hashes []string) Instruction {
|
||||
func NewMoveTodosUpInstruction(hashes []string, distance int) Instruction {
|
||||
return &MoveTodosUpInstruction{
|
||||
Hashes: hashes,
|
||||
Hashes: hashes,
|
||||
Distance: distance,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -288,17 +290,19 @@ func (self *MoveTodosUpInstruction) run(common *common.Common) error {
|
|||
})
|
||||
|
||||
return handleInteractiveRebase(common, func(path string) error {
|
||||
return utils.MoveTodosUp(path, todosToMove, false, getCommentChar())
|
||||
return utils.MoveTodos(path, todosToMove, false, -self.Distance, getCommentChar())
|
||||
})
|
||||
}
|
||||
|
||||
type MoveTodosDownInstruction struct {
|
||||
Hashes []string
|
||||
Hashes []string
|
||||
Distance int
|
||||
}
|
||||
|
||||
func NewMoveTodosDownInstruction(hashes []string) Instruction {
|
||||
func NewMoveTodosDownInstruction(hashes []string, distance int) Instruction {
|
||||
return &MoveTodosDownInstruction{
|
||||
Hashes: hashes,
|
||||
Hashes: hashes,
|
||||
Distance: distance,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -318,7 +322,7 @@ func (self *MoveTodosDownInstruction) run(common *common.Common) error {
|
|||
})
|
||||
|
||||
return handleInteractiveRebase(common, func(path string) error {
|
||||
return utils.MoveTodosDown(path, todosToMove, false, getCommentChar())
|
||||
return utils.MoveTodos(path, todosToMove, false, self.Distance, getCommentChar())
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,29 +112,30 @@ func (self *RebaseCommands) GenericAmend(commits []*models.Commit, start, end in
|
|||
}
|
||||
|
||||
func (self *RebaseCommands) MoveCommitsDown(commits []*models.Commit, startIdx int, endIdx int) error {
|
||||
baseHashOrRoot := getBaseHashOrRoot(commits, endIdx+2)
|
||||
|
||||
hashes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) string {
|
||||
return commit.Hash()
|
||||
})
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseHashOrRoot: baseHashOrRoot,
|
||||
instruction: daemon.NewMoveTodosDownInstruction(hashes),
|
||||
overrideEditor: true,
|
||||
}).Run()
|
||||
return self.MoveCommits(commits, startIdx, endIdx, 1)
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) MoveCommitsUp(commits []*models.Commit, startIdx int, endIdx int) error {
|
||||
baseHashOrRoot := getBaseHashOrRoot(commits, endIdx+1)
|
||||
return self.MoveCommits(commits, startIdx, endIdx, -1)
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) MoveCommits(commits []*models.Commit, startIdx int, endIdx int, offset int) error {
|
||||
baseHashOrRoot := getBaseHashOrRoot(commits, endIdx+max(offset, 0)+1)
|
||||
|
||||
hashes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) string {
|
||||
return commit.Hash()
|
||||
})
|
||||
|
||||
var instruction daemon.Instruction
|
||||
if offset > 0 {
|
||||
instruction = daemon.NewMoveTodosDownInstruction(hashes, offset)
|
||||
} else {
|
||||
instruction = daemon.NewMoveTodosUpInstruction(hashes, -offset)
|
||||
}
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseHashOrRoot: baseHashOrRoot,
|
||||
instruction: daemon.NewMoveTodosUpInstruction(hashes),
|
||||
instruction: instruction,
|
||||
overrideEditor: true,
|
||||
}).Run()
|
||||
}
|
||||
|
|
@ -369,21 +370,20 @@ func (self *RebaseCommands) DeleteUpdateRefTodos(commits []*models.Commit) error
|
|||
}
|
||||
|
||||
func (self *RebaseCommands) MoveTodosDown(commits []*models.Commit) error {
|
||||
fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")
|
||||
todosToMove := lo.Map(commits, func(commit *models.Commit, _ int) utils.Todo {
|
||||
return todoFromCommit(commit)
|
||||
})
|
||||
|
||||
return utils.MoveTodosDown(fileName, todosToMove, true, self.config.GetCoreCommentChar())
|
||||
return self.MoveTodos(commits, 1)
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) MoveTodosUp(commits []*models.Commit) error {
|
||||
return self.MoveTodos(commits, -1)
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) MoveTodos(commits []*models.Commit, offset int) error {
|
||||
fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")
|
||||
todosToMove := lo.Map(commits, func(commit *models.Commit, _ int) utils.Todo {
|
||||
return todoFromCommit(commit)
|
||||
})
|
||||
|
||||
return utils.MoveTodosUp(fileName, todosToMove, true, self.config.GetCoreCommentChar())
|
||||
return utils.MoveTodos(fileName, todosToMove, true, offset, self.config.GetCoreCommentChar())
|
||||
}
|
||||
|
||||
// SquashAllAboveFixupCommits squashes all fixup! commits above the given one
|
||||
|
|
|
|||
|
|
@ -743,13 +743,7 @@ func (self *LocalCommitsController) moveUp(selectedCommits []*models.Commit, sta
|
|||
|
||||
func (self *LocalCommitsController) move(selectedCommits []*models.Commit, startIdx int, endIdx int, offset int) error {
|
||||
if self.isRebasing() {
|
||||
var err error
|
||||
if offset > 0 {
|
||||
err = self.c.Git().Rebase.MoveTodosDown(selectedCommits)
|
||||
} else {
|
||||
err = self.c.Git().Rebase.MoveTodosUp(selectedCommits)
|
||||
}
|
||||
if err != nil {
|
||||
if err := self.c.Git().Rebase.MoveTodos(selectedCommits, offset); err != nil {
|
||||
return err
|
||||
}
|
||||
self.context().MoveSelection(offset)
|
||||
|
|
@ -767,14 +761,12 @@ func (self *LocalCommitsController) move(selectedCommits []*models.Commit, start
|
|||
|
||||
commits := self.c.Model().Commits
|
||||
return self.c.WithWaitingStatusBlockingInput(self.c.Tr.MovingStatus, func(gocui.Task) error {
|
||||
var err error
|
||||
if offset > 0 {
|
||||
self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
|
||||
err = self.c.Git().Rebase.MoveCommitsDown(commits, startIdx, endIdx)
|
||||
} else {
|
||||
self.c.LogAction(self.c.Tr.Actions.MoveCommitUp)
|
||||
err = self.c.Git().Rebase.MoveCommitsUp(commits, startIdx, endIdx)
|
||||
}
|
||||
err := self.c.Git().Rebase.MoveCommits(commits, startIdx, endIdx, offset)
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(
|
||||
err, types.RefreshOptions{
|
||||
BatchUIUpdates: true,
|
||||
|
|
|
|||
|
|
@ -144,27 +144,40 @@ func deleteTodos(todos []todo.Todo, todosToDelete []Todo) ([]todo.Todo, error) {
|
|||
}
|
||||
|
||||
func MoveTodosDown(fileName string, todosToMove []Todo, isInRebase bool, commentChar byte) error {
|
||||
return MoveTodos(fileName, todosToMove, isInRebase, 1, commentChar)
|
||||
}
|
||||
|
||||
func MoveTodosUp(fileName string, todosToMove []Todo, isInRebase bool, commentChar byte) error {
|
||||
return MoveTodos(fileName, todosToMove, isInRebase, -1, commentChar)
|
||||
}
|
||||
|
||||
func MoveTodos(fileName string, todosToMove []Todo, isInRebase bool, offset int, commentChar byte) error {
|
||||
todos, err := ReadRebaseTodoFile(fileName, commentChar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rearrangedTodos, err := moveTodosDown(todos, todosToMove, isInRebase)
|
||||
rearrangedTodos, err := moveTodos(todos, todosToMove, isInRebase, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return WriteRebaseTodoFile(fileName, rearrangedTodos, commentChar)
|
||||
}
|
||||
|
||||
func MoveTodosUp(fileName string, todosToMove []Todo, isInRebase bool, commentChar byte) error {
|
||||
todos, err := ReadRebaseTodoFile(fileName, commentChar)
|
||||
if err != nil {
|
||||
return err
|
||||
func moveTodos(todos []todo.Todo, todosToMove []Todo, isInRebase bool, offset int) ([]todo.Todo, error) {
|
||||
moveOneRow := moveTodosUp
|
||||
if offset > 0 {
|
||||
moveOneRow = moveTodosDown
|
||||
}
|
||||
rearrangedTodos, err := moveTodosUp(todos, todosToMove, isInRebase)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
for range max(offset, -offset) {
|
||||
var err error
|
||||
todos, err = moveOneRow(todos, slices.Clone(todosToMove), isInRebase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return WriteRebaseTodoFile(fileName, rearrangedTodos, commentChar)
|
||||
|
||||
return todos, nil
|
||||
}
|
||||
|
||||
func moveTodoDown(todos []todo.Todo, todoToMove Todo, isInRebase bool) ([]todo.Todo, error) {
|
||||
|
|
|
|||
|
|
@ -3,12 +3,55 @@ package utils
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/stefanhaller/git-todo-parser/todo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMoveTodos(t *testing.T) {
|
||||
todos := []todo.Todo{
|
||||
{Command: todo.Pick, Commit: "a"},
|
||||
{Command: todo.Pick, Commit: "b"},
|
||||
{Command: todo.Label, Label: "hidden"},
|
||||
{Command: todo.Pick, Commit: "c"},
|
||||
{Command: todo.Pick, Commit: "d"},
|
||||
{Command: todo.Pick, Commit: "e"},
|
||||
{Command: todo.Pick, Commit: "f"},
|
||||
}
|
||||
|
||||
t.Run("moves a range up multiple rendered rows", func(t *testing.T) {
|
||||
actual, err := moveTodos(slices.Clone(todos), []Todo{{Hash: "d"}, {Hash: "c"}}, false, -2)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []todo.Todo{
|
||||
{Command: todo.Pick, Commit: "a"},
|
||||
{Command: todo.Pick, Commit: "b"},
|
||||
{Command: todo.Label, Label: "hidden"},
|
||||
{Command: todo.Pick, Commit: "e"},
|
||||
{Command: todo.Pick, Commit: "f"},
|
||||
{Command: todo.Pick, Commit: "c"},
|
||||
{Command: todo.Pick, Commit: "d"},
|
||||
}, actual)
|
||||
})
|
||||
|
||||
t.Run("moves a range down multiple rendered rows", func(t *testing.T) {
|
||||
actual, err := moveTodos(slices.Clone(todos), []Todo{{Hash: "e"}, {Hash: "d"}}, false, 2)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []todo.Todo{
|
||||
{Command: todo.Pick, Commit: "a"},
|
||||
{Command: todo.Pick, Commit: "d"},
|
||||
{Command: todo.Pick, Commit: "e"},
|
||||
{Command: todo.Pick, Commit: "b"},
|
||||
{Command: todo.Label, Label: "hidden"},
|
||||
{Command: todo.Pick, Commit: "c"},
|
||||
{Command: todo.Pick, Commit: "f"},
|
||||
}, actual)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRebaseCommands_moveTodoDown(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
|
|
|
|||
Loading…
Reference in a new issue