1
0
mirror of https://github.com/golang/go synced 2024-11-23 03:40:02 -07:00

cmd/go: redirect vcs-test.golang.org repo URLs to a test-local server

The new server reconstructs the vcs-test repos on the fly using
scripts that run the actual version-control binaries.

This allows those repos to be code-reviewed using our normal tools —
and, crucially, allows contributors to add new vcs-test contents
as part of a contributed CL.

It also prevents failures due to network errors reaching
vcs-test.golang.org (such as when developing offline), and allows us
to iterate on the repo contents without dealing with annoying and
unpredictable GCS caching behavior.

We can't quite turn down vcs-test.golang.org yet — this server doesn't
yet handle "go-import" metadata (and related authentication behaviors),
and doesn't serve Subversion repos.

But we're getting much closer!

For #27494.

Change-Id: I233fc718617aed287b0f7248bd8cfe1e5cebe96b
Reviewed-on: https://go-review.googlesource.com/c/go/+/421455
Run-TryBot: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Bryan C. Mills 2022-07-30 02:54:32 -04:00 committed by Gopher Robot
parent 55eaae452c
commit d5155f6192
49 changed files with 3645 additions and 462 deletions

View File

@ -33,6 +33,8 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/robustio"
"cmd/go/internal/search"
"cmd/go/internal/vcs"
"cmd/go/internal/vcweb/vcstest"
"cmd/go/internal/work"
"cmd/internal/sys"
@ -129,6 +131,12 @@ func TestMain(m *testing.M) {
return fmt.Errorf("%stestgo must not write to GOROOT (installing to %s)", callerPos, filepath.Join("GOROOT", rel))
}
}
if vcsTest := os.Getenv("TESTGO_VCSTEST_URL"); vcsTest != "" {
vcs.VCSTestRepoURL = vcsTest
vcs.VCSTestHosts = vcstest.Hosts
}
cmdgo.Main()
os.Exit(0)
}

View File

@ -495,21 +495,21 @@ func downloadPackage(p *load.Package) error {
vcsCmd, repo, rootPath = rr.VCS, rr.Repo, rr.Root
}
if !blindRepo && !vcsCmd.IsSecure(repo) && security != web.Insecure {
return fmt.Errorf("cannot download, %v uses insecure protocol", repo)
return fmt.Errorf("cannot download: %v uses insecure protocol", repo)
}
if p.Internal.Build.SrcRoot == "" {
// Package not found. Put in first directory of $GOPATH.
list := filepath.SplitList(cfg.BuildContext.GOPATH)
if len(list) == 0 {
return fmt.Errorf("cannot download, $GOPATH not set. For more details see: 'go help gopath'")
return fmt.Errorf("cannot download: $GOPATH not set. For more details see: 'go help gopath'")
}
// Guard against people setting GOPATH=$GOROOT.
if filepath.Clean(list[0]) == filepath.Clean(cfg.GOROOT) {
return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'")
return fmt.Errorf("cannot download: $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'")
}
if _, err := os.Stat(filepath.Join(list[0], "src/cmd/go/alldocs.go")); err == nil {
return fmt.Errorf("cannot download, %s is a GOROOT, not a GOPATH. For more details see: 'go help gopath'", list[0])
return fmt.Errorf("cannot download: %s is a GOROOT, not a GOPATH. For more details see: 'go help gopath'", list[0])
}
p.Internal.Build.Root = list[0]
p.Internal.Build.SrcRoot = filepath.Join(list[0], "src")

View File

@ -7,6 +7,8 @@ package codehost
import (
"archive/zip"
"bytes"
"cmd/go/internal/cfg"
"cmd/go/internal/vcweb/vcstest"
"flag"
"internal/testenv"
"io"
@ -26,17 +28,18 @@ func TestMain(m *testing.M) {
// needed for initializing the test environment variables as testing.Short
// and HasExternalNetwork
flag.Parse()
os.Exit(testMain(m))
if err := testMain(m); err != nil {
log.Fatal(err)
}
}
const (
gitrepo1 = "https://vcs-test.golang.org/git/gitrepo1"
hgrepo1 = "https://vcs-test.golang.org/hg/hgrepo1"
)
var gitrepo1, hgrepo1 string
var altRepos = []string{
"localGitRepo",
hgrepo1,
var altRepos = func() []string {
return []string{
"localGitRepo",
hgrepo1,
}
}
// TODO: Convert gitrepo1 to svn, bzr, fossil and add tests.
@ -45,14 +48,38 @@ var altRepos = []string{
// localGitRepo is like gitrepo1 but allows archive access.
var localGitRepo, localGitURL string
func testMain(m *testing.M) int {
func testMain(m *testing.M) (err error) {
cfg.BuildX = true
srv, err := vcstest.NewServer()
if err != nil {
return err
}
defer func() {
if closeErr := srv.Close(); err == nil {
err = closeErr
}
}()
gitrepo1 = srv.HTTP.URL + "/git/gitrepo1"
hgrepo1 = srv.HTTP.URL + "/hg/hgrepo1"
dir, err := os.MkdirTemp("", "gitrepo-test-")
if err != nil {
log.Fatal(err)
return err
}
defer os.RemoveAll(dir)
defer func() {
if rmErr := os.RemoveAll(dir); err == nil {
err = rmErr
}
}()
if testenv.HasExternalNetwork() && testenv.HasExec() {
// Redirect the module cache to a fresh directory to avoid crosstalk, and make
// it read/write so that the test can still clean it up easily when done.
cfg.GOMODCACHE = filepath.Join(dir, "modcache")
cfg.ModCacheRW = true
if !testing.Short() && testenv.HasExec() {
if _, err := exec.LookPath("git"); err == nil {
// Clone gitrepo1 into a local directory.
// If we use a file:// URL to access the local directory,
@ -60,10 +87,10 @@ func testMain(m *testing.M) int {
// which will let us test remote git archive invocations.
localGitRepo = filepath.Join(dir, "gitrepo2")
if _, err := Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil {
log.Fatal(err)
return err
}
if _, err := Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil {
log.Fatal(err)
return err
}
// Convert absolute path to file URL. LocalGitRepo will not accept
@ -77,7 +104,8 @@ func testMain(m *testing.M) int {
}
}
return m.Run()
m.Run()
return nil
}
func testRepo(t *testing.T, remote string) (Repo, error) {
@ -85,49 +113,31 @@ func testRepo(t *testing.T, remote string) (Repo, error) {
testenv.MustHaveExecPath(t, "git")
return LocalGitRepo(localGitURL)
}
vcs := "git"
vcsName := "git"
for _, k := range []string{"hg"} {
if strings.Contains(remote, "/"+k+"/") {
vcs = k
vcsName = k
}
}
testenv.MustHaveExecPath(t, vcs)
return NewRepo(vcs, remote)
}
var tagsTests = []struct {
repo string
prefix string
tags []Tag
}{
{gitrepo1, "xxx", []Tag{}},
{gitrepo1, "", []Tag{
{"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
{"v2.0.1", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
{"v2.0.2", "9d02800338b8a55be062c838d1f02e0c5780b9eb"},
{"v2.3", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
}},
{gitrepo1, "v", []Tag{
{"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
{"v2.0.1", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
{"v2.0.2", "9d02800338b8a55be062c838d1f02e0c5780b9eb"},
{"v2.3", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
}},
{gitrepo1, "v1", []Tag{
{"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
}},
{gitrepo1, "2", []Tag{}},
testenv.MustHaveExecPath(t, vcsName)
return NewRepo(vcsName, remote)
}
func TestTags(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExec(t)
t.Parallel()
type tagsTest struct {
repo string
prefix string
tags []Tag
}
runTest := func(tt tagsTest) func(*testing.T) {
return func(t *testing.T) {
t.Parallel()
for _, tt := range tagsTests {
f := func(t *testing.T) {
r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
@ -140,7 +150,31 @@ func TestTags(t *testing.T) {
t.Errorf("Tags(%q): incorrect tags\nhave %v\nwant %v", tt.prefix, tags, tt.tags)
}
}
t.Run(path.Base(tt.repo)+"/"+tt.prefix, f)
}
for _, tt := range []tagsTest{
{gitrepo1, "xxx", []Tag{}},
{gitrepo1, "", []Tag{
{"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
{"v2.0.1", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
{"v2.0.2", "9d02800338b8a55be062c838d1f02e0c5780b9eb"},
{"v2.3", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
}},
{gitrepo1, "v", []Tag{
{"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
{"v2.0.1", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
{"v2.0.2", "9d02800338b8a55be062c838d1f02e0c5780b9eb"},
{"v2.3", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
}},
{gitrepo1, "v1", []Tag{
{"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
}},
{gitrepo1, "2", []Tag{}},
} {
t.Run(path.Base(tt.repo)+"/"+tt.prefix, runTest(tt))
if tt.repo == gitrepo1 {
// Clear hashes.
clearTags := []Tag{}
@ -148,60 +182,31 @@ func TestTags(t *testing.T) {
clearTags = append(clearTags, Tag{tag.Name, ""})
}
tags := tt.tags
for _, tt.repo = range altRepos {
for _, tt.repo = range altRepos() {
if strings.Contains(tt.repo, "Git") {
tt.tags = tags
} else {
tt.tags = clearTags
}
t.Run(path.Base(tt.repo)+"/"+tt.prefix, f)
t.Run(path.Base(tt.repo)+"/"+tt.prefix, runTest(tt))
}
}
}
}
var latestTests = []struct {
repo string
info *RevInfo
}{
{
gitrepo1,
&RevInfo{
Origin: &Origin{
VCS: "git",
URL: "https://vcs-test.golang.org/git/gitrepo1",
Ref: "HEAD",
Hash: "ede458df7cd0fdca520df19a33158086a8a68e81",
},
Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
Short: "ede458df7cd0",
Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
Tags: []string{"v1.2.3", "v1.2.4-annotated"},
},
},
{
hgrepo1,
&RevInfo{
Origin: &Origin{
VCS: "hg",
URL: "https://vcs-test.golang.org/hg/hgrepo1",
Hash: "18518c07eb8ed5c80221e997e518cccaa8c0c287",
},
Name: "18518c07eb8ed5c80221e997e518cccaa8c0c287",
Short: "18518c07eb8e",
Version: "18518c07eb8ed5c80221e997e518cccaa8c0c287",
Time: time.Date(2018, 6, 27, 16, 16, 30, 0, time.UTC),
},
},
}
func TestLatest(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExec(t)
t.Parallel()
type latestTest struct {
repo string
info *RevInfo
}
runTest := func(tt latestTest) func(*testing.T) {
return func(t *testing.T) {
t.Parallel()
for _, tt := range latestTests {
f := func(t *testing.T) {
r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
@ -214,7 +219,41 @@ func TestLatest(t *testing.T) {
t.Errorf("Latest: incorrect info\nhave %+v (origin %+v)\nwant %+v (origin %+v)", info, info.Origin, tt.info, tt.info.Origin)
}
}
t.Run(path.Base(tt.repo), f)
}
for _, tt := range []latestTest{
{
gitrepo1,
&RevInfo{
Origin: &Origin{
VCS: "git",
URL: gitrepo1,
Ref: "HEAD",
Hash: "ede458df7cd0fdca520df19a33158086a8a68e81",
},
Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
Short: "ede458df7cd0",
Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
Tags: []string{"v1.2.3", "v1.2.4-annotated"},
},
},
{
hgrepo1,
&RevInfo{
Origin: &Origin{
VCS: "hg",
URL: hgrepo1,
Hash: "18518c07eb8ed5c80221e997e518cccaa8c0c287",
},
Name: "18518c07eb8ed5c80221e997e518cccaa8c0c287",
Short: "18518c07eb8e",
Version: "18518c07eb8ed5c80221e997e518cccaa8c0c287",
Time: time.Date(2018, 6, 27, 16, 16, 30, 0, time.UTC),
},
},
} {
t.Run(path.Base(tt.repo), runTest(tt))
if tt.repo == gitrepo1 {
tt.repo = "localGitRepo"
info := *tt.info
@ -222,44 +261,27 @@ func TestLatest(t *testing.T) {
o := *info.Origin
info.Origin = &o
o.URL = localGitURL
t.Run(path.Base(tt.repo), f)
t.Run(path.Base(tt.repo), runTest(tt))
}
}
}
var readFileTests = []struct {
repo string
rev string
file string
err string
data string
}{
{
repo: gitrepo1,
rev: "latest",
file: "README",
data: "",
},
{
repo: gitrepo1,
rev: "v2",
file: "another.txt",
data: "another\n",
},
{
repo: gitrepo1,
rev: "v2.3.4",
file: "another.txt",
err: fs.ErrNotExist.Error(),
},
}
func TestReadFile(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExec(t)
t.Parallel()
type readFileTest struct {
repo string
rev string
file string
err string
data string
}
runTest := func(tt readFileTest) func(*testing.T) {
return func(t *testing.T) {
t.Parallel()
for _, tt := range readFileTests {
f := func(t *testing.T) {
r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
@ -284,162 +306,37 @@ func TestReadFile(t *testing.T) {
t.Errorf("ReadFile: incorrect data\nhave %q\nwant %q", data, tt.data)
}
}
t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, f)
}
for _, tt := range []readFileTest{
{
repo: gitrepo1,
rev: "latest",
file: "README",
data: "",
},
{
repo: gitrepo1,
rev: "v2",
file: "another.txt",
data: "another\n",
},
{
repo: gitrepo1,
rev: "v2.3.4",
file: "another.txt",
err: fs.ErrNotExist.Error(),
},
} {
t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, runTest(tt))
if tt.repo == gitrepo1 {
for _, tt.repo = range altRepos {
t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, f)
for _, tt.repo = range altRepos() {
t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, runTest(tt))
}
}
}
}
var readZipTests = []struct {
repo string
rev string
subdir string
err string
files map[string]uint64
}{
{
repo: gitrepo1,
rev: "v2.3.4",
subdir: "",
files: map[string]uint64{
"prefix/": 0,
"prefix/README": 0,
"prefix/v2": 3,
},
},
{
repo: hgrepo1,
rev: "v2.3.4",
subdir: "",
files: map[string]uint64{
"prefix/.hg_archival.txt": ^uint64(0),
"prefix/README": 0,
"prefix/v2": 3,
},
},
{
repo: gitrepo1,
rev: "v2",
subdir: "",
files: map[string]uint64{
"prefix/": 0,
"prefix/README": 0,
"prefix/v2": 3,
"prefix/another.txt": 8,
"prefix/foo.txt": 13,
},
},
{
repo: hgrepo1,
rev: "v2",
subdir: "",
files: map[string]uint64{
"prefix/.hg_archival.txt": ^uint64(0),
"prefix/README": 0,
"prefix/v2": 3,
"prefix/another.txt": 8,
"prefix/foo.txt": 13,
},
},
{
repo: gitrepo1,
rev: "v3",
subdir: "",
files: map[string]uint64{
"prefix/": 0,
"prefix/v3/": 0,
"prefix/v3/sub/": 0,
"prefix/v3/sub/dir/": 0,
"prefix/v3/sub/dir/file.txt": 16,
"prefix/README": 0,
},
},
{
repo: hgrepo1,
rev: "v3",
subdir: "",
files: map[string]uint64{
"prefix/.hg_archival.txt": ^uint64(0),
"prefix/.hgtags": 405,
"prefix/v3/sub/dir/file.txt": 16,
"prefix/README": 0,
},
},
{
repo: gitrepo1,
rev: "v3",
subdir: "v3/sub/dir",
files: map[string]uint64{
"prefix/": 0,
"prefix/v3/": 0,
"prefix/v3/sub/": 0,
"prefix/v3/sub/dir/": 0,
"prefix/v3/sub/dir/file.txt": 16,
},
},
{
repo: hgrepo1,
rev: "v3",
subdir: "v3/sub/dir",
files: map[string]uint64{
"prefix/v3/sub/dir/file.txt": 16,
},
},
{
repo: gitrepo1,
rev: "v3",
subdir: "v3/sub",
files: map[string]uint64{
"prefix/": 0,
"prefix/v3/": 0,
"prefix/v3/sub/": 0,
"prefix/v3/sub/dir/": 0,
"prefix/v3/sub/dir/file.txt": 16,
},
},
{
repo: hgrepo1,
rev: "v3",
subdir: "v3/sub",
files: map[string]uint64{
"prefix/v3/sub/dir/file.txt": 16,
},
},
{
repo: gitrepo1,
rev: "aaaaaaaaab",
subdir: "",
err: "unknown revision",
},
{
repo: hgrepo1,
rev: "aaaaaaaaab",
subdir: "",
err: "unknown revision",
},
{
repo: "https://github.com/rsc/vgotest1",
rev: "submod/v1.0.4",
subdir: "submod",
files: map[string]uint64{
"prefix/": 0,
"prefix/submod/": 0,
"prefix/submod/go.mod": 53,
"prefix/submod/pkg/": 0,
"prefix/submod/pkg/p.go": 31,
},
},
}
type zipFile struct {
name string
size int64
@ -448,9 +345,19 @@ type zipFile struct {
func TestReadZip(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExec(t)
t.Parallel()
type readZipTest struct {
repo string
rev string
subdir string
err string
files map[string]uint64
}
runTest := func(tt readZipTest) func(*testing.T) {
return func(t *testing.T) {
t.Parallel()
for _, tt := range readZipTests {
f := func(t *testing.T) {
r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
@ -498,10 +405,152 @@ func TestReadZip(t *testing.T) {
}
}
}
t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, f)
}
for _, tt := range []readZipTest{
{
repo: gitrepo1,
rev: "v2.3.4",
subdir: "",
files: map[string]uint64{
"prefix/": 0,
"prefix/README": 0,
"prefix/v2": 3,
},
},
{
repo: hgrepo1,
rev: "v2.3.4",
subdir: "",
files: map[string]uint64{
"prefix/.hg_archival.txt": ^uint64(0),
"prefix/README": 0,
"prefix/v2": 3,
},
},
{
repo: gitrepo1,
rev: "v2",
subdir: "",
files: map[string]uint64{
"prefix/": 0,
"prefix/README": 0,
"prefix/v2": 3,
"prefix/another.txt": 8,
"prefix/foo.txt": 13,
},
},
{
repo: hgrepo1,
rev: "v2",
subdir: "",
files: map[string]uint64{
"prefix/.hg_archival.txt": ^uint64(0),
"prefix/README": 0,
"prefix/v2": 3,
"prefix/another.txt": 8,
"prefix/foo.txt": 13,
},
},
{
repo: gitrepo1,
rev: "v3",
subdir: "",
files: map[string]uint64{
"prefix/": 0,
"prefix/v3/": 0,
"prefix/v3/sub/": 0,
"prefix/v3/sub/dir/": 0,
"prefix/v3/sub/dir/file.txt": 16,
"prefix/README": 0,
},
},
{
repo: hgrepo1,
rev: "v3",
subdir: "",
files: map[string]uint64{
"prefix/.hg_archival.txt": ^uint64(0),
"prefix/.hgtags": 405,
"prefix/v3/sub/dir/file.txt": 16,
"prefix/README": 0,
},
},
{
repo: gitrepo1,
rev: "v3",
subdir: "v3/sub/dir",
files: map[string]uint64{
"prefix/": 0,
"prefix/v3/": 0,
"prefix/v3/sub/": 0,
"prefix/v3/sub/dir/": 0,
"prefix/v3/sub/dir/file.txt": 16,
},
},
{
repo: hgrepo1,
rev: "v3",
subdir: "v3/sub/dir",
files: map[string]uint64{
"prefix/v3/sub/dir/file.txt": 16,
},
},
{
repo: gitrepo1,
rev: "v3",
subdir: "v3/sub",
files: map[string]uint64{
"prefix/": 0,
"prefix/v3/": 0,
"prefix/v3/sub/": 0,
"prefix/v3/sub/dir/": 0,
"prefix/v3/sub/dir/file.txt": 16,
},
},
{
repo: hgrepo1,
rev: "v3",
subdir: "v3/sub",
files: map[string]uint64{
"prefix/v3/sub/dir/file.txt": 16,
},
},
{
repo: gitrepo1,
rev: "aaaaaaaaab",
subdir: "",
err: "unknown revision",
},
{
repo: hgrepo1,
rev: "aaaaaaaaab",
subdir: "",
err: "unknown revision",
},
{
repo: "https://github.com/rsc/vgotest1",
rev: "submod/v1.0.4",
subdir: "submod",
files: map[string]uint64{
"prefix/": 0,
"prefix/submod/": 0,
"prefix/submod/go.mod": 53,
"prefix/submod/pkg/": 0,
"prefix/submod/pkg/p.go": 31,
},
},
} {
t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, runTest(tt))
if tt.repo == gitrepo1 {
tt.repo = "localGitRepo"
t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, f)
t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, runTest(tt))
}
}
}
@ -514,112 +563,21 @@ var hgmap = map[string]string{
"97f6aa59c81c623494825b43d39e445566e429a4": "c0cbbfb24c7c3c50c35c7b88e7db777da4ff625d",
}
var statTests = []struct {
repo string
rev string
err string
info *RevInfo
}{
{
repo: gitrepo1,
rev: "HEAD",
info: &RevInfo{
Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
Short: "ede458df7cd0",
Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
Tags: []string{"v1.2.3", "v1.2.4-annotated"},
},
},
{
repo: gitrepo1,
rev: "v2", // branch
info: &RevInfo{
Name: "9d02800338b8a55be062c838d1f02e0c5780b9eb",
Short: "9d02800338b8",
Version: "9d02800338b8a55be062c838d1f02e0c5780b9eb",
Time: time.Date(2018, 4, 17, 20, 00, 32, 0, time.UTC),
Tags: []string{"v2.0.2"},
},
},
{
repo: gitrepo1,
rev: "v2.3.4", // badly-named branch (semver should be a tag)
info: &RevInfo{
Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
Short: "76a00fb249b7",
Version: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
Tags: []string{"v2.0.1", "v2.3"},
},
},
{
repo: gitrepo1,
rev: "v2.3", // badly-named tag (we only respect full semver v2.3.0)
info: &RevInfo{
Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
Short: "76a00fb249b7",
Version: "v2.3",
Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
Tags: []string{"v2.0.1", "v2.3"},
},
},
{
repo: gitrepo1,
rev: "v1.2.3", // tag
info: &RevInfo{
Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
Short: "ede458df7cd0",
Version: "v1.2.3",
Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
Tags: []string{"v1.2.3", "v1.2.4-annotated"},
},
},
{
repo: gitrepo1,
rev: "ede458df", // hash prefix in refs
info: &RevInfo{
Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
Short: "ede458df7cd0",
Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
Tags: []string{"v1.2.3", "v1.2.4-annotated"},
},
},
{
repo: gitrepo1,
rev: "97f6aa59", // hash prefix not in refs
info: &RevInfo{
Name: "97f6aa59c81c623494825b43d39e445566e429a4",
Short: "97f6aa59c81c",
Version: "97f6aa59c81c623494825b43d39e445566e429a4",
Time: time.Date(2018, 4, 17, 20, 0, 19, 0, time.UTC),
},
},
{
repo: gitrepo1,
rev: "v1.2.4-annotated", // annotated tag uses unwrapped commit hash
info: &RevInfo{
Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
Short: "ede458df7cd0",
Version: "v1.2.4-annotated",
Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
Tags: []string{"v1.2.3", "v1.2.4-annotated"},
},
},
{
repo: gitrepo1,
rev: "aaaaaaaaab",
err: "unknown revision",
},
}
func TestStat(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExec(t)
t.Parallel()
type statTest struct {
repo string
rev string
err string
info *RevInfo
}
runTest := func(tt statTest) func(*testing.T) {
return func(t *testing.T) {
t.Parallel()
for _, tt := range statTests {
f := func(t *testing.T) {
r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
@ -642,9 +600,105 @@ func TestStat(t *testing.T) {
t.Errorf("Stat: incorrect info\nhave %+v\nwant %+v", *info, *tt.info)
}
}
t.Run(path.Base(tt.repo)+"/"+tt.rev, f)
}
for _, tt := range []statTest{
{
repo: gitrepo1,
rev: "HEAD",
info: &RevInfo{
Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
Short: "ede458df7cd0",
Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
Tags: []string{"v1.2.3", "v1.2.4-annotated"},
},
},
{
repo: gitrepo1,
rev: "v2", // branch
info: &RevInfo{
Name: "9d02800338b8a55be062c838d1f02e0c5780b9eb",
Short: "9d02800338b8",
Version: "9d02800338b8a55be062c838d1f02e0c5780b9eb",
Time: time.Date(2018, 4, 17, 20, 00, 32, 0, time.UTC),
Tags: []string{"v2.0.2"},
},
},
{
repo: gitrepo1,
rev: "v2.3.4", // badly-named branch (semver should be a tag)
info: &RevInfo{
Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
Short: "76a00fb249b7",
Version: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
Tags: []string{"v2.0.1", "v2.3"},
},
},
{
repo: gitrepo1,
rev: "v2.3", // badly-named tag (we only respect full semver v2.3.0)
info: &RevInfo{
Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
Short: "76a00fb249b7",
Version: "v2.3",
Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
Tags: []string{"v2.0.1", "v2.3"},
},
},
{
repo: gitrepo1,
rev: "v1.2.3", // tag
info: &RevInfo{
Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
Short: "ede458df7cd0",
Version: "v1.2.3",
Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
Tags: []string{"v1.2.3", "v1.2.4-annotated"},
},
},
{
repo: gitrepo1,
rev: "ede458df", // hash prefix in refs
info: &RevInfo{
Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
Short: "ede458df7cd0",
Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
Tags: []string{"v1.2.3", "v1.2.4-annotated"},
},
},
{
repo: gitrepo1,
rev: "97f6aa59", // hash prefix not in refs
info: &RevInfo{
Name: "97f6aa59c81c623494825b43d39e445566e429a4",
Short: "97f6aa59c81c",
Version: "97f6aa59c81c623494825b43d39e445566e429a4",
Time: time.Date(2018, 4, 17, 20, 0, 19, 0, time.UTC),
},
},
{
repo: gitrepo1,
rev: "v1.2.4-annotated", // annotated tag uses unwrapped commit hash
info: &RevInfo{
Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
Short: "ede458df7cd0",
Version: "v1.2.4-annotated",
Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
Tags: []string{"v1.2.3", "v1.2.4-annotated"},
},
},
{
repo: gitrepo1,
rev: "aaaaaaaaab",
err: "unknown revision",
},
} {
t.Run(path.Base(tt.repo)+"/"+tt.rev, runTest(tt))
if tt.repo == gitrepo1 {
for _, tt.repo = range altRepos {
for _, tt.repo = range altRepos() {
old := tt
var m map[string]string
if tt.repo == hgrepo1 {
@ -658,7 +712,7 @@ func TestStat(t *testing.T) {
tt.info.Short = remap(tt.info.Short, m)
}
tt.rev = remap(tt.rev, m)
t.Run(path.Base(tt.repo)+"/"+tt.rev, f)
t.Run(path.Base(tt.repo)+"/"+tt.rev, runTest(tt))
tt = old
}
}

View File

@ -8,11 +8,13 @@ import (
"archive/zip"
"crypto/sha256"
"encoding/hex"
"flag"
"hash"
"internal/testenv"
"io"
"log"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
@ -20,15 +22,20 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/vcweb/vcstest"
"golang.org/x/mod/sumdb/dirhash"
)
func TestMain(m *testing.M) {
os.Exit(testMain(m))
flag.Parse()
if err := testMain(m); err != nil {
log.Fatal(err)
}
}
func testMain(m *testing.M) int {
func testMain(m *testing.M) (err error) {
cfg.GOPROXY = "direct"
// The sum database is populated using a released version of the go command,
@ -39,12 +46,31 @@ func testMain(m *testing.M) int {
dir, err := os.MkdirTemp("", "gitrepo-test-")
if err != nil {
log.Fatal(err)
return err
}
defer os.RemoveAll(dir)
defer func() {
if rmErr := os.RemoveAll(dir); err == nil {
err = rmErr
}
}()
cfg.GOMODCACHE = dir
return m.Run()
cfg.GOMODCACHE = filepath.Join(dir, "modcache")
if err := os.Mkdir(cfg.GOMODCACHE, 0755); err != nil {
return err
}
srv, err := vcstest.NewServer()
if err != nil {
return err
}
defer func() {
if closeErr := srv.Close(); err == nil {
err = closeErr
}
}()
m.Run()
return nil
}
const (

View File

@ -6,6 +6,7 @@ package modload
import (
"context"
"flag"
"internal/testenv"
"log"
"os"
@ -15,27 +16,47 @@ import (
"testing"
"cmd/go/internal/cfg"
"cmd/go/internal/vcweb/vcstest"
"golang.org/x/mod/module"
)
func TestMain(m *testing.M) {
os.Exit(testMain(m))
flag.Parse()
if err := testMain(m); err != nil {
log.Fatal(err)
}
}
func testMain(m *testing.M) int {
func testMain(m *testing.M) (err error) {
cfg.GOPROXY = "direct"
cfg.ModCacheRW = true
srv, err := vcstest.NewServer()
if err != nil {
return err
}
defer func() {
if closeErr := srv.Close(); err == nil {
err = closeErr
}
}()
dir, err := os.MkdirTemp("", "modload-test-")
if err != nil {
log.Fatal(err)
return err
}
defer os.RemoveAll(dir)
defer func() {
if rmErr := os.RemoveAll(dir); err == nil {
err = rmErr
}
}()
os.Setenv("GOPATH", dir)
cfg.BuildContext.GOPATH = dir
cfg.GOMODCACHE = filepath.Join(dir, "pkg/mod")
return m.Run()
m.Run()
return nil
}
var (
@ -55,42 +76,6 @@ var queryTests = []struct {
vers string
err string
}{
/*
git init
echo module vcs-test.golang.org/git/querytest.git >go.mod
git add go.mod
git commit -m v1 go.mod
git tag start
for i in v0.0.0-pre1 v0.0.0 v0.0.1 v0.0.2 v0.0.3 v0.1.0 v0.1.1 v0.1.2 v0.3.0 v1.0.0 v1.1.0 v1.9.0 v1.9.9 v1.9.10-pre1 v1.9.10-pre2+metadata unversioned; do
echo before $i >status
git add status
git commit -m "before $i" status
echo at $i >status
git commit -m "at $i" status
git tag $i
done
git tag favorite v0.0.3
git branch v2 start
git checkout v2
echo module vcs-test.golang.org/git/querytest.git/v2 >go.mod
git commit -m v2 go.mod
for i in v2.0.0 v2.1.0 v2.2.0 v2.5.5 v2.6.0-pre1; do
echo before $i >status
git add status
git commit -m "before $i" status
echo at $i >status
git commit -m "at $i" status
git tag $i
done
git checkout v2.5.5
echo after v2.5.5 >status
git commit -m 'after v2.5.5' status
git checkout master
zip -r ../querytest.zip
gsutil cp ../querytest.zip gs://vcs-test/git/querytest.zip
curl 'https://vcs-test.golang.org/git/querytest?go-get=1'
*/
{path: queryRepo, query: "<v0.0.0", vers: "v0.0.0-pre1"},
{path: queryRepo, query: "<v0.0.0-pre1", err: `no matching versions for query "<v0.0.0-pre1"`},
{path: queryRepo, query: "<=v0.0.0", vers: "v0.0.0"},

View File

@ -60,6 +60,22 @@ type Status struct {
Uncommitted bool // Required.
}
var (
// VCSTestRepoURL is the URL of the HTTP server that serves the repos for
// vcs-test.golang.org.
//
// In tests, this is set to the URL of an httptest.Server hosting a
// cmd/go/internal/vcweb.Server.
VCSTestRepoURL string
// VCSTestHosts is the set of hosts supported by the vcs-test server.
VCSTestHosts []string
// VCSTestIsLocalHost reports whether the given URL refers to a local
// (loopback) host, such as "localhost" or "127.0.0.1:8080".
VCSTestIsLocalHost func(*urlpkg.URL) bool
)
var defaultSecureScheme = map[string]bool{
"https": true,
"git+ssh": true,
@ -74,6 +90,12 @@ func (v *Cmd) IsSecure(repo string) bool {
// If repo is not a URL, it's not secure.
return false
}
if VCSTestRepoURL != "" && web.IsLocalHost(u) {
// If the vcstest server is in use, it may redirect to other local ports for
// other protocols (such as svn). Assume that all loopback addresses are
// secure during testing.
return true
}
return v.isSecureScheme(u.Scheme)
}
@ -1151,21 +1173,25 @@ func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths
if !srv.schemelessRepo {
repoURL = match["repo"]
} else {
scheme := vcs.Scheme[0] // default to first scheme
repo := match["repo"]
if vcs.PingCmd != "" {
// If we know how to test schemes, scan to find one.
for _, s := range vcs.Scheme {
if security == web.SecureOnly && !vcs.isSecureScheme(s) {
continue
}
if vcs.Ping(s, repo) == nil {
scheme = s
break
var ok bool
repoURL, ok = interceptVCSTest(repo, vcs, security)
if !ok {
scheme := vcs.Scheme[0] // default to first scheme
if vcs.PingCmd != "" {
// If we know how to test schemes, scan to find one.
for _, s := range vcs.Scheme {
if security == web.SecureOnly && !vcs.isSecureScheme(s) {
continue
}
if vcs.Ping(s, repo) == nil {
scheme = s
break
}
}
}
repoURL = scheme + "://" + repo
}
repoURL = scheme + "://" + repo
}
rr := &RepoRoot{
Repo: repoURL,
@ -1177,6 +1203,32 @@ func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths
return nil, errUnknownSite
}
func interceptVCSTest(repo string, vcs *Cmd, security web.SecurityMode) (repoURL string, ok bool) {
if VCSTestRepoURL == "" {
return "", false
}
if vcs == vcsMod {
return "", false // Will be implemented in CL 427254.
}
if vcs == vcsSvn {
return "", false // Will be implemented in CL 427914.
}
if scheme, path, ok := strings.Cut(repo, "://"); ok {
if security == web.SecureOnly && !vcs.isSecureScheme(scheme) {
return "", false // Let the caller reject the original URL.
}
repo = path // Remove leading URL scheme if present.
}
for _, host := range VCSTestHosts {
if !str.HasPathPrefix(repo, host) {
continue
}
return VCSTestRepoURL + strings.TrimPrefix(repo, host), true
}
return "", false
}
// urlForImportPath returns a partially-populated URL for the given Go import path.
//
// The URL leaves the Scheme field blank so that web.Get will try any scheme
@ -1275,8 +1327,12 @@ func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.Se
return nil, err
}
repoURL, ok := interceptVCSTest(mmi.RepoRoot, vcs, security)
if !ok {
repoURL = mmi.RepoRoot
}
rr := &RepoRoot{
Repo: mmi.RepoRoot,
Repo: repoURL,
Root: mmi.Prefix,
IsCustom: true,
VCS: vcs,

View File

@ -0,0 +1,18 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcweb
import (
"log"
"net/http"
)
type bzrHandler struct{}
func (*bzrHandler) Available() bool { return true }
func (*bzrHandler) Handler(dir string, env []string, logger *log.Logger) (http.Handler, error) {
return http.FileServer(http.Dir(dir)), nil
}

View File

@ -0,0 +1,19 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcweb
import (
"log"
"net/http"
)
// dirHandler is a vcsHandler that serves the raw contents of a directory.
type dirHandler struct{}
func (*dirHandler) Available() bool { return true }
func (*dirHandler) Handler(dir string, env []string, logger *log.Logger) (http.Handler, error) {
return http.FileServer(http.Dir(dir)), nil
}

View File

@ -0,0 +1,62 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcweb
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/cgi"
"os"
"os/exec"
"path/filepath"
"sync"
)
type fossilHandler struct {
once sync.Once
fossilPath string
fossilPathErr error
}
func (h *fossilHandler) Available() bool {
h.once.Do(func() {
h.fossilPath, h.fossilPathErr = exec.LookPath("fossil")
})
return h.fossilPathErr == nil
}
func (h *fossilHandler) Handler(dir string, env []string, logger *log.Logger) (http.Handler, error) {
if !h.Available() {
return nil, ServerNotInstalledError{name: "fossil"}
}
name := filepath.Base(dir)
db := filepath.Join(dir, name+".fossil")
cgiPath := db + ".cgi"
cgiScript := fmt.Sprintf("#!%s\nrepository: %s\n", h.fossilPath, db)
if err := ioutil.WriteFile(cgiPath, []byte(cgiScript), 0755); err != nil {
return nil, err
}
handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if _, err := os.Stat(db); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
ch := &cgi.Handler{
Env: env,
Logger: logger,
Path: h.fossilPath,
Args: []string{cgiPath},
Dir: dir,
}
ch.ServeHTTP(w, req)
})
return handler, nil
}

View File

@ -0,0 +1,51 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcweb
import (
"log"
"net/http"
"net/http/cgi"
"os/exec"
"runtime"
"sync"
)
type gitHandler struct {
once sync.Once
gitPath string
gitPathErr error
}
func (h *gitHandler) Available() bool {
if runtime.GOOS == "plan9" {
// The Git command is usually not the real Git on Plan 9.
// See https://golang.org/issues/29640.
return false
}
h.once.Do(func() {
h.gitPath, h.gitPathErr = exec.LookPath("git")
})
return h.gitPathErr == nil
}
func (h *gitHandler) Handler(dir string, env []string, logger *log.Logger) (http.Handler, error) {
if !h.Available() {
return nil, ServerNotInstalledError{name: "git"}
}
handler := &cgi.Handler{
Path: h.gitPath,
Logger: logger,
Args: []string{"http-backend"},
Dir: dir,
Env: append(env[:len(env):len(env)],
"GIT_PROJECT_ROOT="+dir,
"GIT_HTTP_EXPORT_ALL=1",
),
}
return handler, nil
}

View File

@ -0,0 +1,121 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcweb
import (
"bufio"
"errors"
"io"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"os/exec"
"strings"
"sync"
)
type hgHandler struct {
once sync.Once
hgPath string
hgPathErr error
}
func (h *hgHandler) Available() bool {
h.once.Do(func() {
h.hgPath, h.hgPathErr = exec.LookPath("hg")
})
return h.hgPathErr == nil
}
func (h *hgHandler) Handler(dir string, env []string, logger *log.Logger) (http.Handler, error) {
if !h.Available() {
return nil, ServerNotInstalledError{name: "hg"}
}
handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// Mercurial has a CGI server implementation (called hgweb). In theory we
// could use that — however, assuming that hgweb is even installed, the
// configuration for hgweb varies by Python version (2 vs 3), and we would
// rather not go rooting around trying to find the right Python version to
// run.
//
// Instead, we'll take a somewhat more roundabout approach: we assume that
// if "hg" works at all then "hg serve" works too, and we'll execute that as
// a subprocess, using a reverse proxy to forward the request and response.
cmd := exec.Command(h.hgPath, "serve", "--port", "0", "--address", "localhost", "--accesslog", os.DevNull, "--name", "vcweb", "--print-url")
cmd.Dir = dir
cmd.Env = append(env[:len(env):len(env)], "PWD="+dir)
stderr := new(strings.Builder)
cmd.Stderr = stderr
stdout, err := cmd.StdoutPipe()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
readDone := make(chan struct{})
defer func() {
stdout.Close()
<-readDone
}()
hgURL := make(chan *url.URL, 1)
hgURLError := make(chan error, 1)
go func() {
defer close(readDone)
r := bufio.NewReader(stdout)
for {
line, err := r.ReadString('\n')
if err != nil {
return
}
u, err := url.Parse(strings.TrimSpace(line))
if err == nil {
hgURL <- u
} else {
hgURLError <- err
}
break
}
io.Copy(io.Discard, r)
}()
if err := cmd.Start(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer func() {
if err := cmd.Process.Signal(os.Interrupt); err != nil && !errors.Is(err, os.ErrProcessDone) {
cmd.Process.Kill()
}
err := cmd.Wait()
if out := strings.TrimSuffix(stderr.String(), "interrupted!\n"); out != "" {
logger.Printf("%v: %v\n%s", cmd, err, out)
} else {
logger.Printf("%v", cmd)
}
}()
select {
case <-req.Context().Done():
logger.Printf("%v: %v", req.Context().Err(), cmd)
http.Error(w, req.Context().Err().Error(), http.StatusBadGateway)
return
case err := <-hgURLError:
logger.Printf("%v: %v", cmd, err)
http.Error(w, err.Error(), http.StatusBadGateway)
return
case url := <-hgURL:
logger.Printf("proxying hg request to %s", url)
httputil.NewSingleHostReverseProxy(url).ServeHTTP(w, req)
}
})
return handler, nil
}

View File

@ -0,0 +1,304 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcweb
import (
"bufio"
"bytes"
"cmd/go/internal/script"
"context"
"errors"
"fmt"
"internal/txtar"
"io"
"log"
"net/http"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
)
// newScriptEngine returns a script engine augmented with commands for
// reproducing version-control repositories by replaying commits.
func newScriptEngine() *script.Engine {
conds := script.DefaultConds()
interrupt := os.Interrupt
gracePeriod := 1 * time.Second // arbitrary
cmds := script.DefaultCmds()
cmds["at"] = scriptAt()
cmds["bzr"] = script.Program("bzr", interrupt, gracePeriod)
cmds["fossil"] = script.Program("fossil", interrupt, gracePeriod)
cmds["git"] = script.Program("git", interrupt, gracePeriod)
cmds["hg"] = script.Program("hg", interrupt, gracePeriod)
cmds["handle"] = scriptHandle()
cmds["svn"] = script.Program("svn", interrupt, gracePeriod)
cmds["unquote"] = scriptUnquote()
return &script.Engine{
Cmds: cmds,
Conds: conds,
}
}
// loadScript interprets the given script content using the vcweb script engine.
// loadScript always returns either a non-nil handler or a non-nil error.
//
// The script content must be a txtar archive with a comment containing a script
// with exactly one "handle" command and zero or more VCS commands to prepare
// the repository to be served.
func (s *Server) loadScript(ctx context.Context, logger *log.Logger, scriptPath string, scriptContent []byte, workDir string) (http.Handler, error) {
ar := txtar.Parse(scriptContent)
if err := os.MkdirAll(workDir, 0755); err != nil {
return nil, err
}
st, err := s.newState(ctx, workDir)
if err != nil {
return nil, err
}
if err := st.ExtractFiles(ar); err != nil {
return nil, err
}
scriptName := filepath.Base(scriptPath)
scriptLog := new(strings.Builder)
err = s.engine.Execute(st, scriptName, bufio.NewReader(bytes.NewReader(ar.Comment)), scriptLog)
closeErr := st.CloseAndWait(scriptLog)
logger.Printf("%s:", scriptName)
io.WriteString(logger.Writer(), scriptLog.String())
io.WriteString(logger.Writer(), "\n")
if err != nil {
return nil, err
}
if closeErr != nil {
return nil, err
}
sc, err := getScriptCtx(st)
if err != nil {
return nil, err
}
if sc.handler == nil {
return nil, errors.New("script completed without setting handler")
}
return sc.handler, nil
}
// newState returns a new script.State for executing scripts in workDir.
func (s *Server) newState(ctx context.Context, workDir string) (*script.State, error) {
ctx = &scriptCtx{
Context: ctx,
server: s,
}
st, err := script.NewState(ctx, workDir, s.env)
if err != nil {
return nil, err
}
return st, nil
}
// scriptEnviron returns a new environment that attempts to provide predictable
// behavior for the supported version-control tools.
func scriptEnviron(homeDir string) []string {
env := []string{
"USER=gopher",
homeEnvName() + "=" + homeDir,
"GIT_CONFIG_NOSYSTEM=1",
"HGRCPATH=" + filepath.Join(homeDir, ".hgrc"),
"HGENCODING=utf-8",
}
// Preserve additional environment variables that may be needed by VCS tools.
for _, k := range []string{
pathEnvName(),
tempEnvName(),
"SYSTEMROOT", // must be preserved on Windows to find DLLs; golang.org/issue/25210
"WINDIR", // must be preserved on Windows to be able to run PowerShell command; golang.org/issue/30711
"DYLD_LIBRARY_PATH", // must be preserved on macOS systems to find shared libraries
"LD_LIBRARY_PATH", // must be preserved on Unix systems to find shared libraries
"LIBRARY_PATH", // allow override of non-standard static library paths
"PYTHONPATH", // may be needed by hg to find imported modules
} {
if v, ok := os.LookupEnv(k); ok {
env = append(env, k+"="+v)
}
}
if os.Getenv("GO_BUILDER_NAME") != "" || os.Getenv("GIT_TRACE_CURL") == "1" {
// To help diagnose https://go.dev/issue/52545,
// enable tracing for Git HTTPS requests.
env = append(env,
"GIT_TRACE_CURL=1",
"GIT_TRACE_CURL_NO_DATA=1",
"GIT_REDACT_COOKIES=o,SSO,GSSO_Uberproxy")
}
return env
}
// homeEnvName returns the environment variable used by os.UserHomeDir
// to locate the user's home directory.
func homeEnvName() string {
switch runtime.GOOS {
case "windows":
return "USERPROFILE"
case "plan9":
return "home"
default:
return "HOME"
}
}
// tempEnvName returns the environment variable used by os.TempDir
// to locate the default directory for temporary files.
func tempEnvName() string {
switch runtime.GOOS {
case "windows":
return "TMP"
case "plan9":
return "TMPDIR" // actually plan 9 doesn't have one at all but this is fine
default:
return "TMPDIR"
}
}
// pathEnvName returns the environment variable used by exec.LookPath to
// identify directories to search for executables.
func pathEnvName() string {
switch runtime.GOOS {
case "plan9":
return "path"
default:
return "PATH"
}
}
// A scriptCtx is a context.Context that stores additional state for script
// commands.
type scriptCtx struct {
context.Context
server *Server
commitTime time.Time
handlerName string
handler http.Handler
}
// scriptCtxKey is the key associating the *scriptCtx in a script's Context..
type scriptCtxKey struct{}
func (sc *scriptCtx) Value(key any) any {
if key == (scriptCtxKey{}) {
return sc
}
return sc.Context.Value(key)
}
func getScriptCtx(st *script.State) (*scriptCtx, error) {
sc, ok := st.Context().Value(scriptCtxKey{}).(*scriptCtx)
if !ok {
return nil, errors.New("scriptCtx not found in State.Context")
}
return sc, nil
}
func scriptAt() script.Cmd {
return script.Command(
script.CmdUsage{
Summary: "set the current commit time for all version control systems",
Args: "time",
Detail: []string{
"The argument must be an absolute timestamp in RFC3339 format.",
},
},
func(st *script.State, args ...string) (script.WaitFunc, error) {
if len(args) != 1 {
return nil, script.ErrUsage
}
sc, err := getScriptCtx(st)
if err != nil {
return nil, err
}
sc.commitTime, err = time.ParseInLocation(time.RFC3339, args[0], time.UTC)
if err == nil {
st.Setenv("GIT_COMMITTER_DATE", args[0])
st.Setenv("GIT_AUTHOR_DATE", args[0])
}
return nil, err
})
}
func scriptHandle() script.Cmd {
return script.Command(
script.CmdUsage{
Summary: "set the HTTP handler that will serve the script's output",
Args: "handler [dir]",
Detail: []string{
"The handler will be passed the script's current working directory and environment as arguments.",
"Valid handlers include 'dir' (for general http.Dir serving), 'bzr', 'fossil', 'git', and 'hg'",
},
},
func(st *script.State, args ...string) (script.WaitFunc, error) {
if len(args) == 0 || len(args) > 2 {
return nil, script.ErrUsage
}
sc, err := getScriptCtx(st)
if err != nil {
return nil, err
}
if sc.handler != nil {
return nil, fmt.Errorf("server handler already set to %s", sc.handlerName)
}
name := args[0]
h, ok := sc.server.vcsHandlers[name]
if !ok {
return nil, fmt.Errorf("unrecognized VCS %q", name)
}
sc.handlerName = name
if !h.Available() {
return nil, ServerNotInstalledError{name}
}
dir := st.Getwd()
if len(args) >= 2 {
dir = st.Path(args[1])
}
sc.handler, err = h.Handler(dir, st.Environ(), sc.server.logger)
return nil, err
})
}
func scriptUnquote() script.Cmd {
return script.Command(
script.CmdUsage{
Summary: "unquote the argument as a Go string",
Args: "string",
},
func(st *script.State, args ...string) (script.WaitFunc, error) {
if len(args) != 1 {
return nil, script.ErrUsage
}
s, err := strconv.Unquote(`"` + args[0] + `"`)
if err != nil {
return nil, err
}
wait := func(*script.State) (stdout, stderr string, err error) {
return s, "", nil
}
return wait, nil
})
}

View File

@ -0,0 +1,83 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package vcstest serves the repository scripts in cmd/go/testdata/vcstest
// using the [vcweb] script engine.
package vcstest
import (
"cmd/go/internal/vcs"
"cmd/go/internal/vcweb"
"fmt"
"internal/testenv"
"io"
"log"
"net/http/httptest"
"os"
"path/filepath"
"testing"
)
var Hosts = []string{
"vcs-test.golang.org",
}
type Server struct {
workDir string
HTTP *httptest.Server
}
// NewServer returns a new test-local vcweb server that serves VCS requests
// for modules with paths that begin with "vcs-test.golang.org" using the
// scripts in cmd/go/testdata/vcstest.
func NewServer() (srv *Server, err error) {
if vcs.VCSTestRepoURL != "" {
panic("vcs URL hooks already set")
}
scriptDir := filepath.Join(testenv.GOROOT(nil), "src/cmd/go/testdata/vcstest")
workDir, err := os.MkdirTemp("", "vcstest")
if err != nil {
return nil, err
}
defer func() {
if err != nil {
os.RemoveAll(workDir)
}
}()
logger := log.Default()
if !testing.Verbose() {
logger = log.New(io.Discard, "", log.LstdFlags)
}
handler, err := vcweb.NewServer(scriptDir, workDir, logger)
if err != nil {
return nil, err
}
srvHTTP := httptest.NewServer(handler)
srv = &Server{
workDir: workDir,
HTTP: srvHTTP,
}
vcs.VCSTestRepoURL = srv.HTTP.URL
vcs.VCSTestHosts = Hosts
fmt.Fprintln(os.Stderr, "vcs-test.golang.org rerouted to "+srv.HTTP.URL)
return srv, nil
}
func (srv *Server) Close() error {
if vcs.VCSTestRepoURL != srv.HTTP.URL {
panic("vcs URL hooks modified before Close")
}
vcs.VCSTestRepoURL = ""
vcs.VCSTestHosts = nil
srv.HTTP.Close()
return os.RemoveAll(srv.workDir)
}

View File

@ -0,0 +1,151 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcstest_test
import (
"cmd/go/internal/vcweb"
"errors"
"flag"
"fmt"
"io"
"io/fs"
"log"
"net"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
var (
dir = flag.String("dir", "../../../testdata/vcstest", "directory containing scripts to serve")
host = flag.String("host", "localhost", "hostname on which to serve HTTP")
port = flag.Int("port", -1, "port on which to serve HTTP; if nonnegative, skips running tests")
)
func TestMain(m *testing.M) {
flag.Parse()
if *port >= 0 {
err := serveStandalone(*host, *port)
if err != nil {
log.Fatal(err)
}
os.Exit(0)
}
m.Run()
}
// serveStandalone serves the vcweb testdata in a standalone HTTP server.
func serveStandalone(host string, port int) (err error) {
scriptDir, err := filepath.Abs(*dir)
if err != nil {
return err
}
work, err := os.MkdirTemp("", "vcweb")
if err != nil {
return err
}
defer func() {
if rmErr := os.RemoveAll(work); err == nil {
err = rmErr
}
}()
log.Printf("running scripts in %s", work)
v, err := vcweb.NewServer(scriptDir, work, log.Default())
if err != nil {
return err
}
l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, port))
if err != nil {
return err
}
log.Printf("serving on http://%s:%d/", host, l.Addr().(*net.TCPAddr).Port)
return http.Serve(l, v)
}
// TestScripts verifies that the VCS setup scripts in cmd/go/testdata/vcstest
// run successfully.
func TestScripts(t *testing.T) {
scriptDir, err := filepath.Abs(*dir)
if err != nil {
t.Fatal(err)
}
s, err := vcweb.NewServer(scriptDir, t.TempDir(), log.Default())
if err != nil {
t.Fatal(err)
}
srv := httptest.NewServer(s)
t.Cleanup(func() {
// The subtests spawned by WalkDir run in parallel. When they complete, this
// Cleanup callback will run. At that point we fetch the root URL (which
// contains a status page), both to test that the root handler runs without
// crashing and to display a nice summary of the server's view of the test
// coverage.
resp, err := http.Get(srv.URL)
if err == nil {
var body []byte
body, err = io.ReadAll(resp.Body)
if err == nil && testing.Verbose() {
t.Logf("GET %s:\n%s", srv.URL, body)
}
resp.Body.Close()
}
if err != nil {
t.Error(err)
}
srv.Close()
})
err = filepath.WalkDir(scriptDir, func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return err
}
rel, err := filepath.Rel(scriptDir, path)
if err != nil {
return err
}
if rel == "README" {
return nil
}
t.Run(filepath.ToSlash(rel), func(t *testing.T) {
t.Parallel()
buf := new(strings.Builder)
logger := log.New(buf, "", log.LstdFlags)
// Load the script but don't try to serve the results:
// different VCS tools have different handler protocols,
// and the tests that actually use these repos will ensure
// that they are served correctly as a side effect anyway.
err := s.HandleScript(rel, logger, func(http.Handler) {})
if buf.Len() > 0 {
t.Log(buf)
}
if err != nil {
if notInstalled := (vcweb.ServerNotInstalledError{}); errors.As(err, &notInstalled) || errors.Is(err, exec.ErrNotFound) {
t.Skip(err)
}
t.Error(err)
}
})
return nil
})
if err != nil {
t.Error(err)
}
}

View File

@ -0,0 +1,407 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package vcweb serves version control repos for testing the go command.
//
// It is loosely derived from golang.org/x/build/vcs-test/vcweb,
// which ran as a service hosted at vcs-test.golang.org.
//
// When a repository URL is first requested, the vcweb [Server] dynamically
// regenerates the repository using a script interpreted by a [script.Engine].
// The script produces the server's contents for a corresponding root URL and
// all subdirectories of that URL, which are then cached: subsequent requests
// for any URL generated by the script will serve the script's previous output
// until the script is modified.
//
// The script engine includes all of the engine's default commands and
// conditions, as well as commands for each supported VCS binary (bzr, fossil,
// git, hg, and svn), a "handle" command that informs the script which protocol
// or handler to use to serve the request, and utilities "at" (which sets
// environment variables for Git timestamps) and "unquote" (which unquotes its
// argument as if it were a Go string literal).
//
// The server's "/" endpoint provides a summary of the available scripts,
// and "/help" provides documentation for the script environment.
//
// To run a standalone server based on the vcweb engine, use:
//
// go test cmd/go/internal/vcweb/vcstest -v --port=0
package vcweb
import (
"bufio"
"cmd/go/internal/script"
"context"
"crypto/sha256"
"errors"
"fmt"
"io"
"io/fs"
"log"
"net/http"
"os"
"os/exec"
"path"
"path/filepath"
"runtime/debug"
"strings"
"sync"
"text/tabwriter"
"time"
)
// A Server serves cached, dynamically-generated version control repositories.
type Server struct {
env []string
logger *log.Logger
scriptDir string
workDir string
homeDir string // $workdir/home
engine *script.Engine
scriptCache sync.Map // script path → *scriptResult
vcsHandlers map[string]vcsHandler
}
// A vcsHandler serves repositories over HTTP for a known version-control tool.
type vcsHandler interface {
Available() bool
Handler(dir string, env []string, logger *log.Logger) (http.Handler, error)
}
// A scriptResult describes the cached result of executing a vcweb script.
type scriptResult struct {
mu sync.RWMutex
hash [sha256.Size]byte // hash of the script file, for cache invalidation
hashTime time.Time // timestamp at which the script was run, for diagnostics
handler http.Handler // HTTP handler configured by the script
err error // error from executing the script, if any
}
// NewServer returns a Server that generates and serves repositories in workDir
// using the scripts found in scriptDir and its subdirectories.
//
// A request for the path /foo/bar/baz will be handled by the first script along
// that path that exists: $scriptDir/foo.txt, $scriptDir/foo/bar.txt, or
// $scriptDir/foo/bar/baz.txt.
func NewServer(scriptDir, workDir string, logger *log.Logger) (*Server, error) {
if scriptDir == "" {
panic("vcweb.NewServer: scriptDir is required")
}
var err error
scriptDir, err = filepath.Abs(scriptDir)
if err != nil {
return nil, err
}
if workDir == "" {
workDir, err = os.MkdirTemp("", "vcweb-*")
if err != nil {
return nil, err
}
logger.Printf("vcweb work directory: %s", workDir)
} else {
workDir, err = filepath.Abs(workDir)
if err != nil {
return nil, err
}
}
homeDir := filepath.Join(workDir, "home")
if err := os.MkdirAll(homeDir, 0755); err != nil {
return nil, err
}
env := scriptEnviron(homeDir)
s := &Server{
env: env,
logger: logger,
scriptDir: scriptDir,
workDir: workDir,
homeDir: homeDir,
engine: newScriptEngine(),
vcsHandlers: map[string]vcsHandler{
"dir": new(dirHandler),
"bzr": new(bzrHandler),
"fossil": new(fossilHandler),
"git": new(gitHandler),
"hg": new(hgHandler),
},
}
if err := os.WriteFile(filepath.Join(s.homeDir, ".gitconfig"), []byte(gitConfig), 0644); err != nil {
return nil, err
}
gitConfigDir := filepath.Join(s.homeDir, ".config", "git")
if err := os.MkdirAll(gitConfigDir, 0755); err != nil {
return nil, err
}
if err := os.WriteFile(filepath.Join(gitConfigDir, "ignore"), []byte(""), 0644); err != nil {
return nil, err
}
if err := os.WriteFile(filepath.Join(s.homeDir, ".hgrc"), []byte(hgrc), 0644); err != nil {
return nil, err
}
return s, nil
}
// gitConfig contains a ~/.gitconfg file that attempts to provide
// deterministic, platform-agnostic behavior for the 'git' command.
var gitConfig = `
[user]
name = Go Gopher
email = gopher@golang.org
[init]
defaultBranch = main
[core]
eol = lf
[gui]
encoding = utf-8
`[1:]
// hgrc contains a ~/.hgrc file that attempts to provide
// deterministic, platform-agnostic behavior for the 'hg' command.
var hgrc = `
[ui]
username=Go Gopher <gopher@golang.org>
[phases]
new-commit=public
[extensions]
convert=
`[1:]
// ServeHTTP implements [http.Handler] for version-control repositories.
func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
s.logger.Printf("serving %s", req.URL)
defer func() {
if v := recover(); v != nil {
debug.PrintStack()
s.logger.Fatal(v)
}
}()
urlPath := req.URL.Path
if !strings.HasPrefix(urlPath, "/") {
urlPath = "/" + urlPath
}
clean := path.Clean(urlPath)[1:]
if clean == "" {
s.overview(w, req)
return
}
if clean == "help" {
s.help(w, req)
return
}
// Locate the script that generates the requested path.
// We follow directories all the way to the end, then look for a ".txt" file
// matching the first component that doesn't exist. That guarantees
// uniqueness: if a path exists as a directory, then it cannot exist as a
// ".txt" script (because the search would ignore that file).
scriptPath := "."
for _, part := range strings.Split(clean, "/") {
scriptPath = filepath.Join(scriptPath, part)
dir := filepath.Join(s.scriptDir, scriptPath)
if _, err := os.Stat(dir); err != nil {
if !os.IsNotExist(err) {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// scriptPath does not exist as a directory, so it either is the script
// location or the script doesn't exist.
break
}
}
scriptPath += ".txt"
err := s.HandleScript(scriptPath, s.logger, func(handler http.Handler) {
handler.ServeHTTP(w, req)
})
if err != nil {
s.logger.Print(err)
if notFound := (ScriptNotFoundError{}); errors.As(err, &notFound) {
http.NotFound(w, req)
} else if notInstalled := (ServerNotInstalledError{}); errors.As(err, &notInstalled) || errors.Is(err, exec.ErrNotFound) {
http.Error(w, err.Error(), http.StatusNotImplemented)
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
}
// A ScriptNotFoundError indicates that the requested script file does not exist.
// (It typically wraps a "stat" error for the script file.)
type ScriptNotFoundError struct{ err error }
func (e ScriptNotFoundError) Error() string { return e.err.Error() }
func (e ScriptNotFoundError) Unwrap() error { return e.err }
// A ServerNotInstalledError indicates that the server binary required for the
// indicated VCS does not exist.
type ServerNotInstalledError struct{ name string }
func (v ServerNotInstalledError) Error() string {
return fmt.Sprintf("server for %#q VCS is not installed", v.name)
}
// HandleScript ensures that the script at scriptRelPath has been evaluated
// with its current contents.
//
// If the script completed successfully, HandleScript invokes f on the handler
// with the script's result still read-locked, and waits for it to return. (That
// ensures that cache invalidation does not race with an in-flight handler.)
//
// Otherwise, HandleScript returns the (cached) error from executing the script.
func (s *Server) HandleScript(scriptRelPath string, logger *log.Logger, f func(http.Handler)) error {
ri, ok := s.scriptCache.Load(scriptRelPath)
if !ok {
ri, _ = s.scriptCache.LoadOrStore(scriptRelPath, new(scriptResult))
}
r := ri.(*scriptResult)
relDir := strings.TrimSuffix(scriptRelPath, filepath.Ext(scriptRelPath))
workDir := filepath.Join(s.workDir, relDir)
prefix := path.Join("/", filepath.ToSlash(relDir))
r.mu.RLock()
defer r.mu.RUnlock()
for {
// For efficiency, we cache the script's output (in the work directory)
// across invocations. However, to allow for rapid iteration, we hash the
// script's contents and regenerate its output if the contents change.
//
// That way, one can use 'go run main.go' in this directory to stand up a
// server and see the output of the test script in order to fine-tune it.
content, err := os.ReadFile(filepath.Join(s.scriptDir, scriptRelPath))
if err != nil {
if !os.IsNotExist(err) {
return err
}
return ScriptNotFoundError{err}
}
hash := sha256.Sum256(content)
if prevHash := r.hash; prevHash != hash {
// The script's hash has changed, so regenerate its output.
func() {
r.mu.RUnlock()
r.mu.Lock()
defer func() {
r.mu.Unlock()
r.mu.RLock()
}()
if r.hash != prevHash {
// The cached result changed while we were waiting on the lock.
// It may have been updated to our hash or something even newer,
// so don't overwrite it.
return
}
r.hash = hash
r.hashTime = time.Now()
r.handler, r.err = nil, nil
if err := os.RemoveAll(workDir); err != nil {
r.err = err
return
}
// Note: we use context.Background here instead of req.Context() so that we
// don't cache a spurious error (and lose work) if the request is canceled
// while the script is still running.
scriptHandler, err := s.loadScript(context.Background(), logger, scriptRelPath, content, workDir)
if err != nil {
r.err = err
return
}
r.handler = http.StripPrefix(prefix, scriptHandler)
}()
}
if r.hash != hash {
continue // Raced with an update from another handler; try again.
}
if r.err != nil {
return r.err
}
f(r.handler)
return nil
}
}
// overview serves an HTML summary of the status of the scripts in the server's
// script directory.
func (s *Server) overview(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<html>\n")
fmt.Fprintf(w, "<title>vcweb</title>\n<pre>\n")
fmt.Fprintf(w, "<b>vcweb</b>\n\n")
fmt.Fprintf(w, "This server serves various version control repos for testing the go command.\n\n")
fmt.Fprintf(w, "For an overview of the script lanugage, see <a href=\"/help\">/help</a>.\n\n")
fmt.Fprintf(w, "<b>cache</b>\n")
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
err := filepath.WalkDir(s.scriptDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if filepath.Ext(path) != ".txt" {
return nil
}
rel, err := filepath.Rel(s.scriptDir, path)
if err != nil {
return err
}
hashTime := "(not loaded)"
status := ""
if ri, ok := s.scriptCache.Load(rel); ok {
r := ri.(*scriptResult)
if !r.hashTime.IsZero() {
hashTime = r.hashTime.Format(time.RFC3339)
}
if r.err == nil {
status = "ok"
} else {
status = r.err.Error()
}
}
fmt.Fprintf(tw, "%s\t%s\t%s\n", rel, hashTime, status)
return nil
})
tw.Flush()
if err != nil {
fmt.Fprintln(w, err)
}
}
// help serves a plain-text summary of the server's supported script language.
func (s *Server) help(w http.ResponseWriter, req *http.Request) {
st, err := s.newState(req.Context(), s.workDir)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
scriptLog := new(strings.Builder)
err = s.engine.Execute(st, "help", bufio.NewReader(strings.NewReader("help")), scriptLog)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
io.WriteString(w, scriptLog.String())
}

View File

@ -0,0 +1,63 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcweb_test
import (
"cmd/go/internal/vcweb"
"io"
"log"
"net/http"
"net/http/httptest"
"os"
"testing"
)
func TestHelp(t *testing.T) {
s, err := vcweb.NewServer(os.DevNull, t.TempDir(), log.Default())
if err != nil {
t.Fatal(err)
}
srv := httptest.NewServer(s)
defer srv.Close()
resp, err := http.Get(srv.URL + "/help")
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
t.Fatal(resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
t.Logf("%s", body)
}
func TestOverview(t *testing.T) {
s, err := vcweb.NewServer(os.DevNull, t.TempDir(), log.Default())
if err != nil {
t.Fatal(err)
}
srv := httptest.NewServer(s)
defer srv.Close()
resp, err := http.Get(srv.URL)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
t.Fatal(resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
t.Logf("%s", body)
}

View File

@ -238,3 +238,9 @@ func (b *errorDetailBuffer) Read(p []byte) (n int, err error) {
return n, err
}
// IsLocalHost reports whether the given URL refers to a local
// (loopback) host, such as "localhost" or "127.0.0.1:8080".
func IsLocalHost(u *url.URL) bool {
return isLocalHost(u)
}

View File

@ -21,3 +21,5 @@ func get(security SecurityMode, url *urlpkg.URL) (*Response, error) {
}
func openBrowser(url string) bool { return false }
func isLocalHost(u *urlpkg.URL) bool { return false }

View File

@ -255,3 +255,20 @@ func getFile(u *urlpkg.URL) (*Response, error) {
}
func openBrowser(url string) bool { return browser.Open(url) }
func isLocalHost(u *urlpkg.URL) bool {
// VCSTestRepoURL itself is secure, and it may redirect requests to other
// ports (such as a port serving the "svn" protocol) which should also be
// considered secure.
host, _, err := net.SplitHostPort(u.Host)
if err != nil {
host = u.Host
}
if host == "localhost" {
return true
}
if ip := net.ParseIP(host); ip != nil && ip.IsLoopback() {
return true
}
return false
}

View File

@ -29,6 +29,8 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/script"
"cmd/go/internal/script/scripttest"
"cmd/go/internal/vcs"
"cmd/go/internal/vcweb/vcstest"
)
var testSum = flag.String("testsum", "", `may be tidy, listm, or listall. If set, TestScript generates a go.sum file at the beginning of each test and updates test files if they pass.`)
@ -38,6 +40,16 @@ func TestScript(t *testing.T) {
testenv.MustHaveGoBuild(t)
testenv.SkipIfShortAndSlow(t)
srv, err := vcstest.NewServer()
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := srv.Close(); err != nil {
t.Fatal(err)
}
})
StartProxy()
var (
@ -187,6 +199,7 @@ func scriptEnv() ([]string, error) {
"GOROOT_FINAL=" + testGOROOT_FINAL, // causes spurious rebuilds and breaks the "stale" built-in if not propagated
"GOTRACEBACK=system",
"TESTGO_GOROOT=" + testGOROOT,
"TESTGO_VCSTEST_URL=" + vcs.VCSTestRepoURL,
"GOSUMDB=" + testSumDBVerifierKey,
"GONOPROXY=",
"GONOSUMDB=",

View File

@ -13,7 +13,7 @@ cp stdout hellopseudo.json
! stdout '"(Query|TagPrefix|TagSum|Ref)"'
stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/hello"'
stdout '"URL": ".*/git/hello"'
stdout '"Hash": "fc3a09f3dc5cfe0d7a743ea18f1f5226e68b3777"'
go clean -modcache
@ -23,7 +23,7 @@ stderr 'git fetch'
cp stdout hello.json
stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/hello"'
stdout '"URL": ".*/git/hello"'
stdout '"Query": "latest"'
! stdout '"TagPrefix"'
stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="'
@ -36,7 +36,7 @@ stdout '"Hash": "fc3a09f3dc5cfe0d7a743ea18f1f5226e68b3777"'
go mod download -x -json vcs-test.golang.org/git/hello.git@v0.0.0-20170922010558-fc3a09f3dc5c
! stderr 'git fetch'
cp stdout hellopseudo2.json
cmp hellopseudo.json hellopseudo2.json
cmpenv hellopseudo.json hellopseudo2.json
# go mod download vcstest/hello@hash needs to check TagSum to find pseudoversion base.
go mod download -x -json vcs-test.golang.org/git/hello.git@fc3a09f3dc5c
@ -45,7 +45,7 @@ cp stdout hellohash.json
stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"'
stdout '"Query": "fc3a09f3dc5c"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/hello"'
stdout '"URL": ".*/git/hello"'
stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="'
stdout '"Hash": "fc3a09f3dc5cfe0d7a743ea18f1f5226e68b3777"'
@ -100,7 +100,7 @@ cp stdout tagtests.json
stdout '"Version": "v0.2.2"'
stdout '"Query": "latest"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/tagtests"'
stdout '"URL": ".*/git/tagtests"'
! stdout '"TagPrefix"'
stdout '"TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo="'
stdout '"Ref": "refs/tags/v0.2.2"'
@ -112,7 +112,7 @@ cp stdout tagtestsv022.json
stdout '"Version": "v0.2.2"'
! stdout '"Query":'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/tagtests"'
stdout '"URL": ".*/git/tagtests"'
! stdout '"TagPrefix"'
! stdout '"TagSum"'
stdout '"Ref": "refs/tags/v0.2.2"'
@ -124,7 +124,7 @@ cp stdout tagtestsmaster.json
stdout '"Version": "v0.2.3-0.20190509225625-c7818c24fa2f"'
stdout '"Query": "master"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/tagtests"'
stdout '"URL": ".*/git/tagtests"'
! stdout '"TagPrefix"'
stdout '"TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo="'
stdout '"Ref": "refs/heads/master"'
@ -137,7 +137,7 @@ cp stdout prefixtagtests.json
stdout '"Version": "v0.0.10"'
stdout '"Query": "latest"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/prefixtagtests"'
stdout '"URL": ".*/git/prefixtagtests"'
stdout '"Subdir": "sub"'
stdout '"TagPrefix": "sub/"'
stdout '"TagSum": "t1:YGSbWkJ8dn9ORAr[+]BlKHFK/2ZhXLb9hVuYfTZ9D8C7g="'
@ -160,7 +160,7 @@ go mod download -reuse=hello.json -x -json vcs-test.golang.org/git/hello.git@lat
stdout '"Reuse": true'
stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/hello"'
stdout '"URL": ".*/git/hello"'
! stdout '"TagPrefix"'
stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="'
stdout '"Ref": "HEAD"'
@ -176,7 +176,7 @@ go mod download -reuse=hellopseudo.json -x -json vcs-test.golang.org/git/hello.g
stdout '"Reuse": true'
stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/hello"'
stdout '"URL": ".*/git/hello"'
! stdout '"(Query|TagPrefix|TagSum|Ref)"'
stdout '"Hash": "fc3a09f3dc5cfe0d7a743ea18f1f5226e68b3777"'
! stdout '"(Dir|Info|GoMod|Zip)"'
@ -188,7 +188,7 @@ stdout '"Reuse": true'
stdout '"Query": "fc3a09f3dc5c"'
stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/hello"'
stdout '"URL": ".*/git/hello"'
! stdout '"(TagPrefix|Ref)"'
stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="'
stdout '"Hash": "fc3a09f3dc5cfe0d7a743ea18f1f5226e68b3777"'
@ -251,7 +251,7 @@ stdout '"Reuse": true'
stdout '"Version": "v0.2.2"'
stdout '"Query": "latest"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/tagtests"'
stdout '"URL": ".*/git/tagtests"'
! stdout '"TagPrefix"'
stdout '"TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo="'
stdout '"Ref": "refs/tags/v0.2.2"'
@ -265,7 +265,7 @@ stdout '"Reuse": true'
stdout '"Version": "v0.2.2"'
! stdout '"Query":'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/tagtests"'
stdout '"URL": ".*/git/tagtests"'
! stdout '"TagPrefix"'
! stdout '"TagSum"'
stdout '"Ref": "refs/tags/v0.2.2"'
@ -279,7 +279,7 @@ stdout '"Reuse": true'
stdout '"Version": "v0.2.3-0.20190509225625-c7818c24fa2f"'
stdout '"Query": "master"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/tagtests"'
stdout '"URL": ".*/git/tagtests"'
! stdout '"TagPrefix"'
stdout '"TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo="'
stdout '"Ref": "refs/heads/master"'
@ -293,7 +293,7 @@ stdout '"Reuse": true'
stdout '"Version": "v0.2.3-0.20190509225625-c7818c24fa2f"'
stdout '"Query": "master"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/tagtests"'
stdout '"URL": ".*/git/tagtests"'
! stdout '"TagPrefix"'
stdout '"TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo="'
stdout '"Ref": "refs/heads/master"'
@ -306,7 +306,7 @@ go mod download -reuse=prefixtagtests.json -x -json vcs-test.golang.org/git/pref
stdout '"Version": "v0.0.10"'
stdout '"Query": "latest"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/prefixtagtests"'
stdout '"URL": ".*/git/prefixtagtests"'
stdout '"Subdir": "sub"'
stdout '"TagPrefix": "sub/"'
stdout '"TagSum": "t1:YGSbWkJ8dn9ORAr[+]BlKHFK/2ZhXLb9hVuYfTZ9D8C7g="'
@ -329,7 +329,7 @@ stderr 'git fetch'
stdout '"Version": "v0.2.2"'
! stdout '"Query"'
stdout '"VCS": "git"'
stdout '"URL": "https://vcs-test.golang.org/git/tagtests"'
stdout '"URL": ".*/git/tagtests"'
! stdout '"(TagPrefix|TagSum)"'
stdout '"Ref": "refs/tags/v0.2.2"'
stdout '"Hash": "59356c8cd18c5fe9a598167d98a6843e52d57952"'
@ -343,7 +343,7 @@ cp tagtestsv022.json tagtestsv022badurl.json
replace 'git/tagtests\"' 'git/tagtestsXXX\"' tagtestsv022badurl.json
go mod download -reuse=tagtestsv022badurl.json -x -json vcs-test.golang.org/git/tagtests.git@v0.2.2
! stdout '"Reuse": true'
stdout '"URL": "https://vcs-test.golang.org/git/tagtests"'
stdout '"URL": ".*/git/tagtests"'
stdout '"Dir"'
stdout '"Info"'
stdout '"GoMod"'
@ -354,14 +354,14 @@ cp tagtestsv022.json tagtestsv022badvcs.json
replace '\"git\"' '\"gitXXX\"' tagtestsv022badvcs.json
go mod download -reuse=tagtestsv022badvcs.json -x -json vcs-test.golang.org/git/tagtests.git@v0.2.2
! stdout '"Reuse": true'
stdout '"URL": "https://vcs-test.golang.org/git/tagtests"'
stdout '"URL": ".*/git/tagtests"'
# reuse with stale Dir
cp tagtestsv022.json tagtestsv022baddir.json
replace '\t\t\"Ref\":' '\t\t\"Subdir\": \"subdir\",\n\t\t\"Ref\":' tagtestsv022baddir.json
go mod download -reuse=tagtestsv022baddir.json -x -json vcs-test.golang.org/git/tagtests.git@v0.2.2
! stdout '"Reuse": true'
stdout '"URL": "https://vcs-test.golang.org/git/tagtests"'
stdout '"URL": ".*/git/tagtests"'
# reuse with stale TagSum
cp tagtests.json tagtestsbadtagsum.json

14
src/cmd/go/testdata/vcstest/README vendored Normal file
View File

@ -0,0 +1,14 @@
The scripts in this directory set up version-control repos for use in
tests of cmd/go and its subpackages.
They are written in a dialect of the same script language as in
cmd/go/testdata/script, and the outputs are hosted by the server in
cmd/go/internal/vcweb.
To see the conditions and commands available for these scripts, run:
go test cmd/go/internal/vcweb -v --run=TestHelp
To host these scripts in a standalone server, run:
go test cmd/go/internal/vcweb/vcstest -v --port=0

View File

@ -0,0 +1,32 @@
handle bzr
env BZR_EMAIL='Russ Cox <rsc@google.com>'
bzr init-repo .
bzr init b
cd b
cp ../hello.go .
bzr add hello.go
bzr commit --commit-time='2017-09-21 21:20:12 -0400' -m 'hello world'
bzr push ..
cd ..
rm b
bzr log
cmp stdout .bzr-log
-- .bzr-log --
------------------------------------------------------------
revno: 1
committer: Russ Cox <rsc@google.com>
branch nick: b
timestamp: Thu 2017-09-21 21:20:12 -0400
message:
hello world
-- hello.go --
package main
func main() {
println("hello, world")
}

View File

@ -0,0 +1,22 @@
handle fossil
env USER=rsc
fossil init --date-override 2017-09-22T01:15:36Z hello.fossil
fossil open --keep hello.fossil
fossil add hello.go
fossil commit --no-prompt --nosign --date-override 2017-09-22T01:19:07Z --comment 'hello world'
fossil timeline --oneline
cmp stdout .fossil-timeline
-- .fossil-timeline --
d4c7dcdc29 hello world
58da0d15e9 initial empty check-in
+++ no more data (2) +++
-- hello.go --
package main
func main() {
println("hello, world")
}

View File

@ -0,0 +1,39 @@
handle git
env GIT_AUTHOR_NAME='Bryan C. Mills'
env GIT_AUTHOR_EMAIL='bcmills@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2019-07-15T17:16:47-04:00
git add go.mod main.go
git commit -m 'all: add go.mod and main.go'
git tag v1.0.0
at 2019-07-15T17:17:27-04:00
cp _next/main.go main.go
git add main.go
git commit -m 'add init function'
git log --oneline --decorate=short
cmp stdout .git-log
-- .git-log --
b325d82 (HEAD -> master) add init function
8da67e0 (tag: v1.0.0) all: add go.mod and main.go
-- go.mod --
module vcs-test.golang.org/git/commit-after-tag.git
go 1.13
-- main.go --
package main
func main() {}
-- _next/main.go --
package main
func main() {}
func init() {}

View File

@ -0,0 +1,24 @@
handle git
env GIT_AUTHOR_NAME='Bryan C. Mills'
env GIT_AUTHOR_EMAIL='bcmills@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2019-10-07T14:15:32-04:00
git add go.mod
git commit -m 'add go.mod file without go source files'
git tag v2.0.0
git log --oneline --decorate=short
cmp stdout .git-log
-- .git-log --
122733c (HEAD -> master, tag: v2.0.0) add go.mod file without go source files
-- go.mod --
module vcs-test.golang.org/git/empty-v2-without-v1.git/v2
go 1.14

View File

@ -0,0 +1,21 @@
handle git
env GIT_AUTHOR_NAME='Russ Cox'
env GIT_AUTHOR_EMAIL='rsc@golang.org'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2018-07-03T22:35:49-04:00
git add go.mod
git commit -m 'initial'
git log --oneline
cmp stdout .git-log
-- .git-log --
7bb9146 initial
-- go.mod --
module vcs-test.golang.org/git/emptytest.git

View File

@ -0,0 +1,67 @@
handle git
env GIT_AUTHOR_NAME='Russ Cox'
env GIT_AUTHOR_EMAIL='rsc@golang.org'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2018-04-17T15:43:22-04:00
unquote ''
cp stdout README
git add README
git commit -a -m 'empty README'
git tag v1.2.3
at 2018-04-17T15:45:48-04:00
git branch -c v2
git checkout v2
echo 'v2'
cp stdout v2
git add v2
git commit -a -m 'v2'
git tag v2.3
git tag v2.0.1
git branch -c v2.3.4
at 2018-04-17T16:00:19-04:00
echo 'intermediate'
cp stdout foo.txt
git add foo.txt
git commit -a -m 'intermediate'
at 2018-04-17T16:00:32-04:00
echo 'another'
cp stdout another.txt
git add another.txt
git commit -a -m 'another'
git tag v2.0.2
at 2018-04-17T16:16:52-04:00
git branch -c master v3
git checkout v3
mkdir v3/sub/dir
echo 'v3/sub/dir/file'
cp stdout v3/sub/dir/file.txt
git add v3
git commit -a -m 'add v3/sub/dir/file.txt'
at 2018-04-17T22:23:00-04:00
git checkout master
git tag -a v1.2.4-annotated -m 'v1.2.4-annotated'
git show-ref --tags --heads
cmp stdout .git-refs
-- .git-refs --
ede458df7cd0fdca520df19a33158086a8a68e81 refs/heads/master
9d02800338b8a55be062c838d1f02e0c5780b9eb refs/heads/v2
76a00fb249b7f93091bc2c89a789dab1fc1bc26f refs/heads/v2.3.4
a8205f853c297ad2c3c502ba9a355b35b7dd3ca5 refs/heads/v3
ede458df7cd0fdca520df19a33158086a8a68e81 refs/tags/v1.2.3
b004e48a345a86ed7a2fb7debfa7e0b2f9b0dd91 refs/tags/v1.2.4-annotated
76a00fb249b7f93091bc2c89a789dab1fc1bc26f refs/tags/v2.0.1
9d02800338b8a55be062c838d1f02e0c5780b9eb refs/tags/v2.0.2
76a00fb249b7f93091bc2c89a789dab1fc1bc26f refs/tags/v2.3

View File

@ -0,0 +1,25 @@
handle git
env GIT_AUTHOR_NAME=bwk
env GIT_AUTHOR_EMAIL=bwk
env GIT_COMMITTER_NAME='Russ Cox'
env GIT_COMMITTER_EMAIL='rsc@golang.org'
git init
git branch -m master
at 2017-09-21T21:05:58-04:00
git add hello.go
git commit -a -m 'hello'
git log --oneline --decorate=short
cmp stdout .git-log
-- .git-log --
fc3a09f (HEAD -> master) hello
-- hello.go --
package main
func main() {
println("hello, world")
}

View File

@ -0,0 +1,32 @@
handle git
env GIT_AUTHOR_NAME='Bryan C. Mills'
env GIT_AUTHOR_EMAIL='bcmills@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2019-04-03T13:30:35-04:00
git add go.mod
git commit -m 'all: initialize module'
at 2019-09-04T14:39:48-04:00
git add main.go
git commit -m 'main: add Go source file'
git log --oneline --decorate=short
cmp stdout .git-log
-- .git-log --
6fecd21 (HEAD -> master) main: add Go source file
d1a15cd all: initialize module
-- go.mod --
module vcs-test.golang.org/insecure/go/insecure
go 1.13
-- main.go --
package main
func main() {}

View File

@ -0,0 +1,23 @@
handle git
env GIT_AUTHOR_NAME='Bryan C. Mills'
env GIT_AUTHOR_EMAIL='bcmills@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2019-09-05T14:07:43-04:00
git add main.go
git commit -a -m 'add main.go'
git log --oneline --decorate=short
cmp stdout .git-log
-- .git-log --
8a27e8b (HEAD -> master) add main.go
-- main.go --
package main
func main() {}

View File

@ -0,0 +1,10 @@
handle dir
-- missingrepo-git/index.html --
<!DOCTYPE html>
<html>
<meta name="go-import" content="vcs-test.golang.org/go/missingrepo/missingrepo-git git https://vcs-test.golang.org/git/missingrepo">
-- missingrepo-git/notmissing/index.html --
<!DOCTYPE html>
<html>
<meta name="go-import" content="vcs-test.golang.org/go/missingrepo/missingrepo-git/notmissing git https://vcs-test.golang.org/git/mainonly">

View File

@ -0,0 +1,33 @@
handle git
env GIT_AUTHOR_NAME='Russ Cox'
env GIT_AUTHOR_EMAIL='rsc@golang.org'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2018-04-25T11:00:57-04:00
git add go.mod new.go p1 p2
git commit -m 'initial commit'
git log --oneline --decorate=short
cmp stdout .git-log
-- .git-log --
36cc50a (HEAD -> master) initial commit
-- go.mod --
module "vcs-test.golang.org/git/modlegacy1-new.git/v2"
-- new.go --
package new
import _ "vcs-test.golang.org/git/modlegacy1-new.git/v2/p2"
-- p1/p1.go --
package p1
import _ "vcs-test.golang.org/git/modlegacy1-old.git/p2"
import _ "vcs-test.golang.org/git/modlegacy1-new.git"
import _ "vcs-test.golang.org/git/modlegacy1-new.git/p2"
-- p2/p2.go --
package p2

View File

@ -0,0 +1,27 @@
handle git
env GIT_AUTHOR_NAME='Russ Cox'
env GIT_AUTHOR_EMAIL='rsc@golang.org'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2018-04-25T10:59:24-04:00
git add p1 p2
git commit -m 'initial commit'
git log --oneline --decorate=short
cmp stdout .git-log
-- .git-log --
6b4ba8b (HEAD -> master) initial commit
-- p1/p1.go --
package p1
import _ "vcs-test.golang.org/git/modlegacy1-old.git/p2"
import _ "vcs-test.golang.org/git/modlegacy1-new.git/p1"
import _ "vcs-test.golang.org/git/modlegacy1-new.git"
-- p2/p2.go --
package p2

View File

@ -0,0 +1,27 @@
handle git
env GIT_AUTHOR_NAME='Bryan C. Mills'
env GIT_AUTHOR_EMAIL='bcmills@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2019-07-15T17:20:47-04:00
git add go.mod main.go
git commit -m 'all: add go.mod and main.go'
git log --oneline --decorate=short
cmp stdout .git-log
-- .git-log --
e706ba1 (HEAD -> master) all: add go.mod and main.go
-- go.mod --
module vcs-test.golang.org/git/no-tags.git
go 1.13
-- main.go --
package main
func main() {}

View File

@ -0,0 +1,48 @@
handle git
env GIT_AUTHOR_NAME='Bryan C. Mills'
env GIT_AUTHOR_EMAIL='bcmills@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
at 2022-02-23T13:48:02-05:00
git add README.txt
git commit -m 'initial state'
git tag 'v2.0.0+incompatible'
at 2022-02-23T13:48:35-05:00
git rm -r README.txt
git add go.mod
git commit -m 'migrate to Go modules'
git tag 'v0.1.0+build-metadata'
at 2022-02-23T14:41:55-05:00
git branch -c v3-dev
git checkout v3-dev
cp v3/go.mod go.mod
git commit go.mod -m 'update to /v3'
git tag 'v3.0.0-20220223184802-12d19af20458'
git checkout main
git show-ref --tags --heads
cmp stdout .git-refs
-- .git-refs --
9d863d525bbfcc8eda09364738c4032393711a56 refs/heads/main
cce3d0f5d2ec85678cca3c45ac4a87f3be5efaca refs/heads/v3-dev
9d863d525bbfcc8eda09364738c4032393711a56 refs/tags/v0.1.0+build-metadata
12d19af204585b0db3d2a876ceddf5b9323f5a4a refs/tags/v2.0.0+incompatible
cce3d0f5d2ec85678cca3c45ac4a87f3be5efaca refs/tags/v3.0.0-20220223184802-12d19af20458
-- README.txt --
This module lacks a go.mod file.
-- go.mod --
module vcs-test.golang.org/git/odd-tags.git
go 1.18
-- v3/go.mod --
module vcs-test.golang.org/git/odd-tags.git/v3
go 1.18

View File

@ -0,0 +1,53 @@
handle git
env GIT_AUTHOR_NAME='Jay Conrod'
env GIT_AUTHOR_EMAIL='jayconrod@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
at 2019-05-09T18:35:00-04:00
git init
git branch -m master
git add sub
git commit -m 'create module sub'
echo 'v0.1.0'
cp stdout status
git add status
git commit -a -m 'v0.1.0'
git tag 'v0.1.0'
echo 'sub/v0.0.9'
cp stdout status
git commit -a -m 'sub/v0.0.9'
git tag 'sub/v0.0.9'
echo 'sub/v0.0.10'
cp stdout status
git commit -a -m 'sub/v0.0.10'
git tag 'sub/v0.0.10'
echo 'v0.2.0'
cp stdout status
git commit -a -m 'v0.2.0'
git tag 'v0.2.0'
echo 'after last tag'
cp stdout status
git commit -a -m 'after last tag'
git show-ref --tags --heads
cmp stdout .git-refs
-- .git-refs --
c3ee5d0dfbb9bf3c4d8bb2bce24cd8d14d2d4238 refs/heads/master
2b7c4692e12c109263cab51b416fcc835ddd7eae refs/tags/sub/v0.0.10
883885166298d79a0561d571a3044ec5db2e7c28 refs/tags/sub/v0.0.9
db89fc573cfb939faf0aa0660671eb4cf8b8b673 refs/tags/v0.1.0
1abe41965749e50828dd41de8d12c6ebc8e4e131 refs/tags/v0.2.0
-- sub/go.mod --
module vcs-test.golang.org/git/prefixtagtests.git/sub
-- sub/sub.go --
package sub

View File

@ -0,0 +1,273 @@
handle git
env GIT_AUTHOR_NAME='Russ Cox'
env GIT_AUTHOR_EMAIL='rsc@golang.org'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2018-07-03T22:31:01-04:00
git add go.mod
git commit -a -m 'v1'
git tag start
git branch -c v2
at 2018-07-03T22:33:47-04:00
echo 'before v0.0.0-pre1'
cp stdout status
git add status
git commit -a -m 'before v0.0.0-pre1'
echo 'at v0.0.0-pre1'
cp stdout status
git commit -a -m 'at v0.0.0-pre1'
git tag 'v0.0.0-pre1'
echo 'before v0.0.0'
cp stdout status
git commit -a -m 'before v0.0.0'
echo 'at v0.0.0'
cp stdout status
git commit -a -m 'at v0.0.0'
git tag 'v0.0.0'
echo 'before v0.0.1'
cp stdout status
git commit -a -m 'before v0.0.1'
echo 'at v0.0.1'
cp stdout status
git commit -a -m 'at v0.0.1'
git tag 'v0.0.1'
echo 'before v0.0.2'
cp stdout status
git commit -a -m 'before v0.0.2'
echo 'at v0.0.2'
cp stdout status
git commit -a -m 'at v0.0.2'
git tag 'v0.0.2'
echo 'before v0.0.3'
cp stdout status
git commit -a -m 'before v0.0.3'
echo 'at v0.0.3'
cp stdout status
git commit -a -m 'at v0.0.3'
git tag 'v0.0.3'
git tag favorite
echo 'before v0.1.0'
cp stdout status
git commit -a -m 'before v0.1.0'
echo 'at v0.1.0'
cp stdout status
git commit -a -m 'at v0.1.0'
git tag v0.1.0
echo 'before v0.1.1'
cp stdout status
git commit -a -m 'before v0.1.1'
echo 'at v0.1.1'
cp stdout status
git commit -a -m 'at v0.1.1'
git tag 'v0.1.1'
echo 'before v0.1.2'
cp stdout status
git commit -a -m 'before v0.1.2'
echo 'at v0.1.2'
cp stdout status
git commit -a -m 'at v0.1.2'
git tag 'v0.1.2'
echo 'before v0.3.0'
cp stdout status
git commit -a -m 'before v0.3.0'
echo 'at v0.3.0'
cp stdout status
git commit -a -m 'at v0.3.0'
git tag 'v0.3.0'
echo 'before v1.0.0'
cp stdout status
git commit -a -m 'before v1.0.0'
echo 'at v1.0.0'
cp stdout status
git commit -a -m 'at v1.0.0'
git tag 'v1.0.0'
echo 'before v1.1.0'
cp stdout status
git commit -a -m 'before v1.1.0'
echo 'at v1.1.0'
cp stdout status
git commit -a -m 'at v1.1.0'
git tag 'v1.1.0'
echo 'before v1.9.0'
cp stdout status
git commit -a -m 'before v1.9.0'
echo 'at v1.9.0'
cp stdout status
git commit -a -m 'at v1.9.0'
git tag 'v1.9.0'
echo 'before v1.9.9'
cp stdout status
git commit -a -m 'before v1.9.9'
echo 'at v1.9.9'
cp stdout status
git commit -a -m 'at v1.9.9'
git tag 'v1.9.9'
at 2018-07-03T22:45:01-04:00
echo 'before v1.9.10-pre1'
cp stdout status
git commit -a -m 'before v1.9.10-pre1'
echo 'at v1.9.10-pre1'
cp stdout status
git commit -a -m 'at v1.9.10-pre1'
git tag 'v1.9.10-pre1'
at 2018-07-03T22:50:24-04:00
git checkout v2
cp v2/go.mod go.mod
git add go.mod
git commit -a -m 'v2'
at 2018-07-03T22:51:14-04:00
echo 'before v2.0.0'
cp stdout status
git add status
git commit -a -m 'before v2.0.0'
at 2018-07-03T22:51:14-04:00
echo 'at v2.0.0'
cp stdout status
git commit -a -m 'at v2.0.0'
git tag 'v2.0.0'
at 2018-07-03T22:51:14-04:00
echo 'before v2.1.0'
cp stdout status
git commit -a -m 'before v2.1.0'
at 2018-07-03T22:51:14-04:00
echo 'at v2.1.0'
cp stdout status
git commit -a -m 'at v2.1.0'
git tag 'v2.1.0'
at 2018-07-03T22:51:14-04:00
echo 'before v2.2.0'
cp stdout status
git commit -a -m 'before v2.2.0'
at 2018-07-03T22:51:14-04:00
echo 'at v2.2.0'
cp stdout status
git commit -a -m 'at v2.2.0'
git tag 'v2.2.0'
at 2018-07-03T22:51:14-04:00
echo 'before v2.5.5'
cp stdout status
git commit -a -m 'before v2.5.5'
at 2018-07-03T22:51:14-04:00
echo 'at v2.5.5'
cp stdout status
git commit -a -m 'at v2.5.5'
git tag 'v2.5.5'
at 2018-07-03T23:35:18-04:00
echo 'after v2.5.5'
cp stdout status
git commit -a -m 'after v2.5.5'
env GIT_AUTHOR_NAME='Bryan C. Mills'
env GIT_AUTHOR_EMAIL=bcmills@google.com
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git checkout v2.5.5
at 2019-05-13T17:13:56-04:00
echo 'before v2.6.0-pre1'
cp stdout status
git commit -a -m 'before v2.6.0-pre1'
at 2019-05-13T17:13:56-04:00
echo 'at v2.6.0-pre1'
cp stdout status
git commit -a -m 'at v2.6.0-pre1'
git tag 'v2.6.0-pre1'
git checkout master
at 2019-05-13T16:11:25-04:00
echo 'before v1.9.10-pre2+metadata'
cp stdout status
git commit -a -m 'before v1.9.10-pre2+metadata'
at 2019-05-13T16:11:26-04:00
echo 'at v1.9.10-pre2+metadata'
cp stdout status
git commit -a -m 'at v1.9.10-pre2+metadata'
git tag 'v1.9.10-pre2+metadata'
at 2019-12-20T08:46:14-05:00
echo 'after v1.9.10-pre2+metadata'
cp stdout status
git commit -a -m 'after v1.9.10-pre2+metadata'
git show-ref --tags --heads
cmp stdout .git-refs
-- .git-refs --
ed5ffdaa1f5e7e0be6f5ba2d63097026506224f2 refs/heads/master
feed8f518cf4a7215a3b2a8268b8b0746dcbb12d refs/heads/v2
f6abd4e3ed7f2297bc8fd2888bd6d5412e255fcc refs/tags/favorite
5e9e31667ddfe16e9350f4bd00acc933c8cd5e56 refs/tags/start
0de900e0063bcc310ea0621bfbc227a9b4e3b020 refs/tags/v0.0.0
e5ec98b1c15df29e3bd346d538d73b6e8c3b500c refs/tags/v0.0.0-pre1
179bc86b1be3f6d4553f77ebe68a8b6d750ceff8 refs/tags/v0.0.1
81da2346e009fa1072fe4de3a9a223398ea8ec39 refs/tags/v0.0.2
f6abd4e3ed7f2297bc8fd2888bd6d5412e255fcc refs/tags/v0.0.3
7a1b6bf60ae5bb2b2bd49d152e0bbad806056122 refs/tags/v0.1.0
daedca9abee3171fe45e0344098a993675ac799e refs/tags/v0.1.1
ce829e0f1c45a2eca0f1ad16d7c1aca7cddb433b refs/tags/v0.1.2
44aadfee25d86acb32d6f352afd1d602b0e3a651 refs/tags/v0.3.0
20756d3a393908b2edb5db0f0bb954e962860168 refs/tags/v1.0.0
b0bf267f64b7d5b5cabe22fbcad22f3f1642b7e5 refs/tags/v1.1.0
609dca58c03f0ddf1d8ebe46c1f74fc6a99f3e73 refs/tags/v1.9.0
e0cf3de987e660c21b6950e85b317ce5f7fbb9d9 refs/tags/v1.9.10-pre1
42abcb6df8eee6983aeca9a307c28ea40530aceb refs/tags/v1.9.10-pre2+metadata
5ba9a4ea62136ae86213feba68bc73858f55b7e1 refs/tags/v1.9.9
9763aa065ae27c6cacec5ca8b6dfa43a1b31dea0 refs/tags/v2.0.0
23c28cb696ff40a2839ce406f2c173aa6c3cdda6 refs/tags/v2.1.0
1828ee9f8074075675013e4d488d5d49ddc1b502 refs/tags/v2.2.0
d7352560158175e3b6aa11e22efb06d9e87e6eea refs/tags/v2.5.5
fb9e35b393eb0cccc37e13e243ce60b4ff8c7eea refs/tags/v2.6.0-pre1
-- go.mod --
module vcs-test.golang.org/git/querytest.git
-- v2/go.mod --
module vcs-test.golang.org/git/querytest.git/v2

View File

@ -0,0 +1,32 @@
handle git
env GIT_AUTHOR_NAME='Jay Conrod'
env GIT_AUTHOR_EMAIL='jayconrod@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
at 2020-10-09T13:37:47-04:00
git init
git add go.mod p.go
git commit -m 'create module retract-pseudo'
git tag v1.0.0
git mv p.go q.go
git commit -m 'trivial change'
git show-ref --tags --heads
cmp stdout .git-refs
-- .git-refs --
713affd19d7b9b6dc876b603017f3dcaab8ba674 refs/heads/main
64c061ed4371ef372b6bbfd58ee32015d6bfc3e5 refs/tags/v1.0.0
-- go.mod --
module vcs-test.golang.org/git/retract-pseudo.git
go 1.16
retract v1.0.0
-- p.go --
package p

View File

@ -0,0 +1,52 @@
handle git
env GIT_AUTHOR_NAME='Bryan C. Mills'
env GIT_AUTHOR_EMAIL='bcmills@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
at 2022-02-02T14:15:21-05:00
git add pkg go.mod
git commit -a -m 'pkg: add empty package'
git tag 'v0.1.0'
at 2022-02-02T14:19:44-05:00
git branch -c 'v1.0.0'
git branch -c 'v2.0.0'
git checkout 'v1.0.0'
cp v1/pkg/pkg.go pkg/pkg.go
git commit -a -m 'pkg: start developing toward v1.0.0'
at 2022-02-03T10:53:13-05:00
git branch -c 'v3.0.0-devel'
git checkout 'v3.0.0-devel'
git checkout v0.1.0 pkg/pkg.go
git commit -a -m 'pkg: remove panic'
git tag v4.0.0-beta.1
git checkout main
git show-ref --tags --heads
cmp stdout .git-refs
-- .git-refs --
33ea7ee36f3e3f44f528664b3712c9fa0cef7502 refs/heads/main
09c4d8f6938c7b5eeae46858a72712b8700fa46a refs/heads/v1.0.0
33ea7ee36f3e3f44f528664b3712c9fa0cef7502 refs/heads/v2.0.0
d59622f6e4d77f008819083582fde71ea1921b0c refs/heads/v3.0.0-devel
33ea7ee36f3e3f44f528664b3712c9fa0cef7502 refs/tags/v0.1.0
d59622f6e4d77f008819083582fde71ea1921b0c refs/tags/v4.0.0-beta.1
-- go.mod --
module vcs-test.golang.org/git/semver-branch.git
go 1.16
-- pkg/pkg.go --
package pkg
-- v1/pkg/pkg.go --
package pkg
func init() {
panic("TODO")
}

View File

@ -0,0 +1,44 @@
handle git
env GIT_AUTHOR_NAME='Jay Conrod'
env GIT_AUTHOR_EMAIL='jayconrod@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
at 2019-05-09T18:56:25-04:00
git init
git branch -m master
git add go.mod tagtests.go
git commit -m 'create module tagtests'
git branch -c b
git add v0.2.1
git commit -m 'v0.2.1'
git tag 'v0.2.1'
git checkout b
git add 'v0.2.2'
git commit -m 'v0.2.2'
git tag 'v0.2.2'
git checkout master
git merge b -m 'merge'
git show-ref --tags --heads
cmp stdout .git-refs
-- .git-refs --
59356c8cd18c5fe9a598167d98a6843e52d57952 refs/heads/b
c7818c24fa2f3f714c67d0a6d3e411c85a518d1f refs/heads/master
101c49f5af1b2466332158058cf5f03c8cca6429 refs/tags/v0.2.1
59356c8cd18c5fe9a598167d98a6843e52d57952 refs/tags/v0.2.2
-- go.mod --
module vcs-test.golang.org/git/tagtests.git
-- tagtests.go --
package tagtests
-- v0.2.1 --
v0.2.1
-- v0.2.2 --
v0.2.2

View File

@ -0,0 +1,26 @@
handle git
env GIT_AUTHOR_NAME='Bryan C. Mills'
env GIT_AUTHOR_EMAIL='bcmills@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2019-04-03T11:52:15-04:00
env GIT_AUTHOR_DATE=2019-04-03T11:44:11-04:00
git add go.mod
git commit -m 'all: add go.mod'
git tag 'v2.0.0'
git show-ref --tags --heads
cmp stdout .git-refs
-- .git-refs --
203b91c896acd173aa719e4cdcb7d463c4b090fa refs/heads/master
203b91c896acd173aa719e4cdcb7d463c4b090fa refs/tags/v2.0.0
-- go.mod --
module vcs-test.golang.org/go/v2module/v2
go 1.12

View File

@ -0,0 +1,34 @@
handle git
env GIT_AUTHOR_NAME='Bryan C. Mills'
env GIT_AUTHOR_EMAIL='bcmills@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
at 2022-02-22T15:53:33-05:00
git add v2sub.go v2
git commit -m 'all: add package v2sub and v2sub/v2'
git tag v2.0.0
at 2022-02-22T15:55:07-05:00
git add README.txt
git commit -m 'v2sub: add README.txt'
git show-ref --tags --heads
cmp stdout .git-refs
-- .git-refs --
80beb17a16036f17a5aedd1bb5bd6d407b3c6dc5 refs/heads/main
5fcd3eaeeb391d399f562fd45a50dac9fc34ae8b refs/tags/v2.0.0
-- v2/go.mod --
module vcs-test.golang.org/git/v2sub.git/v2
go 1.16
-- v2/v2sub.go --
package v2sub
-- v2sub.go --
package v2sub
-- README.txt --
This root module lacks a go.mod file.

View File

@ -0,0 +1,28 @@
handle git
env GIT_AUTHOR_NAME='Bryan C. Mills'
env GIT_AUTHOR_EMAIL='bcmills@google.com'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2019-07-15T14:01:24-04:00
env GIT_AUTHOR_DATE=2019-07-15T13:59:34-04:00
git add go.mod v3pkg.go
git commit -a -m 'all: add go.mod with v3 path'
git tag 'v3.0.0'
git show-ref --tags --heads
cmp stdout .git-refs
-- .git-refs --
a3eab1261b8e3164bcbde9171c23d5fd36e32a85 refs/heads/master
a3eab1261b8e3164bcbde9171c23d5fd36e32a85 refs/tags/v3.0.0
-- go.mod --
module vcs-test.golang.org/git/v3pkg.git/v3
go 1.13
-- v3pkg.go --
package v3pkg

View File

@ -0,0 +1,257 @@
handle git
env GIT_AUTHOR_NAME='Russ Cox'
env GIT_AUTHOR_EMAIL='rsc@golang.org'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2018-02-19T17:21:09-05:00
git add LICENSE README.md
git commit -m 'initial commit'
git checkout --detach HEAD
at 2018-02-19T18:10:06-05:00
mkdir pkg
echo 'package p // pkg/p.go'
cp stdout pkg/p.go
git add pkg/p.go
git commit -m 'add pkg/p.go'
git tag v0.0.0
git tag v1.0.0
git tag mytag
git checkout --detach HEAD
at 2018-02-19T18:14:23-05:00
mkdir v2
echo 'module "github.com/rsc/vgotest1/v2" // root go.mod'
cp stdout go.mod
git add go.mod
git commit -m 'go.mod v2'
git tag v2.0.1
at 2018-02-19T18:15:11-05:00
mkdir submod/pkg
echo 'package p // submod/pkg/p.go'
cp stdout submod/pkg/p.go
git add submod/pkg/p.go
git commit -m 'submod/pkg/p.go'
git tag v2.0.2
at 2018-02-19T18:16:04-05:00
echo 'module "github.com/rsc/vgotest" // v2/go.mod'
cp stdout v2/go.mod
git add v2/go.mod
git commit -m 'v2/go.mod: bad go.mod (no version)'
git tag v2.0.3
at 2018-02-19T19:03:38-05:00
env GIT_AUTHOR_DATE=2018-02-19T18:16:38-05:00
echo 'module "github.com/rsc/vgotest1/v2" // v2/go.mod'
cp stdout v2/go.mod
git add v2/go.mod
git commit -m 'v2/go.mod: fix'
git tag v2.0.4
at 2018-02-19T19:03:59-05:00
env GIT_AUTHOR_DATE=2018-02-19T18:17:02-05:00
echo 'module "github.com/rsc/vgotest1" // root go.mod'
cp stdout go.mod
git add go.mod
git commit -m 'go.mod: drop v2'
git tag v2.0.5
git checkout --detach mytag
at 2018-02-19T18:10:28-05:00
echo 'module "github.com/rsc/vgotest1" // root go.mod'
cp stdout go.mod
git add go.mod
git commit -m 'go.mod'
git tag v0.0.1
git tag v1.0.1
at 2018-02-19T18:11:28-05:00
mkdir submod/pkg
echo 'package pkg // submod/pkg/p.go'
cp stdout submod/pkg/p.go
git add submod
git commit -m 'submod/pkg/p.go'
git tag v1.0.2
at 2018-02-19T18:12:07-05:00
echo 'module "github.com/vgotest1/submod" // submod/go.mod'
cp stdout submod/go.mod
git add submod/go.mod
git commit -m 'submod/go.mod'
git tag v1.0.3
git tag submod/v1.0.4
at 2018-02-19T18:12:59-05:00
git apply 0001-submod-go.mod-add-require-vgotest1-v1.1.0.patch
git commit -a -m 'submod/go.mod: add require vgotest1 v1.1.0'
git tag submod/v1.0.5
at 2018-02-19T18:13:36-05:00
git apply 0002-go.mod-add-require-submod-v1.0.5.patch
git commit -a -m 'go.mod: add require submod v1.0.5'
git tag v1.1.0
git checkout master
at 2018-02-19T17:23:01-05:00
mkdir pkg
echo 'package pkg'
cp stdout pkg/p.go
git add pkg/p.go
git commit -m 'pkg: add'
at 2018-02-19T17:30:23-05:00
env GIT_AUTHOR_DATE=2018-02-19T17:24:48-05:00
echo 'module "github.com/vgotest1/v2"'
cp stdout go.mod
git add go.mod
git commit -m 'add go.mod'
at 2018-02-19T17:30:45-05:00
echo 'module "github.com/vgotest1"'
cp stdout go.mod
git add go.mod
git commit -m 'bad mod path'
at 2018-02-19T17:31:34-05:00
mkdir v2
echo 'module "github.com/vgotest1/v2"'
cp stdout v2/go.mod
git add v2/go.mod
git commit -m 'add v2/go.mod'
at 2018-02-19T17:32:37-05:00
echo 'module "github.com/vgotest1/v2"'
cp stdout go.mod
git add go.mod
git commit -m 'say v2 in root go.mod'
git checkout --detach HEAD
at 2018-02-19T17:51:24-05:00
# README.md at this commit lacked a trailing newline, so 'git apply' can't
# seem to apply it correctly as a patch. Instead, we use 'echo -e' to write
# the exact contents.
unquote 'This is a test repo for versioned go.\nThere''s nothing useful here.\n\n v0.0.0 - has pkg/p.go\n v0.0.1 - has go.mod\n \n v1.0.0 - has pkg/p.go\n v1.0.1 - has go.mod\n v1.0.2 - has submod/pkg/p.go\n v1.0.3 - has submod/go.mod\n submod/v1.0.4 - same\n submod/v1.0.5 - add requirement on v1.1.0\n v1.1.0 - add requirement on submod/v1.0.5\n \n v2.0.0 - has pkg/p.go\n v2.0.1 - has go.mod with v2 module path\n v2.0.2 - has go.mod with v1 (no version) module path\n v2.0.3 - has v2/go.mod with v2 module path\n v2.0.5 - has go.mod AND v2/go.mod with v2 module path\n '
cp stdout README.md
mkdir v2/pkg
echo 'package q'
cp stdout v2/pkg/q.go
git add README.md v2/pkg/q.go
git commit -m 'add q'
git tag v2.0.6
git checkout --detach mytag~1
at 2018-07-18T21:21:27-04:00
env GIT_AUTHOR_DATE=2018-02-19T18:10:06-05:00
mkdir pkg
echo 'package p // pkg/p.go'
cp stdout pkg/p.go
git add pkg/p.go
unquote 'add pkg/p.go\n\nv2\n'
cp stdout COMMIT_MSG
git commit -F COMMIT_MSG
git tag v2.0.0
git checkout master
git show-ref --tags --heads
cmp stdout .git-refs
-- .git-refs --
a08abb797a6764035a9314ed5f1d757e0224f3bf refs/heads/master
80d85c5d4d17598a0e9055e7c175a32b415d6128 refs/tags/mytag
8afe2b2efed96e0880ecd2a69b98a53b8c2738b6 refs/tags/submod/v1.0.4
70fd92eaa4dacf82548d0c6099f5b853ae2c1fc8 refs/tags/submod/v1.0.5
80d85c5d4d17598a0e9055e7c175a32b415d6128 refs/tags/v0.0.0
5a115c66393dd8c4a5cc3215653850d7f5640d0e refs/tags/v0.0.1
80d85c5d4d17598a0e9055e7c175a32b415d6128 refs/tags/v1.0.0
5a115c66393dd8c4a5cc3215653850d7f5640d0e refs/tags/v1.0.1
2e38a1a347ba4d9e9946ec0ce480710ff445c919 refs/tags/v1.0.2
8afe2b2efed96e0880ecd2a69b98a53b8c2738b6 refs/tags/v1.0.3
b769f2de407a4db81af9c5de0a06016d60d2ea09 refs/tags/v1.1.0
45f53230a74ad275c7127e117ac46914c8126160 refs/tags/v2.0.0
ea65f87c8f52c15ea68f3bdd9925ef17e20d91e9 refs/tags/v2.0.1
f7b23352af1cd750b11e4673b20b24c2d239430a refs/tags/v2.0.2
f18795870fb14388a21ef3ebc1d75911c8694f31 refs/tags/v2.0.3
1f863feb76bc7029b78b21c5375644838962f88d refs/tags/v2.0.4
2f615117ce481c8efef46e0cc0b4b4dccfac8fea refs/tags/v2.0.5
a01a0aef06cbd571294fc5451788cd4eadbfd651 refs/tags/v2.0.6
-- LICENSE --
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- README.md --
This is a test repo for versioned go.
There's nothing useful here.
-- 0001-submod-go.mod-add-require-vgotest1-v1.1.0.patch --
From 70fd92eaa4dacf82548d0c6099f5b853ae2c1fc8 Mon Sep 17 00:00:00 2001
From: Russ Cox <rsc@golang.org>
Date: Mon, 19 Feb 2018 18:12:59 -0500
Subject: [PATCH] submod/go.mod: add require vgotest1 v1.1.0
---
submod/go.mod | 1 +
1 file changed, 1 insertion(+)
diff --git a/submod/go.mod b/submod/go.mod
index 7b18d93..c88de0f 100644
--- a/submod/go.mod
+++ b/submod/go.mod
@@ -1 +1,2 @@
module "github.com/vgotest1/submod" // submod/go.mod
+require "github.com/vgotest1" v1.1.0
--
2.36.1.838.g23b219f8e3
-- 0002-go.mod-add-require-submod-v1.0.5.patch --
From b769f2de407a4db81af9c5de0a06016d60d2ea09 Mon Sep 17 00:00:00 2001
From: Russ Cox <rsc@golang.org>
Date: Mon, 19 Feb 2018 18:13:36 -0500
Subject: [PATCH] go.mod: add require submod v1.0.5
---
go.mod | 1 +
1 file changed, 1 insertion(+)
diff --git a/go.mod b/go.mod
index ac7a6d7..6118671 100644
--- a/go.mod
+++ b/go.mod
@@ -1 +1,2 @@
module "github.com/rsc/vgotest1" // root go.mod
+require "github.com/rsc/vgotest1/submod" v1.0.5
--
2.36.1.838.g23b219f8e3

View File

@ -0,0 +1,17 @@
handle hg
hg init
hg add hello.go
hg commit --user 'Russ Cox <rsc@golang.org>' --date '2017-10-10T19:39:36-04:00' --message 'hello'
hg log -r ':' --template '{node|short} {desc|strip|firstline}\n'
cmp stdout .hg-log
-- .hg-log --
a8c8e7a40da9 hello
-- hello.go --
package main // import "vcs-test.golang.org/go/custom-hg-hello"
func main() {
println("hello")
}

View File

@ -0,0 +1,17 @@
handle hg
hg init
hg add hello.go
hg commit --user 'bwk' --date '2017-09-21T21:14:14-04:00' --message 'hello world'
hg log -r ':' --template '{node|short} {desc|strip|firstline}\n'
cmp stdout .hg-log
-- .hg-log --
e483a7d9f8c9 hello world
-- hello.go --
package main
func main() {
println("hello, world")
}

View File

@ -0,0 +1,138 @@
handle hg
mkdir git
cd git
env GIT_AUTHOR_NAME='Russ Cox'
env GIT_AUTHOR_EMAIL='rsc@golang.org'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
at 2018-04-17T15:43:22-04:00
unquote ''
cp stdout README
git add README
git commit -a -m 'empty README'
git tag v1.2.3
at 2018-04-17T15:45:48-04:00
git branch -c v2
git checkout v2
echo 'v2'
cp stdout v2
git add v2
git commit -a -m 'v2'
git tag v2.3
git tag v2.0.1
git branch -c v2.3.4
git tag branch-v2.3.4
at 2018-04-17T16:00:19-04:00
echo 'intermediate'
cp stdout foo.txt
git add foo.txt
git commit -a -m 'intermediate'
at 2018-04-17T16:00:32-04:00
echo 'another'
cp stdout another.txt
git add another.txt
git commit -a -m 'another'
git tag v2.0.2
git tag branch-v2
at 2018-04-17T16:16:52-04:00
git branch -c master v3
git checkout v3
mkdir v3/sub/dir
echo 'v3/sub/dir/file'
cp stdout v3/sub/dir/file.txt
git add v3
git commit -a -m 'add v3/sub/dir/file.txt'
git tag branch-v3
at 2018-04-17T22:23:00-04:00
git checkout master
git tag -a v1.2.4-annotated -m 'v1.2.4-annotated'
cd ..
hg init
hg convert --datesort ./git .
rm ./git
hg update -C v2
hg branch v2
unquote ''
cp stdout dummy
hg add dummy
hg commit --user 'Russ Cox <rsc@golang.org>' --date '2018-06-27T12:15:24-04:00' -m 'dummy'
# 'hg convert' blindly stamps a tag-update commit at the end of whatever branch
# happened to contain the last converted commit — in this case, v3. However, the
# original vcs-test.golang.org copy of this repo had this commit on the v3
# branch as a descendent of 'add v3/sub/dir/file.txt', so that's where we put it
# here. That leaves the convert-repo 'update tags' commit only reachable as the
# head of the default branch.
hg update -r 4
hg branch v3
unquote ''
cp stdout dummy
hg add dummy
hg commit --user 'Russ Cox <rsc@golang.org>' --date '2018-06-27T12:15:45-04:00' -m 'dummy'
hg update v2.3.4
hg branch v2.3.4
unquote ''
cp stdout dummy
hg add dummy
hg commit --user 'Russ Cox <rsc@golang.org>' --date '2018-06-27T12:16:10-04:00' -m 'dummy'
hg tag --user 'Russ Cox <rsc@golang.org>' --date '2018-06-27T12:16:30-04:00' -m 'Removed tag branch-v2, branch-v3, branch-v2.3.4' --remove branch-v2 branch-v3 branch-v2.3.4
# Adding commits to the above branches updates both the branch heads and the
# corresponding bookmarks.
# But apparently at some point it did not do so? The original copy of this repo
# had bookmarks pointing to the base of each branch instead of the tip. 🤔
# Either way, force the bookmarks we care about to match the original copy of
# the repo.
hg book v2 -r 3 --force
hg book v2.3.4 -r 1 --force
hg book v3 -r 5 --force
hg log -G --debug
hg tags
cmp stdout .hg-tags
# 'hg convert' leaves an 'update tags' commit on the default branch, and that
# commit always uses the current date (so is not reproducible). Fortunately,
# that commit lands on the 'default' branch and is not tagged as 'tip', so it
# seems to be mostly harmless. However, because it is nondeterministic we
# should avoid listing it here.
hg branches -r 6 -r 7 -r 9
cmp stdout .hg-branches
# Likewise, omit bookmark v3, which ends up on the nondeterministic commit.
hg bookmarks -l master v2 v2.3.4
cmp stdout .hg-bookmarks
-- .hg-branches --
v2.3.4 9:18518c07eb8e
v3 7:a2cad8a2b1bb
v2 6:9a4f43d231ec
-- .hg-tags --
tip 9:18518c07eb8e
v2.0.2 3:8f49ee7a6ddc
v2.3 1:88fde824ec8b
v2.0.1 1:88fde824ec8b
v1.2.4-annotated 0:41964ddce118
v1.2.3 0:41964ddce118
-- .hg-bookmarks --
master 0:41964ddce118
v2 3:8f49ee7a6ddc
v2.3.4 1:88fde824ec8b

View File

@ -0,0 +1,322 @@
handle hg
cd git
env GIT_AUTHOR_NAME='Russ Cox'
env GIT_AUTHOR_EMAIL='rsc@golang.org'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
git init
git branch -m master
# 0
at 2018-02-19T17:21:09-05:00
git add LICENSE README.md
git commit -m 'initial commit'
# 1
git branch -c mybranch
git checkout mybranch
at 2018-02-19T18:10:06-05:00
mkdir pkg
echo 'package p // pkg/p.go'
cp stdout pkg/p.go
git add pkg/p.go
git commit -m 'add pkg/p.go'
git tag v0.0.0
git tag v1.0.0
git tag v2.0.0
git tag mytag
git branch -c v1
git branch -c v2
git checkout v2
# 2
at 2018-02-19T18:14:23-05:00
mkdir v2
echo 'module "github.com/rsc/vgotest1/v2" // root go.mod'
cp stdout go.mod
git add go.mod
git commit -m 'go.mod v2'
git tag v2.0.1
# 3
at 2018-02-19T18:15:11-05:00
mkdir submod/pkg
echo 'package p // submod/pkg/p.go'
cp stdout submod/pkg/p.go
git add submod/pkg/p.go
git commit -m 'submod/pkg/p.go'
git tag v2.0.2
# 4
at 2018-02-19T18:16:04-05:00
echo 'module "github.com/rsc/vgotest" // v2/go.mod'
cp stdout v2/go.mod
git add v2/go.mod
git commit -m 'v2/go.mod: bad go.mod (no version)'
git tag v2.0.3
# 5
at 2018-02-19T19:03:38-05:00
env GIT_AUTHOR_DATE=2018-02-19T18:16:38-05:00
echo 'module "github.com/rsc/vgotest1/v2" // v2/go.mod'
cp stdout v2/go.mod
git add v2/go.mod
git commit -m 'v2/go.mod: fix'
git tag v2.0.4
# 6
at 2018-02-19T19:03:59-05:00
env GIT_AUTHOR_DATE=2018-02-19T18:17:02-05:00
echo 'module "github.com/rsc/vgotest1" // root go.mod'
cp stdout go.mod
git add go.mod
git commit -m 'go.mod: drop v2'
git tag v2.0.5
git checkout v1
# 7
at 2018-02-19T18:10:28-05:00
echo 'module "github.com/rsc/vgotest1" // root go.mod'
cp stdout go.mod
git add go.mod
git commit -m 'go.mod'
git tag v0.0.1
git tag v1.0.1
# 8
at 2018-02-19T18:11:28-05:00
mkdir submod/pkg
echo 'package pkg // submod/pkg/p.go'
cp stdout submod/pkg/p.go
git add submod
git commit -m 'submod/pkg/p.go'
git tag v1.0.2
# 9
at 2018-02-19T18:12:07-05:00
echo 'module "github.com/vgotest1/submod" // submod/go.mod'
cp stdout submod/go.mod
git add submod/go.mod
git commit -m 'submod/go.mod'
git tag v1.0.3
git tag submod/v1.0.4
# 10
at 2018-02-19T18:12:59-05:00
git apply ../0001-submod-go.mod-add-require-vgotest1-v1.1.0.patch
git commit -a -m 'submod/go.mod: add require vgotest1 v1.1.0'
git tag submod/v1.0.5
# 11
at 2018-02-19T18:13:36-05:00
git apply ../0002-go.mod-add-require-submod-v1.0.5.patch
git commit -a -m 'go.mod: add require submod v1.0.5'
git tag v1.1.0
git checkout master
# 12
at 2018-02-19T17:23:01-05:00
mkdir pkg
echo 'package pkg'
cp stdout pkg/p.go
git add pkg/p.go
git commit -m 'pkg: add'
# 13
at 2018-02-19T17:30:23-05:00
env GIT_AUTHOR_DATE=2018-02-19T17:24:48-05:00
echo 'module "github.com/vgotest1/v2"'
cp stdout go.mod
git add go.mod
git commit -m 'add go.mod'
# 14
at 2018-02-19T17:30:45-05:00
echo 'module "github.com/vgotest1"'
cp stdout go.mod
git add go.mod
git commit -m 'bad mod path'
# 15
at 2018-02-19T17:31:34-05:00
mkdir v2
echo 'module "github.com/vgotest1/v2"'
cp stdout v2/go.mod
git add v2/go.mod
git commit -m 'add v2/go.mod'
# 16
at 2018-02-19T17:32:37-05:00
echo 'module "github.com/vgotest1/v2"'
cp stdout go.mod
git add go.mod
git commit -m 'say v2 in root go.mod'
# 17
at 2018-02-19T17:51:24-05:00
# README.md at this commit lacked a trailing newline, so 'git apply' can't
# seem to apply it correctly as a patch. Instead, we use 'unquote' to write
# the exact contents.
unquote 'This is a test repo for versioned go.\nThere''s nothing useful here.\n\n v0.0.0 - has pkg/p.go\n v0.0.1 - has go.mod\n \n v1.0.0 - has pkg/p.go\n v1.0.1 - has go.mod\n v1.0.2 - has submod/pkg/p.go\n v1.0.3 - has submod/go.mod\n submod/v1.0.4 - same\n submod/v1.0.5 - add requirement on v1.1.0\n v1.1.0 - add requirement on submod/v1.0.5\n \n v2.0.0 - has pkg/p.go\n v2.0.1 - has go.mod with v2 module path\n v2.0.2 - has go.mod with v1 (no version) module path\n v2.0.3 - has v2/go.mod with v2 module path\n v2.0.5 - has go.mod AND v2/go.mod with v2 module path\n '
cp stdout README.md
mkdir v2/pkg
echo 'package q'
cp stdout v2/pkg/q.go
git add README.md v2/pkg/q.go
git commit -m 'add q'
git tag v2.0.6
cd ..
hg init
hg convert ./git .
rm ./git
# Note: commit #18 is an 'update tags' commit automatically generated by 'hg
# convert'. We have no control over its timestamp, so it and its descendent
# commit #19 both end up with unpredictable commit hashes.
#
# Fortunately, these commits don't seem to matter for the purpose of reproducing
# the final branches and heads from the original copy of this repo.
# 19
hg update -C -r 18
hg tag --user 'Russ Cox <rsc@golang.org>' --date '2018-07-18T21:24:45-04:00' -m 'Removed tag v2.0.0' --remove v2.0.0
# 20
hg branch default
hg update -C -r 1
echo 'v2'
cp stdout v2
hg add v2
hg commit --user 'Russ Cox <rsc@golang.org>' --date '2018-07-18T21:25:08-04:00' -m 'v2.0.0'
# 21
hg tag --user 'Russ Cox <rsc@golang.org>' --date '2018-07-18T21:25:13-04:00' -r f0ababb31f75 -m 'Added tag v2.0.0 for changeset f0ababb31f75' v2.0.0
# 22
hg tag --user 'Russ Cox <rsc@golang.org>' --date '2018-07-18T21:26:02-04:00' -m 'Removed tag v2.0.0' --remove v2.0.0
# 23
hg update -C -r 1
echo 'v2'
cp stdout v2
hg add v2
hg commit --user 'Russ Cox <rsc@golang.org>' --date '2018-07-19T01:21:27+00:00' -m 'v2'
# 24
hg tag --user 'Russ Cox <rsc@golang.org>' --date '2018-07-18T21:26:33-04:00' -m 'Added tag v2.0.0 for changeset 814fce58e83a' -r 814fce58e83a v2.0.0
hg book --delete v1
hg book --delete v2
hg book --force -r 16 master
hg log -G --debug
hg tags
cmp stdout .hg-tags
hg branches
cmp stdout .hg-branches
hg bookmarks -l master mybranch
cmp stdout .hg-bookmarks
-- .hg-tags --
tip 24:645b06ca536d
v2.0.0 23:814fce58e83a
v2.0.6 17:3d4b89a2d059
v1.1.0 11:92c7eb888b4f
submod/v1.0.5 10:f3f560a6065c
v1.0.3 9:4e58084d459a
submod/v1.0.4 9:4e58084d459a
v1.0.2 8:3ccdce3897f9
v1.0.1 7:7890ea771ced
v0.0.1 7:7890ea771ced
v2.0.5 6:879ea98f7743
v2.0.4 5:bf6388016230
v2.0.3 4:a9ad6d1d14eb
v2.0.2 3:de3663002f0f
v2.0.1 2:f1fc0f22021b
v1.0.0 1:e125018e286a
v0.0.0 1:e125018e286a
mytag 1:e125018e286a
-- .hg-branches --
default 24:645b06ca536d
-- .hg-bookmarks --
master 16:577bde103b24
mybranch 1:e125018e286a
-- git/LICENSE --
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- git/README.md --
This is a test repo for versioned go.
There's nothing useful here.
-- 0001-submod-go.mod-add-require-vgotest1-v1.1.0.patch --
From 70fd92eaa4dacf82548d0c6099f5b853ae2c1fc8 Mon Sep 17 00:00:00 2001
From: Russ Cox <rsc@golang.org>
Date: Mon, 19 Feb 2018 18:12:59 -0500
Subject: [PATCH] submod/go.mod: add require vgotest1 v1.1.0
---
submod/go.mod | 1 +
1 file changed, 1 insertion(+)
diff --git a/submod/go.mod b/submod/go.mod
index 7b18d93..c88de0f 100644
--- a/submod/go.mod
+++ b/submod/go.mod
@@ -1 +1,2 @@
module "github.com/vgotest1/submod" // submod/go.mod
+require "github.com/vgotest1" v1.1.0
--
2.36.1.838.g23b219f8e3
-- 0002-go.mod-add-require-submod-v1.0.5.patch --
From b769f2de407a4db81af9c5de0a06016d60d2ea09 Mon Sep 17 00:00:00 2001
From: Russ Cox <rsc@golang.org>
Date: Mon, 19 Feb 2018 18:13:36 -0500
Subject: [PATCH] go.mod: add require submod v1.0.5
---
go.mod | 1 +
1 file changed, 1 insertion(+)
diff --git a/go.mod b/go.mod
index ac7a6d7..6118671 100644
--- a/go.mod
+++ b/go.mod
@@ -1 +1,2 @@
module "github.com/rsc/vgotest1" // root go.mod
+require "github.com/rsc/vgotest1/submod" v1.0.5
--
2.36.1.838.g23b219f8e3