2022-01-16 05:46:53 +02:00
|
|
|
package controllers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
2022-02-05 08:04:10 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
2022-01-16 05:46:53 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
type BisectController struct {
|
2022-02-05 05:42:56 +02:00
|
|
|
baseController
|
2023-03-23 09:47:29 +02:00
|
|
|
c *ControllerCommon
|
2022-01-16 05:46:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var _ types.IController = &BisectController{}
|
|
|
|
|
|
|
|
func NewBisectController(
|
2023-03-23 09:47:29 +02:00
|
|
|
common *ControllerCommon,
|
2022-01-16 05:46:53 +02:00
|
|
|
) *BisectController {
|
|
|
|
return &BisectController{
|
2023-03-23 09:47:29 +02:00
|
|
|
baseController: baseController{},
|
|
|
|
c: common,
|
2022-01-16 05:46:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-05 01:31:07 +02:00
|
|
|
func (self *BisectController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
2022-01-16 05:46:53 +02:00
|
|
|
bindings := []*types.Binding{
|
|
|
|
{
|
2022-02-05 01:31:07 +02:00
|
|
|
Key: opts.GetKey(opts.Config.Commits.ViewBisectOptions),
|
|
|
|
Handler: opts.Guards.OutsideFilterMode(self.checkSelected(self.openMenu)),
|
2022-01-16 05:46:53 +02:00
|
|
|
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
|
2023-03-23 04:04:57 +02:00
|
|
|
info := self.c.Git().Bisect.GetInfo()
|
2022-01-16 05:46:53 +02:00
|
|
|
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.
|
2023-03-23 04:04:57 +02:00
|
|
|
waitToReselect := selectCurrentAfter && !self.c.Git().Bisect.ReachableFromStart(info)
|
2022-01-16 05:46:53 +02:00
|
|
|
|
2022-01-29 10:09:20 +02:00
|
|
|
menuItems := []*types.MenuItem{
|
2022-01-16 05:46:53 +02:00
|
|
|
{
|
2022-05-08 06:23:32 +02:00
|
|
|
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.NewTerm()),
|
2022-01-16 05:46:53 +02:00
|
|
|
OnPress: func() error {
|
|
|
|
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
2023-03-23 04:04:57 +02:00
|
|
|
if err := self.c.Git().Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
2022-01-16 05:46:53 +02:00
|
|
|
return self.c.Error(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return self.afterMark(selectCurrentAfter, waitToReselect)
|
|
|
|
},
|
2022-03-27 08:19:31 +02:00
|
|
|
Key: 'b',
|
2022-01-16 05:46:53 +02:00
|
|
|
},
|
|
|
|
{
|
2022-05-08 06:23:32 +02:00
|
|
|
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.OldTerm()),
|
2022-01-16 05:46:53 +02:00
|
|
|
OnPress: func() error {
|
|
|
|
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
2023-03-23 04:04:57 +02:00
|
|
|
if err := self.c.Git().Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
2022-01-16 05:46:53 +02:00
|
|
|
return self.c.Error(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return self.afterMark(selectCurrentAfter, waitToReselect)
|
|
|
|
},
|
2022-03-27 08:19:31 +02:00
|
|
|
Key: 'g',
|
2022-01-16 05:46:53 +02:00
|
|
|
},
|
|
|
|
{
|
2022-05-08 06:23:32 +02:00
|
|
|
Label: fmt.Sprintf(self.c.Tr.Bisect.Skip, commit.ShortSha()),
|
2022-01-16 05:46:53 +02:00
|
|
|
OnPress: func() error {
|
|
|
|
self.c.LogAction(self.c.Tr.Actions.BisectSkip)
|
2023-03-23 04:04:57 +02:00
|
|
|
if err := self.c.Git().Bisect.Skip(commit.Sha); err != nil {
|
2022-01-16 05:46:53 +02:00
|
|
|
return self.c.Error(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return self.afterMark(selectCurrentAfter, waitToReselect)
|
|
|
|
},
|
2022-03-27 08:19:31 +02:00
|
|
|
Key: 's',
|
2022-01-16 05:46:53 +02:00
|
|
|
},
|
|
|
|
{
|
2022-05-08 06:23:32 +02:00
|
|
|
Label: self.c.Tr.Bisect.ResetOption,
|
2022-01-16 05:46:53 +02:00
|
|
|
OnPress: func() error {
|
2023-03-23 09:47:29 +02:00
|
|
|
return self.c.Helpers().Bisect.Reset()
|
2022-01-16 05:46:53 +02:00
|
|
|
},
|
2022-03-27 08:19:31 +02:00
|
|
|
Key: 'r',
|
2022-01-16 05:46:53 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-01-29 10:09:20 +02:00
|
|
|
return self.c.Menu(types.CreateMenuOptions{
|
2022-01-16 05:46:53 +02:00
|
|
|
Title: self.c.Tr.Bisect.BisectMenuTitle,
|
|
|
|
Items: menuItems,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo, commit *models.Commit) error {
|
2022-01-29 10:09:20 +02:00
|
|
|
return self.c.Menu(types.CreateMenuOptions{
|
2022-01-16 05:46:53 +02:00
|
|
|
Title: self.c.Tr.Bisect.BisectMenuTitle,
|
2022-01-29 10:09:20 +02:00
|
|
|
Items: []*types.MenuItem{
|
2022-01-16 05:46:53 +02:00
|
|
|
{
|
2022-05-08 06:23:32 +02:00
|
|
|
Label: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.NewTerm()),
|
2022-01-16 05:46:53 +02:00
|
|
|
OnPress: func() error {
|
|
|
|
self.c.LogAction(self.c.Tr.Actions.StartBisect)
|
2023-03-23 04:04:57 +02:00
|
|
|
if err := self.c.Git().Bisect.Start(); err != nil {
|
2022-01-16 05:46:53 +02:00
|
|
|
return self.c.Error(err)
|
|
|
|
}
|
|
|
|
|
2023-03-23 04:04:57 +02:00
|
|
|
if err := self.c.Git().Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
2022-01-16 05:46:53 +02:00
|
|
|
return self.c.Error(err)
|
|
|
|
}
|
|
|
|
|
2023-03-23 09:47:29 +02:00
|
|
|
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
2022-01-16 05:46:53 +02:00
|
|
|
},
|
2022-03-27 08:19:31 +02:00
|
|
|
Key: 'b',
|
2022-01-16 05:46:53 +02:00
|
|
|
},
|
|
|
|
{
|
2022-05-08 06:23:32 +02:00
|
|
|
Label: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.OldTerm()),
|
2022-01-16 05:46:53 +02:00
|
|
|
OnPress: func() error {
|
|
|
|
self.c.LogAction(self.c.Tr.Actions.StartBisect)
|
2023-03-23 04:04:57 +02:00
|
|
|
if err := self.c.Git().Bisect.Start(); err != nil {
|
2022-01-16 05:46:53 +02:00
|
|
|
return self.c.Error(err)
|
|
|
|
}
|
|
|
|
|
2023-03-23 04:04:57 +02:00
|
|
|
if err := self.c.Git().Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
2022-01-16 05:46:53 +02:00
|
|
|
return self.c.Error(err)
|
|
|
|
}
|
|
|
|
|
2023-03-23 09:47:29 +02:00
|
|
|
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
2022-01-16 05:46:53 +02:00
|
|
|
},
|
2022-03-27 08:19:31 +02:00
|
|
|
Key: 'g',
|
2022-01-16 05:46:53 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *BisectController) showBisectCompleteMessage(candidateShas []string) error {
|
|
|
|
prompt := self.c.Tr.Bisect.CompletePrompt
|
|
|
|
if len(candidateShas) > 1 {
|
|
|
|
prompt = self.c.Tr.Bisect.CompletePromptIndeterminate
|
|
|
|
}
|
|
|
|
|
2023-03-23 04:04:57 +02:00
|
|
|
formattedCommits, err := self.c.Git().Commit.GetCommitsOneline(candidateShas)
|
2022-01-16 05:46:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return self.c.Error(err)
|
|
|
|
}
|
|
|
|
|
2022-03-30 08:48:29 +02:00
|
|
|
return self.c.Confirm(types.ConfirmOpts{
|
2022-01-16 05:46:53 +02:00
|
|
|
Title: self.c.Tr.Bisect.CompleteTitle,
|
|
|
|
Prompt: fmt.Sprintf(prompt, strings.TrimSpace(formattedCommits)),
|
|
|
|
HandleConfirm: func() error {
|
|
|
|
self.c.LogAction(self.c.Tr.Actions.ResetBisect)
|
2023-03-23 04:04:57 +02:00
|
|
|
if err := self.c.Git().Bisect.Reset(); err != nil {
|
2022-01-16 05:46:53 +02:00
|
|
|
return self.c.Error(err)
|
|
|
|
}
|
|
|
|
|
2023-03-23 09:47:29 +02:00
|
|
|
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
2022-01-16 05:46:53 +02:00
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *BisectController) afterMark(selectCurrent bool, waitToReselect bool) error {
|
2023-03-23 04:04:57 +02:00
|
|
|
done, candidateShas, err := self.c.Git().Bisect.IsDone()
|
2022-01-16 05:46:53 +02:00
|
|
|
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) 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()
|
|
|
|
|
2023-03-23 09:47:29 +02:00
|
|
|
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
2022-01-16 05:46:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *BisectController) selectCurrentBisectCommit() {
|
2023-03-23 04:04:57 +02:00
|
|
|
info := self.c.Git().Bisect.GetInfo()
|
2022-01-16 05:46:53 +02:00
|
|
|
if info.GetCurrentSha() != "" {
|
|
|
|
// find index of commit with that sha, move cursor to that.
|
2023-03-23 04:04:57 +02:00
|
|
|
for i, commit := range self.c.Model().Commits {
|
2022-01-16 05:46:53 +02:00
|
|
|
if commit.Sha == info.GetCurrentSha() {
|
2022-02-06 06:54:26 +02:00
|
|
|
self.context().SetSelectedLineIdx(i)
|
2022-06-13 03:01:26 +02:00
|
|
|
_ = self.context().HandleFocus(types.OnFocusOpts{})
|
2022-01-16 05:46:53 +02:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *BisectController) checkSelected(callback func(*models.Commit) error) func() error {
|
|
|
|
return func() error {
|
2022-02-06 06:54:26 +02:00
|
|
|
commit := self.context().GetSelected()
|
2022-01-16 05:46:53 +02:00
|
|
|
if commit == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return callback(commit)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *BisectController) Context() types.Context {
|
2022-02-06 06:54:26 +02:00
|
|
|
return self.context()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *BisectController) context() *context.LocalCommitsContext {
|
2023-03-23 04:04:57 +02:00
|
|
|
return self.c.Contexts().LocalCommits
|
2022-01-16 05:46:53 +02:00
|
|
|
}
|