Skip to content

Commit 945b023

Browse files
committed
Support range selection for reverting commits
1 parent 1c91999 commit 945b023

File tree

6 files changed

+71
-65
lines changed

6 files changed

+71
-65
lines changed

Diff for: pkg/commands/git_commands/commit.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -285,14 +285,14 @@ func (self *CommitCommands) ShowFileContentCmdObj(hash string, filePath string)
285285
return self.cmd.New(cmdArgs).DontLog()
286286
}
287287

288-
// Revert reverts the selected commit by hash. If isMerge is true, we'll pass -m 1
288+
// Revert reverts the selected commits by hash. If isMerge is true, we'll pass -m 1
289289
// to say we want to revert the first parent of the merge commit, which is the one
290290
// people want in 99.9% of cases. In current git versions we could unconditionally
291291
// pass -m 1 even for non-merge commits, but older versions of git choke on it.
292-
func (self *CommitCommands) Revert(hash string, isMerge bool) error {
292+
func (self *CommitCommands) Revert(hashes []string, isMerge bool) error {
293293
cmdArgs := NewGitCmd("revert").
294-
Arg(hash).
295294
ArgIf(isMerge, "-m", "1").
295+
Arg(hashes...).
296296
ToArgv()
297297

298298
return self.cmd.New(cmdArgs).Run()

Diff for: pkg/gui/controllers/local_commits_controller.go

+19-10
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
248248
},
249249
{
250250
Key: opts.GetKey(opts.Config.Commits.RevertCommit),
251-
Handler: self.withItem(self.revert),
252-
GetDisabledReason: self.require(self.singleItemSelected()),
251+
Handler: self.withItemsRange(self.revert),
252+
GetDisabledReason: self.require(self.itemRangeSelected()),
253253
Description: self.c.Tr.Revert,
254254
Tooltip: self.c.Tr.RevertCommitTooltip,
255255
},
@@ -857,22 +857,31 @@ func (self *LocalCommitsController) addCoAuthor(start, end int) error {
857857
return nil
858858
}
859859

860-
func (self *LocalCommitsController) revert(commit *models.Commit) error {
861-
self.c.Confirm(types.ConfirmOpts{
862-
Title: self.c.Tr.Actions.RevertCommit,
863-
Prompt: utils.ResolvePlaceholderString(
860+
func (self *LocalCommitsController) revert(commits []*models.Commit, start, end int) error {
861+
var promptText string
862+
if len(commits) == 1 {
863+
promptText = utils.ResolvePlaceholderString(
864864
self.c.Tr.ConfirmRevertCommit,
865865
map[string]string{
866-
"selectedCommit": commit.ShortHash(),
867-
}),
866+
"selectedCommit": commits[0].ShortHash(),
867+
})
868+
} else {
869+
promptText = self.c.Tr.ConfirmRevertCommitRange
870+
}
871+
hashes := lo.Map(commits, func(c *models.Commit, _ int) string { return c.Hash })
872+
isMerge := lo.SomeBy(commits, func(c *models.Commit) bool { return c.IsMerge() })
873+
874+
self.c.Confirm(types.ConfirmOpts{
875+
Title: self.c.Tr.Actions.RevertCommit,
876+
Prompt: promptText,
868877
HandleConfirm: func() error {
869878
self.c.LogAction(self.c.Tr.Actions.RevertCommit)
870879
return self.c.WithWaitingStatusSync(self.c.Tr.RevertingStatus, func() error {
871-
result := self.c.Git().Commit.Revert(commit.Hash, commit.IsMerge())
880+
result := self.c.Git().Commit.Revert(hashes, isMerge)
872881
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(result); err != nil {
873882
return err
874883
}
875-
self.context().MoveSelection(1)
884+
self.context().MoveSelection(len(commits))
876885
return self.c.Refresh(types.RefreshOptions{
877886
Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS, types.BRANCHES},
878887
})

Diff for: pkg/i18n/english.go

+2
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ type TranslationSet struct {
771771
OpenCommitInBrowser string
772772
ViewBisectOptions string
773773
ConfirmRevertCommit string
774+
ConfirmRevertCommitRange string
774775
RewordInEditorTitle string
775776
RewordInEditorPrompt string
776777
CheckoutAutostashPrompt string
@@ -1837,6 +1838,7 @@ func EnglishTranslationSet() *TranslationSet {
18371838
OpenCommitInBrowser: "Open commit in browser",
18381839
ViewBisectOptions: "View bisect options",
18391840
ConfirmRevertCommit: "Are you sure you want to revert {{.selectedCommit}}?",
1841+
ConfirmRevertCommitRange: "Are you sure you want to revert the selected commits?",
18401842
RewordInEditorTitle: "Reword in editor",
18411843
RewordInEditorPrompt: "Are you sure you want to reword this commit in your editor?",
18421844
HardResetAutostashPrompt: "Are you sure you want to hard reset to '%s'? An auto-stash will be performed if necessary.",

Diff for: pkg/integration/tests/commit/revert_with_conflict_multiple_commits.go

+13-17
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,7 @@ var RevertWithConflictMultipleCommits = NewIntegrationTest(NewIntegrationTestArg
99
Description: "Reverts a range of commits, the first of which conflicts",
1010
ExtraCmdArgs: []string{},
1111
Skip: false,
12-
SetupConfig: func(cfg *config.AppConfig) {
13-
// TODO: use our revert UI once we support range-select for reverts
14-
cfg.GetUserConfig().CustomCommands = []config.CustomCommand{
15-
{
16-
Key: "X",
17-
Context: "commits",
18-
Command: "git -c core.editor=: revert HEAD^ HEAD^^",
19-
},
20-
}
21-
},
12+
SetupConfig: func(cfg *config.AppConfig) {},
2213
SetupRepo: func(shell *Shell) {
2314
shell.CreateFileAndAdd("myfile", "")
2415
shell.Commit("add empty file")
@@ -38,13 +29,18 @@ var RevertWithConflictMultipleCommits = NewIntegrationTest(NewIntegrationTestArg
3829
Contains("CI ◯ unrelated change"),
3930
Contains("CI ◯ add empty file"),
4031
).
41-
Press("X").
32+
SelectNextItem().
33+
Press(keys.Universal.RangeSelectDown).
34+
Press(keys.Commits.RevertCommit).
4235
Tap(func() {
43-
t.ExpectPopup().Alert().
44-
Title(Equals("Error")).
45-
// The exact error message is different on different git versions,
46-
// but they all contain the word 'conflict' somewhere.
47-
Content(Contains("conflict")).
36+
t.ExpectPopup().Confirmation().
37+
Title(Equals("Revert commit")).
38+
Content(Equals("Are you sure you want to revert the selected commits?")).
39+
Confirm()
40+
41+
t.ExpectPopup().Menu().
42+
Title(Equals("Conflicts!")).
43+
Select(Contains("View conflicts")).
4844
Confirm()
4945
}).
5046
Lines(
@@ -59,7 +55,7 @@ var RevertWithConflictMultipleCommits = NewIntegrationTest(NewIntegrationTestArg
5955
t.Views().Options().Content(Contains("View revert options: m"))
6056
t.Views().Information().Content(Contains("Reverting (Reset)"))
6157

62-
t.Views().Files().Focus().
58+
t.Views().Files().IsFocused().
6359
Lines(
6460
Contains("UU myfile").IsSelected(),
6561
).

Diff for: pkg/integration/tests/interactive_rebase/revert_during_rebase_when_stopped_on_edit.go

+21-18
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,10 @@ var RevertDuringRebaseWhenStoppedOnEdit = NewIntegrationTest(NewIntegrationTestA
99
Description: "Revert a series of commits while stopped in a rebase",
1010
ExtraCmdArgs: []string{},
1111
Skip: false,
12-
SetupConfig: func(cfg *config.AppConfig) {
13-
// TODO: use our revert UI once we support range-select for reverts
14-
cfg.GetUserConfig().CustomCommands = []config.CustomCommand{
15-
{
16-
Key: "X",
17-
Context: "commits",
18-
Command: "git -c core.editor=: revert HEAD^ HEAD^^",
19-
},
20-
}
21-
},
12+
SetupConfig: func(cfg *config.AppConfig) {},
2213
SetupRepo: func(shell *Shell) {
23-
shell.EmptyCommit("master commit")
14+
shell.EmptyCommit("master commit 1")
15+
shell.EmptyCommit("master commit 2")
2416
shell.NewBranch("branch")
2517
shell.CreateNCommits(4)
2618
},
@@ -32,7 +24,8 @@ var RevertDuringRebaseWhenStoppedOnEdit = NewIntegrationTest(NewIntegrationTestA
3224
Contains("commit 03"),
3325
Contains("commit 02"),
3426
Contains("commit 01"),
35-
Contains("master commit"),
27+
Contains("master commit 2"),
28+
Contains("master commit 1"),
3629
).
3730
NavigateToLine(Contains("commit 03")).
3831
Press(keys.Universal.Edit).
@@ -41,17 +34,27 @@ var RevertDuringRebaseWhenStoppedOnEdit = NewIntegrationTest(NewIntegrationTestA
4134
Contains("<-- YOU ARE HERE --- commit 03").IsSelected(),
4235
Contains("commit 02"),
4336
Contains("commit 01"),
44-
Contains("master commit"),
37+
Contains("master commit 2"),
38+
Contains("master commit 1"),
4539
).
46-
Press("X").
40+
SelectNextItem().
41+
Press(keys.Universal.RangeSelectDown).
42+
Press(keys.Commits.RevertCommit).
43+
Tap(func() {
44+
t.ExpectPopup().Confirmation().
45+
Title(Equals("Revert commit")).
46+
Content(MatchesRegexp(`Are you sure you want to revert \w+?`)).
47+
Confirm()
48+
}).
4749
Lines(
4850
Contains("pick").Contains("commit 04"),
49-
Contains(`<-- YOU ARE HERE --- Revert "commit 01"`).IsSelected(),
51+
Contains(`<-- YOU ARE HERE --- Revert "commit 01"`),
5052
Contains(`Revert "commit 02"`),
5153
Contains("commit 03"),
52-
Contains("commit 02"),
53-
Contains("commit 01"),
54-
Contains("master commit"),
54+
Contains("commit 02").IsSelected(),
55+
Contains("commit 01").IsSelected(),
56+
Contains("master commit 2"),
57+
Contains("master commit 1"),
5558
)
5659
},
5760
})

Diff for: pkg/integration/tests/interactive_rebase/revert_multiple_commits_in_interactive_rebase.go

+13-17
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,7 @@ var RevertMultipleCommitsInInteractiveRebase = NewIntegrationTest(NewIntegration
99
Description: "Reverts a range of commits, the first of which conflicts, in the middle of an interactive rebase",
1010
ExtraCmdArgs: []string{},
1111
Skip: false,
12-
SetupConfig: func(cfg *config.AppConfig) {
13-
// TODO: use our revert UI once we support range-select for reverts
14-
cfg.GetUserConfig().CustomCommands = []config.CustomCommand{
15-
{
16-
Key: "X",
17-
Context: "commits",
18-
Command: "git -c core.editor=: revert HEAD^ HEAD^^",
19-
},
20-
}
21-
},
12+
SetupConfig: func(cfg *config.AppConfig) {},
2213
SetupRepo: func(shell *Shell) {
2314
shell.CreateFileAndAdd("myfile", "")
2415
shell.Commit("add empty file")
@@ -44,13 +35,18 @@ var RevertMultipleCommitsInInteractiveRebase = NewIntegrationTest(NewIntegration
4435
).
4536
NavigateToLine(Contains("add second line")).
4637
Press(keys.Universal.Edit).
47-
Press("X").
38+
SelectNextItem().
39+
Press(keys.Universal.RangeSelectDown).
40+
Press(keys.Commits.RevertCommit).
4841
Tap(func() {
49-
t.ExpectPopup().Alert().
50-
Title(Equals("Error")).
51-
// The exact error message is different on different git versions,
52-
// but they all contain the word 'conflict' somewhere.
53-
Content(Contains("conflict")).
42+
t.ExpectPopup().Confirmation().
43+
Title(Equals("Revert commit")).
44+
Content(Equals("Are you sure you want to revert the selected commits?")).
45+
Confirm()
46+
47+
t.ExpectPopup().Menu().
48+
Title(Equals("Conflicts!")).
49+
Select(Contains("View conflicts")).
5450
Confirm()
5551
}).
5652
Lines(
@@ -67,7 +63,7 @@ var RevertMultipleCommitsInInteractiveRebase = NewIntegrationTest(NewIntegration
6763
t.Views().Options().Content(Contains("View revert options: m"))
6864
t.Views().Information().Content(Contains("Reverting (Reset)"))
6965

70-
t.Views().Files().Focus().
66+
t.Views().Files().IsFocused().
7167
Lines(
7268
Contains("UU myfile").IsSelected(),
7369
).

0 commit comments

Comments
 (0)