jesseduffield.lazygit/pkg/gui/controllers/helpers/gpg_helper.go
Stefan Haller c15ab5db5d Keep selected commits stable across refreshes
With the recently added external change detection, it happens more often
now that we refresh the commits list because an agent made a commit in
the background. In this case, if we keep the selection index the same,
it now points at a different commit, making the main view show a
different commit too, which is confusing and annoying. To fix this,
track the selected commit and range anchor by hash before reloading,
then restore those rows if both hashes still exist. This also allows us
to get rid of some bespoke code that did this for the specific cases of
reverting a commit or cherry-picking commits, because those are now
handled by the generic mechanism.
2026-06-23 09:20:05 +02:00

107 lines
3.1 KiB
Go

package helpers
import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type GpgHelper struct {
c *HelperCommon
}
func NewGpgHelper(c *HelperCommon) *GpgHelper {
return &GpgHelper{
c: c,
}
}
func (self *GpgHelper) WithGpgHandling(
cmdObj *oscommands.CmdObj,
configKey git_commands.GpgConfigKey,
waitingStatus string,
onSuccess func() error,
refreshScope []types.RefreshableView,
) error {
refreshOptions := types.RefreshOptions{Mode: types.ASYNC, Scope: refreshScope}
return self.withGpgHandling(
cmdObj, configKey, waitingStatus, onSuccess, refreshOptions, refreshOptions)
}
// WithGpgHandlingAndSelectHeadCommit is like WithGpgHandling, but on success it
// selects the new HEAD commit rather than restoring the previous selection. For
// committing, where the commit we just created is the one we want selected.
func (self *GpgHelper) WithGpgHandlingAndSelectHeadCommit(
cmdObj *oscommands.CmdObj,
configKey git_commands.GpgConfigKey,
waitingStatus string,
onSuccess func() error,
) error {
failureRefreshOptions := types.RefreshOptions{Mode: types.ASYNC}
successRefreshOptions := types.RefreshOptions{Mode: types.ASYNC, CommitSelection: types.SelectHeadCommit}
return self.withGpgHandling(
cmdObj, configKey, waitingStatus, onSuccess, failureRefreshOptions, successRefreshOptions)
}
// Currently there is a bug where if we switch to a subprocess from within
// WithWaitingStatus we get stuck there and can't return to lazygit. We could
// fix this bug, or just stop running subprocesses from within there, given that
// we don't need to see a loading status if we're in a subprocess.
func (self *GpgHelper) withGpgHandling(
cmdObj *oscommands.CmdObj,
configKey git_commands.GpgConfigKey,
waitingStatus string,
onSuccess func() error,
failureRefreshOptions types.RefreshOptions,
successRefreshOptions types.RefreshOptions,
) error {
useSubprocess := self.c.Git().Config.NeedsGpgSubprocess(configKey)
if useSubprocess {
success, err := self.c.RunSubprocess(cmdObj)
if success && onSuccess != nil {
if err := onSuccess(); err != nil {
return err
}
}
if success {
self.c.Refresh(successRefreshOptions)
} else {
self.c.Refresh(failureRefreshOptions)
}
return err
}
return self.runAndStream(
cmdObj, waitingStatus, onSuccess, failureRefreshOptions, successRefreshOptions)
}
func (self *GpgHelper) runAndStream(
cmdObj *oscommands.CmdObj,
waitingStatus string,
onSuccess func() error,
failureRefreshOptions types.RefreshOptions,
successRefreshOptions types.RefreshOptions,
) error {
return self.c.WithWaitingStatus(waitingStatus, func(gocui.Task) error {
if err := cmdObj.StreamOutput().Run(); err != nil {
self.c.Refresh(failureRefreshOptions)
return fmt.Errorf(
self.c.Tr.GitCommandFailed, self.c.UserConfig().Keybinding.Universal.ExtrasMenu,
)
}
if onSuccess != nil {
if err := onSuccess(); err != nil {
return err
}
}
self.c.Refresh(successRefreshOptions)
return nil
})
}