Fix panic when moving a custom patch after its commit leaves the view

When a custom patch is built from a commit and you then check out a branch
that doesn't contain that commit, getPatchCommitIndex() returns -1, which was
passed straight into commits[commitIndex], panicking with 'index out of range
[-1]' (#5802). This affected all five custom-patch menu actions.

Make getPatchCommitIndex() return an error when the patch's commit is no longer
present, and have each action surface it as a friendly message instead of
crashing. Add an integration test reproducing the scenario.
This commit is contained in:
Mohammed Faizan Mohiuddin 2026-07-27 15:43:35 +05:30
parent 292035709f
commit bf586ff0c6
4 changed files with 84 additions and 8 deletions

View file

@ -114,13 +114,13 @@ func (self *CustomPatchOptionsMenuAction) Call() error {
return self.c.Menu(types.CreateMenuOptions{Title: self.c.Tr.PatchOptionsTitle, Items: menuItems})
}
func (self *CustomPatchOptionsMenuAction) getPatchCommitIndex() int {
func (self *CustomPatchOptionsMenuAction) getPatchCommitIndex() (int, error) {
for index, commit := range self.c.Model().Commits {
if commit.Hash() == self.c.Git().Patch.PatchBuilder.To {
return index
return index, nil
}
}
return -1
return -1, errors.New(self.c.Tr.PatchCommitNotInCommitsErr)
}
func (self *CustomPatchOptionsMenuAction) returnFocusFromPatchExplorerIfNecessary() {
@ -133,7 +133,10 @@ func (self *CustomPatchOptionsMenuAction) handleDeletePatchFromCommit() error {
self.returnFocusFromPatchExplorerIfNecessary()
commits := self.c.Model().Commits
commitIndex := self.getPatchCommitIndex()
commitIndex, err := self.getPatchCommitIndex()
if err != nil {
return err
}
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.RemovePatchFromCommit)
err := self.c.Git().Patch.DeletePatchesFromCommit(commits, commitIndex)
@ -145,7 +148,10 @@ func (self *CustomPatchOptionsMenuAction) handleMovePatchToSelectedCommit() erro
self.returnFocusFromPatchExplorerIfNecessary()
commits := self.c.Model().Commits
commitIndex := self.getPatchCommitIndex()
commitIndex, err := self.getPatchCommitIndex()
if err != nil {
return err
}
toCommitIndex := self.c.Contexts().LocalCommits.GetSelectedLineIdx()
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.MovePatchToSelectedCommit)
@ -163,7 +169,10 @@ func (self *CustomPatchOptionsMenuAction) handleMovePatchIntoWorkingTree() error
Prompt: self.c.Tr.MustStashWarning,
HandleConfirm: func() error {
commits := self.c.Model().Commits
commitIndex := self.getPatchCommitIndex()
commitIndex, err := self.getPatchCommitIndex()
if err != nil {
return err
}
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.MovePatchIntoIndex)
err := self.c.Git().Patch.MovePatchIntoIndex(commits, commitIndex, mustStash)
@ -176,7 +185,10 @@ func (self *CustomPatchOptionsMenuAction) handleMovePatchIntoWorkingTree() error
func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error {
self.returnFocusFromPatchExplorerIfNecessary()
commitIndex := self.getPatchCommitIndex()
commitIndex, err := self.getPatchCommitIndex()
if err != nil {
return err
}
self.c.Helpers().Commits.OpenCommitMessagePanel(
&helpers.OpenCommitMessagePanelOpts{
// Pass a commit index of one less than the moved-from commit, so that
@ -211,7 +223,10 @@ func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error {
func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommitBefore() error {
self.returnFocusFromPatchExplorerIfNecessary()
commitIndex := self.getPatchCommitIndex()
commitIndex, err := self.getPatchCommitIndex()
if err != nil {
return err
}
self.c.Helpers().Commits.OpenCommitMessagePanel(
&helpers.OpenCommitMessagePanelOpts{
// Pass a commit index of one less than the moved-from commit, so that

View file

@ -461,6 +461,7 @@ type TranslationSet struct {
AutoStashForCheckout string
AutoStashForNewBranch string
AutoStashForMovingPatchToIndex string
PatchCommitNotInCommitsErr string
AutoStashForCherryPicking string
AutoStashForReverting string
Discard string
@ -1611,6 +1612,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",
PatchCommitNotInCommitsErr: "Cannot find the commit this custom patch was created from in the current commits list. This can happen after switching branches; recreate the patch to continue.",
AutoStashForCherryPicking: "Auto-stashing changes for cherry-picking commits",
AutoStashForReverting: "Auto-stashing changes for reverting commits",
Discard: "Discard",

View file

@ -0,0 +1,58 @@
package patch_building
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var MoveToIndexWhenCommitNotInCurrentBranch = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Move a patch into the index after checking out a branch that doesn't contain the patch's commit; expect a friendly error rather than a crash",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.EmptyCommit("base commit")
shell.NewBranch("feature")
shell.CreateFileAndAdd("file1", "file1 content\n")
shell.Commit("feature commit")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
// Build a custom patch from a commit that only exists on the feature branch
t.Views().Commits().
Focus().
Lines(
Contains("feature commit").IsSelected(),
Contains("base commit"),
).
PressEnter()
t.Views().CommitFiles().
IsFocused().
PressPrimaryAction()
t.Views().Information().Content(Contains("Building patch"))
// Check out a branch that does not contain the patch's commit, so the
// commit the patch was built from is no longer in the commits list.
t.Views().Branches().
Focus().
NavigateToLine(Contains("master")).
PressPrimaryAction()
t.Views().Commits().
Focus().
Lines(
Contains("base commit").IsSelected(),
)
// Previously this panicked with "index out of range [-1]" because the
// patch's commit could not be found in the current commits list. It
// should now report a friendly error instead.
t.Common().SelectPatchOption(Contains("Move patch out into index"))
t.ExpectPopup().Alert().
Title(Equals("Error")).
Content(Contains("Cannot find the commit this custom patch was created from")).
Confirm()
},
})

View file

@ -364,6 +364,7 @@ var tests = []*components.IntegrationTest{
patch_building.MoveToIndexFromAddedFileWithConflict,
patch_building.MoveToIndexPartOfAdjacentAddedLines,
patch_building.MoveToIndexPartial,
patch_building.MoveToIndexWhenCommitNotInCurrentBranch,
patch_building.MoveToIndexWithConflict,
patch_building.MoveToIndexWithModifiedFile,
patch_building.MoveToIndexWorksEvenIfNoprefixIsSet,