summaryrefslogtreecommitdiffstats
path: root/pkg/gui/controllers/local_commits_controller.go
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2022-01-16 14:46:53 +1100
committerJesse Duffield <jessedduffield@gmail.com>2022-03-17 19:13:40 +1100
commit1dd7307fde033dae5fececac15810a99e26c3d91 (patch)
tree4e851c9e3229a6fe3b4191f6311d05d7a9142960 /pkg/gui/controllers/local_commits_controller.go
parenta90b6efded49abcfa2516db794d7875b0396f558 (diff)
start moving commit panel handlers into controller
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
Diffstat (limited to 'pkg/gui/controllers/local_commits_controller.go')
-rw-r--r--pkg/gui/controllers/local_commits_controller.go783
1 files changed, 783 insertions, 0 deletions
diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go
new file mode 100644
index 000000000..1d79bd2dd
--- /dev/null
+++ b/pkg/gui/controllers/local_commits_controller.go
@@ -0,0 +1,783 @@
+package controllers
+
+import (
+ "fmt"
+
+ "github.com/jesseduffield/gocui"
+ "github.com/jesseduffield/lazygit/pkg/commands"
+ "github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
+ "github.com/jesseduffield/lazygit/pkg/commands/models"
+ "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
+ "github.com/jesseduffield/lazygit/pkg/config"
+ "github.com/jesseduffield/lazygit/pkg/gui/popup"
+ "github.com/jesseduffield/lazygit/pkg/gui/types"
+ "github.com/jesseduffield/lazygit/pkg/utils"
+)
+
+type (
+ CheckoutRefFn func(refName string, opts types.CheckoutRefOptions) error
+ CreateGitResetMenuFn func(refName string) error
+ SwitchToCommitFilesContextFn func(SwitchToCommitFilesContextOpts) error
+ CreateTagMenuFn func(commitSha string) error
+ GetHostingServiceMgrFn func() *hosting_service.HostingServiceMgr
+ PullFilesFn func() error
+ CheckMergeOrRebase func(error) error
+ OpenSearchFn func(viewName string) error
+)
+
+type LocalCommitsController struct {
+ c *ControllerCommon
+ context types.IListContext
+ os *oscommands.OSCommand
+ git *commands.GitCommand
+ refHelper IRefHelper
+
+ getSelectedLocalCommit func() *models.Commit
+ getCommits func() []*models.Commit
+ getSelectedLocalCommitIdx func() int
+ checkMergeOrRebase CheckMergeOrRebase
+ pullFiles PullFilesFn
+ createTagMenu CreateTagMenuFn
+ getHostingServiceMgr GetHostingServiceMgrFn
+ switchToCommitFilesContext SwitchToCommitFilesContextFn
+ openSearch OpenSearchFn
+ getLimitCommits func() bool
+ setLimitCommits func(bool)
+ getShowWholeGitGraph func() bool
+ setShowWholeGitGraph func(bool)
+}
+
+var _ types.IController = &LocalCommitsController{}
+
+func NewLocalCommitsController(
+ c *ControllerCommon,
+ context types.IListContext,
+ os *oscommands.OSCommand,
+ git *commands.GitCommand,
+ refHelper IRefHelper,
+ getSelectedLocalCommit func() *models.Commit,
+ getCommits func() []*models.Commit,
+ getSelectedLocalCommitIdx func() int,
+ checkMergeOrRebase CheckMergeOrRebase,
+ pullFiles PullFilesFn,
+ createTagMenu CreateTagMenuFn,
+ getHostingServiceMgr GetHostingServiceMgrFn,
+ switchToCommitFilesContext SwitchToCommitFilesContextFn,
+ openSearch OpenSearchFn,
+ getLimitCommits func() bool,
+ setLimitCommits func(bool),
+ getShowWholeGitGraph func() bool,
+ setShowWholeGitGraph func(bool),
+) *LocalCommitsController {
+ return &LocalCommitsController{
+ c: c,
+ context: context,
+ os: os,
+ git: git,
+ refHelper: refHelper,
+ getSelectedLocalCommit: getSelectedLocalCommit,
+ getCommits: getCommits,
+ getSelectedLocalCommitIdx: getSelectedLocalCommitIdx,
+ checkMergeOrRebase: checkMergeOrRebase,
+ pullFiles: pullFiles,
+ createTagMenu: createTagMenu,
+ getHostingServiceMgr: getHostingServiceMgr,
+ switchToCommitFilesContext: switchToCommitFilesContext,
+ openSearch: openSearch,
+ getLimitCommits: getLimitCommits,
+ setLimitCommits: setLimitCommits,
+ getShowWholeGitGraph: getShowWholeGitGraph,
+ setShowWholeGitGraph: setShowWholeGitGraph,
+ }
+}
+
+func (self *LocalCommitsController) Keybindings(
+ getKey func(key string) interface{},
+ config config.KeybindingConfig,
+ guards types.KeybindingGuards,
+) []*types.Binding {
+ outsideFilterModeBindings := []*types.Binding{
+ {
+ Key: getKey(config.Commits.SquashDown),
+ Handler: self.squashDown,
+ Description: self.c.Tr.LcSquashDown,
+ },
+ {
+ Key: getKey(config.Commits.MarkCommitAsFixup),
+ Handler: self.fixup,
+ Description: self.c.Tr.LcFixupCommit,
+ },
+ {
+ Key: getKey(config.Commits.RenameCommit),
+ Handler: self.checkSelected(self.reword),
+ Description: self.c.Tr.LcRewordCommit,
+ },
+ {
+ Key: getKey(config.Commits.RenameCommitWithEditor),
+ Handler: self.rewordEditor,
+ Description: self.c.Tr.LcRenameCommitEditor,
+ },
+ {
+ Key: getKey(config.Universal.Remove),
+ Handler: self.drop,
+ Description: self.c.Tr.LcDeleteCommit,
+ },
+ {
+ Key: getKey(config.Universal.Edit),
+ Handler: self.edit,
+ Description: self.c.Tr.LcEditCommit,
+ },
+ {
+ Key: getKey(config.Commits.PickCommit),
+ Handler: self.pick,
+ Description: self.c.Tr.LcPickCommit,
+ },
+ {
+ Key: getKey(config.Commits.CreateFixupCommit),
+ Handler: self.checkSelected(self.handleCreateFixupCommit),
+ Description: self.c.Tr.LcCreateFixupCommit,
+ },
+ {
+ Key: getKey(config.Commits.SquashAboveCommits),
+ Handler: self.checkSelected(self.handleSquashAllAboveFixupCommits),
+ Description: self.c.Tr.LcSquashAboveCommits,
+ },
+ {
+ Key: getKey(config.Commits.MoveDownCommit),
+ Handler: self.handleCommitMoveDown,
+ Description: self.c.Tr.LcMoveDownCommit,
+ },
+ {
+ Key: getKey(config.Commits.MoveUpCommit),
+ Handler: self.handleCommitMoveUp,
+ Description: self.c.Tr.LcMoveUpCommit,
+ },
+ {
+ Key: getKey(config.Commits.AmendToCommit),
+ Handler: self.handleCommitAmendTo,
+ Description: self.c.Tr.LcAmendToCommit,
+ },
+ {
+ Key: getKey(config.Commits.RevertCommit),
+ Handler: self.checkSelected(self.handleCommitRevert),
+ Description: self.c.Tr.LcRevertCommit,
+ },
+ // overriding these navigation keybindings because we might need to load
+ // more commits on demand
+ {
+ Key: getKey(config.Universal.StartSearch),
+ Handler: func() error { return self.handleOpenSearch("commits") },
+ Description: self.c.Tr.LcStartSearch,
+ Tag: "navigation",
+ },
+ {
+ Key: getKey(config.Universal.GotoBottom),
+ Handler: self.gotoBottom,
+ Description: self.c.Tr.LcGotoBottom,
+ Tag: "navigation",
+ },
+ {
+ Key: gocui.MouseLeft,
+ Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) },
+ },
+ }
+
+ for _, binding := range outsideFilterModeBindings {
+ binding.Handler = guards.OutsideFilterMode(binding.Handler)
+ }
+
+ bindings := append(outsideFilterModeBindings, []*types.Binding{
+ {
+ Key: getKey(config.Commits.OpenLogMenu),
+ Handler: self.handleOpenLogMenu,
+ Description: self.c.Tr.LcOpenLogMenu,
+ OpensMenu: true,
+ },
+ {
+ Key: getKey(config.Commits.ViewResetOptions),
+ Handler: self.checkSelected(self.handleCreateCommitResetMenu),
+ Description: self.c.Tr.LcResetToThisCommit,
+ },
+ {
+ Key: getKey(config.Universal.GoInto),
+ Handler: self.checkSelected(self.enter),
+ Description: self.c.Tr.LcViewCommitFiles,
+ },
+ {
+ Key: getKey(config.Commits.CheckoutCommit),
+ Handler: self.checkSelected(self.handleCheckoutCommit),
+ Description: self.c.Tr.LcCheckoutCommit,
+ },
+ {
+ Key: getKey(config.Commits.TagCommit),
+ Handler: self.checkSelected(self.handleTagCommit),
+ Description: self.c.Tr.LcTagCommit,
+ },
+ {
+ Key: getKey(config.Commits.CopyCommitMessageToClipboard),
+ Handler: self.checkSelected(self.handleCopySelectedCommitMessageToClipboard),
+ Description: self.c.Tr.LcCopyCommitMessageToClipboard,
+ },
+ {
+ Key: getKey(config.Commits.OpenInBrowser),
+ Handler: self.checkSelected(self.handleOpenCommitInBrowser),
+ Description: self.c.Tr.LcOpenCommitInBrowser,
+ },
+ }...)
+
+ return append(bindings, self.context.Keybindings(getKey, config, guards)...)
+}
+
+func (self *LocalCommitsController) squashDown() error {
+ if len(self.getCommits()) <= 1 {
+ return self.c.ErrorMsg(self.c.Tr.YouNoCommitsToSquash)
+ }
+
+ applied, err := self.handleMidRebaseCommand("squash")
+ if err != nil {
+ return err
+ }
+ if applied {
+ return nil
+ }
+
+ return self.c.Ask(popup.AskOpts{
+ Title: self.c.Tr.Squash,
+ Prompt: self.c.Tr.SureSquashThisCommit,
+ HandleConfirm: func() error {
+ return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func() error {
+ self.c.LogAction(self.c.Tr.Actions.SquashCommitDown)
+ return self.interactiveRebase("squash")
+ })
+ },
+ })
+}
+
+func (self *LocalCommitsController) fixup() error {
+ if len(self.getCommits()) <= 1 {
+ return self.c.ErrorMsg(self.c.Tr.YouNoCommitsToSquash)
+ }
+
+ applied, err := self.handleMidRebaseCommand("fixup")
+ if err != nil {
+ return err
+ }
+ if applied {
+ return nil
+ }
+
+ return self.c.Ask(popup.AskOpts{
+ Title: self.c.Tr.Fixup,
+ Prompt: self.c.Tr.SureFixupThisCommit,
+ HandleConfirm: func() error {
+ return self.c.WithWaitingStatus(self.c.Tr.FixingStatus, func() error {
+ self.c.LogAction(self.c.Tr.Actions.FixupCommit)
+ return self.interactiveRebase("fixup")
+ })
+ },
+ })
+}
+
+func (self *LocalCommitsController) reword(commit *models.Commit) error {
+ applied, err := self.handleMidRebaseCommand("reword")
+ if err != nil {
+ return err
+ }
+ if applied {
+ return nil
+ }
+
+ message, err := self.git.Commit.GetCommitMessage(commit.Sha)
+ if err != nil {
+ return self.c.Error(err)
+ }
+
+ // TODO: use the commit message panel here
+ return self.c.Prompt(popup.PromptOpts{
+ Title: self.c.Tr.LcRewordCommit,
+ InitialContent: message,
+ HandleConfirm: func(response string) error {
+ self.c.LogAction(self.c.Tr.Actions.RewordCommit)
+ if err := self.git.Rebase.RewordCommit(self.getCommits(), self.getSelectedLocalCommitIdx(), response); err != nil {
+ return self.c.Error(err)
+ }
+
+ return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
+ },
+ })
+}
+
+func (self *LocalCommitsController) rewordEditor() error {
+ applied, err := self.handleMidRebaseCommand("reword")
+ if err != nil {
+ return err
+ }
+ if applied {
+ return nil
+ }
+
+ self.c.LogAction(self.c.Tr.Actions.RewordCommit)
+ subProcess, err := self.git.Rebase.RewordCommitInEditor(
+ self.getCommits(), self.getSelectedLocalCommitIdx(),
+ )
+ if err != nil {
+ return self.c.Error(err)
+ }
+ if subProcess != nil {
+ return self.c.RunSubprocessAndRefresh(subProcess)
+ }
+
+ return nil
+}
+
+func (self *LocalCommitsController) drop() error {
+ applied, err := self.handleMidRebaseCommand("drop")
+ if err != nil {
+ return err
+ }
+ if applied {
+ return nil
+ }
+
+ return self.c.Ask(popup.AskOpts{
+ Title: self.c.Tr.DeleteCommitTitle,
+ Prompt: self.c.Tr.DeleteCommitPrompt,
+ HandleConfirm: func() error {
+ return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func() error {
+ self.c.LogAction(self.c.Tr.Actions.DropCommit)
+ return self.interactiveRebase("drop")
+ })
+ },
+ })
+}
+
+func (self *LocalCommitsController) edit() error {
+ applied, err := self.handleMidRebaseCommand("edit")
+ if err != nil {
+ return err
+ }
+ if applied {
+ return nil
+ }
+
+ return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
+ self.c.LogAction(self.c.Tr.Actions.EditCommit)
+ return self.interactiveRebase("edit")
+ })
+}
+
+func (self *LocalCommitsController) pick() error {
+ applied, err := self.handleMidRebaseCommand("pick")
+ if err != nil {
+ return err
+ }
+ if applied {
+ return nil
+ }
+
+ // at this point we aren't actually rebasing so we will interpret this as an
+ // attempt to pull. We might revoke this later after enabling configurable keybindings
+ return self.pullFiles()
+}
+
+func (self *LocalCommitsController) interactiveRebase(action string) error {
+ err := self.git.Rebase.InteractiveRebase(self.getCommits(), self.getSelectedLocalCommitIdx(), action)
+ return self.checkMergeOrRebase(err)
+}
+
+// handleMidRebaseCommand sees if the selected commit is in fact a rebasing
+// commit meaning you are trying to edit the todo file rather than actually
+// begin a rebase. It then updates the todo file with that action
+func (self *LocalCommitsController) handleMidRebaseCommand(action string) (bool, error) {
+ selectedCommit := self.getSelectedLocalCommit()
+ if selectedCommit.Status != "rebasing" {
+ return false, nil
+ }
+
+ // for now we do not support setting 'reword' because it requires an editor
+ // and that means we either unconditionally wait around for the subprocess to ask for
+ // our input or we set a lazygit client as the EDITOR env variable and have it
+ // request us to edit the commit message when prompted.
+ if action == "reword" {
+ return true, self.c.ErrorMsg(self.c.Tr.LcRewordNotSupported)
+ }
+
+ self.c.LogAction("Update rebase TODO")
+ self.c.LogCommand(
+ fmt.Sprintf("Updating rebase action of commit %s to '%s'", selectedCommit.ShortSha(), action),
+ false,
+ )
+
+ if err := self.git.Rebase.EditRebaseTodo(
+ self.getSelectedLocalCommitIdx(), action,
+ ); err != nil {
+ return false, self.c.Error(err)
+ }
+
+ return true, self.c.Refresh(types.RefreshOptions{
+ Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS},
+ })
+}
+
+func (self *LocalCommitsController) handleCommitMoveDown() error {
+ index := self.context.GetPanelState().GetSelectedLineIdx()
+ commits := self.getCommits()
+ selectedCommit := self.getCommits()[index]
+ if selectedCommit.Status == "rebasing" {
+ if commits[index+1].Status != "rebasing" {
+ return nil
+ }
+
+ // logging directly here because MoveTodoDown doesn't have enough information
+ // to provide a useful log
+ self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
+ self.c.LogCommand(fmt.Sprintf("Moving commit %s down", selectedCommit.ShortSha()), false)
+
+ if err := self.git.Rebase.MoveTodoDown(index); err != nil {
+ return self.c.Error(err)
+ }
+ self.context.HandleNextLine()
+ return self.c.Refresh(types.RefreshOptions{
+ Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS},
+ })
+ }
+
+ return self.c.WithWaitingStatus(self.c.Tr.MovingStatus, func() error {
+ self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
+ err := self.git.Rebase.MoveCommitDown(self.getCommits(), index)
+ if err == nil {
+ self.context.HandleNextLine()
+ }
+ return self.checkMergeOrRebase(err)
+ })
+}
+
+func (self *LocalCommitsController) handleCommitMoveUp() error {
+ index := self.context.GetPanelState().GetSelectedLineIdx()
+ if index == 0 {
+ return nil
+ }
+
+ selectedCommit := self.getCommits()[index]
+ if selectedCommit.Status == "rebasing" {
+ // logging directly here because MoveTodoDown doesn't have enough information
+ // to provide a useful log
+ self.c.LogAction(self.c.Tr.Actions.MoveCommitUp)
+ self.c.LogCommand(
+ fmt.Sprintf("Moving commit %s up", selectedCommit.ShortSha()),
+ false,
+ )
+
+ if err := self.git.Rebase.MoveTodoDown(index - 1); err != nil {
+ return self.c.Error(err)
+ }
+ self.context.HandlePrevLine()
+ return self.c.Refresh(types.RefreshOptions{
+ Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS},
+ })
+ }
+
+ return self.c.WithWaitingStatus(self.c.Tr.MovingStatus, func() error {
+ self.c.LogAction(self.c.Tr.Actions.MoveCommitUp)
+ err := self.git.Rebase.MoveCommitDown(self.getCommits(), index-1)
+ if err == nil {
+ self.context.HandlePrevLine()
+ }
+ return self.checkMergeOrRebase(err)
+ })
+}
+
+func (self *LocalCommitsController) handleCommitAmendTo() error {
+ return self.c.Ask(popup.AskOpts{
+ Title: self.c.Tr.AmendCommitTitle,
+ Prompt: self.c.Tr.AmendCommitPrompt,
+ HandleConfirm: func() error {
+ return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
+ self.c.LogAction(self.c.Tr.Actions.AmendCommit)
+ err := self.git.Rebase.AmendTo(self.getSelectedLocalCommit().Sha)
+ return self.checkMergeOrRebase(err)
+ })
+ },
+ })
+}
+
+func (self *LocalCommitsController) handleCommitRevert(commit *models.Commit) error {
+ if commit.IsMerge() {
+ return self.createRevertMergeCommitMenu(commit)
+ } else {
+ return self.c.Ask(popup.AskOpts{
+ Title: self.c.Tr.Actions.RevertCommit,
+ Prompt: utils.ResolvePlaceholderString(
+ self.c.Tr.ConfirmRevertCommit,
+ map[string]string{
+ "selectedCommit": commit.ShortSha(),
+ }),
+ HandleConfirm: func() error {
+ self.c.LogAction(self.c.Tr.Actions.RevertCommit)
+ if err := self.git.Commit.Revert(commit.Sha); err != nil {
+ return self.c.Error(err)
+ }
+ return self.afterRevertCommit()
+ },
+ })
+ }
+}
+
+func (self *LocalCommitsController) createRevertMergeCommitMenu(commit *models.Commit) error {
+ menuItems := make([]*popup.MenuItem, len(commit.Parents))
+ for i, parentSha := range commit.Parents {
+ i := i
+ message, err := self.git.Commit.GetCommitMessageFirstLine(parentSha)
+ if err != nil {
+ return self.c.Error(err)
+ }
+
+ menuItems[i] = &popup.MenuItem{
+ DisplayString: fmt.Sprintf("%s: %s", utils.SafeTruncate(parentSha, 8), message),
+ OnPress: func() error {
+ parentNumber := i + 1
+ self.c.LogAction(self.c.Tr.Actions.RevertCommit)
+ if err := self.git.Commit.RevertMerge(commit.Sha, parentNumber); err != nil {
+ return self.c.Error(err)
+ }
+ return self.afterRevertCommit()
+ },
+ }
+ }
+
+ return self.c.Menu(popup.CreateMenuOptions{Title: self.c.Tr.SelectParentCommitForMerge, Items: menuItems})
+}
+
+func (self *LocalCommitsController) afterRevertCommit() error {
+ self.context.HandleNextLine()
+ return self.c.Refresh(types.RefreshOptions{
+ Mode: types.BLOCK_UI, Scope: []types.RefreshableView{types.COMMITS, types.BRANCHES},
+ })
+}
+
+func (self *LocalCommitsController) enter(commit *models.Commit) error {
+ return self.switchToCommitFilesContext(SwitchToCommitFilesContextOpts{
+ RefName: commit.Sha,
+ CanRebase: true,
+ Context: self.context,
+ WindowName: "commits",
+ })
+}
+
+func (self *LocalCommitsController) handleCreateFixupCommit(commit *models.Commit) error {
+ prompt := utils.ResolvePlaceholderString(
+ self.c.Tr.SureCreateFixupCommit,
+ map[string]string{
+ "commit": commit.Sha,
+ },
+ )
+
+ return self.c.Ask(popup.AskOpts{
+ Title: self.c.Tr.CreateFixupCommit,
+ Prompt: prompt,
+ HandleConfirm: func() error {
+ self.c.LogAction(self.c.Tr.Actions.CreateFixupCommit)
+ if err := self.git.Commit.CreateFixupCommit(commit.Sha); err != nil {
+ return self.c.Error(err)
+ }
+
+ return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
+ },
+ })
+}
+
+func (self *LocalCommitsController) handleSquashAllAboveFixupCommits(commit *models.Commit) error {
+ prompt := utils.ResolvePlaceholderString(
+ self.c.Tr.SureSquashAboveCommits,
+ map[string]string{
+ "commit": commit.Sha,
+ },
+ )
+
+ return self.c.Ask(popup.AskOpts{
+ Title: self.c.Tr.SquashAboveCommits,
+ Prompt: prompt,
+ HandleConfirm: func() error {
+ return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func() error {
+ self.c.LogAction(self.c.Tr.Actions.SquashAllAboveFixupCommits)
+ err := self.git.Rebase.SquashAllAboveFixupCommits(commit.Sha)
+ return self.checkMergeOrRebase(err)
+ })
+ },
+ })
+}
+
+func (self *LocalCommitsController) handleTagCommit(commit *models.Commit) error {
+ return self.createTagMenu(commit.Sha)
+}
+
+func (self *LocalCommitsController) handleCheckoutCommit(commit *models.Commit) error {
+ return self.c.Ask(popup.AskOpts{
+ Title: self.c.Tr.LcCheckoutCommit,
+ Prompt: self.c.Tr.SureCheckoutThisCommit,
+ HandleConfirm: func() error {
+ self.c.LogAction(self.c.Tr.Actions.CheckoutCommit)
+ return self.refHelper.CheckoutRef(commit.Sha, types.CheckoutRefOptions{})
+ },
+ })
+}
+
+func (self *LocalCommitsController) handleCreateCommitResetMenu(commit *models.Commit) error {
+ return self.refHelper.CreateGitResetMenu(commit.Sha)
+}
+
+func (self *LocalCommitsController) handleOpenSearch(string) error {
+ // we usually lazyload these commits but now that we're searching we need to load them now
+ if self.getLimitCommits() {
+ self.setLimitCommits(false)
+ if err := self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.COMMITS}}); err != nil {
+ return err
+ }
+ }
+
+ return self.openSearch("commits")
+}
+
+func (self *LocalCommitsController) gotoBottom() error {
+ // we usually lazyload these commits but now that we're jumping to the bottom we need to load them now
+ if self.getLimitCommits() {
+ self.setLimitCommits(false)
+ if err := self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS}}); err != nil {
+ return err
+ }
+ }
+
+ self.context.HandleGotoBottom()
+
+ return nil
+}
+
+func (self *LocalCommitsController) handleCopySelectedCommitMessageToClipboard(commit *models.Commit) error {
+ message, err := self.git.Commit.GetCommitMessage(commit.Sha)
+ if err != nil {
+ return self.c.Error(err)
+ }
+
+ self.c.LogAction(self.c.Tr.Actions.CopyCommitMessageToClipboard)
+ if err := self.os.CopyToClipboard(message); err != nil {
+ return self.c.Error(err)
+ }
+
+ self.c.Toast(self.c.Tr.CommitMessageCopiedToClipboard)
+
+ return nil
+}
+
+func (self *LocalCommitsController) handleOpenLogMenu() error {
+ return self.c.Menu(popup.CreateMenuOptions{
+ Title: self.c.Tr.LogMenuTitle,
+ Items: []*popup.MenuItem{
+ {
+ DisplayString: self.c.Tr.ToggleShowGitGraphAll,
+ OnPress: func() error {
+ self.setShowWholeGitGraph(!self.getShowWholeGitGraph())
+
+ if self.getShowWholeGitGraph() {
+ self.setLimitCommits(false)
+ }
+
+ return self.c.WithWaitingStatus(self.c.Tr.LcLoadingCommits, func() error {
+ return self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS}})
+ })
+ },
+ },
+ {
+ DisplayString: self.c.Tr.ShowGitGraph,
+ OpensMenu: true,
+ OnPress: func() error {
+ onPress := func(value string) func() error {
+ return func() error {
+ self.c.UserConfig.Git.Log.ShowGraph = value
+ return nil
+ }
+ }
+ return self.c.Menu(popup.CreateMenuOptions{
+ Title: self.c.Tr.LogMenuTitle,
+ Items: []*popup.MenuItem{
+ {
+ DisplayString: "always",
+ OnPress: onPress("always"),
+ },
+ {
+ DisplayString: "never",
+ OnPress: onPress("never"),
+ },
+ {
+ DisplayString: "when maximised",
+ OnPress: onPress("when-maximised"),
+ },
+ },
+ })
+ },
+ },
+ {
+ DisplayString: self.c.Tr.SortCommits,
+ OpensMenu: true,
+ OnPress: func() error {
+ onPress := func(value string) func() error {
+ return func() error {
+ self.c.UserConfig.Git.Log.Order = value
+ return self.c.WithWaitingStatus(self.c.Tr.LcLoadingCommits, func() error {
+ return self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS}})
+ })
+ }
+ }
+
+ return self.c.Menu(popup.CreateMenuOptions{
+ Title: self.c.Tr.LogMenuTitle,
+ Items: []*popup.MenuItem{
+ {
+ DisplayString: "topological (topo-order)",
+ OnPress: onPress("topo-order"),
+ },
+ {
+ DisplayString: "date-order",
+ OnPress: onPress("date-order"),
+ },
+ {
+ DisplayString: "author-date-order",
+ OnPress: onPress("author-date-order"),
+ },
+ },
+ })
+ },
+ },
+ },
+ })
+}
+
+func (self *LocalCommitsController) handleOpenCommitInBrowser(commit *models.Commit) error {
+ hostingServiceMgr := self.getHostingServiceMgr()
+
+ url, err := hostingServiceMgr.GetCommitURL(commit.Sha)
+ if err != nil {
+ return self.c.Error(err)
+ }
+
+ self.c.LogAction(self.c.Tr.Actions.OpenCommitInBrowser)
+ if err := self.os.OpenLink(url); err != nil {
+ return self.c.Error(err)
+ }
+
+ return nil
+}
+
+func (self *LocalCommitsController) checkSelected(callback func(*models.Commit) error) func() error {
+ return func() error {
+ commit := self.getSelectedLocalCommit()
+ if commit == nil {
+ return nil
+ }
+
+ return callback(commit)
+ }
+}
+
+func (self *LocalCommitsController) Context() types.Context {
+ return self.context
+}