1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-15 11:56:37 +02:00

Allow deleting update-ref todos

This commit is contained in:
Stefan Haller 2024-03-13 07:58:39 +01:00
parent 64a1a455d6
commit 0608fc6471
7 changed files with 215 additions and 1 deletions

View File

@ -297,6 +297,18 @@ func (self *RebaseCommands) EditRebaseTodo(commits []*models.Commit, action todo
)
}
func (self *RebaseCommands) DeleteUpdateRefTodos(commits []*models.Commit) error {
todosToDelete := lo.Map(commits, func(commit *models.Commit, _ int) utils.Todo {
return todoFromCommit(commit)
})
return utils.DeleteTodos(
filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"),
todosToDelete,
self.config.GetCoreCommentChar(),
)
}
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 {

View File

@ -106,7 +106,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
Handler: self.withItemsRange(self.drop),
GetDisabledReason: self.require(
self.itemRangeSelected(
self.midRebaseCommandEnabled,
self.canDropCommits,
),
),
Description: self.c.Tr.DropCommit,
@ -439,6 +439,36 @@ func (self *LocalCommitsController) rewordEditor(commit *models.Commit) error {
func (self *LocalCommitsController) drop(selectedCommits []*models.Commit, startIdx int, endIdx int) error {
if self.isRebasing() {
groupedTodos := lo.GroupBy(selectedCommits, func(c *models.Commit) bool {
return c.Action == todo.UpdateRef
})
updateRefTodos := groupedTodos[true]
nonUpdateRefTodos := groupedTodos[false]
if len(updateRefTodos) > 0 {
return self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.DropCommitTitle,
Prompt: self.c.Tr.DropUpdateRefPrompt,
HandleConfirm: func() error {
selectedIdx, rangeStartIdx, rangeSelectMode := self.context().GetSelectionRangeAndMode()
if err := self.c.Git().Rebase.DeleteUpdateRefTodos(updateRefTodos); err != nil {
return err
}
if selectedIdx > rangeStartIdx {
selectedIdx = utils.Max(selectedIdx-len(updateRefTodos), rangeStartIdx)
} else {
rangeStartIdx = utils.Max(rangeStartIdx-len(updateRefTodos), selectedIdx)
}
self.context().SetSelectionRangeAndMode(selectedIdx, rangeStartIdx, rangeSelectMode)
return self.updateTodos(todo.Drop, nonUpdateRefTodos)
},
})
}
return self.updateTodos(todo.Drop, selectedCommits)
}
@ -1213,6 +1243,28 @@ func (self *LocalCommitsController) midRebaseMoveCommandEnabled(selectedCommits
return nil
}
func (self *LocalCommitsController) canDropCommits(selectedCommits []*models.Commit, startIdx int, endIdx int) *types.DisabledReason {
if !self.isRebasing() {
return nil
}
nonUpdateRefTodos := lo.Filter(selectedCommits, func(c *models.Commit, _ int) bool {
return c.Action != todo.UpdateRef
})
for _, commit := range nonUpdateRefTodos {
if !commit.IsTODO() {
return &types.DisabledReason{Text: self.c.Tr.MustSelectTodoCommits}
}
if !isChangeOfRebaseTodoAllowed(commit.Action) {
return &types.DisabledReason{Text: self.c.Tr.ChangingThisActionIsNotAllowed}
}
}
return nil
}
// These actions represent standard things you might want to do with a commit,
// as opposed to TODO actions like 'merge', 'update-ref', etc.
var standardActions = []todo.TodoCommand{

View File

@ -325,6 +325,7 @@ type TranslationSet struct {
AmendCommitPrompt string
DropCommitTitle string
DropCommitPrompt string
DropUpdateRefPrompt string
PullingStatus string
PushingStatus string
FetchingStatus string
@ -1280,6 +1281,7 @@ func EnglishTranslationSet() TranslationSet {
AmendCommitPrompt: "Are you sure you want to amend this commit with your staged files?",
DropCommitTitle: "Drop commit",
DropCommitPrompt: "Are you sure you want to drop the selected commit(s)?",
DropUpdateRefPrompt: "Are you sure you want to delete the selected update-ref todo(s)? This is irreversible except by aborting the rebase.",
PullingStatus: "Pulling",
PushingStatus: "Pushing",
FetchingStatus: "Fetching",

View File

@ -0,0 +1,65 @@
package interactive_rebase
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var DeleteUpdateRefTodo = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Delete an update-ref item from the rebase todo list",
ExtraCmdArgs: []string{},
Skip: false,
GitVersion: AtLeast("2.38.0"),
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.
NewBranch("branch1").
CreateNCommits(3).
NewBranch("branch2").
CreateNCommitsStartingAt(3, 4)
shell.SetConfig("rebase.updateRefs", "true")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().
Focus().
NavigateToLine(Contains("commit 01")).
Press(keys.Universal.Edit).
Lines(
Contains("pick").Contains("CI commit 06"),
Contains("pick").Contains("CI commit 05"),
Contains("pick").Contains("CI commit 04"),
Contains("update-ref").Contains("branch1"),
Contains("pick").Contains("CI commit 03"),
Contains("pick").Contains("CI commit 02"),
Contains("CI ◯ <-- YOU ARE HERE --- commit 01"),
).
NavigateToLine(Contains("update-ref")).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().Confirmation().
Title(Equals("Drop commit")).
Content(Contains("Are you sure you want to delete the selected update-ref todo(s)?")).
Confirm()
}).
Lines(
Contains("pick").Contains("CI commit 06"),
Contains("pick").Contains("CI commit 05"),
Contains("pick").Contains("CI commit 04"),
Contains("pick").Contains("CI commit 03").IsSelected(),
Contains("pick").Contains("CI commit 02"),
Contains("CI ◯ <-- YOU ARE HERE --- commit 01"),
).
Tap(func() {
t.Common().ContinueRebase()
}).
Lines(
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 01"),
)
},
})

View File

@ -164,6 +164,7 @@ var tests = []*components.IntegrationTest{
interactive_rebase.AmendHeadCommitDuringRebase,
interactive_rebase.AmendMerge,
interactive_rebase.AmendNonHeadCommitDuringRebase,
interactive_rebase.DeleteUpdateRefTodo,
interactive_rebase.DontShowBranchHeadsForTodoItems,
interactive_rebase.DropTodoCommitWithUpdateRef,
interactive_rebase.DropWithCustomCommentChar,

View File

@ -106,6 +106,33 @@ func PrependStrToTodoFile(filePath string, linesToPrepend []byte) error {
return os.WriteFile(filePath, linesToPrepend, 0o644)
}
func DeleteTodos(fileName string, todosToDelete []Todo, commentChar byte) error {
todos, err := ReadRebaseTodoFile(fileName, commentChar)
if err != nil {
return err
}
rearrangedTodos, err := deleteTodos(todos, todosToDelete)
if err != nil {
return err
}
return WriteRebaseTodoFile(fileName, rearrangedTodos, commentChar)
}
func deleteTodos(todos []todo.Todo, todosToDelete []Todo) ([]todo.Todo, error) {
for _, todoToDelete := range todosToDelete {
idx, ok := findTodo(todos, todoToDelete)
if !ok {
// Should never happen
return []todo.Todo{}, fmt.Errorf("Todo %s not found in git-rebase-todo", todoToDelete.Sha)
}
todos = Remove(todos, idx)
}
return todos, nil
}
func MoveTodosDown(fileName string, todosToMove []Todo, commentChar byte) error {
todos, err := ReadRebaseTodoFile(fileName, commentChar)
if err != nil {

View File

@ -360,3 +360,58 @@ func TestRebaseCommands_moveFixupCommitDown(t *testing.T) {
})
}
}
func TestRebaseCommands_deleteTodos(t *testing.T) {
scenarios := []struct {
name string
todos []todo.Todo
todosToDelete []Todo
expectedTodos []todo.Todo
expectedErr error
}{
{
name: "success",
todos: []todo.Todo{
{Command: todo.Pick, Commit: "1234"},
{Command: todo.UpdateRef, Ref: "refs/heads/some_branch"},
{Command: todo.Pick, Commit: "5678"},
{Command: todo.Pick, Commit: "abcd"},
},
todosToDelete: []Todo{
{Ref: "refs/heads/some_branch", Action: todo.UpdateRef},
{Sha: "abcd", Action: todo.Pick},
},
expectedTodos: []todo.Todo{
{Command: todo.Pick, Commit: "1234"},
{Command: todo.Pick, Commit: "5678"},
},
expectedErr: nil,
},
{
name: "failure",
todos: []todo.Todo{
{Command: todo.Pick, Commit: "1234"},
{Command: todo.Pick, Commit: "5678"},
},
todosToDelete: []Todo{
{Sha: "abcd", Action: todo.Pick},
},
expectedTodos: []todo.Todo{},
expectedErr: errors.New("Todo abcd not found in git-rebase-todo"),
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
actualTodos, actualErr := deleteTodos(scenario.todos, scenario.todosToDelete)
if scenario.expectedErr == nil {
assert.NoError(t, actualErr)
} else {
assert.EqualError(t, actualErr, scenario.expectedErr.Error())
}
assert.EqualValues(t, scenario.expectedTodos, actualTodos)
})
}
}