cmd/gitmirror: test that an empty repository can be mirrored

I accidentally fixed this during the refactoring. Add a test.

Fixes golang/go#39597.

Change-Id: Iac576514ad3685a06f61f1ad93c254914bd4e518
Reviewed-on: https://go-review.googlesource.com/c/build/+/325770
Trust: Heschi Kreinick <heschi@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/cmd/gitmirror/gitmirror.go b/cmd/gitmirror/gitmirror.go
index 9545ff2..38b87d2 100644
--- a/cmd/gitmirror/gitmirror.go
+++ b/cmd/gitmirror/gitmirror.go
@@ -84,6 +84,7 @@
 		gerritClient: gerrit.NewClient("https://go-review.googlesource.com", gerrit.NoAuth),
 		mirrorGitHub: *flagMirrorGitHub,
 		mirrorCSR:    *flagMirrorCSR,
+		timeoutScale: 1,
 	}
 	http.HandleFunc("/", m.handleRoot)
 
@@ -213,6 +214,7 @@
 	homeDir                 string
 	gerritClient            *gerrit.Client
 	mirrorGitHub, mirrorCSR bool
+	timeoutScale            int
 }
 
 func (m *gitMirror) addRepo(meta *repospkg.Repo) *repo {
@@ -456,7 +458,7 @@
 func (r *repo) loop() {
 	for {
 		if err := r.loopOnce(); err != nil {
-			time.Sleep(10 * time.Second)
+			time.Sleep(10 * time.Second * time.Duration(r.mirror.timeoutScale))
 			continue
 		}
 
@@ -499,7 +501,7 @@
 // fetch runs "git fetch" in the repository root.
 // It tries three times, just in case it failed because of a transient error.
 func (r *repo) fetch() error {
-	err := try(3, func(attempt int) error {
+	err := r.try(3, func(attempt int) error {
 		r.setStatus(fmt.Sprintf("running git fetch origin, attempt %d", attempt))
 		if _, stderr, err := r.runGitLogged("fetch", "--prune", "origin"); err != nil {
 			return fmt.Errorf("%v\n\n%s", err, stderr)
@@ -517,7 +519,7 @@
 // push runs "git push -f --mirror dest" in the repository root.
 // It tries three times, just in case it failed because of a transient error.
 func (r *repo) push(dest string) error {
-	err := try(3, func(attempt int) error {
+	err := r.try(3, func(attempt int) error {
 		r.setStatus(fmt.Sprintf("syncing to %v, attempt %d", dest, attempt))
 		if _, stderr, err := r.runGitLogged("push", "-f", "--mirror", dest); err != nil {
 			return fmt.Errorf("%v\n\n%s", err, stderr)
@@ -588,10 +590,10 @@
 	fmt.Fprintf(w, "\n</pre></body></html>")
 }
 
-func try(n int, fn func(attempt int) error) error {
+func (r *repo) try(n int, fn func(attempt int) error) error {
 	var err error
 	for tries := 0; tries < n; tries++ {
-		time.Sleep(time.Duration(tries) * 5 * time.Second) // Linear back-off.
+		time.Sleep(time.Duration(tries) * 5 * time.Second * time.Duration(r.mirror.timeoutScale)) // Linear back-off.
 		if err = fn(tries); err == nil {
 			break
 		}
diff --git a/cmd/gitmirror/gitmirror_test.go b/cmd/gitmirror/gitmirror_test.go
index 2898a4f..344b5f0 100644
--- a/cmd/gitmirror/gitmirror_test.go
+++ b/cmd/gitmirror/gitmirror_test.go
@@ -79,6 +79,21 @@
 	}
 }
 
+// Tests that mirroring an initially empty repository works. See golang/go#39597.
+// The repository still has to exist.
+func TestMirrorInitiallyEmpty(t *testing.T) {
+	tm := newTestMirror(t)
+	if err := tm.m.repos["build"].loopOnce(); err == nil {
+		t.Error("expected error mirroring empty repository, got none")
+	}
+	tm.commit("first commit")
+	tm.loopOnce()
+	rev := tm.git(tm.gerrit, "rev-parse", "HEAD")
+	if githubRev := tm.git(tm.github, "rev-parse", "HEAD"); rev != githubRev {
+		t.Errorf("github HEAD is %v, want %v", githubRev, rev)
+	}
+}
+
 type testMirror struct {
 	gerrit, github, csr string
 	m                   *gitMirror
@@ -107,6 +122,7 @@
 			repos:        map[string]*repo{},
 			mirrorGitHub: true,
 			mirrorCSR:    true,
+			timeoutScale: 0,
 		},
 		t: t,
 	}