1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-25 12:24:47 +02:00

Fix deleting update ref todos (#3439)

- **PR Description**

Deleting an update-ref todo in an interactive rebase now behaves as
expected (i.e. it leaves the branch referenced by the update-ref
untouched). Previously it would have deleted the branch referenced by
the update-ref todo when the rebase was continued. See #3418 for more
details.

Fixes #3418.
This commit is contained in:
Stefan Haller 2024-03-26 22:43:46 +01:00 committed by GitHub
commit e295ccefab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 88 additions and 8 deletions

View File

@ -39,6 +39,7 @@ const (
DaemonKindInsertBreak
DaemonKindChangeTodoActions
DaemonKindMoveFixupCommitDown
DaemonKindWriteRebaseTodo
)
const (
@ -59,6 +60,7 @@ func getInstruction() Instruction {
DaemonKindMoveTodosUp: deserializeInstruction[*MoveTodosUpInstruction],
DaemonKindMoveTodosDown: deserializeInstruction[*MoveTodosDownInstruction],
DaemonKindInsertBreak: deserializeInstruction[*InsertBreakInstruction],
DaemonKindWriteRebaseTodo: deserializeInstruction[*WriteRebaseTodoInstruction],
}
return mapping[getDaemonKind()](jsonData)
@ -330,3 +332,27 @@ func (self *InsertBreakInstruction) run(common *common.Common) error {
return utils.PrependStrToTodoFile(path, []byte("break\n"))
})
}
type WriteRebaseTodoInstruction struct {
TodosFileContent []byte
}
func NewWriteRebaseTodoInstruction(todosFileContent []byte) Instruction {
return &WriteRebaseTodoInstruction{
TodosFileContent: todosFileContent,
}
}
func (self *WriteRebaseTodoInstruction) Kind() DaemonKind {
return DaemonKindWriteRebaseTodo
}
func (self *WriteRebaseTodoInstruction) SerializedInstructions() string {
return serializeInstruction(self)
}
func (self *WriteRebaseTodoInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return os.WriteFile(path, self.TodosFileContent, 0o644)
})
}

View File

@ -204,7 +204,7 @@ type PrepareInteractiveRebaseCommandOpts struct {
// PrepareInteractiveRebaseCommand returns the cmd for an interactive rebase
// we tell git to run lazygit to edit the todo list, and we pass the client
// lazygit a todo string to write to the todo file
// lazygit instructions what to do with the todo file
func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteractiveRebaseCommandOpts) oscommands.ICmdObj {
ex := oscommands.GetLazygitPath()
@ -250,6 +250,36 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
return cmdObj
}
// GitRebaseEditTodo runs "git rebase --edit-todo", saving the given todosFileContent to the file
func (self *RebaseCommands) GitRebaseEditTodo(todosFileContent []byte) error {
ex := oscommands.GetLazygitPath()
cmdArgs := NewGitCmd("rebase").
Arg("--edit-todo").
ToArgv()
debug := "FALSE"
if self.Debug {
debug = "TRUE"
}
self.Log.WithField("command", cmdArgs).Debug("RunCommand")
cmdObj := self.cmd.New(cmdArgs)
cmdObj.AddEnvVars(daemon.ToEnvVars(daemon.NewWriteRebaseTodoInstruction(todosFileContent))...)
cmdObj.AddEnvVars(
"DEBUG="+debug,
"LANG=en_US.UTF-8", // Force using EN as language
"LC_ALL=en_US.UTF-8", // Force using EN as language
"GIT_EDITOR="+ex,
"GIT_SEQUENCE_EDITOR="+ex,
)
return cmdObj.Run()
}
// AmendTo amends the given commit with whatever files are staged
func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) error {
commit := commits[commitIndex]
@ -302,11 +332,16 @@ func (self *RebaseCommands) DeleteUpdateRefTodos(commits []*models.Commit) error
return todoFromCommit(commit)
})
return utils.DeleteTodos(
todosFileContent, err := utils.DeleteTodos(
filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"),
todosToDelete,
self.config.GetCoreCommentChar(),
)
if err != nil {
return err
}
return self.GitRebaseEditTodo(todosFileContent)
}
func (self *RebaseCommands) MoveTodosDown(commits []*models.Commit) error {

View File

@ -50,6 +50,8 @@ var DeleteUpdateRefTodo = NewIntegrationTest(NewIntegrationTestArgs{
Contains("pick").Contains("CI commit 02"),
Contains("CI ◯ <-- YOU ARE HERE --- commit 01"),
).
NavigateToLine(Contains("commit 02")).
Press(keys.Universal.Remove).
Tap(func() {
t.Common().ContinueRebase()
}).
@ -57,9 +59,14 @@ var DeleteUpdateRefTodo = NewIntegrationTest(NewIntegrationTestArgs{
Contains("CI ◯ commit 06"),
Contains("CI ◯ commit 05"),
Contains("CI ◯ commit 04"),
Contains("CI ◯ commit 03"), // No start on this commit, so there's no branch head here
Contains("CI ◯ commit 02"),
Contains("CI ◯ commit 03"), // No star on this commit, so there's no branch head here
Contains("CI ◯ commit 01"),
)
t.Views().Branches().
Lines(
Contains("branch2"),
Contains("branch1"),
)
},
})

View File

@ -1,6 +1,7 @@
package utils
import (
"bytes"
"fmt"
"os"
"strings"
@ -96,6 +97,12 @@ func WriteRebaseTodoFile(fileName string, todos []todo.Todo, commentChar byte) e
return err
}
func todosToString(todos []todo.Todo, commentChar byte) ([]byte, error) {
buffer := bytes.Buffer{}
err := todo.Write(&buffer, todos, commentChar)
return buffer.Bytes(), err
}
func PrependStrToTodoFile(filePath string, linesToPrepend []byte) error {
existingContent, err := os.ReadFile(filePath)
if err != nil {
@ -106,16 +113,21 @@ func PrependStrToTodoFile(filePath string, linesToPrepend []byte) error {
return os.WriteFile(filePath, linesToPrepend, 0o644)
}
func DeleteTodos(fileName string, todosToDelete []Todo, commentChar byte) error {
// Unlike the other functions in this file, which write the changed todos file
// back to disk, this one returns the new content as a byte slice. This is
// because when deleting update-ref todos, we must perform a "git rebase
// --edit-todo" command to pass the changed todos to git so that it can do some
// housekeeping around the deleted todos. This can only be done by our caller.
func DeleteTodos(fileName string, todosToDelete []Todo, commentChar byte) ([]byte, error) {
todos, err := ReadRebaseTodoFile(fileName, commentChar)
if err != nil {
return err
return nil, err
}
rearrangedTodos, err := deleteTodos(todos, todosToDelete)
if err != nil {
return err
return nil, err
}
return WriteRebaseTodoFile(fileName, rearrangedTodos, commentChar)
return todosToString(rearrangedTodos, commentChar)
}
func deleteTodos(todos []todo.Todo, todosToDelete []Todo) ([]todo.Todo, error) {