review: check form of commit messages
Fixes golang/go#9254
Change-Id: Iac4cc8474096209cd1b0f8ee812c0637babc2560
Reviewed-on: https://go-review.googlesource.com/1351
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/git-review/change.go b/git-review/change.go
index d1a21c5..0990d58 100644
--- a/git-review/change.go
+++ b/git-review/change.go
@@ -7,6 +7,7 @@
import (
"fmt"
"os"
+ "regexp"
"strings"
)
@@ -57,17 +58,27 @@
if !HasStagedChanges() {
printf("no staged changes. Did you forget to 'git add'?")
}
- args := []string{"commit", "-q", "--allow-empty"}
- if amend {
- args = append(args, "--amend")
- if changeQuick {
- args = append(args, "--no-edit")
+ commit := func(amend bool) {
+ args := []string{"commit", "-q", "--allow-empty"}
+ if amend {
+ args = append(args, "--amend")
+ if changeQuick {
+ args = append(args, "--no-edit")
+ }
}
+ if testCommitMsg != "" {
+ args = append(args, "-m", testCommitMsg)
+ }
+ run("git", args...)
}
- if testCommitMsg != "" {
- args = append(args, "-m", testCommitMsg)
+ commit(amend)
+ for !commitMessageOK() {
+ fmt.Print("re-edit commit message (y/n)? ")
+ if !scanYes() {
+ break
+ }
+ commit(true)
}
- run("git", args...)
printf("change updated.")
}
@@ -111,3 +122,55 @@
run("git", "branch", "-q", "--set-upstream-to", origin)
printf("created branch %v tracking %s.", target, origin)
}
+
+var (
+ messageRE = regexp.MustCompile(`^(\[[a-zA-Z0-9.-]+\] )?[a-zA-Z0-9-/, ]+: `)
+ oldFixesRE = regexp.MustCompile(`Fixes +(issue +#?)?([0-9]+)`)
+)
+
+func commitMessageOK() bool {
+ body := getOutput("git", "log", "--format=format:%B", "-n", "1")
+ ok := true
+ if !messageRE.MatchString(body) {
+ fmt.Print(commitMessageWarning)
+ ok = false
+ }
+ if m := oldFixesRE.FindStringSubmatch(body); m != nil {
+ fmt.Printf(fixesIssueWarning, m[0], m[2])
+ ok = false
+ }
+ return ok
+}
+
+const commitMessageWarning = `
+Your CL description appears not to use the standard form.
+
+The first line of your change description is conventionally a one-line summary
+of the change, prefixed by the primary affected package, and is used as the
+subject for code review mail; the rest of the description elaborates.
+
+Examples:
+
+ encoding/rot13: new package
+
+ math: add IsInf, IsNaN
+
+ net: fix cname in LookupHost
+
+ unicode: update to Unicode 5.0.2
+
+`
+
+const fixesIssueWarning = `
+Your CL description contains the string %q, which is
+the old Google Code way of linking commits to issues.
+
+You should rewrite it to use the GitHub convention: "Fixes #%v".
+
+`
+
+func scanYes() bool {
+ var s string
+ fmt.Scan(&s)
+ return strings.HasPrefix(strings.ToLower(s), "y")
+}
diff --git a/git-review/change_test.go b/git-review/change_test.go
index f493d94..ad33e7a 100644
--- a/git-review/change_test.go
+++ b/git-review/change_test.go
@@ -14,12 +14,12 @@
testMain(t, "change", "master")
testRan(t, "git checkout -q master")
- testCommitMsg = "my commit msg"
+ testCommitMsg = "foo: my commit msg"
t.Logf("master -> work")
testMain(t, "change", "work")
testRan(t, "git checkout -q -b work",
"git branch -q --set-upstream-to origin/master",
- "git commit -q --allow-empty -m my commit msg")
+ "git commit -q --allow-empty -m foo: my commit msg")
t.Logf("work -> master")
testMain(t, "change", "master")
@@ -29,3 +29,22 @@
testMain(t, "change", "dev.branch")
testRan(t, "git checkout -q -t -b dev.branch origin/dev.branch")
}
+
+func TestMessageRE(t *testing.T) {
+ for _, c := range []struct {
+ in string
+ want bool
+ }{
+ {"blah", false},
+ {"[release-branch.go1.4] blah", false},
+ {"[release-branch.go1.4] math: fix cosine", true},
+ {"math: fix cosine", true},
+ {"math/rand: make randomer", true},
+ {"math/rand, crypto/rand: fix random sources", true},
+ } {
+ got := messageRE.MatchString(c.in)
+ if got != c.want {
+ t.Errorf("MatchString(%q) = %v, want %v", c.in, got, c.want)
+ }
+ }
+}