diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/commands/git_commands/rebase.go | 12 | ||||
-rw-r--r-- | pkg/gui/controllers/local_commits_controller.go | 54 | ||||
-rw-r--r-- | pkg/i18n/english.go | 2 | ||||
-rw-r--r-- | pkg/integration/tests/interactive_rebase/delete_update_ref_todo.go | 65 | ||||
-rw-r--r-- | pkg/integration/tests/test_list.go | 1 | ||||
-rw-r--r-- | pkg/utils/rebase_todo.go | 27 | ||||
-rw-r--r-- | pkg/utils/rebase_todo_test.go | 55 |
7 files changed, 215 insertions, 1 deletions
diff --git a/pkg/commands/git_commands/rebase.go b/pkg/commands/git_commands/rebase.go index c628ad414..eeae66167 100644 --- a/pkg/commands/git_commands/rebase.go +++ b/pkg/commands/git_commands/rebase.go @@ -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 { diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 4dd19109b..fe15a37d3 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -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{ diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 1ec3af2c1..d38bb1180 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -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", diff --git a/pkg/integration/tests/interactive_rebase/delete_update_ref_todo.go b/pkg/integration/tests/interactive_rebase/delete_update_ref_todo.go new file mode 100644 index 000000000..d08f518ec --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/delete_update_ref_todo.go @@ -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"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index be463fc87..0a78d0193 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -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, diff --git a/pkg/utils/rebase_todo.go b/pkg/utils/rebase_todo.go index abd15b9dc..3403eef97 100644 --- a/pkg/utils/rebase_todo.go +++ b/pkg/utils/rebase_todo.go @@ -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 { diff --git a/pkg/utils/rebase_todo_test.go b/pkg/utils/rebase_todo_test.go index 458b4ee14..fd87d7c7c 100644 --- a/pkg/utils/rebase_todo_test.go +++ b/pkg/utils/rebase_todo_test.go @@ -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) + }) + } +} |