From 4065175a5811d688adc65b4b974f6fced0cdba67 Mon Sep 17 00:00:00 2001 From: Gabriel Lanata Date: Sun, 12 Jan 2025 12:33:21 -0800 Subject: [PATCH] Improve undo action to restore files upon undoing a commit --- pkg/gui/controllers/undo_controller.go | 55 ++++++----- pkg/i18n/english.go | 6 +- pkg/integration/tests/test_list.go | 1 + pkg/integration/tests/undo/undo_commit.go | 110 ++++++++++++++++++++++ 4 files changed, 145 insertions(+), 27 deletions(-) create mode 100644 pkg/integration/tests/undo/undo_commit.go diff --git a/pkg/gui/controllers/undo_controller.go b/pkg/gui/controllers/undo_controller.go index d6f8ab256c8..198aa7d7bcb 100644 --- a/pkg/gui/controllers/undo_controller.go +++ b/pkg/gui/controllers/undo_controller.go @@ -88,7 +88,20 @@ func (self *UndoController) reflogUndo() error { } switch action.kind { - case COMMIT, REBASE: + case COMMIT: + self.c.Confirm(types.ConfirmOpts{ + Title: self.c.Tr.Actions.Undo, + Prompt: fmt.Sprintf(self.c.Tr.SoftResetPrompt, action.from), + HandleConfirm: func() error { + self.c.LogAction(self.c.Tr.Actions.Undo) + return self.c.WithWaitingStatus(undoingStatus, func(gocui.Task) error { + return self.c.Helpers().Refs.ResetToRef(action.from, "soft", undoEnvVars) + }) + }, + }) + return true, nil + + case REBASE: self.c.Confirm(types.ConfirmOpts{ Title: self.c.Tr.Actions.Undo, Prompt: fmt.Sprintf(self.c.Tr.HardResetAutostashPrompt, action.from), @@ -105,7 +118,7 @@ func (self *UndoController) reflogUndo() error { case CHECKOUT: self.c.Confirm(types.ConfirmOpts{ Title: self.c.Tr.Actions.Undo, - Prompt: fmt.Sprintf(self.c.Tr.CheckoutPrompt, action.from), + Prompt: fmt.Sprintf(self.c.Tr.CheckoutAutostashPrompt, action.from), HandleConfirm: func() error { self.c.LogAction(self.c.Tr.Actions.Undo) return self.c.Helpers().Refs.CheckoutRef(action.from, types.CheckoutRefOptions{ @@ -159,7 +172,7 @@ func (self *UndoController) reflogRedo() error { case CHECKOUT: self.c.Confirm(types.ConfirmOpts{ Title: self.c.Tr.Actions.Redo, - Prompt: fmt.Sprintf(self.c.Tr.CheckoutPrompt, action.to), + Prompt: fmt.Sprintf(self.c.Tr.CheckoutAutostashPrompt, action.to), HandleConfirm: func() error { self.c.LogAction(self.c.Tr.Actions.Redo) return self.c.Helpers().Refs.CheckoutRef(action.to, types.CheckoutRefOptions{ @@ -244,31 +257,23 @@ func (self *UndoController) hardResetWithAutoStash(commitHash string, options ha return self.c.Helpers().Refs.ResetToRef(commitHash, "hard", options.EnvVars) } - // if we have any modified tracked files we need to ask the user if they want us to stash for them + // if we have any modified tracked files we need to auto-stash dirtyWorkingTree := self.c.Helpers().WorkingTree.IsWorkingTreeDirty() if dirtyWorkingTree { - // offer to autostash changes - self.c.Confirm(types.ConfirmOpts{ - Title: self.c.Tr.AutoStashTitle, - Prompt: self.c.Tr.AutoStashPrompt, - HandleConfirm: func() error { - return self.c.WithWaitingStatus(options.WaitingStatus, func(gocui.Task) error { - if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + commitHash); err != nil { - return err - } - if err := reset(); err != nil { - return err - } - - err := self.c.Git().Stash.Pop(0) - if err != nil { - return err - } - return self.c.Refresh(types.RefreshOptions{}) - }) - }, + return self.c.WithWaitingStatus(options.WaitingStatus, func(gocui.Task) error { + if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + commitHash); err != nil { + return err + } + if err := reset(); err != nil { + return err + } + + err := self.c.Git().Stash.Pop(0) + if err != nil { + return err + } + return self.c.Refresh(types.RefreshOptions{}) }) - return nil } return self.c.WithWaitingStatus(options.WaitingStatus, func(gocui.Task) error { diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 8e8db94967c..3efe269b262 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -732,8 +732,9 @@ type TranslationSet struct { ConfirmRevertCommit string RewordInEditorTitle string RewordInEditorPrompt string - CheckoutPrompt string + CheckoutAutostashPrompt string HardResetAutostashPrompt string + SoftResetPrompt string UpstreamGone string NukeDescription string DiscardStagedChangesDescription string @@ -1745,7 +1746,8 @@ func EnglishTranslationSet() *TranslationSet { RewordInEditorTitle: "Reword in 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.", - CheckoutPrompt: "Are you sure you want to checkout '%s'?", + SoftResetPrompt: "Are you sure you want to soft reset to '%s'?", + CheckoutAutostashPrompt: "Are you sure you want to checkout '%s'? An auto-stash will be performed if necessary.", UpstreamGone: "(upstream gone)", NukeDescription: "If you want to make all the changes in the worktree go away, this is the way to do it. If there are dirty submodule changes this will stash those changes in the submodule(s).", DiscardStagedChangesDescription: "This will create a new stash entry containing only staged files and then drop it, so that the working tree is left with only unstaged changes", diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 2e1eef023f2..a6825676af0 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -367,6 +367,7 @@ var tests = []*components.IntegrationTest{ ui.SwitchTabFromMenu, ui.SwitchTabWithPanelJumpKeys, undo.UndoCheckoutAndDrop, + undo.UndoCommit, undo.UndoDrop, worktree.AddFromBranch, worktree.AddFromBranchDetached, diff --git a/pkg/integration/tests/undo/undo_commit.go b/pkg/integration/tests/undo/undo_commit.go new file mode 100644 index 00000000000..a636385ebf1 --- /dev/null +++ b/pkg/integration/tests/undo/undo_commit.go @@ -0,0 +1,110 @@ +package undo + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var UndoCommit = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Undo/redo a commit", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateFileAndAdd("other-file", "other-file-1") + shell.Commit("one") + shell.CreateFileAndAdd("file", "file-1") + shell.Commit("two") + shell.UpdateFile("other-file", "other-file-2") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + confirmUndo := func() { + t.ExpectPopup().Confirmation(). + Title(Equals("Undo")). + Content(MatchesRegexp(`Are you sure you want to soft reset to '.*'\?`)). + Confirm() + } + + confirmRedo := func() { + t.ExpectPopup().Confirmation(). + Title(Equals("Redo")). + Content(MatchesRegexp(`Are you sure you want to hard reset to '.*'\? An auto-stash will be performed if necessary\.`)). + Confirm() + } + + confirmDiscardFile := func() { + t.ExpectPopup().Menu(). + Title(Equals("Discard changes")). + Select(Contains("Discard all changes")). + Confirm() + } + + t.Views().Files(). + Lines( + Contains(" M other-file"), + ) + + t.Views().Commits().Focus(). + Lines( + Contains("two").IsSelected(), + Contains("one"), + ). + Press(keys.Universal.Undo). + Tap(confirmUndo). + Lines( + Contains("one").IsSelected(), + ) + + t.Views().Files(). + Lines( + Contains("A file"), + Contains(" M other-file"), + ) + + t.Views().Commits().Focus(). + Press(keys.Universal.Redo). + Tap(confirmRedo). + Lines( + Contains("two").IsSelected(), + Contains("one"), + ) + + t.Views().Files(). + Lines( + Contains(" M other-file"), + ) + + // Undo again, this time discarding the original change before redoing again + t.Views().Commits().Focus(). + Press(keys.Universal.Undo). + Tap(confirmUndo). + Lines( + Contains("one").IsSelected(), + ) + + t.Views().Files().Focus(). + Lines( + Contains("A file"), + Contains(" M other-file").IsSelected(), + ). + Press(keys.Universal.PrevItem). + Press(keys.Universal.Remove). + Tap(confirmDiscardFile). + Lines( + Contains(" M other-file"), + ). + Press(keys.Universal.Redo). + Tap(confirmRedo) + + t.Views().Commits(). + Lines( + Contains("two"), + Contains("one"), + ) + + t.Views().Files(). + Lines( + Contains(" M other-file"), + ) + }, +})