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:
parent
64a1a455d6
commit
0608fc6471
@ -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 {
|
||||
|
@ -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{
|
||||
|
@ -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",
|
||||
|
@ -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"),
|
||||
)
|
||||
},
|
||||
})
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user