mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-02 23:27:32 +02:00
Support range selection for reverting commits
This commit is contained in:
parent
1c91999f61
commit
945b023eb5
@ -285,14 +285,14 @@ func (self *CommitCommands) ShowFileContentCmdObj(hash string, filePath string)
|
|||||||
return self.cmd.New(cmdArgs).DontLog()
|
return self.cmd.New(cmdArgs).DontLog()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revert reverts the selected commit by hash. If isMerge is true, we'll pass -m 1
|
// Revert reverts the selected commits by hash. If isMerge is true, we'll pass -m 1
|
||||||
// to say we want to revert the first parent of the merge commit, which is the one
|
// to say we want to revert the first parent of the merge commit, which is the one
|
||||||
// people want in 99.9% of cases. In current git versions we could unconditionally
|
// people want in 99.9% of cases. In current git versions we could unconditionally
|
||||||
// pass -m 1 even for non-merge commits, but older versions of git choke on it.
|
// pass -m 1 even for non-merge commits, but older versions of git choke on it.
|
||||||
func (self *CommitCommands) Revert(hash string, isMerge bool) error {
|
func (self *CommitCommands) Revert(hashes []string, isMerge bool) error {
|
||||||
cmdArgs := NewGitCmd("revert").
|
cmdArgs := NewGitCmd("revert").
|
||||||
Arg(hash).
|
|
||||||
ArgIf(isMerge, "-m", "1").
|
ArgIf(isMerge, "-m", "1").
|
||||||
|
Arg(hashes...).
|
||||||
ToArgv()
|
ToArgv()
|
||||||
|
|
||||||
return self.cmd.New(cmdArgs).Run()
|
return self.cmd.New(cmdArgs).Run()
|
||||||
|
@ -248,8 +248,8 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Commits.RevertCommit),
|
Key: opts.GetKey(opts.Config.Commits.RevertCommit),
|
||||||
Handler: self.withItem(self.revert),
|
Handler: self.withItemsRange(self.revert),
|
||||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
GetDisabledReason: self.require(self.itemRangeSelected()),
|
||||||
Description: self.c.Tr.Revert,
|
Description: self.c.Tr.Revert,
|
||||||
Tooltip: self.c.Tr.RevertCommitTooltip,
|
Tooltip: self.c.Tr.RevertCommitTooltip,
|
||||||
},
|
},
|
||||||
@ -857,22 +857,31 @@ func (self *LocalCommitsController) addCoAuthor(start, end int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) revert(commit *models.Commit) error {
|
func (self *LocalCommitsController) revert(commits []*models.Commit, start, end int) error {
|
||||||
self.c.Confirm(types.ConfirmOpts{
|
var promptText string
|
||||||
Title: self.c.Tr.Actions.RevertCommit,
|
if len(commits) == 1 {
|
||||||
Prompt: utils.ResolvePlaceholderString(
|
promptText = utils.ResolvePlaceholderString(
|
||||||
self.c.Tr.ConfirmRevertCommit,
|
self.c.Tr.ConfirmRevertCommit,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"selectedCommit": commit.ShortHash(),
|
"selectedCommit": commits[0].ShortHash(),
|
||||||
}),
|
})
|
||||||
|
} else {
|
||||||
|
promptText = self.c.Tr.ConfirmRevertCommitRange
|
||||||
|
}
|
||||||
|
hashes := lo.Map(commits, func(c *models.Commit, _ int) string { return c.Hash })
|
||||||
|
isMerge := lo.SomeBy(commits, func(c *models.Commit) bool { return c.IsMerge() })
|
||||||
|
|
||||||
|
self.c.Confirm(types.ConfirmOpts{
|
||||||
|
Title: self.c.Tr.Actions.RevertCommit,
|
||||||
|
Prompt: promptText,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.RevertCommit)
|
self.c.LogAction(self.c.Tr.Actions.RevertCommit)
|
||||||
return self.c.WithWaitingStatusSync(self.c.Tr.RevertingStatus, func() error {
|
return self.c.WithWaitingStatusSync(self.c.Tr.RevertingStatus, func() error {
|
||||||
result := self.c.Git().Commit.Revert(commit.Hash, commit.IsMerge())
|
result := self.c.Git().Commit.Revert(hashes, isMerge)
|
||||||
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(result); err != nil {
|
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(result); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
self.context().MoveSelection(1)
|
self.context().MoveSelection(len(commits))
|
||||||
return self.c.Refresh(types.RefreshOptions{
|
return self.c.Refresh(types.RefreshOptions{
|
||||||
Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS, types.BRANCHES},
|
Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS, types.BRANCHES},
|
||||||
})
|
})
|
||||||
|
@ -771,6 +771,7 @@ type TranslationSet struct {
|
|||||||
OpenCommitInBrowser string
|
OpenCommitInBrowser string
|
||||||
ViewBisectOptions string
|
ViewBisectOptions string
|
||||||
ConfirmRevertCommit string
|
ConfirmRevertCommit string
|
||||||
|
ConfirmRevertCommitRange string
|
||||||
RewordInEditorTitle string
|
RewordInEditorTitle string
|
||||||
RewordInEditorPrompt string
|
RewordInEditorPrompt string
|
||||||
CheckoutAutostashPrompt string
|
CheckoutAutostashPrompt string
|
||||||
@ -1837,6 +1838,7 @@ func EnglishTranslationSet() *TranslationSet {
|
|||||||
OpenCommitInBrowser: "Open commit in browser",
|
OpenCommitInBrowser: "Open commit in browser",
|
||||||
ViewBisectOptions: "View bisect options",
|
ViewBisectOptions: "View bisect options",
|
||||||
ConfirmRevertCommit: "Are you sure you want to revert {{.selectedCommit}}?",
|
ConfirmRevertCommit: "Are you sure you want to revert {{.selectedCommit}}?",
|
||||||
|
ConfirmRevertCommitRange: "Are you sure you want to revert the selected commits?",
|
||||||
RewordInEditorTitle: "Reword in editor",
|
RewordInEditorTitle: "Reword in editor",
|
||||||
RewordInEditorPrompt: "Are you sure you want to reword this commit in your editor?",
|
RewordInEditorPrompt: "Are you sure you want to reword this commit in your editor?",
|
||||||
HardResetAutostashPrompt: "Are you sure you want to hard reset to '%s'? An auto-stash will be performed if necessary.",
|
HardResetAutostashPrompt: "Are you sure you want to hard reset to '%s'? An auto-stash will be performed if necessary.",
|
||||||
|
@ -9,16 +9,7 @@ var RevertWithConflictMultipleCommits = NewIntegrationTest(NewIntegrationTestArg
|
|||||||
Description: "Reverts a range of commits, the first of which conflicts",
|
Description: "Reverts a range of commits, the first of which conflicts",
|
||||||
ExtraCmdArgs: []string{},
|
ExtraCmdArgs: []string{},
|
||||||
Skip: false,
|
Skip: false,
|
||||||
SetupConfig: func(cfg *config.AppConfig) {
|
SetupConfig: func(cfg *config.AppConfig) {},
|
||||||
// TODO: use our revert UI once we support range-select for reverts
|
|
||||||
cfg.GetUserConfig().CustomCommands = []config.CustomCommand{
|
|
||||||
{
|
|
||||||
Key: "X",
|
|
||||||
Context: "commits",
|
|
||||||
Command: "git -c core.editor=: revert HEAD^ HEAD^^",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
SetupRepo: func(shell *Shell) {
|
SetupRepo: func(shell *Shell) {
|
||||||
shell.CreateFileAndAdd("myfile", "")
|
shell.CreateFileAndAdd("myfile", "")
|
||||||
shell.Commit("add empty file")
|
shell.Commit("add empty file")
|
||||||
@ -38,13 +29,18 @@ var RevertWithConflictMultipleCommits = NewIntegrationTest(NewIntegrationTestArg
|
|||||||
Contains("CI ◯ unrelated change"),
|
Contains("CI ◯ unrelated change"),
|
||||||
Contains("CI ◯ add empty file"),
|
Contains("CI ◯ add empty file"),
|
||||||
).
|
).
|
||||||
Press("X").
|
SelectNextItem().
|
||||||
|
Press(keys.Universal.RangeSelectDown).
|
||||||
|
Press(keys.Commits.RevertCommit).
|
||||||
Tap(func() {
|
Tap(func() {
|
||||||
t.ExpectPopup().Alert().
|
t.ExpectPopup().Confirmation().
|
||||||
Title(Equals("Error")).
|
Title(Equals("Revert commit")).
|
||||||
// The exact error message is different on different git versions,
|
Content(Equals("Are you sure you want to revert the selected commits?")).
|
||||||
// but they all contain the word 'conflict' somewhere.
|
Confirm()
|
||||||
Content(Contains("conflict")).
|
|
||||||
|
t.ExpectPopup().Menu().
|
||||||
|
Title(Equals("Conflicts!")).
|
||||||
|
Select(Contains("View conflicts")).
|
||||||
Confirm()
|
Confirm()
|
||||||
}).
|
}).
|
||||||
Lines(
|
Lines(
|
||||||
@ -59,7 +55,7 @@ var RevertWithConflictMultipleCommits = NewIntegrationTest(NewIntegrationTestArg
|
|||||||
t.Views().Options().Content(Contains("View revert options: m"))
|
t.Views().Options().Content(Contains("View revert options: m"))
|
||||||
t.Views().Information().Content(Contains("Reverting (Reset)"))
|
t.Views().Information().Content(Contains("Reverting (Reset)"))
|
||||||
|
|
||||||
t.Views().Files().Focus().
|
t.Views().Files().IsFocused().
|
||||||
Lines(
|
Lines(
|
||||||
Contains("UU myfile").IsSelected(),
|
Contains("UU myfile").IsSelected(),
|
||||||
).
|
).
|
||||||
|
@ -9,18 +9,10 @@ var RevertDuringRebaseWhenStoppedOnEdit = NewIntegrationTest(NewIntegrationTestA
|
|||||||
Description: "Revert a series of commits while stopped in a rebase",
|
Description: "Revert a series of commits while stopped in a rebase",
|
||||||
ExtraCmdArgs: []string{},
|
ExtraCmdArgs: []string{},
|
||||||
Skip: false,
|
Skip: false,
|
||||||
SetupConfig: func(cfg *config.AppConfig) {
|
SetupConfig: func(cfg *config.AppConfig) {},
|
||||||
// TODO: use our revert UI once we support range-select for reverts
|
|
||||||
cfg.GetUserConfig().CustomCommands = []config.CustomCommand{
|
|
||||||
{
|
|
||||||
Key: "X",
|
|
||||||
Context: "commits",
|
|
||||||
Command: "git -c core.editor=: revert HEAD^ HEAD^^",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
SetupRepo: func(shell *Shell) {
|
SetupRepo: func(shell *Shell) {
|
||||||
shell.EmptyCommit("master commit")
|
shell.EmptyCommit("master commit 1")
|
||||||
|
shell.EmptyCommit("master commit 2")
|
||||||
shell.NewBranch("branch")
|
shell.NewBranch("branch")
|
||||||
shell.CreateNCommits(4)
|
shell.CreateNCommits(4)
|
||||||
},
|
},
|
||||||
@ -32,7 +24,8 @@ var RevertDuringRebaseWhenStoppedOnEdit = NewIntegrationTest(NewIntegrationTestA
|
|||||||
Contains("commit 03"),
|
Contains("commit 03"),
|
||||||
Contains("commit 02"),
|
Contains("commit 02"),
|
||||||
Contains("commit 01"),
|
Contains("commit 01"),
|
||||||
Contains("master commit"),
|
Contains("master commit 2"),
|
||||||
|
Contains("master commit 1"),
|
||||||
).
|
).
|
||||||
NavigateToLine(Contains("commit 03")).
|
NavigateToLine(Contains("commit 03")).
|
||||||
Press(keys.Universal.Edit).
|
Press(keys.Universal.Edit).
|
||||||
@ -41,17 +34,27 @@ var RevertDuringRebaseWhenStoppedOnEdit = NewIntegrationTest(NewIntegrationTestA
|
|||||||
Contains("<-- YOU ARE HERE --- commit 03").IsSelected(),
|
Contains("<-- YOU ARE HERE --- commit 03").IsSelected(),
|
||||||
Contains("commit 02"),
|
Contains("commit 02"),
|
||||||
Contains("commit 01"),
|
Contains("commit 01"),
|
||||||
Contains("master commit"),
|
Contains("master commit 2"),
|
||||||
|
Contains("master commit 1"),
|
||||||
).
|
).
|
||||||
Press("X").
|
SelectNextItem().
|
||||||
|
Press(keys.Universal.RangeSelectDown).
|
||||||
|
Press(keys.Commits.RevertCommit).
|
||||||
|
Tap(func() {
|
||||||
|
t.ExpectPopup().Confirmation().
|
||||||
|
Title(Equals("Revert commit")).
|
||||||
|
Content(MatchesRegexp(`Are you sure you want to revert \w+?`)).
|
||||||
|
Confirm()
|
||||||
|
}).
|
||||||
Lines(
|
Lines(
|
||||||
Contains("pick").Contains("commit 04"),
|
Contains("pick").Contains("commit 04"),
|
||||||
Contains(`<-- YOU ARE HERE --- Revert "commit 01"`).IsSelected(),
|
Contains(`<-- YOU ARE HERE --- Revert "commit 01"`),
|
||||||
Contains(`Revert "commit 02"`),
|
Contains(`Revert "commit 02"`),
|
||||||
Contains("commit 03"),
|
Contains("commit 03"),
|
||||||
Contains("commit 02"),
|
Contains("commit 02").IsSelected(),
|
||||||
Contains("commit 01"),
|
Contains("commit 01").IsSelected(),
|
||||||
Contains("master commit"),
|
Contains("master commit 2"),
|
||||||
|
Contains("master commit 1"),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -9,16 +9,7 @@ var RevertMultipleCommitsInInteractiveRebase = NewIntegrationTest(NewIntegration
|
|||||||
Description: "Reverts a range of commits, the first of which conflicts, in the middle of an interactive rebase",
|
Description: "Reverts a range of commits, the first of which conflicts, in the middle of an interactive rebase",
|
||||||
ExtraCmdArgs: []string{},
|
ExtraCmdArgs: []string{},
|
||||||
Skip: false,
|
Skip: false,
|
||||||
SetupConfig: func(cfg *config.AppConfig) {
|
SetupConfig: func(cfg *config.AppConfig) {},
|
||||||
// TODO: use our revert UI once we support range-select for reverts
|
|
||||||
cfg.GetUserConfig().CustomCommands = []config.CustomCommand{
|
|
||||||
{
|
|
||||||
Key: "X",
|
|
||||||
Context: "commits",
|
|
||||||
Command: "git -c core.editor=: revert HEAD^ HEAD^^",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
SetupRepo: func(shell *Shell) {
|
SetupRepo: func(shell *Shell) {
|
||||||
shell.CreateFileAndAdd("myfile", "")
|
shell.CreateFileAndAdd("myfile", "")
|
||||||
shell.Commit("add empty file")
|
shell.Commit("add empty file")
|
||||||
@ -44,13 +35,18 @@ var RevertMultipleCommitsInInteractiveRebase = NewIntegrationTest(NewIntegration
|
|||||||
).
|
).
|
||||||
NavigateToLine(Contains("add second line")).
|
NavigateToLine(Contains("add second line")).
|
||||||
Press(keys.Universal.Edit).
|
Press(keys.Universal.Edit).
|
||||||
Press("X").
|
SelectNextItem().
|
||||||
|
Press(keys.Universal.RangeSelectDown).
|
||||||
|
Press(keys.Commits.RevertCommit).
|
||||||
Tap(func() {
|
Tap(func() {
|
||||||
t.ExpectPopup().Alert().
|
t.ExpectPopup().Confirmation().
|
||||||
Title(Equals("Error")).
|
Title(Equals("Revert commit")).
|
||||||
// The exact error message is different on different git versions,
|
Content(Equals("Are you sure you want to revert the selected commits?")).
|
||||||
// but they all contain the word 'conflict' somewhere.
|
Confirm()
|
||||||
Content(Contains("conflict")).
|
|
||||||
|
t.ExpectPopup().Menu().
|
||||||
|
Title(Equals("Conflicts!")).
|
||||||
|
Select(Contains("View conflicts")).
|
||||||
Confirm()
|
Confirm()
|
||||||
}).
|
}).
|
||||||
Lines(
|
Lines(
|
||||||
@ -67,7 +63,7 @@ var RevertMultipleCommitsInInteractiveRebase = NewIntegrationTest(NewIntegration
|
|||||||
t.Views().Options().Content(Contains("View revert options: m"))
|
t.Views().Options().Content(Contains("View revert options: m"))
|
||||||
t.Views().Information().Content(Contains("Reverting (Reset)"))
|
t.Views().Information().Content(Contains("Reverting (Reset)"))
|
||||||
|
|
||||||
t.Views().Files().Focus().
|
t.Views().Files().IsFocused().
|
||||||
Lines(
|
Lines(
|
||||||
Contains("UU myfile").IsSelected(),
|
Contains("UU myfile").IsSelected(),
|
||||||
).
|
).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user