// A Cmd describes how to use a version control system
// like Mercurial, Git, or Subversion.
type Cmd struct {
- Name string
- Cmd string // name of binary to invoke command
+ Name string
+ Cmd string // name of binary to invoke command
+ RootNames []string // filename indicating the root of a checkout directory
CreateCmd []string // commands to download a fresh copy of a repository
DownloadCmd []string // commands to download updates into an existing repository
// vcsHg describes how to use Mercurial.
var vcsHg = &Cmd{
- Name: "Mercurial",
- Cmd: "hg",
+ Name: "Mercurial",
+ Cmd: "hg",
+ RootNames: []string{".hg"},
CreateCmd: []string{"clone -U -- {repo} {dir}"},
DownloadCmd: []string{"pull"},
// vcsGit describes how to use Git.
var vcsGit = &Cmd{
- Name: "Git",
- Cmd: "git",
+ Name: "Git",
+ Cmd: "git",
+ RootNames: []string{".git"},
CreateCmd: []string{"clone -- {repo} {dir}", "-go-internal-cd {dir} submodule update --init --recursive"},
DownloadCmd: []string{"pull --ff-only", "submodule update --init --recursive"},
// vcsBzr describes how to use Bazaar.
var vcsBzr = &Cmd{
- Name: "Bazaar",
- Cmd: "bzr",
+ Name: "Bazaar",
+ Cmd: "bzr",
+ RootNames: []string{".bzr"},
CreateCmd: []string{"branch -- {repo} {dir}"},
// vcsSvn describes how to use Subversion.
var vcsSvn = &Cmd{
- Name: "Subversion",
- Cmd: "svn",
+ Name: "Subversion",
+ Cmd: "svn",
+ RootNames: []string{".svn"},
CreateCmd: []string{"checkout -- {repo} {dir}"},
DownloadCmd: []string{"update"},
// vcsFossil describes how to use Fossil (fossil-scm.org)
var vcsFossil = &Cmd{
- Name: "Fossil",
- Cmd: "fossil",
+ Name: "Fossil",
+ Cmd: "fossil",
+ RootNames: []string{".fslckout", "_FOSSIL_"},
CreateCmd: []string{"-go-internal-mkdir {dir} clone -- {repo} " + filepath.Join("{dir}", fossilRepoName), "-go-internal-cd {dir} open .fossil"},
DownloadCmd: []string{"up"},
origDir := dir
for len(dir) > len(srcRoot) {
for _, vcs := range vcsList {
- if _, err := os.Stat(filepath.Join(dir, "."+vcs.Cmd)); err == nil {
+ if _, err := statAny(dir, vcs.RootNames); err == nil {
// Record first VCS we find.
// If allowNesting is false (as it is in GOPATH), keep looking for
// repositories in parent directories and report an error if one is
return repoDir, vcsCmd, nil
}
+// statAny provides FileInfo for the first filename found in the directory.
+// Otherwise, it returns the last error seen.
+func statAny(dir string, filenames []string) (os.FileInfo, error) {
+ if len(filenames) == 0 {
+ return nil, errors.New("invalid argument: no filenames provided")
+ }
+
+ var err error
+ var fi os.FileInfo
+ for _, name := range filenames {
+ fi, err = os.Stat(filepath.Join(dir, name))
+ if err == nil {
+ return fi, nil
+ }
+ }
+
+ return nil, err
+}
+
type vcsNotFoundError struct {
dir string
}
otherDir := dir
for len(otherDir) > len(srcRoot) {
for _, otherVCS := range vcsList {
- if _, err := os.Stat(filepath.Join(otherDir, "."+otherVCS.Cmd)); err == nil {
+ if _, err := statAny(otherDir, otherVCS.RootNames); err == nil {
// Allow expected vcs in original dir.
if otherDir == dir && otherVCS == vcs {
continue
import (
"errors"
+ "fmt"
"internal/testenv"
"os"
"path/filepath"
defer os.RemoveAll(tempDir)
for j, vcs := range vcsList {
- dir := filepath.Join(tempDir, "example.com", vcs.Name, "."+vcs.Cmd)
- if j&1 == 0 {
- err := os.MkdirAll(dir, 0755)
- if err != nil {
- t.Fatal(err)
+ for r, rootName := range vcs.RootNames {
+ vcsName := fmt.Sprint(vcs.Name, r)
+ dir := filepath.Join(tempDir, "example.com", vcsName, rootName)
+ if j&1 == 0 {
+ err := os.MkdirAll(dir, 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ } else {
+ err := os.MkdirAll(filepath.Dir(dir), 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ f, err := os.Create(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
}
- } else {
- err := os.MkdirAll(filepath.Dir(dir), 0755)
+
+ wantRepoDir := filepath.Dir(dir)
+ gotRepoDir, gotVCS, err := FromDir(dir, tempDir, false)
if err != nil {
- t.Fatal(err)
+ t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
+ continue
}
- f, err := os.Create(dir)
- if err != nil {
- t.Fatal(err)
+ if gotRepoDir != wantRepoDir || gotVCS.Name != vcs.Name {
+ t.Errorf("FromDir(%q, %q) = RepoDir(%s), VCS(%s); want RepoDir(%s), VCS(%s)", dir, tempDir, gotRepoDir, gotVCS.Name, wantRepoDir, vcs.Name)
}
- f.Close()
- }
-
- wantRepoDir := filepath.Dir(dir)
- gotRepoDir, gotVCS, err := FromDir(dir, tempDir, false)
- if err != nil {
- t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
- continue
- }
- if gotRepoDir != wantRepoDir || gotVCS.Name != vcs.Name {
- t.Errorf("FromDir(%q, %q) = RepoDir(%s), VCS(%s); want RepoDir(%s), VCS(%s)", dir, tempDir, gotRepoDir, gotVCS.Name, wantRepoDir, vcs.Name)
}
}
}