git-codereview: fix hooks install when using 'git worktree'
Use 'git rev-parse' to obtain the repo root (toplevel) and the hooks
directory (git-path hooks).
Git knows better how to handle more complex scenarios, e.g. in a
linked worktree (created using "git worktree add"), ".git" is not
a directory but a regular file, that contains the path to the linked
gitdir, that then contains the path to the main gitdir. The hooks
should be there.
For older version git versions without --git-path (and without
worktree), fallback to the previous approach.
Updates golang/go#12182.
Change-Id: I7a90362409fc5000282db95c4ec2ab5052ae59a8
Reviewed-on: https://go-review.googlesource.com/19882
Run-TryBot: Andrew Gerrand <adg@golang.org>
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/git-codereview/hook.go b/git-codereview/hook.go
index 264c85b..f10c070 100644
--- a/git-codereview/hook.go
+++ b/git-codereview/hook.go
@@ -13,19 +13,18 @@
"os"
"path/filepath"
"regexp"
- "runtime"
"strings"
)
-var hookPath = ".git/hooks/"
var hookFiles = []string{
"commit-msg",
"pre-commit",
}
func installHook() {
+ hooksDir := gitPath("hooks")
for _, hookFile := range hookFiles {
- filename := filepath.Join(repoRoot(), hookPath+hookFile)
+ filename := filepath.Join(hooksDir, hookFile)
hookContent := fmt.Sprintf(hookScript, hookFile)
if data, err := ioutil.ReadFile(filename); err == nil {
@@ -70,23 +69,22 @@
}
func repoRoot() string {
- dir, err := os.Getwd()
+ return filepath.Clean(trim(cmdOutput("git", "rev-parse", "--show-toplevel")))
+}
+
+// gitPath resolve the $GIT_DIR/path, taking in consideration
+// 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))
if err != nil {
- dief("could not get current directory: %v", err)
+ // When --git-path is not available, assume the common case.
+ p = filepath.Join(".git", path)
}
- rootlen := 1
- if runtime.GOOS == "windows" {
- rootlen += len(filepath.VolumeName(dir))
+ if !filepath.IsAbs(p) {
+ p = filepath.Join(repoRoot(), p)
}
- for {
- if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil {
- return dir
- }
- if len(dir) == rootlen && dir[rootlen-1] == filepath.Separator {
- dief("git root not found. Rerun from within the Git tree.")
- }
- dir = filepath.Dir(dir)
- }
+ return p
}
var hookScript = `#!/bin/sh
diff --git a/git-codereview/hook_test.go b/git-codereview/hook_test.go
index 247666c..fe083bb 100644
--- a/git-codereview/hook_test.go
+++ b/git-codereview/hook_test.go
@@ -10,6 +10,7 @@
"io/ioutil"
"os"
"path/filepath"
+ "regexp"
"strings"
"testing"
)
@@ -411,6 +412,36 @@
}
}
+var worktreeRE = regexp.MustCompile(`\sworktree\s`)
+
+func mustHaveWorktree(t *testing.T) {
+ commands := trun(t, "", "git", "help", "-a")
+ if !worktreeRE.MatchString(commands) {
+ t.Skip("git doesn't support worktree")
+ }
+}
+
+func TestHooksInWorktree(t *testing.T) {
+ gt := newGitTest(t)
+ defer gt.done()
+
+ mustHaveWorktree(t)
+
+ trun(t, gt.client, "git", "worktree", "add", "../worktree")
+ chdir(t, filepath.Join("..", "worktree"))
+
+ gt.removeStubHooks()
+ 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()