git-codereview: fix hook installation in git 2.13.0

git 2.13.0 changed the behavior of git rev-parse --git-path from
printing a path relative to the repo root to printing a path relative
to the working directory. This breaks git-codereview's hook
installation when a git-codereview command is invoked from a directory
other than the repo root, since it will try to resolve this path as
relative to the repo root.

Fix this by running git rev-parse from the repo root, so it behaves
the same either way.

Fixes golang/go#19477.

Change-Id: Ia5e1b81da5041365d817daf9f5979df0772ede4b
Reviewed-on: https://go-review.googlesource.com/38010
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/git-codereview/hook.go b/git-codereview/hook.go
index 0e8f075..f953478 100644
--- a/git-codereview/hook.go
+++ b/git-codereview/hook.go
@@ -77,13 +77,18 @@
 // all other path relocations, e.g. hooks for linked worktrees
 // are not kept in their gitdir, but shared in the main one.
 func gitPath(path string) string {
-	p, err := trimErr(cmdOutputErr("git", "rev-parse", "--git-path", path))
+	root := repoRoot()
+	// git 2.13.0 changed the behavior of --git-path from printing
+	// a path relative to the repo root to printing a path
+	// relative to the working directory (issue #19477). Normalize
+	// both behaviors by running the command from the repo root.
+	p, err := trimErr(cmdOutputErr("git", "-C", root, "rev-parse", "--git-path", path))
 	if err != nil {
 		// When --git-path is not available, assume the common case.
 		p = filepath.Join(".git", path)
 	}
 	if !filepath.IsAbs(p) {
-		p = filepath.Join(repoRoot(), p)
+		p = filepath.Join(root, p)
 	}
 	return p
 }
diff --git a/git-codereview/hook_test.go b/git-codereview/hook_test.go
index 0d171c0..8cb04f9 100644
--- a/git-codereview/hook_test.go
+++ b/git-codereview/hook_test.go
@@ -440,6 +440,27 @@
 	}
 }
 
+func TestHooksInSubdir(t *testing.T) {
+	gt := newGitTest(t)
+	defer gt.done()
+
+	gt.removeStubHooks()
+	if err := os.MkdirAll(gt.client+"/test", 0755); err != nil {
+		t.Fatal(err)
+	}
+	chdir(t, gt.client+"/test")
+
+	testMain(t, "hooks") // install hooks
+
+	data, err := ioutil.ReadFile(gt.client + "/.git/hooks/commit-msg")
+	if err != nil {
+		t.Fatalf("hooks did not write commit-msg hook: %v", err)
+	}
+	if string(data) != "#!/bin/sh\nexec git-codereview hook-invoke commit-msg \"$@\"\n" {
+		t.Fatalf("invalid commit-msg hook:\n%s", string(data))
+	}
+}
+
 func TestHooksOverwriteOldCommitMsg(t *testing.T) {
 	gt := newGitTest(t)
 	defer gt.done()