From 50785a2217b265731e581a64cde35da74161d034 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 3 Jul 2025 15:28:52 +0200 Subject: [PATCH 01/13] Exclude vendor directory from linting It seems to be excluded already when you run the lint.sh script, but in VS Code when setting Lint on Save to package it would still lint a file in gocui when you save it, which is annoying. (Remove the other paths that were there before; they seem to be unused, and they were added by the auto-migration.) Unfortunately, gopls will still lint gocui files with its builtin staticcheck linter, and I couldn't find a way to turn this off. This might be a reason to turn off staticcheck in gopls (not sure yet). --- .golangci.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 6a02b1dc5..b217d78a0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -99,9 +99,7 @@ linters: - legacy - std-error-handling paths: - - third_party$ - - builtin$ - - examples$ + - vendor/ formatters: enable: - gofumpt @@ -109,6 +107,4 @@ formatters: exclusions: generated: lax paths: - - third_party$ - - builtin$ - - examples$ + - vendor/ From 2c206c374d27685586ec063a1e4cf923bfb8e040 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 3 Jul 2025 16:31:40 +0200 Subject: [PATCH 02/13] Use abbreviated hash in undo confirmations --- pkg/gui/controllers/undo_controller.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/gui/controllers/undo_controller.go b/pkg/gui/controllers/undo_controller.go index 6ed947813..b7979d2b6 100644 --- a/pkg/gui/controllers/undo_controller.go +++ b/pkg/gui/controllers/undo_controller.go @@ -90,7 +90,7 @@ func (self *UndoController) reflogUndo() error { case COMMIT: self.c.Confirm(types.ConfirmOpts{ Title: self.c.Tr.Actions.Undo, - Prompt: fmt.Sprintf(self.c.Tr.SoftResetPrompt, action.from), + Prompt: fmt.Sprintf(self.c.Tr.SoftResetPrompt, utils.ShortHash(action.from)), HandleConfirm: func() error { self.c.LogAction(self.c.Tr.Actions.Undo) return self.c.WithWaitingStatus(undoingStatus, func(gocui.Task) error { @@ -103,7 +103,7 @@ func (self *UndoController) reflogUndo() error { case REBASE: self.c.Confirm(types.ConfirmOpts{ Title: self.c.Tr.Actions.Undo, - Prompt: fmt.Sprintf(self.c.Tr.HardResetAutostashPrompt, action.from), + Prompt: fmt.Sprintf(self.c.Tr.HardResetAutostashPrompt, utils.ShortHash(action.from)), HandleConfirm: func() error { self.c.LogAction(self.c.Tr.Actions.Undo) return self.hardResetWithAutoStash(action.from, hardResetOptions{ @@ -157,7 +157,7 @@ func (self *UndoController) reflogRedo() error { case COMMIT, REBASE: self.c.Confirm(types.ConfirmOpts{ Title: self.c.Tr.Actions.Redo, - Prompt: fmt.Sprintf(self.c.Tr.HardResetAutostashPrompt, action.to), + Prompt: fmt.Sprintf(self.c.Tr.HardResetAutostashPrompt, utils.ShortHash(action.to)), HandleConfirm: func() error { self.c.LogAction(self.c.Tr.Actions.Redo) return self.hardResetWithAutoStash(action.to, hardResetOptions{ From a83fc5e34328dae1a2d5fe98f9ef40bcd58e48b0 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 3 Jul 2025 16:23:11 +0200 Subject: [PATCH 03/13] Use a better name for the auto-stash for undo --- pkg/gui/controllers/undo_controller.go | 2 +- pkg/i18n/english.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/gui/controllers/undo_controller.go b/pkg/gui/controllers/undo_controller.go index b7979d2b6..ecff5bea8 100644 --- a/pkg/gui/controllers/undo_controller.go +++ b/pkg/gui/controllers/undo_controller.go @@ -260,7 +260,7 @@ func (self *UndoController) hardResetWithAutoStash(commitHash string, options ha dirtyWorkingTree := self.c.Helpers().WorkingTree.IsWorkingTreeDirty() if dirtyWorkingTree { return self.c.WithWaitingStatus(options.WaitingStatus, func(gocui.Task) error { - if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + commitHash); err != nil { + if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForUndo, utils.ShortHash(commitHash))); err != nil { return err } if err := reset(); err != nil { diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index ca7377a8e..9c6663d9a 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -448,6 +448,7 @@ type TranslationSet struct { AutoStashTitle string AutoStashPrompt string StashPrefix string + AutoStashForUndo string Discard string DiscardChangesTitle string DiscardFileChangesTooltip string @@ -1541,6 +1542,7 @@ func EnglishTranslationSet() *TranslationSet { AutoStashTitle: "Autostash?", AutoStashPrompt: "You must stash and pop your changes to bring them across. Do this automatically? (enter/esc)", StashPrefix: "Auto-stashing changes for ", + AutoStashForUndo: "Auto-stashing changes for undoing to %s", Discard: "Discard", DiscardFileChangesTooltip: "View options for discarding changes to the selected file.", DiscardChangesTitle: "Discard changes", From 908975c7580ca1b9c4072d94223c92bccc6c1f07 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 3 Jul 2025 16:44:44 +0200 Subject: [PATCH 04/13] Use a better name for the auto-stash for checking out a branch --- pkg/gui/controllers/helpers/refs_helper.go | 2 +- pkg/i18n/english.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/gui/controllers/helpers/refs_helper.go b/pkg/gui/controllers/helpers/refs_helper.go index 6b07b6ec6..d7ac0845f 100644 --- a/pkg/gui/controllers/helpers/refs_helper.go +++ b/pkg/gui/controllers/helpers/refs_helper.go @@ -79,7 +79,7 @@ func (self *RefsHelper) CheckoutRef(ref string, options types.CheckoutRefOptions Prompt: self.c.Tr.AutoStashPrompt, HandleConfirm: func() error { return withCheckoutStatus(func(gocui.Task) error { - if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + ref); err != nil { + if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForCheckout, ref)); err != nil { return err } if err := self.c.Git().Branch.Checkout(ref, cmdOptions); err != nil { diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 9c6663d9a..07301af74 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -449,6 +449,7 @@ type TranslationSet struct { AutoStashPrompt string StashPrefix string AutoStashForUndo string + AutoStashForCheckout string Discard string DiscardChangesTitle string DiscardFileChangesTooltip string @@ -1543,6 +1544,7 @@ func EnglishTranslationSet() *TranslationSet { AutoStashPrompt: "You must stash and pop your changes to bring them across. Do this automatically? (enter/esc)", StashPrefix: "Auto-stashing changes for ", AutoStashForUndo: "Auto-stashing changes for undoing to %s", + AutoStashForCheckout: "Auto-stashing changes for checking out %s", Discard: "Discard", DiscardFileChangesTooltip: "View options for discarding changes to the selected file.", DiscardChangesTitle: "Discard changes", From 2a7ce19b73676d99d27ac9291ecabd1dbb84f94a Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 2 Jul 2025 10:54:51 +0200 Subject: [PATCH 05/13] Use a better name for the auto-stash for creating a new branch For the case of creating a new branch by moving commits to it, we were using the current (old) branch name in the stash name; change this to use the new name instead. --- pkg/gui/controllers/helpers/refs_helper.go | 18 +++++++++--------- pkg/i18n/english.go | 2 ++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pkg/gui/controllers/helpers/refs_helper.go b/pkg/gui/controllers/helpers/refs_helper.go index d7ac0845f..993ffb423 100644 --- a/pkg/gui/controllers/helpers/refs_helper.go +++ b/pkg/gui/controllers/helpers/refs_helper.go @@ -355,7 +355,7 @@ func (self *RefsHelper) NewBranch(from string, fromFormattedName string, suggest Title: self.c.Tr.AutoStashTitle, Prompt: self.c.Tr.AutoStashPrompt, HandleConfirm: func() error { - if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + newBranchName); err != nil { + if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil { return err } if err := newBranchFunc(newBranchName, from); err != nil { @@ -389,7 +389,7 @@ func (self *RefsHelper) MoveCommitsToNewBranch() error { return err } - withNewBranchNamePrompt := func(baseBranchName string, f func(string, string) error) error { + withNewBranchNamePrompt := func(baseBranchName string, f func(string) error) error { prompt := utils.ResolvePlaceholderString( self.c.Tr.NewBranchNameBranchOff, map[string]string{ @@ -408,7 +408,7 @@ func (self *RefsHelper) MoveCommitsToNewBranch() error { self.c.LogAction(self.c.Tr.MoveCommitsToNewBranch) newBranchName := SanitizedBranchName(response) return self.c.WithWaitingStatus(self.c.Tr.MovingCommitsToNewBranchStatus, func(gocui.Task) error { - return f(currentBranch.Name, newBranchName) + return f(newBranchName) }) }, }) @@ -447,8 +447,8 @@ func (self *RefsHelper) MoveCommitsToNewBranch() error { { Label: fmt.Sprintf(self.c.Tr.MoveCommitsToNewBranchFromBaseItem, shortBaseBranchName), OnPress: func() error { - return withNewBranchNamePrompt(shortBaseBranchName, func(currentBranch string, newBranchName string) error { - return self.moveCommitsToNewBranchOffOfMainBranch(currentBranch, newBranchName, baseBranchRef) + return withNewBranchNamePrompt(shortBaseBranchName, func(newBranchName string) error { + return self.moveCommitsToNewBranchOffOfMainBranch(newBranchName, baseBranchRef) }) }, }, @@ -462,14 +462,14 @@ func (self *RefsHelper) MoveCommitsToNewBranch() error { }) } -func (self *RefsHelper) moveCommitsToNewBranchStackedOnCurrentBranch(currentBranch string, newBranchName string) error { +func (self *RefsHelper) moveCommitsToNewBranchStackedOnCurrentBranch(newBranchName string) error { if err := self.c.Git().Branch.NewWithoutCheckout(newBranchName, "HEAD"); err != nil { return err } mustStash := IsWorkingTreeDirty(self.c.Model().Files) if mustStash { - if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + currentBranch); err != nil { + if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil { return err } } @@ -495,14 +495,14 @@ func (self *RefsHelper) moveCommitsToNewBranchStackedOnCurrentBranch(currentBran return nil } -func (self *RefsHelper) moveCommitsToNewBranchOffOfMainBranch(currentBranch string, newBranchName string, baseBranchRef string) error { +func (self *RefsHelper) moveCommitsToNewBranchOffOfMainBranch(newBranchName string, baseBranchRef string) error { commitsToCherryPick := lo.Filter(self.c.Model().Commits, func(commit *models.Commit, _ int) bool { return commit.Status == models.StatusUnpushed }) mustStash := IsWorkingTreeDirty(self.c.Model().Files) if mustStash { - if err := self.c.Git().Stash.Push(self.c.Tr.StashPrefix + currentBranch); err != nil { + if err := self.c.Git().Stash.Push(fmt.Sprintf(self.c.Tr.AutoStashForNewBranch, newBranchName)); err != nil { return err } } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 07301af74..d345705bd 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -450,6 +450,7 @@ type TranslationSet struct { StashPrefix string AutoStashForUndo string AutoStashForCheckout string + AutoStashForNewBranch string Discard string DiscardChangesTitle string DiscardFileChangesTooltip string @@ -1545,6 +1546,7 @@ func EnglishTranslationSet() *TranslationSet { StashPrefix: "Auto-stashing changes for ", AutoStashForUndo: "Auto-stashing changes for undoing to %s", AutoStashForCheckout: "Auto-stashing changes for checking out %s", + AutoStashForNewBranch: "Auto-stashing changes for creating new branch %s", Discard: "Discard", DiscardFileChangesTooltip: "View options for discarding changes to the selected file.", DiscardChangesTitle: "Discard changes", From 9511ed9d2c712ff3421f3f607b82bedb73ccf3e9 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 3 Jul 2025 16:50:53 +0200 Subject: [PATCH 06/13] Use a better name for the auto-stash for moving a custom patch to index --- pkg/commands/git_commands/patch.go | 3 ++- pkg/i18n/english.go | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/commands/git_commands/patch.go b/pkg/commands/git_commands/patch.go index 495d50ad6..2a979dd54 100644 --- a/pkg/commands/git_commands/patch.go +++ b/pkg/commands/git_commands/patch.go @@ -1,6 +1,7 @@ package git_commands import ( + "fmt" "path/filepath" "time" @@ -218,7 +219,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitIdx int, stash bool) error { if stash { - if err := self.stash.Push(self.Tr.StashPrefix + commits[commitIdx].Hash()); err != nil { + if err := self.stash.Push(fmt.Sprintf(self.Tr.AutoStashForMovingPatchToIndex, commits[commitIdx].ShortHash())); err != nil { return err } } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index d345705bd..223cf93f8 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -451,6 +451,7 @@ type TranslationSet struct { AutoStashForUndo string AutoStashForCheckout string AutoStashForNewBranch string + AutoStashForMovingPatchToIndex string Discard string DiscardChangesTitle string DiscardFileChangesTooltip string @@ -1547,6 +1548,7 @@ func EnglishTranslationSet() *TranslationSet { AutoStashForUndo: "Auto-stashing changes for undoing to %s", AutoStashForCheckout: "Auto-stashing changes for checking out %s", AutoStashForNewBranch: "Auto-stashing changes for creating new branch %s", + AutoStashForMovingPatchToIndex: "Auto-stashing changes for moving custom patch to index from %s", Discard: "Discard", DiscardFileChangesTooltip: "View options for discarding changes to the selected file.", DiscardChangesTitle: "Discard changes", From e7cb469fd0e889804dc2f460bae7b9d9eed8fb26 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 3 Jul 2025 16:51:28 +0200 Subject: [PATCH 07/13] Remove unused text StashPrefix --- pkg/i18n/english.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 223cf93f8..e0b2f1ed3 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -447,7 +447,6 @@ type TranslationSet struct { IncorrectNotARepository string AutoStashTitle string AutoStashPrompt string - StashPrefix string AutoStashForUndo string AutoStashForCheckout string AutoStashForNewBranch string @@ -1544,7 +1543,6 @@ func EnglishTranslationSet() *TranslationSet { IncorrectNotARepository: "The value of 'notARepository' is incorrect. It should be one of 'prompt', 'create', 'skip', or 'quit'.", AutoStashTitle: "Autostash?", AutoStashPrompt: "You must stash and pop your changes to bring them across. Do this automatically? (enter/esc)", - StashPrefix: "Auto-stashing changes for ", AutoStashForUndo: "Auto-stashing changes for undoing to %s", AutoStashForCheckout: "Auto-stashing changes for checking out %s", AutoStashForNewBranch: "Auto-stashing changes for creating new branch %s", From 3f3e942f60282170b2ca73586a5b09955548b815 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 2 Jul 2025 17:04:12 +0200 Subject: [PATCH 08/13] Auto-stash if necessary when cherry-picking commits --- .../controllers/helpers/cherry_pick_helper.go | 19 +++++++++++++++++++ pkg/i18n/english.go | 2 ++ 2 files changed, 21 insertions(+) diff --git a/pkg/gui/controllers/helpers/cherry_pick_helper.go b/pkg/gui/controllers/helpers/cherry_pick_helper.go index 5b96dc84c..3b9232340 100644 --- a/pkg/gui/controllers/helpers/cherry_pick_helper.go +++ b/pkg/gui/controllers/helpers/cherry_pick_helper.go @@ -78,7 +78,16 @@ func (self *CherryPickHelper) Paste() error { }), HandleConfirm: func() error { return self.c.WithWaitingStatus(self.c.Tr.CherryPickingStatus, func(gocui.Task) error { + mustStash := IsWorkingTreeDirty(self.c.Model().Files) + self.c.LogAction(self.c.Tr.Actions.CherryPick) + + if mustStash { + if err := self.c.Git().Stash.Push(self.c.Tr.AutoStashForCherryPicking); err != nil { + return err + } + } + result := self.c.Git().Rebase.CherryPickCommits(self.getData().CherryPickedCommits) err := self.rebaseHelper.CheckMergeOrRebase(result) if err != nil { @@ -96,7 +105,17 @@ func (self *CherryPickHelper) Paste() error { if !isInCherryPick { self.getData().DidPaste = true self.rerender() + + if mustStash { + if err := self.c.Git().Stash.Pop(0); err != nil { + return err + } + self.c.Refresh(types.RefreshOptions{ + Scope: []types.RefreshableView{types.STASH, types.FILES}, + }) + } } + return nil }) }, diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index e0b2f1ed3..85c075f7b 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -451,6 +451,7 @@ type TranslationSet struct { AutoStashForCheckout string AutoStashForNewBranch string AutoStashForMovingPatchToIndex string + AutoStashForCherryPicking string Discard string DiscardChangesTitle string DiscardFileChangesTooltip string @@ -1547,6 +1548,7 @@ func EnglishTranslationSet() *TranslationSet { AutoStashForCheckout: "Auto-stashing changes for checking out %s", AutoStashForNewBranch: "Auto-stashing changes for creating new branch %s", AutoStashForMovingPatchToIndex: "Auto-stashing changes for moving custom patch to index from %s", + AutoStashForCherryPicking: "Auto-stashing changes for cherry-picking commits", Discard: "Discard", DiscardFileChangesTooltip: "View options for discarding changes to the selected file.", DiscardChangesTitle: "Discard changes", From 6479c52fb2eb05a0cd633144fbbf27a360f15f0c Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 2 Jul 2025 18:19:15 +0200 Subject: [PATCH 09/13] Use sync waiting status for cherry-picking This reduces flicker of the Files panel in the case that an auto-stash needs to be made. --- pkg/gui/controllers/helpers/cherry_pick_helper.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/gui/controllers/helpers/cherry_pick_helper.go b/pkg/gui/controllers/helpers/cherry_pick_helper.go index 3b9232340..f0e4993eb 100644 --- a/pkg/gui/controllers/helpers/cherry_pick_helper.go +++ b/pkg/gui/controllers/helpers/cherry_pick_helper.go @@ -3,7 +3,6 @@ package helpers import ( "strconv" - "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking" "github.com/jesseduffield/lazygit/pkg/gui/types" @@ -77,7 +76,7 @@ func (self *CherryPickHelper) Paste() error { "numCommits": strconv.Itoa(len(self.getData().CherryPickedCommits)), }), HandleConfirm: func() error { - return self.c.WithWaitingStatus(self.c.Tr.CherryPickingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusSync(self.c.Tr.CherryPickingStatus, func() error { mustStash := IsWorkingTreeDirty(self.c.Model().Files) self.c.LogAction(self.c.Tr.Actions.CherryPick) From 09a4e0b2096cf394d675cb4c5e487ad1c31601a5 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 2 Jul 2025 18:56:16 +0200 Subject: [PATCH 10/13] Add selection assertions to cherry-pick tests We are about to change the selection behavior when cherry-picking, and it's good to have tests that document in what way it changes in the next commit. --- pkg/integration/tests/cherry_pick/cherry_pick.go | 4 ++-- pkg/integration/tests/cherry_pick/cherry_pick_conflicts.go | 4 ++-- .../tests/cherry_pick/cherry_pick_during_rebase.go | 4 ++-- pkg/integration/tests/cherry_pick/cherry_pick_range.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/integration/tests/cherry_pick/cherry_pick.go b/pkg/integration/tests/cherry_pick/cherry_pick.go index bfa02c5a3..434e15d8c 100644 --- a/pkg/integration/tests/cherry_pick/cherry_pick.go +++ b/pkg/integration/tests/cherry_pick/cherry_pick.go @@ -73,7 +73,7 @@ var CherryPick = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Information().Content(DoesNotContain("commits copied")) }). Lines( - Contains("four"), + Contains("four").IsSelected(), Contains("three"), Contains("two"), Contains("one"), @@ -102,7 +102,7 @@ var CherryPick = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Information().Content(DoesNotContain("commits copied")) }). Lines( - Contains("four"), + Contains("four").IsSelected(), Contains("three"), Contains("base"), ) diff --git a/pkg/integration/tests/cherry_pick/cherry_pick_conflicts.go b/pkg/integration/tests/cherry_pick/cherry_pick_conflicts.go index 2f0df7498..f3d5eca82 100644 --- a/pkg/integration/tests/cherry_pick/cherry_pick_conflicts.go +++ b/pkg/integration/tests/cherry_pick/cherry_pick_conflicts.go @@ -43,7 +43,7 @@ var CherryPickConflicts = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Commits(). Focus(). TopLines( - Contains("first change"), + Contains("first change").IsSelected(), ). Press(keys.Commits.PasteCommits) @@ -76,7 +76,7 @@ var CherryPickConflicts = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Commits(). Focus(). TopLines( - Contains("second-change-branch unrelated change"), + Contains("second-change-branch unrelated change").IsSelected(), Contains("second change"), Contains("first change"), ). diff --git a/pkg/integration/tests/cherry_pick/cherry_pick_during_rebase.go b/pkg/integration/tests/cherry_pick/cherry_pick_during_rebase.go index 7974995df..e94a0342c 100644 --- a/pkg/integration/tests/cherry_pick/cherry_pick_during_rebase.go +++ b/pkg/integration/tests/cherry_pick/cherry_pick_during_rebase.go @@ -79,7 +79,7 @@ var CherryPickDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{ Contains("--- Pending rebase todos ---"), Contains("pick CI two"), Contains("--- Commits ---"), - Contains(" CI three"), + Contains(" CI three").IsSelected(), Contains(" CI one"), Contains(" CI base"), ). @@ -88,7 +88,7 @@ var CherryPickDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{ }). Lines( Contains("CI two"), - Contains("CI three"), + Contains("CI three").IsSelected(), Contains("CI one"), Contains("CI base"), ) diff --git a/pkg/integration/tests/cherry_pick/cherry_pick_range.go b/pkg/integration/tests/cherry_pick/cherry_pick_range.go index e41d64f4a..fc1c3b2e5 100644 --- a/pkg/integration/tests/cherry_pick/cherry_pick_range.go +++ b/pkg/integration/tests/cherry_pick/cherry_pick_range.go @@ -70,7 +70,7 @@ var CherryPickRange = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Information().Content(DoesNotContain("commits copied")) }). Lines( - Contains("four"), + Contains("four").IsSelected(), Contains("three"), Contains("two"), Contains("one"), From 0fc107c8c11e90a77d8dbdb247fd35475aa9328d Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 2 Jul 2025 18:29:14 +0200 Subject: [PATCH 11/13] Adjust selection after cherry-picking commits Keep the same commit selected, by moving the selection down by the number of cherry-picked commits. We also do this when reverting commits, and it is possible now that we use a sync waiting status. We also need to turn the refresh that happens as part of CheckMergeOrRebase into a sync one, so that the commits list is up to date and the new selection isn't clamped. --- pkg/gui/controllers/helpers/cherry_pick_helper.go | 15 +++++++++++++-- pkg/integration/tests/cherry_pick/cherry_pick.go | 8 ++++---- .../cherry_pick/cherry_pick_during_rebase.go | 8 ++++---- .../tests/cherry_pick/cherry_pick_merge.go | 7 ++++--- .../tests/cherry_pick/cherry_pick_range.go | 4 ++-- pkg/integration/tests/reflog/cherry_pick.go | 4 ++-- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/pkg/gui/controllers/helpers/cherry_pick_helper.go b/pkg/gui/controllers/helpers/cherry_pick_helper.go index f0e4993eb..8f5b37a66 100644 --- a/pkg/gui/controllers/helpers/cherry_pick_helper.go +++ b/pkg/gui/controllers/helpers/cherry_pick_helper.go @@ -87,12 +87,23 @@ func (self *CherryPickHelper) Paste() error { } } - result := self.c.Git().Rebase.CherryPickCommits(self.getData().CherryPickedCommits) - err := self.rebaseHelper.CheckMergeOrRebase(result) + cherryPickedCommits := self.getData().CherryPickedCommits + result := self.c.Git().Rebase.CherryPickCommits(cherryPickedCommits) + err := self.rebaseHelper.CheckMergeOrRebaseWithRefreshOptions(result, types.RefreshOptions{Mode: types.SYNC}) if err != nil { return result } + // Move the selection down by the number of commits we just + // cherry-picked, to keep the same commit selected as before. + // Don't do this if a rebase todo is selected, because in this + // case we are in a rebase and the cherry-picked commits end up + // below the selection. + if commit := self.c.Contexts().LocalCommits.GetSelected(); commit != nil && !commit.IsTODO() { + self.c.Contexts().LocalCommits.MoveSelection(len(cherryPickedCommits)) + self.c.Contexts().LocalCommits.FocusLine() + } + // If we're in the cherry-picking state at this point, it must // be because there were conflicts. Don't clear the copied // commits in this case, since we might want to abort and try diff --git a/pkg/integration/tests/cherry_pick/cherry_pick.go b/pkg/integration/tests/cherry_pick/cherry_pick.go index 434e15d8c..ccdb606f8 100644 --- a/pkg/integration/tests/cherry_pick/cherry_pick.go +++ b/pkg/integration/tests/cherry_pick/cherry_pick.go @@ -73,9 +73,9 @@ var CherryPick = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Information().Content(DoesNotContain("commits copied")) }). Lines( - Contains("four").IsSelected(), + Contains("four"), Contains("three"), - Contains("two"), + Contains("two").IsSelected(), Contains("one"), Contains("base"), ) @@ -102,9 +102,9 @@ var CherryPick = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Information().Content(DoesNotContain("commits copied")) }). Lines( - Contains("four").IsSelected(), + Contains("four"), Contains("three"), - Contains("base"), + Contains("base").IsSelected(), ) }, }) diff --git a/pkg/integration/tests/cherry_pick/cherry_pick_during_rebase.go b/pkg/integration/tests/cherry_pick/cherry_pick_during_rebase.go index e94a0342c..af4e8b9d2 100644 --- a/pkg/integration/tests/cherry_pick/cherry_pick_during_rebase.go +++ b/pkg/integration/tests/cherry_pick/cherry_pick_during_rebase.go @@ -79,8 +79,8 @@ var CherryPickDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{ Contains("--- Pending rebase todos ---"), Contains("pick CI two"), Contains("--- Commits ---"), - Contains(" CI three").IsSelected(), - Contains(" CI one"), + Contains(" CI three"), + Contains(" CI one").IsSelected(), Contains(" CI base"), ). Tap(func() { @@ -88,8 +88,8 @@ var CherryPickDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{ }). Lines( Contains("CI two"), - Contains("CI three").IsSelected(), - Contains("CI one"), + Contains("CI three"), + Contains("CI one").IsSelected(), Contains("CI base"), ) }, diff --git a/pkg/integration/tests/cherry_pick/cherry_pick_merge.go b/pkg/integration/tests/cherry_pick/cherry_pick_merge.go index 77b523ecc..7c68ce8eb 100644 --- a/pkg/integration/tests/cherry_pick/cherry_pick_merge.go +++ b/pkg/integration/tests/cherry_pick/cherry_pick_merge.go @@ -63,9 +63,10 @@ var CherryPickMerge = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Information().Content(DoesNotContain("commit copied")) }). Lines( - Contains("Merge branch 'second-branch'").IsSelected(), - Contains("base"), - ) + Contains("Merge branch 'second-branch'"), + Contains("base").IsSelected(), + ). + SelectPreviousItem() t.Views().Main().ContainsLines( Contains("Merge branch 'second-branch'"), diff --git a/pkg/integration/tests/cherry_pick/cherry_pick_range.go b/pkg/integration/tests/cherry_pick/cherry_pick_range.go index fc1c3b2e5..329ef1e9c 100644 --- a/pkg/integration/tests/cherry_pick/cherry_pick_range.go +++ b/pkg/integration/tests/cherry_pick/cherry_pick_range.go @@ -70,9 +70,9 @@ var CherryPickRange = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Information().Content(DoesNotContain("commits copied")) }). Lines( - Contains("four").IsSelected(), + Contains("four"), Contains("three"), - Contains("two"), + Contains("two").IsSelected(), Contains("one"), Contains("base"), ) diff --git a/pkg/integration/tests/reflog/cherry_pick.go b/pkg/integration/tests/reflog/cherry_pick.go index 1416ef955..1e223c9e0 100644 --- a/pkg/integration/tests/reflog/cherry_pick.go +++ b/pkg/integration/tests/reflog/cherry_pick.go @@ -43,8 +43,8 @@ var CherryPick = NewIntegrationTest(NewIntegrationTestArgs{ Confirm() }). Lines( - Contains("three").IsSelected(), - Contains("one"), + Contains("three"), + Contains("one").IsSelected(), ) }, }) From dc795b5db7f3849a224c23adbed490fce669ccb7 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 2 Jul 2025 18:36:31 +0200 Subject: [PATCH 12/13] Remove unnecessary refresh CheckMergeOrRebase calls Refresh already. However, it does an async refresh by default, so we must turn this into a sync refresh so that moving the selection down by one works even for the very first commit in history. Also, we must add an explicit call to FocusLine so that the view selection is in sync with the model selection; previously this was taken care of by the PostRefreshUpdate call that happens as part of a refresh. --- pkg/gui/controllers/local_commits_controller.go | 6 ++---- pkg/integration/tests/commit/revert.go | 7 ++++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 4a1209642..42059796c 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -874,13 +874,11 @@ func (self *LocalCommitsController) revert(commits []*models.Commit, start, end self.c.LogAction(self.c.Tr.Actions.RevertCommit) return self.c.WithWaitingStatusSync(self.c.Tr.RevertingStatus, func() error { result := self.c.Git().Commit.Revert(hashes, isMerge) - if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(result); err != nil { + if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(result, types.RefreshOptions{Mode: types.SYNC}); err != nil { return err } self.context().MoveSelection(len(commits)) - self.c.Refresh(types.RefreshOptions{ - Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS, types.BRANCHES}, - }) + self.context().FocusLine() return nil }) }, diff --git a/pkg/integration/tests/commit/revert.go b/pkg/integration/tests/commit/revert.go index 5bc81608f..b295abf92 100644 --- a/pkg/integration/tests/commit/revert.go +++ b/pkg/integration/tests/commit/revert.go @@ -29,9 +29,10 @@ var Revert = NewIntegrationTest(NewIntegrationTestArgs{ Confirm() }). Lines( - Contains("Revert \"first commit\"").IsSelected(), - Contains("first commit"), - ) + Contains("Revert \"first commit\""), + Contains("first commit").IsSelected(), + ). + SelectPreviousItem() t.Views().Main().Content(Contains("-myfile content")) t.FileSystem().PathNotPresent("myfile") From 6b1cab7011846e404b0d1499f1bfc8dd49006294 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 2 Jul 2025 17:10:00 +0200 Subject: [PATCH 13/13] Auto-stash if necessary when reverting commits --- .../controllers/local_commits_controller.go | 18 ++++++++++++++++++ pkg/i18n/english.go | 2 ++ 2 files changed, 20 insertions(+) diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 42059796c..2886c8cd0 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -873,12 +873,30 @@ func (self *LocalCommitsController) revert(commits []*models.Commit, start, end HandleConfirm: func() error { self.c.LogAction(self.c.Tr.Actions.RevertCommit) return self.c.WithWaitingStatusSync(self.c.Tr.RevertingStatus, func() error { + mustStash := helpers.IsWorkingTreeDirty(self.c.Model().Files) + + if mustStash { + if err := self.c.Git().Stash.Push(self.c.Tr.AutoStashForReverting); err != nil { + return err + } + } + result := self.c.Git().Commit.Revert(hashes, isMerge) if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(result, types.RefreshOptions{Mode: types.SYNC}); err != nil { return err } self.context().MoveSelection(len(commits)) self.context().FocusLine() + + if mustStash { + if err := self.c.Git().Stash.Pop(0); err != nil { + return err + } + self.c.Refresh(types.RefreshOptions{ + Scope: []types.RefreshableView{types.STASH, types.FILES}, + }) + } + return nil }) }, diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 85c075f7b..ee4014747 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -452,6 +452,7 @@ type TranslationSet struct { AutoStashForNewBranch string AutoStashForMovingPatchToIndex string AutoStashForCherryPicking string + AutoStashForReverting string Discard string DiscardChangesTitle string DiscardFileChangesTooltip string @@ -1549,6 +1550,7 @@ func EnglishTranslationSet() *TranslationSet { AutoStashForNewBranch: "Auto-stashing changes for creating new branch %s", AutoStashForMovingPatchToIndex: "Auto-stashing changes for moving custom patch to index from %s", AutoStashForCherryPicking: "Auto-stashing changes for cherry-picking commits", + AutoStashForReverting: "Auto-stashing changes for reverting commits", Discard: "Discard", DiscardFileChangesTooltip: "View options for discarding changes to the selected file.", DiscardChangesTitle: "Discard changes",