diff options
Diffstat (limited to 'pkg/gui/controllers/local_commits_controller.go')
-rw-r--r-- | pkg/gui/controllers/local_commits_controller.go | 783 |
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 +} |