提交 209d5b0d 编写于 作者: M Madhusudan.C.S 提交者: Madhusudan.C.S

Parse and store both fetch and push URLs from git remotes.

It is a common practice to set the push URL of the "upstream" repository to an
invalid URL to avoid accidentally pushing to upstream instead of a fork. The
trouble with the current approach of obtaining remote URLs is that, the URL
obtained for a remote might not be the valid one, even though a valid remote
fetch URL exists. For example, git remote -v might return the following output

~$ git remote -v

origin      https://github.com/owner/project.git (fetch)
origin      https://github.com/owner/project.git (push)
upstream    https://github.com/main/project.git (fetch)
upstream    no_push (push)

But github.Remotes() currently parses both these URLs but stores only one of
them, the one that comes the last in git remote -v output, for a given remote.
So in this example, the URL for upstream remote might end up to be "no_push"
instead of https://github.com/main/project.git. This causes problems
downstream. For example, pull-request can't proceed with non github.com URLs
and the command fails even when a valid fetch URL for upstream is defined.

This commit fixes the problem by parsing and storing both fetch and push URLs
for each remote. So later, wherever the URLs are required, we can either
look at fetch URL or push URL or both depending on what is required.

Fixes one part of issue #876. Still doesn't recognize "git+ssh" protocol
scheme.
上级 90a4f3d7
package github
import (
"fmt"
"net/url"
"os"
"strings"
......@@ -13,6 +15,14 @@ var (
type GitHubHosts []string
type GithubHostError struct {
url *url.URL
}
func (e *GithubHostError) Error() string {
return fmt.Sprintf("Invalid GitHub URL: %s", e.url)
}
func (h GitHubHosts) Include(host string) bool {
for _, hh := range h {
if hh == host {
......
......@@ -149,6 +149,10 @@ func (r *GitHubRepo) RemoteBranchAndProject(owner string, preferUpstream bool) (
}
func (r *GitHubRepo) OriginRemote() (remote *Remote, err error) {
return r.RemoteByName("origin")
}
func (r *GitHubRepo) MainRemote() (remote *Remote, err error) {
r.loadRemotes()
if len(r.remotes) > 0 {
......@@ -163,7 +167,7 @@ func (r *GitHubRepo) OriginRemote() (remote *Remote, err error) {
}
func (r *GitHubRepo) MainProject() (project *Project, err error) {
origin, err := r.OriginRemote()
origin, err := r.MainRemote()
if err != nil {
err = fmt.Errorf("Aborted: the origin remote doesn't point to a GitHub repository.")
......
......@@ -104,7 +104,7 @@ func preferredProtocol() string {
func NewProjectFromURL(url *url.URL) (p *Project, err error) {
if !knownGitHubHosts().Include(url.Host) {
err = fmt.Errorf("Invalid GitHub URL: %s", url)
err = &GithubHostError{url}
return
}
......
......@@ -16,6 +16,7 @@ var (
type Remote struct {
Name string
URL *url.URL
PushURL *url.URL
}
func (remote *Remote) String() string {
......@@ -23,7 +24,11 @@ func (remote *Remote) String() string {
}
func (remote *Remote) Project() (*Project, error) {
return NewProjectFromURL(remote.URL)
p, err := NewProjectFromURL(remote.URL)
if _, ok := err.(*GithubHostError); ok {
return NewProjectFromURL(remote.PushURL)
}
return p, err
}
func Remotes() (remotes []Remote, err error) {
......@@ -36,13 +41,19 @@ func Remotes() (remotes []Remote, err error) {
}
// build the remotes map
remotesMap := make(map[string]string)
remotesMap := make(map[string]map[string]string)
for _, r := range rs {
if re.MatchString(r) {
match := re.FindStringSubmatch(r)
name := strings.TrimSpace(match[1])
url := strings.TrimSpace(match[2])
remotesMap[name] = url
urlType := strings.TrimSpace(match[3])
utm, ok := remotesMap[name]
if !ok {
utm = make(map[string]string)
remotesMap[name] = utm
}
utm[urlType] = url
}
}
......@@ -50,9 +61,9 @@ func Remotes() (remotes []Remote, err error) {
names := OriginNamesInLookupOrder
for _, name := range names {
if u, ok := remotesMap[name]; ok {
url, e := git.ParseURL(u)
if e == nil {
remotes = append(remotes, Remote{Name: name, URL: url})
r, err := newRemote(name, u)
if err == nil {
remotes = append(remotes, r)
delete(remotesMap, name)
}
}
......@@ -60,11 +71,31 @@ func Remotes() (remotes []Remote, err error) {
// the rest of the remotes
for n, u := range remotesMap {
url, e := git.ParseURL(u)
if e == nil {
remotes = append(remotes, Remote{Name: n, URL: url})
r, err := newRemote(n, u)
if err == nil {
remotes = append(remotes, r)
}
}
return
}
func newRemote(name string, urlMap map[string]string) (Remote, error) {
r := Remote{}
fetchURL, ferr := git.ParseURL(urlMap["fetch"])
pushURL, perr := git.ParseURL(urlMap["push"])
if ferr != nil && perr != nil {
return r, fmt.Errorf("No valid remote URLs")
}
r.Name = name
if ferr == nil {
r.URL = fetchURL
}
if perr == nil {
r.PushURL = pushURL
}
return r, nil
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册