##// END OF EJS Templates
style: drop requirement to only use single lines between top-level objects...
Martijn Pieters -
r40344:47084b5f default
parent child Browse files
Show More
@@ -1,111 +1,109 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # Copyright 2014 Matt Mackall <mpm@selenic.com>
3 # Copyright 2014 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # A tool/hook to run basic sanity checks on commits/patches for
5 # A tool/hook to run basic sanity checks on commits/patches for
6 # submission to Mercurial. Install by adding the following to your
6 # submission to Mercurial. Install by adding the following to your
7 # .hg/hgrc:
7 # .hg/hgrc:
8 #
8 #
9 # [hooks]
9 # [hooks]
10 # pretxncommit = contrib/check-commit
10 # pretxncommit = contrib/check-commit
11 #
11 #
12 # The hook can be temporarily bypassed with:
12 # The hook can be temporarily bypassed with:
13 #
13 #
14 # $ BYPASS= hg commit
14 # $ BYPASS= hg commit
15 #
15 #
16 # See also: https://mercurial-scm.org/wiki/ContributingChanges
16 # See also: https://mercurial-scm.org/wiki/ContributingChanges
17
17
18 from __future__ import absolute_import, print_function
18 from __future__ import absolute_import, print_function
19
19
20 import os
20 import os
21 import re
21 import re
22 import sys
22 import sys
23
23
24 commitheader = r"^(?:# [^\n]*\n)*"
24 commitheader = r"^(?:# [^\n]*\n)*"
25 afterheader = commitheader + r"(?!#)"
25 afterheader = commitheader + r"(?!#)"
26 beforepatch = afterheader + r"(?!\n(?!@@))"
26 beforepatch = afterheader + r"(?!\n(?!@@))"
27
27
28 errors = [
28 errors = [
29 (beforepatch + r".*[(]bc[)]", "(BC) needs to be uppercase"),
29 (beforepatch + r".*[(]bc[)]", "(BC) needs to be uppercase"),
30 (beforepatch + r".*[(]issue \d\d\d",
30 (beforepatch + r".*[(]issue \d\d\d",
31 "no space allowed between issue and number"),
31 "no space allowed between issue and number"),
32 (beforepatch + r".*[(]bug(\d|\s)", "use (issueDDDD) instead of bug"),
32 (beforepatch + r".*[(]bug(\d|\s)", "use (issueDDDD) instead of bug"),
33 (commitheader + r"# User [^@\n]+\n", "username is not an email address"),
33 (commitheader + r"# User [^@\n]+\n", "username is not an email address"),
34 (commitheader + r"(?!merge with )[^#]\S+[^:] ",
34 (commitheader + r"(?!merge with )[^#]\S+[^:] ",
35 "summary line doesn't start with 'topic: '"),
35 "summary line doesn't start with 'topic: '"),
36 (afterheader + r"[A-Z][a-z]\S+", "don't capitalize summary lines"),
36 (afterheader + r"[A-Z][a-z]\S+", "don't capitalize summary lines"),
37 (afterheader + r"[^\n]*: *[A-Z][a-z]\S+", "don't capitalize summary lines"),
37 (afterheader + r"[^\n]*: *[A-Z][a-z]\S+", "don't capitalize summary lines"),
38 (afterheader + r"\S*[^A-Za-z0-9-_]\S*: ",
38 (afterheader + r"\S*[^A-Za-z0-9-_]\S*: ",
39 "summary keyword should be most user-relevant one-word command or topic"),
39 "summary keyword should be most user-relevant one-word command or topic"),
40 (afterheader + r".*\.\s*\n", "don't add trailing period on summary line"),
40 (afterheader + r".*\.\s*\n", "don't add trailing period on summary line"),
41 (afterheader + r".{79,}", "summary line too long (limit is 78)"),
41 (afterheader + r".{79,}", "summary line too long (limit is 78)"),
42 (r"\n\+\n( |\+)\n", "adds double empty line"),
43 (r"\n \n\+\n", "adds double empty line"),
44 # Forbid "_" in function name.
42 # Forbid "_" in function name.
45 #
43 #
46 # We skip the check for cffi related functions. They use names mapping the
44 # We skip the check for cffi related functions. They use names mapping the
47 # name of the C function. C function names may contain "_".
45 # name of the C function. C function names may contain "_".
48 (r"\n\+[ \t]+def (?!cffi)[a-z]+_[a-z]",
46 (r"\n\+[ \t]+def (?!cffi)[a-z]+_[a-z]",
49 "adds a function with foo_bar naming"),
47 "adds a function with foo_bar naming"),
50 ]
48 ]
51
49
52 word = re.compile('\S')
50 word = re.compile('\S')
53 def nonempty(first, second):
51 def nonempty(first, second):
54 if word.search(first):
52 if word.search(first):
55 return first
53 return first
56 return second
54 return second
57
55
58 def checkcommit(commit, node=None):
56 def checkcommit(commit, node=None):
59 exitcode = 0
57 exitcode = 0
60 printed = node is None
58 printed = node is None
61 hits = []
59 hits = []
62 signtag = (afterheader +
60 signtag = (afterheader +
63 r'Added (tag [^ ]+|signature) for changeset [a-f0-9]{12}')
61 r'Added (tag [^ ]+|signature) for changeset [a-f0-9]{12}')
64 if re.search(signtag, commit):
62 if re.search(signtag, commit):
65 return 0
63 return 0
66 for exp, msg in errors:
64 for exp, msg in errors:
67 for m in re.finditer(exp, commit):
65 for m in re.finditer(exp, commit):
68 end = m.end()
66 end = m.end()
69 trailing = re.search(r'(\\n)+$', exp)
67 trailing = re.search(r'(\\n)+$', exp)
70 if trailing:
68 if trailing:
71 end -= len(trailing.group()) / 2
69 end -= len(trailing.group()) / 2
72 hits.append((end, exp, msg))
70 hits.append((end, exp, msg))
73 if hits:
71 if hits:
74 hits.sort()
72 hits.sort()
75 pos = 0
73 pos = 0
76 last = ''
74 last = ''
77 for n, l in enumerate(commit.splitlines(True)):
75 for n, l in enumerate(commit.splitlines(True)):
78 pos += len(l)
76 pos += len(l)
79 while len(hits):
77 while len(hits):
80 end, exp, msg = hits[0]
78 end, exp, msg = hits[0]
81 if pos < end:
79 if pos < end:
82 break
80 break
83 if not printed:
81 if not printed:
84 printed = True
82 printed = True
85 print("node: %s" % node)
83 print("node: %s" % node)
86 print("%d: %s" % (n, msg))
84 print("%d: %s" % (n, msg))
87 print(" %s" % nonempty(l, last)[:-1])
85 print(" %s" % nonempty(l, last)[:-1])
88 if "BYPASS" not in os.environ:
86 if "BYPASS" not in os.environ:
89 exitcode = 1
87 exitcode = 1
90 del hits[0]
88 del hits[0]
91 last = nonempty(l, last)
89 last = nonempty(l, last)
92
90
93 return exitcode
91 return exitcode
94
92
95 def readcommit(node):
93 def readcommit(node):
96 return os.popen("hg export %s" % node).read()
94 return os.popen("hg export %s" % node).read()
97
95
98 if __name__ == "__main__":
96 if __name__ == "__main__":
99 exitcode = 0
97 exitcode = 0
100 node = os.environ.get("HG_NODE")
98 node = os.environ.get("HG_NODE")
101
99
102 if node:
100 if node:
103 commit = readcommit(node)
101 commit = readcommit(node)
104 exitcode = checkcommit(commit)
102 exitcode = checkcommit(commit)
105 elif sys.argv[1:]:
103 elif sys.argv[1:]:
106 for node in sys.argv[1:]:
104 for node in sys.argv[1:]:
107 exitcode |= checkcommit(readcommit(node), node)
105 exitcode |= checkcommit(readcommit(node), node)
108 else:
106 else:
109 commit = sys.stdin.read()
107 commit = sys.stdin.read()
110 exitcode = checkcommit(commit)
108 exitcode = checkcommit(commit)
111 sys.exit(exitcode)
109 sys.exit(exitcode)
General Comments 0
You need to be logged in to leave comments. Login now