Skip to content

Commit

Permalink
Improve undo action to restore files upon undoing a commit
Browse files Browse the repository at this point in the history
  • Loading branch information
gabriellanata authored and jesseduffield committed Jan 17, 2025
1 parent 43106b6 commit 4065175
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 27 deletions.
55 changes: 30 additions & 25 deletions pkg/gui/controllers/undo_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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{
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 4 additions & 2 deletions pkg/i18n/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions pkg/integration/tests/test_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ var tests = []*components.IntegrationTest{
ui.SwitchTabFromMenu,
ui.SwitchTabWithPanelJumpKeys,
undo.UndoCheckoutAndDrop,
undo.UndoCommit,
undo.UndoDrop,
worktree.AddFromBranch,
worktree.AddFromBranchDetached,
Expand Down
110 changes: 110 additions & 0 deletions pkg/integration/tests/undo/undo_commit.go
Original file line number Diff line number Diff line change
@@ -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"),
)
},
})

0 comments on commit 4065175

Please sign in to comment.