summaryrefslogtreecommitdiff
path: root/src/cmd/go/vcs.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/vcs.go')
-rw-r--r--src/cmd/go/vcs.go103
1 files changed, 84 insertions, 19 deletions
diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go
index da35048d5..2a7bdd034 100644
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -23,9 +23,13 @@ type vcsCmd struct {
createCmd string // command to download a fresh copy of a repository
downloadCmd string // command to download updates into an existing repository
- tagCmd []tagCmd // commands to list tags
- tagDefault string // default tag to use
- tagSyncCmd string // command to sync to specific tag
+ tagCmd []tagCmd // commands to list tags
+ tagLookupCmd []tagCmd // commands to lookup tags before running tagSyncCmd
+ tagSyncCmd string // command to sync to specific tag
+ tagSyncDefault string // command to sync to default tag
+
+ scheme []string
+ pingCmd string
}
// A tagCmd describes a command to list available tags
@@ -71,8 +75,11 @@ var vcsHg = &vcsCmd{
{"tags", `^(\S+)`},
{"branches", `^(\S+)`},
},
- tagDefault: "default",
- tagSyncCmd: "update -r {tag}",
+ tagSyncCmd: "update -r {tag}",
+ tagSyncDefault: "update default",
+
+ scheme: []string{"https", "http"},
+ pingCmd: "identify {scheme}://{repo}",
}
// vcsGit describes how to use Git.
@@ -83,9 +90,19 @@ var vcsGit = &vcsCmd{
createCmd: "clone {repo} {dir}",
downloadCmd: "fetch",
- tagCmd: []tagCmd{{"tag", `^(\S+)$`}},
- tagDefault: "master",
- tagSyncCmd: "checkout {tag}",
+ tagCmd: []tagCmd{
+ // tags/xxx matches a git tag named xxx
+ // origin/xxx matches a git branch named xxx on the default remote repository
+ {"show-ref", `(?:tags|origin)/(\S+)$`},
+ },
+ tagLookupCmd: []tagCmd{
+ {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`},
+ },
+ tagSyncCmd: "checkout {tag}",
+ tagSyncDefault: "checkout origin/master",
+
+ scheme: []string{"git", "https", "http"},
+ pingCmd: "ls-remote {scheme}://{repo}",
}
// vcsBzr describes how to use Bazaar.
@@ -99,9 +116,12 @@ var vcsBzr = &vcsCmd{
// Replace by --overwrite-tags after http://pad.lv/681792 goes in.
downloadCmd: "pull --overwrite",
- tagCmd: []tagCmd{{"tags", `^(\S+)`}},
- tagDefault: "revno:-1",
- tagSyncCmd: "update -r {tag}",
+ tagCmd: []tagCmd{{"tags", `^(\S+)`}},
+ tagSyncCmd: "update -r {tag}",
+ tagSyncDefault: "update -r revno:-1",
+
+ scheme: []string{"https", "http", "bzr"},
+ pingCmd: "info {scheme}://{repo}",
}
// vcsSvn describes how to use Subversion.
@@ -114,6 +134,9 @@ var vcsSvn = &vcsCmd{
// There is no tag command in subversion.
// The branch information is all in the path names.
+
+ scheme: []string{"https", "http", "svn"},
+ pingCmd: "info {scheme}://{repo}",
}
func (v *vcsCmd) String() string {
@@ -128,17 +151,23 @@ func (v *vcsCmd) String() string {
// command's combined stdout+stderr to standard error.
// Otherwise run discards the command's output.
func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
- _, err := v.run1(dir, false, cmd, keyval)
+ _, err := v.run1(dir, cmd, keyval, true)
+ return err
+}
+
+// runVerboseOnly is like run but only generates error output to standard error in verbose mode.
+func (v *vcsCmd) runVerboseOnly(dir string, cmd string, keyval ...string) error {
+ _, err := v.run1(dir, cmd, keyval, false)
return err
}
// runOutput is like run but returns the output of the command.
func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
- return v.run1(dir, true, cmd, keyval)
+ return v.run1(dir, cmd, keyval, true)
}
// run1 is the generalized implementation of run and runOutput.
-func (v *vcsCmd) run1(dir string, output bool, cmdline string, keyval []string) ([]byte, error) {
+func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) {
m := make(map[string]string)
for i := 0; i < len(keyval); i += 2 {
m[keyval[i]] = keyval[i+1]
@@ -157,16 +186,23 @@ func (v *vcsCmd) run1(dir string, output bool, cmdline string, keyval []string)
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = &buf
- out := buf.Bytes()
err := cmd.Run()
+ out := buf.Bytes()
if err != nil {
- fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
- os.Stderr.Write(out)
+ if verbose || buildV {
+ fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
+ os.Stderr.Write(out)
+ }
return nil, err
}
return out, nil
}
+// ping pings to determine scheme to use.
+func (v *vcsCmd) ping(scheme, repo string) error {
+ return v.runVerboseOnly(".", v.pingCmd, "scheme", scheme, "repo", repo)
+}
+
// create creates a new copy of repo in dir.
// The parent of dir must exist; dir must not.
func (v *vcsCmd) create(dir, repo string) error {
@@ -187,7 +223,9 @@ func (v *vcsCmd) tags(dir string) ([]string, error) {
return nil, err
}
re := regexp.MustCompile(`(?m-s)` + tc.pattern)
- tags = append(tags, re.FindAllString(string(out), -1)...)
+ for _, m := range re.FindAllStringSubmatch(string(out), -1) {
+ tags = append(tags, m[1])
+ }
}
return tags, nil
}
@@ -198,6 +236,23 @@ func (v *vcsCmd) tagSync(dir, tag string) error {
if v.tagSyncCmd == "" {
return nil
}
+ if tag != "" {
+ for _, tc := range v.tagLookupCmd {
+ out, err := v.runOutput(dir, tc.cmd, "tag", tag)
+ if err != nil {
+ return err
+ }
+ re := regexp.MustCompile(`(?m-s)` + tc.pattern)
+ m := re.FindStringSubmatch(string(out))
+ if len(m) > 1 {
+ tag = m[1]
+ break
+ }
+ }
+ }
+ if tag == "" && v.tagSyncDefault != "" {
+ return v.run(dir, v.tagSyncDefault)
+ }
return v.run(dir, v.tagSyncCmd, "tag", tag)
}
@@ -209,6 +264,7 @@ type vcsPath struct {
repo string // repository to use (expand with match of re)
vcs string // version control system to use (expand with match of re)
check func(match map[string]string) error // additional checks
+ ping bool // ping for scheme to use to download repo
regexp *regexp.Regexp // cached compiled form of re
}
@@ -256,6 +312,14 @@ func vcsForImportPath(importPath string) (vcs *vcsCmd, repo, root string, err er
if vcs == nil {
return nil, "", "", fmt.Errorf("unknown version control system %q", match["vcs"])
}
+ if srv.ping {
+ for _, scheme := range vcs.scheme {
+ if vcs.ping(scheme, match["repo"]) == nil {
+ match["repo"] = scheme + "://" + match["repo"]
+ break
+ }
+ }
+ }
return vcs, match["repo"], match["root"], nil
}
return nil, "", "", fmt.Errorf("unrecognized import path %q", importPath)
@@ -313,7 +377,8 @@ var vcsPaths = []*vcsPath{
// General syntax for any server.
{
- re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
+ re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
+ ping: true,
},
}