git-codereview: make hooks command report conflicting hooks
Display warning message when a hook is already installed and is
different from the one installed by git-codereview.
Improves upon CL 184417.
Fixes golang/go#16777
Change-Id: I7579a3e86572e8b74f92317973e7cc7094b3942d
Reviewed-on: https://go-review.googlesource.com/c/review/+/377034
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
diff --git a/git-codereview/doc.go b/git-codereview/doc.go
index 5f3e0b1..6e65e42 100644
--- a/git-codereview/doc.go
+++ b/git-codereview/doc.go
@@ -201,7 +201,6 @@
the Go project that the first line has the form, pkg/path: summary.
The hooks command will not overwrite an existing hook.
-If it is not installing hooks, use “git codereview hooks -v” for details.
This hook installation is also done at startup by all other git codereview
commands, except “git codereview help”.
diff --git a/git-codereview/hook.go b/git-codereview/hook.go
index 1ad0f09..0b4995f 100644
--- a/git-codereview/hook.go
+++ b/git-codereview/hook.go
@@ -21,9 +21,15 @@
"pre-commit",
}
-func installHook(args []string) {
+// installHook installs Git hooks to enforce code review conventions.
+//
+// auto is whether hooks are being installed automatically as part of
+// running another git-codereview command, rather than an explicit
+// invocation of the 'hooks' command itself.
+func installHook(args []string, auto bool) {
flags.Parse(args)
hooksDir := gitPath("hooks")
+ var existingHooks []string
for _, hookFile := range hookFiles {
filename := filepath.Join(hooksDir, hookFile)
hookContent := fmt.Sprintf(hookScript, hookFile)
@@ -45,15 +51,16 @@
}
}
- // If hook file exists, assume it is okay.
+ // If hook file exists but has different content, let the user know.
_, err := os.Stat(filename)
if err == nil {
- if *verbose > 0 {
- data, err := ioutil.ReadFile(filename)
- if err != nil {
- verbosef("reading hook: %v", err)
- } else if string(data) != hookContent {
- verbosef("unexpected hook content in %s", filename)
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ verbosef("reading hook: %v", err)
+ } else if string(data) != hookContent {
+ verbosef("unexpected hook content in %s", filename)
+ if !auto {
+ existingHooks = append(existingHooks, filename)
}
}
continue
@@ -74,6 +81,19 @@
dief("writing hook: %v", err)
}
}
+
+ switch {
+ case len(existingHooks) == 1:
+ dief("Hooks file %s already exists."+
+ "\nTo install git-codereview hooks, delete that"+
+ " file and re-run 'git-codereview hooks'.",
+ existingHooks[0])
+ case len(existingHooks) > 1:
+ dief("Hooks files %s already exist."+
+ "\nTo install git-codereview hooks, delete these"+
+ " files and re-run 'git-codereview hooks'.",
+ strings.Join(existingHooks, ", "))
+ }
}
// repoRoot returns the root of the currently selected git repo, or
diff --git a/git-codereview/hook_test.go b/git-codereview/hook_test.go
index d4ffbc4..8ffe234 100644
--- a/git-codereview/hook_test.go
+++ b/git-codereview/hook_test.go
@@ -475,6 +475,8 @@
gt := newGitTest(t)
defer gt.done()
+ gt.removeStubHooks()
+ mkdir(t, gt.client+"/.git/hooks")
write(t, gt.client+"/.git/hooks/commit-msg", oldCommitMsgHook, 0755)
testMain(t, "hooks") // install hooks
data, err := ioutil.ReadFile(gt.client + "/.git/hooks/commit-msg")
@@ -489,6 +491,26 @@
}
}
+// Test that 'git-codereview hooks' reports when it fails to write hooks.
+// See go.dev/issue/16777.
+func TestHooksReportConflictingContent(t *testing.T) {
+ gt := newGitTest(t)
+ defer gt.done()
+
+ const pretendUserHook = "#!/bin/sh\necho 'pretend to be a custom hook'\nexit 1\n"
+ for _, h := range hookFiles {
+ write(t, gt.client+"/.git/hooks/"+h, pretendUserHook, 0755)
+ }
+ testMainDied(t, "hooks") // install hooks
+ testPrintedStderr(t, "Hooks files", "already exist.", "To install git-codereview hooks, delete these files and re-run 'git-codereview hooks'.")
+ for _, h := range hookFiles {
+ data := read(t, gt.client+"/.git/hooks/"+h)
+ if string(data) != pretendUserHook {
+ t.Errorf("existing hook file %s was unexpectedly modified", h)
+ }
+ }
+}
+
func testInstallHook(t *testing.T, gt *gitTest) (restore func()) {
trun(t, gt.pwd, "go", "build", "-o", gt.client+"/git-codereview")
path := os.Getenv("PATH")
diff --git a/git-codereview/review.go b/git-codereview/review.go
index 1e79114..fe5bf12 100644
--- a/git-codereview/review.go
+++ b/git-codereview/review.go
@@ -94,7 +94,7 @@
fmt.Fprintf(stdout(), help, progName)
return // avoid installing hooks.
case "hooks": // in case hooks weren't installed.
- installHook(args)
+ installHook(args, false)
return // avoid invoking installHook twice.
case "branchpoint":
@@ -135,7 +135,7 @@
hookArgs = append(hookArgs, arg)
}
}
- installHook(hookArgs)
+ installHook(hookArgs, true)
}
cmd(args)