vcsurl.go
func Parse(spec string) (info *RepoInfo, err error) {
	if parts := gitPreprocessRE.FindStringSubmatch(spec); len(parts) == 3 {
		spec = fmt.Sprintf("git://%s/%s", parts[1], parts[2])
	}

	var parsedURL *url.URL
	if parsedURL, err = url.Parse(spec); err == nil {
		if parsedURL.Scheme == "" {
			spec = "https://" + spec
			if parsedURL, err = url.Parse(spec); err != nil {
				return nil, err
			}
		}

		info = new(RepoInfo)

		info.CloneURL = parsedURL.String()
		info.RepoHost = RepoHost(parsedURL.Host)
		info.Rev = parsedURL.Fragment

		if info.RepoHost == GitHub || parsedURL.Scheme == "git" {
			info.VCS = Git
		} else if info.RepoHost == GoogleCode && parsedURL.Scheme == "https" {
			info.VCS = Mercurial
		} else if info.RepoHost == Bitbucket && (parsedURL.Scheme == "https" || parsedURL.Scheme == "http") {
			if !strings.HasSuffix(parsedURL.Path, ".git") {
				info.VCS = Mercurial
			}
		} else if info.RepoHost == Launchpad {
			info.VCS = Bazaar
		}

		path := parsedURL.Path
		switch info.RepoHost {
		case GitHub:
			parts := strings.Split(path, "/")
			if len(parts) >= 3 {
				info.Username = parts[1]
				parts[2] = removeDotGit.ReplaceAllLiteralString(parts[2], "")
				info.Name = parts[2]
				info.FullName = parts[1] + "/" + parts[2]
				info.CloneURL = "git://github.com/" + info.FullName + ".git"
			}
		case GoogleCode:
			parts := strings.Split(path, "/")
			if len(parts) >= 3 {
				if parts[1] == "p" {
					info.Name = parts[2]
					info.FullName = info.Name
					info.CloneURL = "https://code.google.com/p/" + info.FullName
				}
			}
		case PythonOrg:
			parts := strings.Split(path, "/")
			if len(parts) >= 2 {
				info.CloneURL = "http://hg.python.org" + path
				info.VCS = Mercurial
				info.Name = parts[len(parts)-1]
				info.FullName = strings.Join(parts[1:], "/")
			}
		case Bitbucket:
			parts := strings.Split(path, "/")
			if len(parts) >= 3 {
				info.Username = parts[1]
				if strings.HasSuffix(parts[2], ".git") {
					info.VCS = Git
					parts[2] = strings.TrimSuffix(parts[2], ".git")
				}
				info.Name = parts[2]
				info.FullName = parts[1] + "/" + parts[2]
				info.CloneURL = "https://bitbucket.org/" + info.FullName
				if info.VCS == Git {
					info.CloneURL += ".git"
				}
			}
		default:
			if len(path) == 0 {
				return nil, fmt.Errorf("empty path in repo spec: %q", spec)
			}
			path = path[1:] // remove leading slash
			path = removeDotGit.ReplaceAllLiteralString(path, "")
			info.FullName = path
			info.Name = filepath.Base(path)
			if strings.Contains(spec, "git") {
				info.VCS = Git
			} else if strings.Contains(spec, "hg") || strings.Contains(spec, "mercurial") {
				info.VCS = Mercurial
			}
		}

		if info.RepoHost == Launchpad {
			parsedURL.Scheme = "bzr"
			info.CloneURL = parsedURL.String()
		}

		if info.Name == "" || info.FullName == "" {
			return nil, fmt.Errorf("unable to determine name or full name for repo spec %q", spec)
		}

		return info, nil
	}
	return nil, err
}
func Parse(spec string) (info *RepoInfo, err error) {
	if parts := gitPreprocessRE.FindStringSubmatch(spec); len(parts) == 3 {
		spec = fmt.Sprintf("git://%s/%s", parts[1], parts[2])
	}

	var parsedURL *url.URL
	if parsedURL, err = url.Parse(spec); err == nil {
		if parsedURL.Scheme == "" {
			spec = "https://" + spec
			if parsedURL, err = url.Parse(spec); err != nil {
				return nil, err
			}
		}

		info = new(RepoInfo)

		info.CloneURL = parsedURL.String()
		info.RepoHost = RepoHost(parsedURL.Host)
		info.Rev = parsedURL.Fragment

		if info.RepoHost == GitHub || parsedURL.Scheme == "git" {
			info.VCS = Git
		} else if info.RepoHost == GoogleCode && parsedURL.Scheme == "https" {
			info.VCS = Mercurial
		} else if info.RepoHost == Bitbucket && (parsedURL.Scheme == "https" || parsedURL.Scheme == "http") {
			if !strings.HasSuffix(parsedURL.Path, ".git") {
				info.VCS = Mercurial
			}
		} else if info.RepoHost == Launchpad {
			info.VCS = Bazaar
		}

		path := parsedURL.Path
		switch info.RepoHost {
		case GitHub:
			parts := strings.Split(path, "/")
			if len(parts) >= 3 {
				info.Username = parts[1]
				parts[2] = removeDotGit.ReplaceAllLiteralString(parts[2], "")
				info.Name = parts[2]
				info.FullName = parts[1] + "/" + parts[2]
				info.CloneURL = "git://github.com/" + info.FullName + ".git"
			}
		case GoogleCode:
			parts := strings.Split(path, "/")
			if len(parts) >= 3 {
				if parts[1] == "p" {
					info.Name = parts[2]
					info.FullName = info.Name
					info.CloneURL = "https://code.google.com/p/" + info.FullName
				}
			}
		case PythonOrg:
			parts := strings.Split(path, "/")
			if len(parts) >= 2 {
				info.CloneURL = "http://hg.python.org" + path
				info.VCS = Mercurial
				info.Name = parts[len(parts)-1]
				info.FullName = strings.Join(parts[1:], "/")
			}
		case Bitbucket:
			parts := strings.Split(path, "/")
			if len(parts) >= 3 {
				info.Username = parts[1]
				if strings.HasSuffix(parts[2], ".git") {
					info.VCS = Git
					parts[2] = strings.TrimSuffix(parts[2], ".git")
				}
				info.Name = parts[2]
				info.FullName = parts[1] + "/" + parts[2]
				info.CloneURL = "https://bitbucket.org/" + info.FullName
				if info.VCS == Git {
					info.CloneURL += ".git"
				}
			}
		default:
			if len(path) == 0 {
				return nil, fmt.Errorf("empty path in repo spec: %q", spec)
			}
			path = path[1:] // remove leading slash
			path = removeDotGit.ReplaceAllLiteralString(path, "")
			info.FullName = path
			info.Name = filepath.Base(path)
			if strings.Contains(spec, "git") {
				info.VCS = Git
			} else if strings.Contains(spec, "hg") || strings.Contains(spec, "mercurial") {
				info.VCS = Mercurial
			}
		}

		if info.RepoHost == Launchpad {
			parsedURL.Scheme = "bzr"
			info.CloneURL = parsedURL.String()
		}

		if info.Name == "" || info.FullName == "" {
			return nil, fmt.Errorf("unable to determine name or full name for repo spec %q", spec)
		}

		return info, nil
	}
	return nil, err
}
// Parses a string that resembles a VCS repository URL. See TestParse for a list of supported URL
// formats.