##// END OF EJS Templates
check-commit: try to fix multiline handling...
timeless -
r27782:7291c816 default
parent child Browse files
Show More
@@ -1,74 +1,91 b''
1 1 #!/usr/bin/env python
2 2 #
3 3 # Copyright 2014 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # A tool/hook to run basic sanity checks on commits/patches for
6 6 # submission to Mercurial. Install by adding the following to your
7 7 # .hg/hgrc:
8 8 #
9 9 # [hooks]
10 10 # pretxncommit = contrib/check-commit
11 11 #
12 12 # The hook can be temporarily bypassed with:
13 13 #
14 14 # $ BYPASS= hg commit
15 15 #
16 16 # See also: https://mercurial-scm.org/wiki/ContributingChanges
17 17
18 18 import re, sys, os
19 19
20 commitheader = r"^(?:# [^\n]*\n)*"
21 afterheader = commitheader + r"(?!#)"
22 beforepatch = afterheader + r"(?!\n(?!@@))"
23
20 24 errors = [
21 (r"[(]bc[)]", "(BC) needs to be uppercase"),
22 (r"[(]issue \d\d\d", "no space allowed between issue and number"),
23 (r"[(]bug(\d|\s)", "use (issueDDDD) instead of bug"),
24 (r"^# User [^@\n]+$", "username is not an email address"),
25 (r"^# .*\n(?!merge with )[^#]\S+[^:] ",
25 (beforepatch + r".*[(]bc[)]", "(BC) needs to be uppercase"),
26 (beforepatch + r".*[(]issue \d\d\d", "no space allowed between issue and number"),
27 (beforepatch + r".*[(]bug(\d|\s)", "use (issueDDDD) instead of bug"),
28 (commitheader + r"# User [^@\n]+\n", "username is not an email address"),
29 (commitheader + r"(?!merge with )[^#]\S+[^:] ",
26 30 "summary line doesn't start with 'topic: '"),
27 (r"^# .*\n[A-Z][a-z]\S+", "don't capitalize summary lines"),
28 (r"^# .*\n[^\n]*: *[A-Z][a-z]\S+", "don't capitalize summary lines"),
29 (r"^# [^\n]*\n\S*[^A-Za-z0-9-]\S*: ",
31 (afterheader + r"[A-Z][a-z]\S+", "don't capitalize summary lines"),
32 (afterheader + r"[^\n]*: *[A-Z][a-z]\S+", "don't capitalize summary lines"),
33 (afterheader + r"\S*[^A-Za-z0-9-]\S*: ",
30 34 "summary keyword should be most user-relevant one-word command or topic"),
31 (r"^# .*\n.*\.\s+$", "don't add trailing period on summary line"),
32 (r"^# .*\n[^#].{78,}", "summary line too long (limit is 78)"),
33 (r"^\+\n \n", "adds double empty line"),
34 (r"^ \n\+\n", "adds double empty line"),
35 (r"^\+[ \t]+def [a-z]+_[a-z]", "adds a function with foo_bar naming"),
35 (afterheader + r".*\.\s*\n", "don't add trailing period on summary line"),
36 (afterheader + r".{79,}", "summary line too long (limit is 78)"),
37 (r"\n\+\n \n", "adds double empty line"),
38 (r"\n \n\+\n", "adds double empty line"),
39 (r"\n\+[ \t]+def [a-z]+_[a-z]", "adds a function with foo_bar naming"),
36 40 ]
37 41
42 word = re.compile('\S')
43 def nonempty(first, second):
44 if word.search(first):
45 return first
46 return second
47
38 48 def checkcommit(commit, node = None):
39 49 exitcode = 0
40 50 printed = node is None
41 51 for exp, msg in errors:
42 m = re.search(exp, commit, re.MULTILINE)
52 m = re.search(exp, commit)
43 53 if m:
44 54 pos = 0
55 end = m.end()
56 trailing = re.search(r'(\\n)+$', exp)
57 if trailing:
58 end -= len(trailing.group()) / 2
59 last = ''
45 60 for n, l in enumerate(commit.splitlines(True)):
46 61 pos += len(l)
47 if pos >= m.end():
62 if pos < end:
63 last = nonempty(l, last)
64 else:
48 65 if not printed:
49 66 printed = True
50 67 print "node: %s" % node
51 68 print "%d: %s" % (n, msg)
52 print " %s" % l[:-1]
69 print " %s" % nonempty(l, last)[:-1]
53 70 if "BYPASS" not in os.environ:
54 71 exitcode = 1
55 72 break
56 73 return exitcode
57 74
58 75 def readcommit(node):
59 76 return os.popen("hg export %s" % node).read()
60 77
61 78 if __name__ == "__main__":
62 79 exitcode = 0
63 80 node = os.environ.get("HG_NODE")
64 81
65 82 if node:
66 83 commit = readcommit(node)
67 84 exitcode = checkcommit(commit)
68 85 elif sys.argv[1:]:
69 86 for node in sys.argv[1:]:
70 87 exitcode |= checkcommit(readcommit(node), node)
71 88 else:
72 89 commit = sys.stdin.read()
73 90 exitcode = checkcommit(commit)
74 91 sys.exit(exitcode)
@@ -1,111 +1,111 b''
1 1 Test the 'check-commit' script
2 2 ==============================
3 3
4 4 A fine patch:
5 5
6 6 $ cat > patch-with-long-header.diff << EOF
7 7 > # HG changeset patch
8 8 > # User timeless <timeless@mozdev.org>
9 9 > # Date 1448911706 0
10 10 > # Mon Nov 30 19:28:26 2015 +0000
11 11 > # Node ID c41cb6d2b7dbd62b1033727f8606b8c09fc4aa88
12 12 > # Parent 42aa0e570eaa364a622bc4443b0bcb79b1100a58
13 13 > # ClownJoke This is a veryly long header that should not be warned about because its not the description
14 14 > bundle2: use Oxford comma (issue123) (BC)
15 15 >
16 16 > diff --git a/hgext/transplant.py b/hgext/transplant.py
17 17 > --- a/hgext/transplant.py
18 18 > +++ b/hgext/transplant.py
19 19 > @@ -599,7 +599,7 @@
20 20 > return
21 21 > if not (opts.get('source') or revs or
22 22 > opts.get('merge') or opts.get('branch')):
23 23 > - raise error.Abort(_('no source URL, branch revision or revision '
24 24 > + raise error.Abort(_('no source URL, branch revision, or revision '
25 25 > 'list provided'))
26 26 > if opts.get('all'):
27 27 >
28 28 > + def blahblah(x):
29 29 > + pass
30 30 > EOF
31 31 $ cat patch-with-long-header.diff | $TESTDIR/../contrib/check-commit
32 32
33 33 A patch with lots of errors:
34 34
35 35 $ cat > patch-with-long-header.diff << EOF
36 36 > # HG changeset patch
37 37 > # User timeless
38 38 > # Date 1448911706 0
39 39 > # Mon Nov 30 19:28:26 2015 +0000
40 40 > # Node ID c41cb6d2b7dbd62b1033727f8606b8c09fc4aa88
41 41 > # Parent 42aa0e570eaa364a622bc4443b0bcb79b1100a58
42 42 > # ClownJoke This is a veryly long header that should not be warned about because its not the description
43 43 > transplant/foo: this summary is way too long use Oxford comma (bc) (bug123) (issue 244)
44 44 >
45 45 > diff --git a/hgext/transplant.py b/hgext/transplant.py
46 46 > --- a/hgext/transplant.py
47 47 > +++ b/hgext/transplant.py
48 48 > @@ -599,7 +599,7 @@
49 49 > return
50 50 > if not (opts.get('source') or revs or
51 51 > opts.get('merge') or opts.get('branch')):
52 52 > - raise error.Abort(_('no source URL, branch revision or revision '
53 53 > + raise error.Abort(_('no source URL, branch revision, or revision '
54 54 > 'list provided'))
55 55 > if opts.get('all'):
56 56 > EOF
57 57 $ cat patch-with-long-header.diff | $TESTDIR/../contrib/check-commit
58 58 7: (BC) needs to be uppercase
59 59 transplant/foo: this summary is way too long use Oxford comma (bc) (bug123) (issue 244)
60 60 7: no space allowed between issue and number
61 61 transplant/foo: this summary is way too long use Oxford comma (bc) (bug123) (issue 244)
62 62 7: use (issueDDDD) instead of bug
63 63 transplant/foo: this summary is way too long use Oxford comma (bc) (bug123) (issue 244)
64 64 1: username is not an email address
65 65 # User timeless
66 66 7: summary keyword should be most user-relevant one-word command or topic
67 67 transplant/foo: this summary is way too long use Oxford comma (bc) (bug123) (issue 244)
68 68 7: summary line too long (limit is 78)
69 69 transplant/foo: this summary is way too long use Oxford comma (bc) (bug123) (issue 244)
70 70 [1]
71 71
72 72 A patch with other errors:
73 73
74 74 $ cat > patch-with-long-header.diff << EOF
75 75 > # HG changeset patch
76 76 > # User timeless
77 77 > # Date 1448911706 0
78 78 > # Mon Nov 30 19:28:26 2015 +0000
79 79 > # Node ID c41cb6d2b7dbd62b1033727f8606b8c09fc4aa88
80 80 > # Parent 42aa0e570eaa364a622bc4443b0bcb79b1100a58
81 81 > # ClownJoke This is a veryly long header that should not be warned about because its not the description
82 82 > This has no topic and ends with a period.
83 83 >
84 84 > diff --git a/hgext/transplant.py b/hgext/transplant.py
85 85 > --- a/hgext/transplant.py
86 86 > +++ b/hgext/transplant.py
87 87 > @@ -599,7 +599,7 @@
88 88 > if opts.get('all'):
89 89 >
90 90 > +
91 91 > + def blah_blah(x):
92 92 > + pass
93 93 > +
94 94 >
95 95 > EOF
96 96 $ cat patch-with-long-header.diff | $TESTDIR/../contrib/check-commit
97 97 1: username is not an email address
98 98 # User timeless
99 99 7: summary line doesn't start with 'topic: '
100 100 This has no topic and ends with a period.
101 101 7: don't capitalize summary lines
102 102 This has no topic and ends with a period.
103 103 7: don't add trailing period on summary line
104 104 This has no topic and ends with a period.
105 105 19: adds double empty line
106
106 +
107 107 15: adds double empty line
108 108 +
109 109 16: adds a function with foo_bar naming
110 110 + def blah_blah(x):
111 111 [1]
General Comments 0
You need to be logged in to leave comments. Login now