Skip to content

Commit a8e9859

Browse files
authored
Auto-stash modified files when cherry-picking or reverting commits (#4683)
- **PR Description** For cherry-picking, this used to work in earlier versions, but it broke in #4443. For reverting, it was never supported. Also, we add some minor improvements while we're at it, such as slightly better names for the auto-stashes that are created for the various operations (so that, if an auto-stash pop fails and the stash is kept around, you can tell more easily what it was for). Also, we now adjust the selection of the commits list after cherry-picking, so that the same commit stays selected.
2 parents 91d8c25 + 6b1cab7 commit a8e9859

File tree

14 files changed

+100
-46
lines changed

14 files changed

+100
-46
lines changed

.golangci.yml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,12 @@ linters:
9999
- legacy
100100
- std-error-handling
101101
paths:
102-
- third_party$
103-
- builtin$
104-
- examples$
102+
- vendor/
105103
formatters:
106104
enable:
107105
- gofumpt
108106
- goimports
109107
exclusions:
110108
generated: lax
111109
paths:
112-
- third_party$
113-
- builtin$
114-
- examples$
110+
- vendor/

pkg/commands/git_commands/patch.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package git_commands
22

33
import (
4+
"fmt"
45
"path/filepath"
56
"time"
67

@@ -218,7 +219,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
218219

219220
func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitIdx int, stash bool) error {
220221
if stash {
221-
if err := self.stash.Push(self.Tr.StashPrefix + commits[commitIdx].Hash()); err != nil {
222+
if err := self.stash.Push(fmt.Sprintf(self.Tr.AutoStashForMovingPatchToIndex, commits[commitIdx].ShortHash())); err != nil {
222223
return err
223224
}
224225
}

pkg/gui/controllers/helpers/cherry_pick_helper.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package helpers
33
import (
44
"strconv"
55

6-
"github.com/jesseduffield/gocui"
76
"github.com/jesseduffield/lazygit/pkg/commands/models"
87
"github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking"
98
"github.com/jesseduffield/lazygit/pkg/gui/types"
@@ -77,14 +76,34 @@ func (self *CherryPickHelper) Paste() error {
7776
"numCommits": strconv.Itoa(len(self.getData().CherryPickedCommits)),
7877
}),
7978
HandleConfirm: func() error {
80-
return self.c.WithWaitingStatus(self.c.Tr.CherryPickingStatus, func(gocui.Task) error {
79+
return self.c.WithWaitingStatusSync(self.c.Tr.CherryPickingStatus, func() error {
80+
mustStash := IsWorkingTreeDirty(self.c.Model().Files)
81+
8182
self.c.LogAction(self.c.Tr.Actions.CherryPick)
82-
result := self.c.Git().Rebase.CherryPickCommits(self.getData().CherryPickedCommits)
83-
err := self.rebaseHelper.CheckMergeOrRebase(result)
83+
84+
if mustStash {
85+
if err := self.c.Git().Stash.Push(self.c.Tr.AutoStashForCherryPicking); err != nil {
86+
return err
87+
}
88+
}
89+
90+
cherryPickedCommits := self.getData().CherryPickedCommits
91+
result := self.c.Git().Rebase.CherryPickCommits(cherryPickedCommits)
92+
err := self.rebaseHelper.CheckMergeOrRebaseWithRefreshOptions(result, types.RefreshOptions{Mode: types.SYNC})
8493
if err != nil {
8594
return result
8695
}
8796

97+
// Move the selection down by the number of commits we just
98+
// cherry-picked, to keep the same commit selected as before.
99+
// Don't do this if a rebase todo is selected, because in this
100+
// case we are in a rebase and the cherry-picked commits end up
101+
// below the selection.
102+
if commit := self.c.Contexts().LocalCommits.GetSelected(); commit != nil && !commit.IsTODO() {
103+
self.c.Contexts().LocalCommits.MoveSelection(len(cherryPickedCommits))
104+
self.c.Contexts().LocalCommits.FocusLine()
105+
}
106+
88107
// If we're in the cherry-picking state at this point, it must
89108
// be because there were conflicts. Don't clear the copied
90109
// commits in this case, since we might want to abort and try
@@ -96,7 +115,17 @@ func (self *CherryPickHelper) Paste() error {
96115
if !isInCherryPick {
97116
self.getData().DidPaste = true
98117
self.rerender()
118+
119+
if mustStash {
120+
if err := self.c.Git().Stash.Pop(0); err != nil {
121+
return err
122+
}
123+
self.c.Refresh(types.RefreshOptions{
124+
Scope: []types.RefreshableView{types.STASH, types.FILES},
125+
})
126+
}
99127
}
128+
100129
return nil
101130
})
102131
},

pkg/gui/controllers/helpers/refs_helper.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func (self *RefsHelper) CheckoutRef(ref string, options types.CheckoutRefOptions
7979
Prompt: self.c.Tr.AutoStashPrompt,
8080
HandleConfirm: func() error {
8181
return withCheckoutStatus(func(gocui.Task) error {
82-
if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + ref); err != nil {
82+
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForCheckout, ref)); err != nil {
8383
return err
8484
}
8585
if err := self.c.Git().Branch.Checkout(ref, cmdOptions); err != nil {
@@ -355,7 +355,7 @@ func (self *RefsHelper) NewBranch(from string, fromFormattedName string, suggest
355355
Title: self.c.Tr.AutoStashTitle,
356356
Prompt: self.c.Tr.AutoStashPrompt,
357357
HandleConfirm: func() error {
358-
if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + newBranchName); err != nil {
358+
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil {
359359
return err
360360
}
361361
if err := newBranchFunc(newBranchName, from); err != nil {
@@ -389,7 +389,7 @@ func (self *RefsHelper) MoveCommitsToNewBranch() error {
389389
return err
390390
}
391391

392-
withNewBranchNamePrompt := func(baseBranchName string, f func(string, string) error) error {
392+
withNewBranchNamePrompt := func(baseBranchName string, f func(string) error) error {
393393
prompt := utils.ResolvePlaceholderString(
394394
self.c.Tr.NewBranchNameBranchOff,
395395
map[string]string{
@@ -408,7 +408,7 @@ func (self *RefsHelper) MoveCommitsToNewBranch() error {
408408
self.c.LogAction(self.c.Tr.MoveCommitsToNewBranch)
409409
newBranchName := SanitizedBranchName(response)
410410
return self.c.WithWaitingStatus(self.c.Tr.MovingCommitsToNewBranchStatus, func(gocui.Task) error {
411-
return f(currentBranch.Name, newBranchName)
411+
return f(newBranchName)
412412
})
413413
},
414414
})
@@ -447,8 +447,8 @@ func (self *RefsHelper) MoveCommitsToNewBranch() error {
447447
{
448448
Label: fmt.Sprintf(self.c.Tr.MoveCommitsToNewBranchFromBaseItem, shortBaseBranchName),
449449
OnPress: func() error {
450-
return withNewBranchNamePrompt(shortBaseBranchName, func(currentBranch string, newBranchName string) error {
451-
return self.moveCommitsToNewBranchOffOfMainBranch(currentBranch, newBranchName, baseBranchRef)
450+
return withNewBranchNamePrompt(shortBaseBranchName, func(newBranchName string) error {
451+
return self.moveCommitsToNewBranchOffOfMainBranch(newBranchName, baseBranchRef)
452452
})
453453
},
454454
},
@@ -462,14 +462,14 @@ func (self *RefsHelper) MoveCommitsToNewBranch() error {
462462
})
463463
}
464464

465-
func (self *RefsHelper) moveCommitsToNewBranchStackedOnCurrentBranch(currentBranch string, newBranchName string) error {
465+
func (self *RefsHelper) moveCommitsToNewBranchStackedOnCurrentBranch(newBranchName string) error {
466466
if err := self.c.Git().Branch.NewWithoutCheckout(newBranchName, "HEAD"); err != nil {
467467
return err
468468
}
469469

470470
mustStash := IsWorkingTreeDirty(self.c.Model().Files)
471471
if mustStash {
472-
if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + currentBranch); err != nil {
472+
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil {
473473
return err
474474
}
475475
}
@@ -495,14 +495,14 @@ func (self *RefsHelper) moveCommitsToNewBranchStackedOnCurrentBranch(currentBran
495495
return nil
496496
}
497497

498-
func (self *RefsHelper) moveCommitsToNewBranchOffOfMainBranch(currentBranch string, newBranchName string, baseBranchRef string) error {
498+
func (self *RefsHelper) moveCommitsToNewBranchOffOfMainBranch(newBranchName string, baseBranchRef string) error {
499499
commitsToCherryPick := lo.Filter(self.c.Model().Commits, func(commit *models.Commit, _ int) bool {
500500
return commit.Status == models.StatusUnpushed
501501
})
502502

503503
mustStash := IsWorkingTreeDirty(self.c.Model().Files)
504504
if mustStash {
505-
if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + currentBranch); err != nil {
505+
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil {
506506
return err
507507
}
508508
}

pkg/gui/controllers/local_commits_controller.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -873,14 +873,30 @@ func (self *LocalCommitsController) revert(commits []*models.Commit, start, end
873873
HandleConfirm: func() error {
874874
self.c.LogAction(self.c.Tr.Actions.RevertCommit)
875875
return self.c.WithWaitingStatusSync(self.c.Tr.RevertingStatus, func() error {
876+
mustStash := helpers.IsWorkingTreeDirty(self.c.Model().Files)
877+
878+
if mustStash {
879+
if err := self.c.Git().Stash.Push(self.c.Tr.AutoStashForReverting); err != nil {
880+
return err
881+
}
882+
}
883+
876884
result := self.c.Git().Commit.Revert(hashes, isMerge)
877-
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(result); err != nil {
885+
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(result, types.RefreshOptions{Mode: types.SYNC}); err != nil {
878886
return err
879887
}
880888
self.context().MoveSelection(len(commits))
881-
self.c.Refresh(types.RefreshOptions{
882-
Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS, types.BRANCHES},
883-
})
889+
self.context().FocusLine()
890+
891+
if mustStash {
892+
if err := self.c.Git().Stash.Pop(0); err != nil {
893+
return err
894+
}
895+
self.c.Refresh(types.RefreshOptions{
896+
Scope: []types.RefreshableView{types.STASH, types.FILES},
897+
})
898+
}
899+
884900
return nil
885901
})
886902
},

pkg/gui/controllers/undo_controller.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func (self *UndoController) reflogUndo() error {
9090
case COMMIT:
9191
self.c.Confirm(types.ConfirmOpts{
9292
Title: self.c.Tr.Actions.Undo,
93-
Prompt: fmt.Sprintf(self.c.Tr.SoftResetPrompt, action.from),
93+
Prompt: fmt.Sprintf(self.c.Tr.SoftResetPrompt, utils.ShortHash(action.from)),
9494
HandleConfirm: func() error {
9595
self.c.LogAction(self.c.Tr.Actions.Undo)
9696
return self.c.WithWaitingStatus(undoingStatus, func(gocui.Task) error {
@@ -103,7 +103,7 @@ func (self *UndoController) reflogUndo() error {
103103
case REBASE:
104104
self.c.Confirm(types.ConfirmOpts{
105105
Title: self.c.Tr.Actions.Undo,
106-
Prompt: fmt.Sprintf(self.c.Tr.HardResetAutostashPrompt, action.from),
106+
Prompt: fmt.Sprintf(self.c.Tr.HardResetAutostashPrompt, utils.ShortHash(action.from)),
107107
HandleConfirm: func() error {
108108
self.c.LogAction(self.c.Tr.Actions.Undo)
109109
return self.hardResetWithAutoStash(action.from, hardResetOptions{
@@ -157,7 +157,7 @@ func (self *UndoController) reflogRedo() error {
157157
case COMMIT, REBASE:
158158
self.c.Confirm(types.ConfirmOpts{
159159
Title: self.c.Tr.Actions.Redo,
160-
Prompt: fmt.Sprintf(self.c.Tr.HardResetAutostashPrompt, action.to),
160+
Prompt: fmt.Sprintf(self.c.Tr.HardResetAutostashPrompt, utils.ShortHash(action.to)),
161161
HandleConfirm: func() error {
162162
self.c.LogAction(self.c.Tr.Actions.Redo)
163163
return self.hardResetWithAutoStash(action.to, hardResetOptions{
@@ -260,7 +260,7 @@ func (self *UndoController) hardResetWithAutoStash(commitHash string, options ha
260260
dirtyWorkingTree := self.c.Helpers().WorkingTree.IsWorkingTreeDirty()
261261
if dirtyWorkingTree {
262262
return self.c.WithWaitingStatus(options.WaitingStatus, func(gocui.Task) error {
263-
if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + commitHash); err != nil {
263+
if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForUndo, utils.ShortHash(commitHash))); err != nil {
264264
return err
265265
}
266266
if err := reset(); err != nil {

pkg/i18n/english.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,12 @@ type TranslationSet struct {
447447
IncorrectNotARepository string
448448
AutoStashTitle string
449449
AutoStashPrompt string
450-
StashPrefix string
450+
AutoStashForUndo string
451+
AutoStashForCheckout string
452+
AutoStashForNewBranch string
453+
AutoStashForMovingPatchToIndex string
454+
AutoStashForCherryPicking string
455+
AutoStashForReverting string
451456
Discard string
452457
DiscardChangesTitle string
453458
DiscardFileChangesTooltip string
@@ -1540,7 +1545,12 @@ func EnglishTranslationSet() *TranslationSet {
15401545
IncorrectNotARepository: "The value of 'notARepository' is incorrect. It should be one of 'prompt', 'create', 'skip', or 'quit'.",
15411546
AutoStashTitle: "Autostash?",
15421547
AutoStashPrompt: "You must stash and pop your changes to bring them across. Do this automatically? (enter/esc)",
1543-
StashPrefix: "Auto-stashing changes for ",
1548+
AutoStashForUndo: "Auto-stashing changes for undoing to %s",
1549+
AutoStashForCheckout: "Auto-stashing changes for checking out %s",
1550+
AutoStashForNewBranch: "Auto-stashing changes for creating new branch %s",
1551+
AutoStashForMovingPatchToIndex: "Auto-stashing changes for moving custom patch to index from %s",
1552+
AutoStashForCherryPicking: "Auto-stashing changes for cherry-picking commits",
1553+
AutoStashForReverting: "Auto-stashing changes for reverting commits",
15441554
Discard: "Discard",
15451555
DiscardFileChangesTooltip: "View options for discarding changes to the selected file.",
15461556
DiscardChangesTitle: "Discard changes",

pkg/integration/tests/cherry_pick/cherry_pick.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ var CherryPick = NewIntegrationTest(NewIntegrationTestArgs{
7575
Lines(
7676
Contains("four"),
7777
Contains("three"),
78-
Contains("two"),
78+
Contains("two").IsSelected(),
7979
Contains("one"),
8080
Contains("base"),
8181
)
@@ -104,7 +104,7 @@ var CherryPick = NewIntegrationTest(NewIntegrationTestArgs{
104104
Lines(
105105
Contains("four"),
106106
Contains("three"),
107-
Contains("base"),
107+
Contains("base").IsSelected(),
108108
)
109109
},
110110
})

pkg/integration/tests/cherry_pick/cherry_pick_conflicts.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ var CherryPickConflicts = NewIntegrationTest(NewIntegrationTestArgs{
4343
t.Views().Commits().
4444
Focus().
4545
TopLines(
46-
Contains("first change"),
46+
Contains("first change").IsSelected(),
4747
).
4848
Press(keys.Commits.PasteCommits)
4949

@@ -76,7 +76,7 @@ var CherryPickConflicts = NewIntegrationTest(NewIntegrationTestArgs{
7676
t.Views().Commits().
7777
Focus().
7878
TopLines(
79-
Contains("second-change-branch unrelated change"),
79+
Contains("second-change-branch unrelated change").IsSelected(),
8080
Contains("second change"),
8181
Contains("first change"),
8282
).

pkg/integration/tests/cherry_pick/cherry_pick_during_rebase.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ var CherryPickDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{
8080
Contains("pick CI two"),
8181
Contains("--- Commits ---"),
8282
Contains(" CI three"),
83-
Contains(" CI one"),
83+
Contains(" CI one").IsSelected(),
8484
Contains(" CI base"),
8585
).
8686
Tap(func() {
@@ -89,7 +89,7 @@ var CherryPickDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{
8989
Lines(
9090
Contains("CI two"),
9191
Contains("CI three"),
92-
Contains("CI one"),
92+
Contains("CI one").IsSelected(),
9393
Contains("CI base"),
9494
)
9595
},

0 commit comments

Comments
 (0)