##// 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 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 from __future__ import absolute_import, print_function
19 19
20 20 import os
21 21 import re
22 22 import sys
23 23
24 24 commitheader = r"^(?:# [^\n]*\n)*"
25 25 afterheader = commitheader + r"(?!#)"
26 26 beforepatch = afterheader + r"(?!\n(?!@@))"
27 27
28 28 errors = [
29 29 (beforepatch + r".*[(]bc[)]", "(BC) needs to be uppercase"),
30 30 (beforepatch + r".*[(]issue \d\d\d",
31 31 "no space allowed between issue and number"),
32 32 (beforepatch + r".*[(]bug(\d|\s)", "use (issueDDDD) instead of bug"),
33 33 (commitheader + r"# User [^@\n]+\n", "username is not an email address"),
34 34 (commitheader + r"(?!merge with )[^#]\S+[^:] ",
35 35 "summary line doesn't start with 'topic: '"),
36 36 (afterheader + r"[A-Z][a-z]\S+", "don't capitalize summary lines"),
37 37 (afterheader + r"[^\n]*: *[A-Z][a-z]\S+", "don't capitalize summary lines"),
38 38 (afterheader + r"\S*[^A-Za-z0-9-_]\S*: ",
39 39 "summary keyword should be most user-relevant one-word command or topic"),
40 40 (afterheader + r".*\.\s*\n", "don't add trailing period on summary line"),
41 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 42 # Forbid "_" in function name.
45 43 #
46 44 # We skip the check for cffi related functions. They use names mapping the
47 45 # name of the C function. C function names may contain "_".
48 46 (r"\n\+[ \t]+def (?!cffi)[a-z]+_[a-z]",
49 47 "adds a function with foo_bar naming"),
50 48 ]
51 49
52 50 word = re.compile('\S')
53 51 def nonempty(first, second):
54 52 if word.search(first):
55 53 return first
56 54 return second
57 55
58 56 def checkcommit(commit, node=None):
59 57 exitcode = 0
60 58 printed = node is None
61 59 hits = []
62 60 signtag = (afterheader +
63 61 r'Added (tag [^ ]+|signature) for changeset [a-f0-9]{12}')
64 62 if re.search(signtag, commit):
65 63 return 0
66 64 for exp, msg in errors:
67 65 for m in re.finditer(exp, commit):
68 66 end = m.end()
69 67 trailing = re.search(r'(\\n)+$', exp)
70 68 if trailing:
71 69 end -= len(trailing.group()) / 2
72 70 hits.append((end, exp, msg))
73 71 if hits:
74 72 hits.sort()
75 73 pos = 0
76 74 last = ''
77 75 for n, l in enumerate(commit.splitlines(True)):
78 76 pos += len(l)
79 77 while len(hits):
80 78 end, exp, msg = hits[0]
81 79 if pos < end:
82 80 break
83 81 if not printed:
84 82 printed = True
85 83 print("node: %s" % node)
86 84 print("%d: %s" % (n, msg))
87 85 print(" %s" % nonempty(l, last)[:-1])
88 86 if "BYPASS" not in os.environ:
89 87 exitcode = 1
90 88 del hits[0]
91 89 last = nonempty(l, last)
92 90
93 91 return exitcode
94 92
95 93 def readcommit(node):
96 94 return os.popen("hg export %s" % node).read()
97 95
98 96 if __name__ == "__main__":
99 97 exitcode = 0
100 98 node = os.environ.get("HG_NODE")
101 99
102 100 if node:
103 101 commit = readcommit(node)
104 102 exitcode = checkcommit(commit)
105 103 elif sys.argv[1:]:
106 104 for node in sys.argv[1:]:
107 105 exitcode |= checkcommit(readcommit(node), node)
108 106 else:
109 107 commit = sys.stdin.read()
110 108 exitcode = checkcommit(commit)
111 109 sys.exit(exitcode)
General Comments 0
You need to be logged in to leave comments. Login now