mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Add pull requests to lazygit's model and refresh them
Co-authored-by: Stefan Haller <stefan@haller-berlin.de>
This commit is contained in:
parent
1c89398288
commit
d33fa5bb05
|
|
@ -155,7 +155,7 @@ func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan stru
|
|||
func (self *BackgroundRoutineMgr) backgroundFetch() (err error) {
|
||||
err = self.gui.git.Sync.FetchBackground()
|
||||
|
||||
self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.SYNC})
|
||||
self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS, types.PULL_REQUESTS}, Mode: types.SYNC})
|
||||
|
||||
if err == nil {
|
||||
err = self.gui.helpers.BranchesHelper.AutoForwardBranches()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -13,6 +14,7 @@ import (
|
|||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
|
|
@ -91,6 +93,7 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) {
|
|||
types.STATUS,
|
||||
types.BISECT_INFO,
|
||||
types.STAGING,
|
||||
types.PULL_REQUESTS,
|
||||
})
|
||||
} else {
|
||||
scopeSet = set.NewFromSlice(options.Scope)
|
||||
|
|
@ -117,6 +120,7 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
branchesAndRemotesWg := sync.WaitGroup{}
|
||||
includeWorktreesWithBranches := false
|
||||
if scopeSet.Includes(types.COMMITS) || scopeSet.Includes(types.BRANCHES) || scopeSet.Includes(types.REFLOG) || scopeSet.Includes(types.BISECT_INFO) {
|
||||
// whenever we change commits, we should update branches because the upstream/downstream
|
||||
|
|
@ -126,9 +130,17 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) {
|
|||
|
||||
includeWorktreesWithBranches = scopeSet.Includes(types.WORKTREES)
|
||||
if self.c.UserConfig().Git.LocalBranchSortOrder == "recency" {
|
||||
refresh("reflog and branches", func() { self.refreshReflogAndBranches(includeWorktreesWithBranches, options.KeepBranchSelectionIndex) })
|
||||
branchesAndRemotesWg.Add(1)
|
||||
refresh("reflog and branches", func() {
|
||||
self.refreshReflogAndBranches(includeWorktreesWithBranches, options.KeepBranchSelectionIndex)
|
||||
branchesAndRemotesWg.Done()
|
||||
})
|
||||
} else {
|
||||
refresh("branches", func() { self.refreshBranches(includeWorktreesWithBranches, options.KeepBranchSelectionIndex, true) })
|
||||
branchesAndRemotesWg.Add(1)
|
||||
refresh("branches", func() {
|
||||
self.refreshBranches(includeWorktreesWithBranches, options.KeepBranchSelectionIndex, true)
|
||||
branchesAndRemotesWg.Done()
|
||||
})
|
||||
refresh("reflog", func() { _ = self.refreshReflogCommits() })
|
||||
}
|
||||
} else if scopeSet.Includes(types.REBASE_COMMITS) {
|
||||
|
|
@ -164,7 +176,18 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) {
|
|||
}
|
||||
|
||||
if scopeSet.Includes(types.REMOTES) {
|
||||
refresh("remotes", func() { _ = self.refreshRemotes() })
|
||||
branchesAndRemotesWg.Add(1)
|
||||
refresh("remotes", func() {
|
||||
_ = self.refreshRemotes()
|
||||
branchesAndRemotesWg.Done()
|
||||
})
|
||||
}
|
||||
|
||||
if scopeSet.Includes(types.PULL_REQUESTS) {
|
||||
refresh("pull requests", func() {
|
||||
branchesAndRemotesWg.Wait()
|
||||
self.refreshGithubPullRequests()
|
||||
})
|
||||
}
|
||||
|
||||
if scopeSet.Includes(types.WORKTREES) && !includeWorktreesWithBranches {
|
||||
|
|
@ -225,6 +248,7 @@ func getScopeNames(scopes []types.RefreshableView) []string {
|
|||
types.PATCH_BUILDING: "patchBuilding",
|
||||
types.MERGE_CONFLICTS: "mergeConflicts",
|
||||
types.COMMIT_FILES: "commitFiles",
|
||||
types.PULL_REQUESTS: "pullRequests",
|
||||
}
|
||||
|
||||
return lo.Map(scopes, func(scope types.RefreshableView, _ int) string {
|
||||
|
|
@ -480,6 +504,7 @@ func (self *RefreshHelper) refreshBranches(refreshWorktrees bool, keepBranchSele
|
|||
prevSelectedBranch := self.c.Contexts().Branches.GetSelected()
|
||||
|
||||
self.c.Model().Branches = branches
|
||||
self.rebuildPullRequestsMap()
|
||||
|
||||
if refreshWorktrees {
|
||||
self.loadWorktrees()
|
||||
|
|
@ -661,6 +686,13 @@ func (self *RefreshHelper) refreshRemotes() error {
|
|||
|
||||
self.c.Model().Remotes = remotes
|
||||
|
||||
hadPrs := len(self.c.Model().PullRequestsMap) != 0
|
||||
self.rebuildPullRequestsMap()
|
||||
if !hadPrs && len(self.c.Model().PullRequestsMap) != 0 {
|
||||
// if we didn't have PRs in the map before but now we do, we need to redraw the branches view
|
||||
self.refreshView(self.c.Contexts().Branches)
|
||||
}
|
||||
|
||||
// we need to ensure our selected remote branches aren't now outdated
|
||||
if prevSelectedRemote != nil && self.c.Model().RemoteBranches != nil {
|
||||
// find remote now
|
||||
|
|
@ -760,3 +792,123 @@ func (self *RefreshHelper) refreshView(context types.Context) {
|
|||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshGithubPullRequests() {
|
||||
self.c.Mutexes().RefreshingPullRequestsMutex.Lock()
|
||||
defer self.c.Mutexes().RefreshingPullRequestsMutex.Unlock()
|
||||
|
||||
if !self.c.Git().GitHub.InGithubRepo(self.c.Model().Remotes) {
|
||||
self.c.Model().PullRequests = nil
|
||||
self.c.Model().PullRequestsMap = nil
|
||||
return
|
||||
}
|
||||
|
||||
authToken := self.c.Git().GitHub.GetAuthToken()
|
||||
if authToken == "" {
|
||||
self.c.Model().PullRequests = nil
|
||||
self.c.Model().PullRequestsMap = nil
|
||||
return
|
||||
}
|
||||
|
||||
baseRemote := self.getGithubBaseRemote()
|
||||
if baseRemote == nil {
|
||||
self.promptForBaseGithubRepo(authToken)
|
||||
return
|
||||
}
|
||||
|
||||
if err := self.setGithubPullRequests(authToken, baseRemote); err != nil {
|
||||
self.c.LogAction(fmt.Sprintf("Error fetching pull requests from GitHub: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) getGithubBaseRemote() *models.Remote {
|
||||
remotes := self.c.Model().Remotes
|
||||
|
||||
findRemoteByName := func(name string) *models.Remote {
|
||||
remote, _ := lo.Find(remotes, func(remote *models.Remote) bool {
|
||||
return remote.Name == name
|
||||
})
|
||||
return remote
|
||||
}
|
||||
|
||||
if configuredRemote := self.c.Git().GitHub.ConfiguredBaseRemoteName(); configuredRemote != "" {
|
||||
return findRemoteByName(configuredRemote)
|
||||
}
|
||||
|
||||
if len(remotes) == 1 {
|
||||
return remotes[0]
|
||||
}
|
||||
|
||||
for _, remoteName := range []string{"upstream", "origin"} {
|
||||
if remote := findRemoteByName(remoteName); remote != nil {
|
||||
return remote
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) promptForBaseGithubRepo(authToken string) {
|
||||
menuItems := lo.FilterMap(self.c.Model().Remotes, func(remote *models.Remote, _ int) (*types.MenuItem, bool) {
|
||||
if len(remote.Urls) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
repoName, err := self.c.Git().HostingService.GetRepoNameFromRemoteURL(remote.Urls[0])
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &types.MenuItem{
|
||||
LabelColumns: []string{remote.Name, style.FgCyan.Sprint(repoName)},
|
||||
OnPress: func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.FetchingPullRequests, func(gocui.Task) error {
|
||||
if err := self.c.Git().GitHub.SetConfiguredBaseRemoteName(remote.Name); err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
if err := self.setGithubPullRequests(authToken, remote); err != nil {
|
||||
self.c.LogAction(fmt.Sprintf("Error fetching pull requests from GitHub: %s", err.Error()))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
},
|
||||
}, true
|
||||
})
|
||||
|
||||
_ = self.c.Menu(types.CreateMenuOptions{
|
||||
Title: self.c.Tr.SelectRemoteRepository,
|
||||
Items: menuItems,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) rebuildPullRequestsMap() {
|
||||
self.c.Model().PullRequestsMap = git_commands.GenerateGithubPullRequestMap(
|
||||
self.c.Model().PullRequests,
|
||||
self.c.Model().Branches,
|
||||
self.c.Model().Remotes,
|
||||
)
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) setGithubPullRequests(authToken string, baseRemote *models.Remote) error {
|
||||
if len(self.c.Model().Branches) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
branches := lo.Filter(self.c.Model().Branches, func(branch *models.Branch, _ int) bool {
|
||||
return branch.IsTrackingRemote()
|
||||
})
|
||||
branchNames := lo.Map(branches, func(branch *models.Branch, _ int) string {
|
||||
return branch.UpstreamBranch
|
||||
})
|
||||
|
||||
prs, err := self.c.Git().GitHub.FetchRecentPRs(branchNames, baseRemote, authToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.c.Model().PullRequests = prs
|
||||
self.rebuildPullRequestsMap()
|
||||
|
||||
self.c.PostRefreshUpdate(self.c.Contexts().Branches)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -601,6 +601,8 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
|
|||
Authors: map[string]*models.Author{},
|
||||
MainBranches: git_commands.NewMainBranches(gui.c.Common, gui.os.Cmd),
|
||||
HashPool: &utils.StringPool{},
|
||||
PullRequests: nil,
|
||||
PullRequestsMap: make(map[string]*models.GithubPullRequest),
|
||||
},
|
||||
Modes: &types.Modes{
|
||||
Filtering: filtering.New(startArgs.FilterPath, ""),
|
||||
|
|
|
|||
|
|
@ -287,15 +287,17 @@ func (self *MenuItem) ID() string {
|
|||
}
|
||||
|
||||
type Model struct {
|
||||
CommitFiles []*models.CommitFile
|
||||
Files []*models.File
|
||||
Submodules []*models.SubmoduleConfig
|
||||
Branches []*models.Branch
|
||||
Commits []*models.Commit
|
||||
StashEntries []*models.StashEntry
|
||||
SubCommits []*models.Commit
|
||||
Remotes []*models.Remote
|
||||
Worktrees []*models.Worktree
|
||||
CommitFiles []*models.CommitFile
|
||||
Files []*models.File
|
||||
Submodules []*models.SubmoduleConfig
|
||||
Branches []*models.Branch
|
||||
Commits []*models.Commit
|
||||
StashEntries []*models.StashEntry
|
||||
SubCommits []*models.Commit
|
||||
Remotes []*models.Remote
|
||||
Worktrees []*models.Worktree
|
||||
PullRequests []*models.GithubPullRequest
|
||||
PullRequestsMap map[string]*models.GithubPullRequest
|
||||
|
||||
// FilteredReflogCommits are the ones that appear in the reflog panel.
|
||||
// When in filtering mode we only include the ones that match the given path
|
||||
|
|
@ -326,15 +328,16 @@ type Model struct {
|
|||
}
|
||||
|
||||
type Mutexes struct {
|
||||
RefreshingFilesMutex deadlock.Mutex
|
||||
RefreshingBranchesMutex deadlock.Mutex
|
||||
RefreshingStatusMutex deadlock.Mutex
|
||||
LocalCommitsMutex deadlock.Mutex
|
||||
SubCommitsMutex deadlock.Mutex
|
||||
AuthorsMutex deadlock.Mutex
|
||||
SubprocessMutex deadlock.Mutex
|
||||
PopupMutex deadlock.Mutex
|
||||
PtyMutex deadlock.Mutex
|
||||
RefreshingFilesMutex deadlock.Mutex
|
||||
RefreshingBranchesMutex deadlock.Mutex
|
||||
RefreshingStatusMutex deadlock.Mutex
|
||||
RefreshingPullRequestsMutex deadlock.Mutex
|
||||
LocalCommitsMutex deadlock.Mutex
|
||||
SubCommitsMutex deadlock.Mutex
|
||||
AuthorsMutex deadlock.Mutex
|
||||
SubprocessMutex deadlock.Mutex
|
||||
PopupMutex deadlock.Mutex
|
||||
PtyMutex deadlock.Mutex
|
||||
}
|
||||
|
||||
// A long-running operation associated with an item. For example, we'll show
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ const (
|
|||
PATCH_BUILDING
|
||||
MERGE_CONFLICTS
|
||||
COMMIT_FILES
|
||||
// not actually a view. Will refactor this later
|
||||
// not actually views. Will refactor this later
|
||||
BISECT_INFO
|
||||
PULL_REQUESTS
|
||||
)
|
||||
|
||||
type RefreshMode int
|
||||
|
|
|
|||
|
|
@ -609,6 +609,8 @@ type TranslationSet struct {
|
|||
CyclePagersDisabledReason string
|
||||
StartSearch string
|
||||
StartFilter string
|
||||
SelectRemoteRepository string
|
||||
FetchingPullRequests string
|
||||
Keybindings string
|
||||
KeybindingsLegend string
|
||||
KeybindingsMenuSectionLocal string
|
||||
|
|
@ -1730,6 +1732,8 @@ func EnglishTranslationSet() *TranslationSet {
|
|||
CyclePagersDisabledReason: "No other pagers configured",
|
||||
StartSearch: "Search the current view by text",
|
||||
StartFilter: "Filter the current view by text",
|
||||
SelectRemoteRepository: "Select base repository for pull requests",
|
||||
FetchingPullRequests: "Fetching pull requests",
|
||||
KeybindingsLegend: "Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b",
|
||||
RenameBranch: "Rename branch",
|
||||
BranchUpstreamOptionsTitle: "Upstream options",
|
||||
|
|
|
|||
Loading…
Reference in a new issue