This commit is contained in:
Jesse Duffield 2022-09-22 09:03:11 -07:00
parent ac8e76bee0
commit 79a72fe7a6
17 changed files with 294 additions and 211 deletions

View file

@ -124,17 +124,15 @@ func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
func (app *App) validateGhVersion() error {
output, err := app.OSCommand.Cmd.New("gh --version").RunWithOutput()
// if we get an error anywhere here we'll show the same status
minVersionError := errors.New(app.Tr.MinGhVersionError)
if err != nil {
return minVersionError
return fmt.Errorf(app.Tr.FailedToObtainGhVersionError, err.Error())
}
if isGhVersionValid(output) {
return nil
if !isGhVersionValid(output) {
return errors.New(app.Tr.MinGhVersionError)
}
return minVersionError
return nil
}
func isGhVersionValid(versionStr string) bool {
@ -162,17 +160,15 @@ func isGhVersionValid(versionStr string) bool {
func (app *App) validateGitVersion() error {
output, err := app.OSCommand.Cmd.New("git --version").RunWithOutput()
// if we get an error anywhere here we'll show the same status
minVersionError := errors.New(app.Tr.MinGitVersionError)
if err != nil {
return minVersionError
return fmt.Errorf(app.Tr.FailedToObtainGitVersionError, err.Error())
}
if isGitVersionValid(output) {
return nil
if !isGitVersionValid(output) {
return errors.New(app.Tr.MinGitVersionError)
}
return minVersionError
return nil
}
func isGitVersionValid(versionStr string) bool {

View file

@ -22,23 +22,24 @@ import (
// GitCommand is our main git interface
type GitCommand struct {
Branch *git_commands.BranchCommands
Commit *git_commands.CommitCommands
Config *git_commands.ConfigCommands
Custom *git_commands.CustomCommands
File *git_commands.FileCommands
Flow *git_commands.FlowCommands
Patch *git_commands.PatchCommands
Rebase *git_commands.RebaseCommands
Remote *git_commands.RemoteCommands
Stash *git_commands.StashCommands
Status *git_commands.StatusCommands
Submodule *git_commands.SubmoduleCommands
Sync *git_commands.SyncCommands
Tag *git_commands.TagCommands
WorkingTree *git_commands.WorkingTreeCommands
Bisect *git_commands.BisectCommands
Gh *git_commands.GhCommands
Branch *git_commands.BranchCommands
Commit *git_commands.CommitCommands
Config *git_commands.ConfigCommands
Custom *git_commands.CustomCommands
File *git_commands.FileCommands
Flow *git_commands.FlowCommands
Patch *git_commands.PatchCommands
Rebase *git_commands.RebaseCommands
Remote *git_commands.RemoteCommands
Stash *git_commands.StashCommands
Status *git_commands.StatusCommands
Submodule *git_commands.SubmoduleCommands
Sync *git_commands.SyncCommands
Tag *git_commands.TagCommands
WorkingTree *git_commands.WorkingTreeCommands
Bisect *git_commands.BisectCommands
Gh *git_commands.GhCommands
HostingService *git_commands.HostingService
Loaders Loaders
}
@ -121,25 +122,27 @@ func NewGitCommandAux(
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchManager)
bisectCommands := git_commands.NewBisectCommands(gitCommon)
ghCommands := git_commands.NewGhCommand(gitCommon)
hostingServiceCommands := git_commands.NewHostingServiceCommand(gitCommon)
return &GitCommand{
Branch: branchCommands,
Commit: commitCommands,
Config: configCommands,
Custom: customCommands,
File: fileCommands,
Flow: flowCommands,
Patch: patchCommands,
Rebase: rebaseCommands,
Remote: remoteCommands,
Stash: stashCommands,
Status: statusCommands,
Submodule: submoduleCommands,
Sync: syncCommands,
Tag: tagCommands,
Bisect: bisectCommands,
WorkingTree: workingTreeCommands,
Gh: ghCommands,
Branch: branchCommands,
Commit: commitCommands,
Config: configCommands,
Custom: customCommands,
File: fileCommands,
Flow: flowCommands,
Patch: patchCommands,
Rebase: rebaseCommands,
Remote: remoteCommands,
Stash: stashCommands,
Status: statusCommands,
Submodule: submoduleCommands,
Sync: syncCommands,
Tag: tagCommands,
Bisect: bisectCommands,
WorkingTree: workingTreeCommands,
Gh: ghCommands,
HostingService: hostingServiceCommands,
Loaders: Loaders{
Branches: loaders.NewBranchLoader(cmn, branchCommands.GetRawBranches, branchCommands.CurrentBranchName, configCommands),
CommitFiles: loaders.NewCommitFileLoader(cmn, cmd),

View file

@ -25,11 +25,15 @@ func (self *GhCommands) BaseRepo() error {
// Ex: git config --local --add "remote.origin.gh-resolved" "jesseduffield/lazygit"
func (self *GhCommands) SetBaseRepo(repository string) (string, error) {
return self.cmd.New(fmt.Sprintf("git config --local --add \"remote.origin.gh-resolved\" \"%s\"", repository)).RunWithOutput()
return self.cmd.New(
fmt.Sprintf("git config --local --add \"remote.origin.gh-resolved\" \"%s\"", repository),
).RunWithOutput()
}
func (self *GhCommands) prList() (string, error) {
return self.cmd.New("gh pr list --limit 500 --state all --json state,url,number,headRefName,headRepositoryOwner").RunWithOutput()
return self.cmd.New(
"gh pr list --limit 500 --state all --json state,url,number,headRefName,headRepositoryOwner",
).RunWithOutput()
}
func (self *GhCommands) GithubMostRecentPRs() ([]*models.GithubPullRequest, error) {
@ -60,23 +64,33 @@ func GenerateGithubPullRequestMap(prs []*models.GithubPullRequest, branches []*m
return res
}
prWithStringKey := map[string]models.GithubPullRequest{}
// A PR can be identified by two things: the owner e.g. 'jesseduffield' and the
// branch name e.g. 'feature/my-feature'. The owner might be different
// to the owner of the repo if the PR is from a fork of that repo.
type prKey struct {
owner string
branchName string
}
prByKey := map[prKey]models.GithubPullRequest{}
for _, pr := range prs {
prWithStringKey[pr.UserName()+":"+pr.BranchName()] = *pr
prByKey[prKey{owner: pr.UserName(), branchName: pr.BranchName()}] = *pr
}
for _, branch := range branches {
if !branch.IsTrackingRemote() || branch.UpstreamBranch == "" {
if !branch.IsTrackingRemote() {
continue
}
// TODO: support branches whose UpstreamRemote contains a full git
// URL rather than just a remote name.
owner, foundRemoteOwner := remotesToOwnersMap[branch.UpstreamRemote]
if !foundRemoteOwner {
continue
}
pr, hasPr := prWithStringKey[owner+":"+branch.UpstreamBranch]
pr, hasPr := prByKey[prKey{owner: owner, branchName: branch.UpstreamBranch}]
if !hasPr {
continue
@ -88,6 +102,24 @@ func GenerateGithubPullRequestMap(prs []*models.GithubPullRequest, branches []*m
return res
}
func getRemotesToOwnersMap(remotes []*models.Remote) map[string]string {
res := map[string]string{}
for _, remote := range remotes {
if len(remote.Urls) == 0 {
continue
}
res[remote.Name] = GetRepoInfoFromURL(remote.Urls[0]).Owner
}
return res
}
type RepoInformation struct {
Owner string
Repository string
}
// TODO: move this into hosting_service.go
func GetRepoInfoFromURL(url string) RepoInformation {
isHTTP := strings.HasPrefix(url, "http")
@ -112,20 +144,3 @@ func GetRepoInfoFromURL(url string) RepoInformation {
Repository: repo,
}
}
func getRemotesToOwnersMap(remotes []*models.Remote) map[string]string {
res := map[string]string{}
for _, remote := range remotes {
if len(remote.Urls) == 0 {
continue
}
res[remote.Name] = GetRepoInfoFromURL(remote.Urls[0]).Owner
}
return res
}
type RepoInformation struct {
Owner string
Repository string
}

View file

@ -0,0 +1,34 @@
package git_commands
import "github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
// a hosting service is something like github, gitlab, bitbucket etc
type HostingService struct {
*GitCommon
}
func NewHostingServiceCommand(gitCommon *GitCommon) *HostingService {
return &HostingService{
GitCommon: gitCommon,
}
}
func (self *HostingService) GetPullRequestURL(from string, to string) (string, error) {
return self.getHostingServiceMgr(self.config.GetRemoteURL()).GetPullRequestURL(from, to)
}
func (self *HostingService) GetCommitURL(commitSha string) (string, error) {
return self.getHostingServiceMgr(self.config.GetRemoteURL()).GetCommitURL(commitSha)
}
func (self *HostingService) GetRepoNameFromRemoteURL(remoteURL string) (string, error) {
return self.getHostingServiceMgr(remoteURL).GetRepoName()
}
// getting this on every request rather than storing it in state in case our remoteURL changes
// from one invocation to the next. Note however that we're currently caching config
// results so we might want to invalidate the cache here if it becomes a problem.
func (self *HostingService) getHostingServiceMgr(remoteURL string) *hosting_service.HostingServiceMgr {
configServices := self.UserConfig.Services
return hosting_service.NewHostingServiceMgr(self.Log, self.Tr, remoteURL, configServices)
}

View file

@ -6,7 +6,11 @@ var defaultUrlRegexStrings = []string{
`^(?:https?|ssh)://[^/]+/(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
`^git@.*:(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
}
var defaultRepoURLTemplate = "https://{{.webDomain}}/{{.owner}}/{{.repo}}"
var (
defaultRepoURLTemplate = "https://{{.webDomain}}/{{.owner}}/{{.repo}}"
defaultRepoNameTemplate = "{{.owner}}/{{.repo}}"
)
// we've got less type safety using go templates but this lends itself better to
// users adding custom service definitions in their config
@ -17,6 +21,7 @@ var githubServiceDef = ServiceDefinition{
commitURL: "/commit/{{.CommitSha}}",
regexStrings: defaultUrlRegexStrings,
repoURLTemplate: defaultRepoURLTemplate,
repoNameTemplate: defaultRepoNameTemplate,
}
var bitbucketServiceDef = ServiceDefinition{
@ -28,7 +33,8 @@ var bitbucketServiceDef = ServiceDefinition{
`^(?:https?|ssh)://.*/(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
`^.*@.*:(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
},
repoURLTemplate: defaultRepoURLTemplate,
repoURLTemplate: defaultRepoURLTemplate,
repoNameTemplate: defaultRepoNameTemplate,
}
var gitLabServiceDef = ServiceDefinition{
@ -38,6 +44,7 @@ var gitLabServiceDef = ServiceDefinition{
commitURL: "/commit/{{.CommitSha}}",
regexStrings: defaultUrlRegexStrings,
repoURLTemplate: defaultRepoURLTemplate,
repoNameTemplate: defaultRepoNameTemplate,
}
var azdoServiceDef = ServiceDefinition{
@ -50,6 +57,8 @@ var azdoServiceDef = ServiceDefinition{
`^https://.*@dev.azure.com/(?P<org>.*?)/(?P<project>.*?)/_git/(?P<repo>.*?)(?:\.git)?$`,
},
repoURLTemplate: "https://{{.webDomain}}/{{.org}}/{{.project}}/_git/{{.repo}}",
// TODO: verify this is actually correct
repoNameTemplate: "{{.org}}/{{.project}}/{{.repo}}",
}
var bitbucketServerServiceDef = ServiceDefinition{
@ -62,6 +71,8 @@ var bitbucketServerServiceDef = ServiceDefinition{
`^https://.*/scm/(?P<project>.*)/(?P<repo>.*?)(?:\.git)?$`,
},
repoURLTemplate: "https://{{.webDomain}}/projects/{{.project}}/repos/{{.repo}}",
// TODO: verify this is actually correct
repoNameTemplate: "{{.project}}/{{.repo}}",
}
var serviceDefinitions = []ServiceDefinition{

View file

@ -61,6 +61,18 @@ func (self *HostingServiceMgr) GetCommitURL(commitSha string) (string, error) {
return pullRequestURL, nil
}
// e.g. 'jesseduffield/lazygit'
func (self *HostingServiceMgr) GetRepoName() (string, error) {
gitService, err := self.getService()
if err != nil {
return "", err
}
repoName := gitService.repoName
return repoName, nil
}
func (self *HostingServiceMgr) getService() (*Service, error) {
serviceDomain, err := self.getServiceDomain(self.remoteURL)
if err != nil {
@ -72,8 +84,14 @@ func (self *HostingServiceMgr) getService() (*Service, error) {
return nil, err
}
repoName, err := serviceDomain.serviceDefinition.getRepoNameFromRemoteURL(self.remoteURL)
if err != nil {
return nil, err
}
return &Service{
repoURL: repoURL,
repoName: repoName,
ServiceDefinition: serviceDomain.serviceDefinition,
}, nil
}
@ -147,24 +165,45 @@ type ServiceDefinition struct {
regexStrings []string
// can expect 'webdomain' to be passed in. Otherwise, you get to pick what we match in the regex
repoURLTemplate string
repoURLTemplate string
repoNameTemplate string
}
func (self ServiceDefinition) getRepoURLFromRemoteURL(url string, webDomain string) (string, error) {
matches, err := self.parseRemoteUrl(url)
if err != nil {
return "", err
}
matches["webDomain"] = webDomain
return utils.ResolvePlaceholderString(self.repoURLTemplate, matches), nil
}
func (self ServiceDefinition) getRepoNameFromRemoteURL(url string) (string, error) {
matches, err := self.parseRemoteUrl(url)
if err != nil {
return "", err
}
return utils.ResolvePlaceholderString(self.repoNameTemplate, matches), nil
}
func (self ServiceDefinition) parseRemoteUrl(url string) (map[string]string, error) {
for _, regexStr := range self.regexStrings {
re := regexp.MustCompile(regexStr)
input := utils.FindNamedMatches(re, url)
if input != nil {
input["webDomain"] = webDomain
return utils.ResolvePlaceholderString(self.repoURLTemplate, input), nil
matches := utils.FindNamedMatches(re, url)
if matches != nil {
return matches, nil
}
}
return "", errors.New("Failed to parse repo information from url")
return nil, errors.New("Failed to parse repo information from url")
}
type Service struct {
repoURL string
// e.g. 'jesseduffield/lazygit'
repoName string
ServiceDefinition
}

View file

@ -9,10 +9,12 @@ type GithubPullRequest struct {
}
func (pr *GithubPullRequest) UserName() string {
// e.g. 'jesseduffield'
return pr.HeadRepositoryOwner.Login
}
func (pr *GithubPullRequest) BranchName() string {
// e.g. 'feature/my-feature'
return pr.HeadRefName
}

View file

@ -23,55 +23,3 @@ func (gui *Gui) branchesRenderToMain() error {
},
})
}
func (gui *Gui) refreshGithubPullRequests() error {
if err := gui.git.Gh.BaseRepo(); err == nil {
err := gui.setGithubPullRequests()
if err != nil {
return err
}
gui.refreshBranches()
return nil
}
// when config not exits
err := gui.refreshRemotes()
if err != nil {
return err
}
_ = gui.c.Prompt(types.PromptOpts{
Title: gui.c.Tr.SelectRemoteRepository,
InitialContent: "",
FindSuggestionsFunc: gui.helpers.Suggestions.GetRemoteRepoSuggestionsFunc(),
HandleConfirm: func(repository string) error {
return gui.c.WithWaitingStatus(gui.c.Tr.LcSelectingRemote, func() error {
_, err := gui.git.Gh.SetBaseRepo(repository)
if err != nil {
return err
}
err = gui.setGithubPullRequests()
if err != nil {
return err
}
gui.refreshBranches()
return nil
})
},
})
return nil
}
func (gui *Gui) setGithubPullRequests() error {
prs, err := gui.git.Gh.GithubMostRecentPRs()
if err != nil {
return gui.c.Error(err)
}
gui.State.Model.PullRequests = prs
return nil
}

View file

@ -23,10 +23,9 @@ func (gui *Gui) resetControllers() {
)
rebaseHelper := helpers.NewMergeAndRebaseHelper(helperCommon, gui.State.Contexts, gui.git, refsHelper)
suggestionsHelper := helpers.NewSuggestionsHelper(helperCommon, model, gui.refreshSuggestions)
suggestionsHelper := helpers.NewSuggestionsHelper(helperCommon, gui.git, model, gui.refreshSuggestions)
gui.helpers = &helpers.Helpers{
Refs: refsHelper,
Host: helpers.NewHostHelper(helperCommon, gui.git),
PatchBuilding: helpers.NewPatchBuildingHelper(helperCommon, gui.git, gui.State.Contexts),
Bisect: helpers.NewBisectHelper(helperCommon, gui.git),
Suggestions: suggestionsHelper,

View file

@ -150,7 +150,7 @@ func (self *BasicCommitsController) copyCommitSHAToClipboard(commit *models.Comm
}
func (self *BasicCommitsController) copyCommitURLToClipboard(commit *models.Commit) error {
url, err := self.helpers.Host.GetCommitURL(commit.Sha)
url, err := self.git.HostingService.GetCommitURL(commit.Sha)
if err != nil {
return err
}
@ -212,7 +212,7 @@ func (self *BasicCommitsController) copyCommitMessageToClipboard(commit *models.
}
func (self *BasicCommitsController) openInBrowser(commit *models.Commit) error {
url, err := self.helpers.Host.GetCommitURL(commit.Sha)
url, err := self.git.HostingService.GetCommitURL(commit.Sha)
if err != nil {
return self.c.Error(err)
}

View file

@ -195,7 +195,7 @@ func (self *BranchesController) copyPullRequestURL() error {
return self.c.Error(errors.New(self.c.Tr.NoBranchOnRemote))
}
url, err := self.helpers.Host.GetPullRequestURL(branch.Name, "")
url, err := self.git.HostingService.GetPullRequestURL(branch.Name, "")
if err != nil {
return self.c.Error(err)
}
@ -462,7 +462,7 @@ func (self *BranchesController) createPullRequestMenu(selectedBranch *models.Bra
}
func (self *BranchesController) createPullRequest(from string, to string) error {
url, err := self.helpers.Host.GetPullRequestURL(from, to)
url, err := self.git.HostingService.GetPullRequestURL(from, to)
if err != nil {
return self.c.Error(err)
}

View file

@ -10,7 +10,6 @@ type Helpers struct {
MergeAndRebase *MergeAndRebaseHelper
MergeConflicts *MergeConflictsHelper
CherryPick *CherryPickHelper
Host *HostHelper
PatchBuilding *PatchBuildingHelper
GPG *GpgHelper
Upstream *UpstreamHelper
@ -27,7 +26,6 @@ func NewStubHelpers() *Helpers {
MergeAndRebase: &MergeAndRebaseHelper{},
MergeConflicts: &MergeConflictsHelper{},
CherryPick: &CherryPickHelper{},
Host: &HostHelper{},
PatchBuilding: &PatchBuildingHelper{},
GPG: &GpgHelper{},
Upstream: &UpstreamHelper{},

View file

@ -1,46 +0,0 @@
package helpers
import (
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
// this helper just wraps our hosting_service package
type IHostHelper interface {
GetPullRequestURL(from string, to string) (string, error)
GetCommitURL(commitSha string) (string, error)
}
type HostHelper struct {
c *types.HelperCommon
git *commands.GitCommand
}
func NewHostHelper(
c *types.HelperCommon,
git *commands.GitCommand,
) *HostHelper {
return &HostHelper{
c: c,
git: git,
}
}
func (self *HostHelper) GetPullRequestURL(from string, to string) (string, error) {
return self.getHostingServiceMgr().GetPullRequestURL(from, to)
}
func (self *HostHelper) GetCommitURL(commitSha string) (string, error) {
return self.getHostingServiceMgr().GetCommitURL(commitSha)
}
// getting this on every request rather than storing it in state in case our remoteURL changes
// from one invocation to the next. Note however that we're currently caching config
// results so we might want to invalidate the cache here if it becomes a problem.
func (self *HostHelper) getHostingServiceMgr() *hosting_service.HostingServiceMgr {
remoteUrl := self.git.Config.GetRemoteURL()
configServices := self.c.UserConfig.Services
return hosting_service.NewHostingServiceMgr(self.c.Log, self.c.Tr, remoteUrl, configServices)
}

View file

@ -5,7 +5,7 @@ import (
"os"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@ -37,6 +37,7 @@ type SuggestionsHelper struct {
c *types.HelperCommon
model *types.Model
git *commands.GitCommand
refreshSuggestionsFn func()
}
@ -44,11 +45,13 @@ var _ ISuggestionsHelper = &SuggestionsHelper{}
func NewSuggestionsHelper(
c *types.HelperCommon,
git *commands.GitCommand,
model *types.Model,
refreshSuggestionsFn func(),
) *SuggestionsHelper {
return &SuggestionsHelper{
c: c,
git: git,
model: model,
refreshSuggestionsFn: refreshSuggestionsFn,
}
@ -94,8 +97,12 @@ func (self *SuggestionsHelper) getRemoteRepoNames() []string {
if len(remote.Urls) == 0 {
continue
}
info := git_commands.GetRepoInfoFromURL(remote.Urls[0])
result = append(result, fmt.Sprintf("%s/%s", info.Owner, info.Repository))
repoName, err := self.git.HostingService.GetRepoNameFromRemoteURL(remote.Urls[0])
if err != nil {
self.c.Log.Error(err)
continue
}
result = append(result, repoName)
}
return result

View file

@ -134,15 +134,21 @@ func SetCustomBranches(customBranchColors map[string]string) {
func coloredPrNumber(pr *models.GithubPullRequest, hasPr bool) string {
if hasPr {
colour := style.FgMagenta // = state MERGED
switch pr.State {
case "OPEN":
colour = style.FgGreen
case "CLOSED":
colour = style.FgRed
}
return colour.Sprint("#" + strconv.Itoa(pr.Number))
return prColor(pr.State).Sprint("#" + strconv.Itoa(pr.Number))
}
return ("")
}
func prColor(state string) style.TextStyle {
switch state {
case "OPEN":
return style.FgGreen
case "CLOSED":
return style.FgRed
case "MERGED":
return style.FgMagenta
default:
return style.FgDefault
}
}

View file

@ -164,21 +164,38 @@ func (gui *Gui) Refresh(options types.RefreshOptions) error {
return nil
}
// during startup, the bottleneck is fetching the reflog entries. We need these
// on startup to sort the branches by recency. So we have two phases: INITIAL, and COMPLETE.
// In the initial phase we don't get any reflog commits, but we asynchronously get them
// and refresh the branches after that
func (gui *Gui) refreshReflogCommitsConsideringStartup() {
// during startup, the bottleneck is fetching the reflog entries and Github PRs, both of which affect the contents of the branches view.
// So we have two phases: INITIAL, and COMPLETE.
// In the initial phase we asynchronously refresh the dependencies (reflog entries and Github PRs) and then refresh the branches view.
// After that, we synchronously refresh the dependencies
func (gui *Gui) refreshBranchDependenciesConsideringStartup() {
refreshDeps := func() {
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
_ = gui.refreshReflogCommits()
wg.Done()
}()
go func() {
_ = gui.refreshGithubPullRequests()
wg.Done()
}()
wg.Wait()
}
switch gui.State.StartupStage {
case INITIAL:
go utils.Safe(func() {
_ = gui.refreshReflogCommits()
refreshDeps()
gui.refreshBranches()
gui.State.StartupStage = COMPLETE
})
case COMPLETE:
_ = gui.refreshReflogCommits()
refreshDeps()
}
}
@ -190,7 +207,7 @@ func (gui *Gui) refreshCommits() {
wg.Add(2)
go utils.Safe(func() {
gui.refreshReflogCommitsConsideringStartup()
gui.refreshBranchDependenciesConsideringStartup()
gui.refreshBranches()
wg.Done()
@ -237,14 +254,6 @@ func (gui *Gui) refreshCommitsWithLimit() error {
}
gui.State.Model.Commits = commits
if gui.Config.GetUserConfig().Git.EnableGhCommand {
err = gui.refreshGithubPullRequests()
if err != nil {
return err
}
}
return gui.c.PostRefreshUpdate(gui.State.Contexts.LocalCommits)
}
@ -719,3 +728,61 @@ func (gui *Gui) refreshMergePanel(isFocused bool) error {
},
})
}
func (gui *Gui) refreshGithubPullRequests() error {
if !gui.Config.GetUserConfig().Git.EnableGhCommand {
return nil
}
if err := gui.git.Gh.BaseRepo(); err == nil {
if err := gui.setGithubPullRequests(); err != nil {
return gui.c.Error(err)
}
return nil
}
// when config not exists
err := gui.refreshRemotes()
if err != nil {
return err
}
_ = gui.c.Prompt(types.PromptOpts{
Title: gui.c.Tr.SelectRemoteRepository,
InitialContent: "",
FindSuggestionsFunc: gui.helpers.Suggestions.GetRemoteRepoSuggestionsFunc(),
HandleConfirm: func(repository string) error {
return gui.c.WithWaitingStatus(gui.c.Tr.LcSelectingRemote, func() error {
// `repository` is something like 'jesseduffield/lazygit'
_, err := gui.git.Gh.SetBaseRepo(repository)
if err != nil {
return gui.c.Error(err)
}
if err := gui.setGithubPullRequests(); err != nil {
return gui.c.Error(err)
}
// calling refreshBranches explicitly because it may have taken
// a while for the user to submit their response.
gui.refreshBranches()
return nil
})
},
})
return nil
}
func (gui *Gui) setGithubPullRequests() error {
prs, err := gui.git.Gh.GithubMostRecentPRs()
if err != nil {
return err
}
gui.State.Model.PullRequests = prs
return nil
}

View file

@ -420,7 +420,9 @@ type TranslationSet struct {
LcBuildingPatch string
LcViewCommits string
MinGitVersionError string
FailedToObtainGitVersionError string
MinGhVersionError string
FailedToObtainGhVersionError string
LcRunningCustomCommandStatus string
LcSubmoduleStashAndReset string
LcAndResetSubmodules string
@ -1000,7 +1002,7 @@ func EnglishTranslationSet() TranslationSet {
LcPrevScreenMode: "prev screen mode",
LcStartSearch: "start search",
LcSelectingRemote: "selecting remote",
SelectRemoteRepository: "select base remote repository",
SelectRemoteRepository: "Select GitHub base remote repository (for PRs)",
Panel: "Panel",
Keybindings: "Keybindings",
LcRenameBranch: "rename branch",
@ -1065,6 +1067,8 @@ func EnglishTranslationSet() TranslationSet {
LcViewCommits: "view commits",
MinGitVersionError: "Git version must be at least 2.0 (i.e. from 2014 onwards). Please upgrade your git version. Alternatively raise an issue at https://github.com/jesseduffield/lazygit/issues for lazygit to be more backwards compatible.",
MinGhVersionError: "GH version must be at least 2.0. Please upgrade your gh version. Alternatively raise an issue at https://github.com/jesseduffield/lazygit/issues for lazygit to be more backwards compatible.",
FailedToObtainGitVersionError: "Failed to obtain git version. Output from running 'git --version' was: %s",
FailedToObtainGhVersionError: "Failed to obtain gh version. Output from running 'gh --version' was: %s",
LcRunningCustomCommandStatus: "running custom command",
LcSubmoduleStashAndReset: "stash uncommitted submodule changes and update",
LcAndResetSubmodules: "and reset submodules",