mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-12-14 11:23:09 +02:00
1dd7307fde
more and more move rebase commit refreshing into existing abstraction and more and more WIP and more handling clicks properly fix merge conflicts update cheatsheet lots more preparation to start moving things into controllers WIP better typing expand on remotes controller moving more code into controllers
274 lines
7.8 KiB
Go
274 lines
7.8 KiB
Go
package controllers
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
|
"github.com/jesseduffield/lazygit/pkg/config"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
)
|
|
|
|
type BisectController struct {
|
|
c *ControllerCommon
|
|
context types.IListContext
|
|
git *commands.GitCommand
|
|
|
|
getSelectedLocalCommit func() *models.Commit
|
|
getCommits func() []*models.Commit
|
|
}
|
|
|
|
var _ types.IController = &BisectController{}
|
|
|
|
func NewBisectController(
|
|
c *ControllerCommon,
|
|
context types.IListContext,
|
|
git *commands.GitCommand,
|
|
|
|
getSelectedLocalCommit func() *models.Commit,
|
|
getCommits func() []*models.Commit,
|
|
) *BisectController {
|
|
return &BisectController{
|
|
c: c,
|
|
context: context,
|
|
git: git,
|
|
|
|
getSelectedLocalCommit: getSelectedLocalCommit,
|
|
getCommits: getCommits,
|
|
}
|
|
}
|
|
|
|
func (self *BisectController) Keybindings(getKey func(key string) interface{}, config config.KeybindingConfig, guards types.KeybindingGuards) []*types.Binding {
|
|
bindings := []*types.Binding{
|
|
{
|
|
Key: getKey(config.Commits.ViewBisectOptions),
|
|
Handler: guards.OutsideFilterMode(self.checkSelected(self.openMenu)),
|
|
Description: self.c.Tr.LcViewBisectOptions,
|
|
OpensMenu: true,
|
|
},
|
|
}
|
|
|
|
return bindings
|
|
}
|
|
|
|
func (self *BisectController) openMenu(commit *models.Commit) error {
|
|
// no shame in getting this directly rather than using the cached value
|
|
// given how cheap it is to obtain
|
|
info := self.git.Bisect.GetInfo()
|
|
if info.Started() {
|
|
return self.openMidBisectMenu(info, commit)
|
|
} else {
|
|
return self.openStartBisectMenu(info, commit)
|
|
}
|
|
}
|
|
|
|
func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, commit *models.Commit) error {
|
|
// if there is not yet a 'current' bisect commit, or if we have
|
|
// selected the current commit, we need to jump to the next 'current' commit
|
|
// after we perform a bisect action. The reason we don't unconditionally jump
|
|
// is that sometimes the user will want to go and mark a few commits as skipped
|
|
// in a row and they wouldn't want to be jumped back to the current bisect
|
|
// commit each time.
|
|
// Originally we were allowing the user to, from the bisect menu, select whether
|
|
// they were talking about the selected commit or the current bisect commit,
|
|
// and that was a bit confusing (and required extra keypresses).
|
|
selectCurrentAfter := info.GetCurrentSha() == "" || info.GetCurrentSha() == commit.Sha
|
|
// we need to wait to reselect if our bisect commits aren't ancestors of our 'start'
|
|
// ref, because we'll be reloading our commits in that case.
|
|
waitToReselect := selectCurrentAfter && !self.git.Bisect.ReachableFromStart(info)
|
|
|
|
menuItems := []*popup.MenuItem{
|
|
{
|
|
DisplayString: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.NewTerm()),
|
|
OnPress: func() error {
|
|
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
|
if err := self.git.Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
return self.afterMark(selectCurrentAfter, waitToReselect)
|
|
},
|
|
},
|
|
{
|
|
DisplayString: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.OldTerm()),
|
|
OnPress: func() error {
|
|
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
|
if err := self.git.Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
return self.afterMark(selectCurrentAfter, waitToReselect)
|
|
},
|
|
},
|
|
{
|
|
DisplayString: fmt.Sprintf(self.c.Tr.Bisect.Skip, commit.ShortSha()),
|
|
OnPress: func() error {
|
|
self.c.LogAction(self.c.Tr.Actions.BisectSkip)
|
|
if err := self.git.Bisect.Skip(commit.Sha); err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
return self.afterMark(selectCurrentAfter, waitToReselect)
|
|
},
|
|
},
|
|
{
|
|
DisplayString: self.c.Tr.Bisect.ResetOption,
|
|
OnPress: func() error {
|
|
return self.Reset()
|
|
},
|
|
},
|
|
}
|
|
|
|
return self.c.Menu(popup.CreateMenuOptions{
|
|
Title: self.c.Tr.Bisect.BisectMenuTitle,
|
|
Items: menuItems,
|
|
})
|
|
}
|
|
|
|
func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo, commit *models.Commit) error {
|
|
return self.c.Menu(popup.CreateMenuOptions{
|
|
Title: self.c.Tr.Bisect.BisectMenuTitle,
|
|
Items: []*popup.MenuItem{
|
|
{
|
|
DisplayString: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.NewTerm()),
|
|
OnPress: func() error {
|
|
self.c.LogAction(self.c.Tr.Actions.StartBisect)
|
|
if err := self.git.Bisect.Start(); err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
if err := self.git.Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
return self.postBisectCommandRefresh()
|
|
},
|
|
},
|
|
{
|
|
DisplayString: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.OldTerm()),
|
|
OnPress: func() error {
|
|
self.c.LogAction(self.c.Tr.Actions.StartBisect)
|
|
if err := self.git.Bisect.Start(); err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
if err := self.git.Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
return self.postBisectCommandRefresh()
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func (self *BisectController) Reset() error {
|
|
return self.c.Ask(popup.AskOpts{
|
|
Title: self.c.Tr.Bisect.ResetTitle,
|
|
Prompt: self.c.Tr.Bisect.ResetPrompt,
|
|
HandleConfirm: func() error {
|
|
self.c.LogAction(self.c.Tr.Actions.ResetBisect)
|
|
if err := self.git.Bisect.Reset(); err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
return self.postBisectCommandRefresh()
|
|
},
|
|
})
|
|
}
|
|
|
|
func (self *BisectController) showBisectCompleteMessage(candidateShas []string) error {
|
|
prompt := self.c.Tr.Bisect.CompletePrompt
|
|
if len(candidateShas) > 1 {
|
|
prompt = self.c.Tr.Bisect.CompletePromptIndeterminate
|
|
}
|
|
|
|
formattedCommits, err := self.git.Commit.GetCommitsOneline(candidateShas)
|
|
if err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
return self.c.Ask(popup.AskOpts{
|
|
Title: self.c.Tr.Bisect.CompleteTitle,
|
|
Prompt: fmt.Sprintf(prompt, strings.TrimSpace(formattedCommits)),
|
|
HandleConfirm: func() error {
|
|
self.c.LogAction(self.c.Tr.Actions.ResetBisect)
|
|
if err := self.git.Bisect.Reset(); err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
return self.postBisectCommandRefresh()
|
|
},
|
|
})
|
|
}
|
|
|
|
func (self *BisectController) afterMark(selectCurrent bool, waitToReselect bool) error {
|
|
done, candidateShas, err := self.git.Bisect.IsDone()
|
|
if err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
if err := self.afterBisectMarkRefresh(selectCurrent, waitToReselect); err != nil {
|
|
return self.c.Error(err)
|
|
}
|
|
|
|
if done {
|
|
return self.showBisectCompleteMessage(candidateShas)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (self *BisectController) postBisectCommandRefresh() error {
|
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{}})
|
|
}
|
|
|
|
func (self *BisectController) afterBisectMarkRefresh(selectCurrent bool, waitToReselect bool) error {
|
|
selectFn := func() {
|
|
if selectCurrent {
|
|
self.selectCurrentBisectCommit()
|
|
}
|
|
}
|
|
|
|
if waitToReselect {
|
|
return self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{}, Then: selectFn})
|
|
} else {
|
|
selectFn()
|
|
|
|
return self.postBisectCommandRefresh()
|
|
}
|
|
}
|
|
|
|
func (self *BisectController) selectCurrentBisectCommit() {
|
|
info := self.git.Bisect.GetInfo()
|
|
if info.GetCurrentSha() != "" {
|
|
// find index of commit with that sha, move cursor to that.
|
|
for i, commit := range self.getCommits() {
|
|
if commit.Sha == info.GetCurrentSha() {
|
|
self.context.GetPanelState().SetSelectedLineIdx(i)
|
|
_ = self.context.HandleFocus()
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (self *BisectController) checkSelected(callback func(*models.Commit) error) func() error {
|
|
return func() error {
|
|
commit := self.getSelectedLocalCommit()
|
|
if commit == nil {
|
|
return nil
|
|
}
|
|
|
|
return callback(commit)
|
|
}
|
|
}
|
|
|
|
func (self *BisectController) Context() types.Context {
|
|
return self.context
|
|
}
|