1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-12 04:23:03 +02:00
lazygit/pkg/commands/pull_request.go
2021-12-26 16:48:23 +11:00

195 lines
6.1 KiB
Go

package commands
import (
"fmt"
"regexp"
"strings"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// if you want to make a custom regex for a given service feel free to test it out
// at regoio.herokuapp.com
var defaultUrlRegexStrings = []string{
`^https?://.*/(?P<owner>.*)/(?P<repo>.*?)(\.git)?$`,
`^git@.*:(?P<owner>.*)/(?P<repo>.*?)(\.git)?$`,
}
// Service is a service that repository is on (Github, Bitbucket, ...)
type Service struct {
Name string
pullRequestURLIntoDefaultBranch func(owner string, repository string, from string) string
pullRequestURLIntoTargetBranch func(owner string, repository string, from string, to string) string
URLRegexStrings []string
}
func NewGithubService(repositoryDomain string, siteDomain string) *Service {
return &Service{
Name: repositoryDomain,
pullRequestURLIntoDefaultBranch: func(owner string, repository string, from string) string {
return fmt.Sprintf("https://%s/%s/%s/compare/%s?expand=1", siteDomain, owner, repository, from)
},
pullRequestURLIntoTargetBranch: func(owner string, repository string, from string, to string) string {
return fmt.Sprintf("https://%s/%s/%s/compare/%s...%s?expand=1", siteDomain, owner, repository, to, from)
},
URLRegexStrings: defaultUrlRegexStrings,
}
}
func NewBitBucketService(repositoryDomain string, siteDomain string) *Service {
return &Service{
Name: repositoryDomain,
pullRequestURLIntoDefaultBranch: func(owner string, repository string, from string) string {
return fmt.Sprintf("https://%s/%s/%s/pull-requests/new?source=%s&t=1", siteDomain, owner, repository, from)
},
pullRequestURLIntoTargetBranch: func(owner string, repository string, from string, to string) string {
return fmt.Sprintf("https://%s/%s/%s/pull-requests/new?source=%s&dest=%s&t=1", siteDomain, owner, repository, from, to)
},
URLRegexStrings: defaultUrlRegexStrings,
}
}
func NewGitLabService(repositoryDomain string, siteDomain string) *Service {
return &Service{
Name: repositoryDomain,
pullRequestURLIntoDefaultBranch: func(owner string, repository string, from string) string {
return fmt.Sprintf("https://%s/%s/%s/merge_requests/new?merge_request[source_branch]=%s", siteDomain, owner, repository, from)
},
pullRequestURLIntoTargetBranch: func(owner string, repository string, from string, to string) string {
return fmt.Sprintf("https://%s/%s/%s/merge_requests/new?merge_request[source_branch]=%s&merge_request[target_branch]=%s", siteDomain, owner, repository, from, to)
},
URLRegexStrings: defaultUrlRegexStrings,
}
}
func (s *Service) PullRequestURL(repoURL string, from string, to string) string {
repoInfo := s.getRepoInfoFromURL(repoURL)
if to == "" {
return s.pullRequestURLIntoDefaultBranch(repoInfo.Owner, repoInfo.Repository, from)
} else {
return s.pullRequestURLIntoTargetBranch(repoInfo.Owner, repoInfo.Repository, from, to)
}
}
// PullRequest opens a link in browser to create new pull request
// with selected branch
type PullRequest struct {
GitCommand *GitCommand
}
// RepoInformation holds some basic information about the repo
type RepoInformation struct {
Owner string
Repository string
}
// NewPullRequest creates new instance of PullRequest
func NewPullRequest(gitCommand *GitCommand) *PullRequest {
return &PullRequest{
GitCommand: gitCommand,
}
}
func (pr *PullRequest) getServices() []*Service {
services := []*Service{
NewGithubService("github.com", "github.com"),
NewBitBucketService("bitbucket.org", "bitbucket.org"),
NewGitLabService("gitlab.com", "gitlab.com"),
}
configServices := pr.GitCommand.Config.GetUserConfig().Services
if len(configServices) > 0 {
serviceFuncMap := map[string]func(repositoryDomain string, siteDomain string) *Service{
"github": NewGithubService,
"bitbucket": NewBitBucketService,
"gitlab": NewGitLabService,
}
for repoDomain, typeAndDomain := range configServices {
splitData := strings.Split(typeAndDomain, ":")
if len(splitData) != 2 {
pr.GitCommand.Log.Errorf("Unexpected format for git service: '%s'. Expected something like 'github.com:github.com'", typeAndDomain)
continue
}
serviceFunc := serviceFuncMap[splitData[0]]
if serviceFunc == nil {
serviceNames := []string{}
for serviceName := range serviceFuncMap {
serviceNames = append(serviceNames, serviceName)
}
pr.GitCommand.Log.Errorf("Unknown git service type: '%s'. Expected one of %s", splitData[0], strings.Join(serviceNames, ", "))
continue
}
services = append(services, serviceFunc(repoDomain, splitData[1]))
}
}
return services
}
// Create opens link to new pull request in browser
func (pr *PullRequest) Create(from string, to string) (string, error) {
pullRequestURL, err := pr.getPullRequestURL(from, to)
if err != nil {
return "", err
}
return pullRequestURL, pr.GitCommand.OSCommand.OpenLink(pullRequestURL)
}
// CopyURL copies the pull request URL to the clipboard
func (pr *PullRequest) CopyURL(from string, to string) (string, error) {
pullRequestURL, err := pr.getPullRequestURL(from, to)
if err != nil {
return "", err
}
return pullRequestURL, pr.GitCommand.OSCommand.CopyToClipboard(pullRequestURL)
}
func (pr *PullRequest) getPullRequestURL(from string, to string) (string, error) {
branchExistsOnRemote := pr.GitCommand.CheckRemoteBranchExists(from)
if !branchExistsOnRemote {
return "", errors.New(pr.GitCommand.Tr.NoBranchOnRemote)
}
repoURL := pr.GitCommand.GetRemoteURL()
var gitService *Service
for _, service := range pr.getServices() {
if strings.Contains(repoURL, service.Name) {
gitService = service
break
}
}
if gitService == nil {
return "", errors.New(pr.GitCommand.Tr.UnsupportedGitService)
}
pullRequestURL := gitService.PullRequestURL(repoURL, from, to)
return pullRequestURL, nil
}
func (s *Service) getRepoInfoFromURL(url string) *RepoInformation {
for _, regexStr := range s.URLRegexStrings {
re := regexp.MustCompile(regexStr)
matches := utils.FindNamedMatches(re, url)
if matches != nil {
return &RepoInformation{
Owner: matches["owner"],
Repository: matches["repo"],
}
}
}
return nil
}