diff options
Diffstat (limited to 'src/cmd/go/vcs.go')
-rw-r--r-- | src/cmd/go/vcs.go | 103 |
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, }, } |