git-codereview: make tests pass on windows
- "git gofmt" uses "git checkout-index" command that is broken if
used with many arguments - limit number of arguments passed to
"git checkout-index";
- change / into \ on windows when matching git output on windows;
- add .gitattributes to test repository to make it look more like
all real Go repositories;
- make sure no files left behind in /tmp on windows.
Fixes golang/go#9433.
Change-Id: Ifceacfffd1cd4b02190d7e941611d0d0f3c48cdd
Reviewed-on: https://go-review.googlesource.com/2089
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/git-codereview/gofmt.go b/git-codereview/gofmt.go
index 4507a2b..93da314 100644
--- a/git-codereview/gofmt.go
+++ b/git-codereview/gofmt.go
@@ -145,18 +145,68 @@
// Git stores the temporary files, named .merge_*, in the repo root.
// Unlike the Git commands above, the non-temp file names printed
// here are relative to the current directory, not the repo root.
- args := []string{"checkout-index", "--temp", "--"}
- args = append(args, needTemp...)
- for _, line := range getLines("git", args...) {
- i := strings.Index(line, "\t")
- if i < 0 {
- continue
+
+ // git checkout-index --temp is broken on windows. Running this command:
+ //
+ // git checkout-index --temp -- bad-bad-bad2.go bad-bad-broken.go bad-bad-good.go bad-bad2-bad.go bad-bad2-broken.go bad-bad2-good.go bad-broken-bad.go bad-broken-bad2.go bad-broken-good.go bad-good-bad.go bad-good-bad2.go bad-good-broken.go bad2-bad-bad2.go bad2-bad-broken.go bad2-bad-good.go bad2-bad2-bad.go bad2-bad2-broken.go bad2-bad2-good.go bad2-broken-bad.go bad2-broken-bad2.go bad2-broken-good.go bad2-good-bad.go bad2-good-bad2.go bad2-good-broken.go broken-bad-bad2.go broken-bad-broken.go broken-bad-good.go broken-bad2-bad.go broken-bad2-broken.go broken-bad2-good.go
+ //
+ // produces this output
+ //
+ // .merge_file_a05448 bad-bad-bad2.go
+ // .merge_file_b05448 bad-bad-broken.go
+ // .merge_file_c05448 bad-bad-good.go
+ // .merge_file_d05448 bad-bad2-bad.go
+ // .merge_file_e05448 bad-bad2-broken.go
+ // .merge_file_f05448 bad-bad2-good.go
+ // .merge_file_g05448 bad-broken-bad.go
+ // .merge_file_h05448 bad-broken-bad2.go
+ // .merge_file_i05448 bad-broken-good.go
+ // .merge_file_j05448 bad-good-bad.go
+ // .merge_file_k05448 bad-good-bad2.go
+ // .merge_file_l05448 bad-good-broken.go
+ // .merge_file_m05448 bad2-bad-bad2.go
+ // .merge_file_n05448 bad2-bad-broken.go
+ // .merge_file_o05448 bad2-bad-good.go
+ // .merge_file_p05448 bad2-bad2-bad.go
+ // .merge_file_q05448 bad2-bad2-broken.go
+ // .merge_file_r05448 bad2-bad2-good.go
+ // .merge_file_s05448 bad2-broken-bad.go
+ // .merge_file_t05448 bad2-broken-bad2.go
+ // .merge_file_u05448 bad2-broken-good.go
+ // .merge_file_v05448 bad2-good-bad.go
+ // .merge_file_w05448 bad2-good-bad2.go
+ // .merge_file_x05448 bad2-good-broken.go
+ // .merge_file_y05448 broken-bad-bad2.go
+ // .merge_file_z05448 broken-bad-broken.go
+ // error: unable to create file .merge_file_XXXXXX (No error)
+ // .merge_file_XXXXXX broken-bad-good.go
+ // error: unable to create file .merge_file_XXXXXX (No error)
+ // .merge_file_XXXXXX broken-bad2-bad.go
+ // error: unable to create file .merge_file_XXXXXX (No error)
+ // .merge_file_XXXXXX broken-bad2-broken.go
+ // error: unable to create file .merge_file_XXXXXX (No error)
+ // .merge_file_XXXXXX broken-bad2-good.go
+ //
+ // so limit the number of file arguments to 25.
+ for len(needTemp) > 0 {
+ n := len(needTemp)
+ if n > 25 {
+ n = 25
}
- temp, file := line[:i], line[i+1:]
- temp = filepath.Join(repo, temp)
- file = filepath.Join(pwd, file)
- tempToFile[temp] = file
- fileToTemp[file] = temp
+ args := []string{"checkout-index", "--temp", "--"}
+ args = append(args, needTemp[:n]...)
+ for _, line := range getLines("git", args...) {
+ i := strings.Index(line, "\t")
+ if i < 0 {
+ continue
+ }
+ temp, file := line[:i], line[i+1:]
+ temp = filepath.Join(repo, temp)
+ file = filepath.Join(pwd, file)
+ tempToFile[temp] = file
+ fileToTemp[file] = temp
+ }
+ needTemp = needTemp[n:]
}
cleanup = func() {
for temp := range tempToFile {
diff --git a/git-codereview/gofmt_test.go b/git-codereview/gofmt_test.go
index 94fd132..e96c717 100644
--- a/git-codereview/gofmt_test.go
+++ b/git-codereview/gofmt_test.go
@@ -40,10 +40,10 @@
trun(t, gt.client, "git", "add", ".") // make files tracked
testMain(t, "gofmt", "-l")
- testPrintedStdout(t, "bad.go\n", "!good.go", "!test/bad", "test/bench/bad.go")
+ testPrintedStdout(t, "bad.go\n", "!good.go", fromSlash("!test/bad"), fromSlash("test/bench/bad.go"))
testMain(t, "gofmt", "-l")
- testPrintedStdout(t, "bad.go\n", "!good.go", "!test/bad", "test/bench/bad.go")
+ testPrintedStdout(t, "bad.go\n", "!good.go", fromSlash("!test/bad"), fromSlash("test/bench/bad.go"))
testMain(t, "gofmt")
testNoStdout(t)
diff --git a/git-codereview/hook_test.go b/git-codereview/hook_test.go
index 15b04a0..9465b55 100644
--- a/git-codereview/hook_test.go
+++ b/git-codereview/hook_test.go
@@ -71,13 +71,13 @@
testMainDied(t, "hook-invoke", "pre-commit")
testPrintedStderr(t, "gofmt needs to format these files (run 'git gofmt'):",
- "bad.go", "!good.go", "!test/bad", "test/bench/bad.go")
+ "bad.go", "!good.go", fromSlash("!test/bad"), fromSlash("test/bench/bad.go"))
write(t, gt.client+"/broken.go", brokenGo)
trun(t, gt.client, "git", "add", "broken.go")
testMainDied(t, "hook-invoke", "pre-commit")
testPrintedStderr(t, "gofmt needs to format these files (run 'git gofmt'):",
- "bad.go", "!good.go", "!test/bad", "test/bench/bad.go",
+ "bad.go", "!good.go", fromSlash("!test/bad"), fromSlash("test/bench/bad.go"),
"gofmt reported errors:", "broken.go")
}
diff --git a/git-codereview/util_test.go b/git-codereview/util_test.go
index 9b3e81f..d9933ad 100644
--- a/git-codereview/util_test.go
+++ b/git-codereview/util_test.go
@@ -26,9 +26,37 @@
client string // client repo root
}
+// resetReadOnlyFlagAll resets windows read-only flag
+// set on path and any children it contains.
+// The flag is set by git and has to be removed.
+// os.Remove refuses to remove files with read-only flag set.
+func resetReadOnlyFlagAll(path string) error {
+ fi, err := os.Stat(path)
+ if err != nil {
+ return err
+ }
+
+ if !fi.IsDir() {
+ return os.Chmod(path, 0666)
+ }
+
+ fd, err := os.Open(path)
+ if err != nil {
+ return err
+ }
+ defer fd.Close()
+
+ names, _ := fd.Readdirnames(-1)
+ for _, name := range names {
+ resetReadOnlyFlagAll(path + string(filepath.Separator) + name)
+ }
+ return nil
+}
+
func (gt *gitTest) done() {
+ os.Chdir(gt.pwd) // change out of gt.tmpdir first, otherwise following os.RemoveAll fails on windows
+ resetReadOnlyFlagAll(gt.tmpdir)
os.RemoveAll(gt.tmpdir)
- os.Chdir(gt.pwd)
}
func (gt *gitTest) work(t *testing.T) {
@@ -51,10 +79,11 @@
mkdir(t, server)
write(t, server+"/file", "this is master")
+ write(t, server+"/.gitattributes", "* -text\n")
trun(t, server, "git", "init", ".")
trun(t, server, "git", "config", "user.name", "gopher")
trun(t, server, "git", "config", "user.email", "gopher@example.com")
- trun(t, server, "git", "add", "file")
+ trun(t, server, "git", "add", "file", ".gitattributes")
trun(t, server, "git", "commit", "-m", "on master")
for _, name := range []string{"dev.branch", "release.branch"} {
@@ -128,6 +157,14 @@
return string(out)
}
+// fromSlash is like filepath.FromSlash, but it ignores ! at the start of the path.
+func fromSlash(path string) string {
+ if len(path) > 0 && path[0] == '!' {
+ return "!" + filepath.FromSlash(path[1:])
+ }
+ return filepath.FromSlash(path)
+}
+
var (
runLog []string
testStderr *bytes.Buffer