##// END OF EJS Templates
check-commit: use raw string for regular expression...
Gregory Szorc -
r41680:45a4789d default
parent child Browse files
Show More
@@ -1,109 +1,109
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"^\S+: *[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 42 # Forbid "_" in function name.
43 43 #
44 44 # We skip the check for cffi related functions. They use names mapping the
45 45 # name of the C function. C function names may contain "_".
46 46 (r"\n\+[ \t]+def (?!cffi)[a-z]+_[a-z]",
47 47 "adds a function with foo_bar naming"),
48 48 ]
49 49
50 word = re.compile('\S')
50 word = re.compile(r'\S')
51 51 def nonempty(first, second):
52 52 if word.search(first):
53 53 return first
54 54 return second
55 55
56 56 def checkcommit(commit, node=None):
57 57 exitcode = 0
58 58 printed = node is None
59 59 hits = []
60 60 signtag = (afterheader +
61 61 r'Added (tag [^ ]+|signature) for changeset [a-f0-9]{12}')
62 62 if re.search(signtag, commit):
63 63 return 0
64 64 for exp, msg in errors:
65 65 for m in re.finditer(exp, commit):
66 66 end = m.end()
67 67 trailing = re.search(r'(\\n)+$', exp)
68 68 if trailing:
69 69 end -= len(trailing.group()) / 2
70 70 hits.append((end, exp, msg))
71 71 if hits:
72 72 hits.sort()
73 73 pos = 0
74 74 last = ''
75 75 for n, l in enumerate(commit.splitlines(True)):
76 76 pos += len(l)
77 77 while len(hits):
78 78 end, exp, msg = hits[0]
79 79 if pos < end:
80 80 break
81 81 if not printed:
82 82 printed = True
83 83 print("node: %s" % node)
84 84 print("%d: %s" % (n, msg))
85 85 print(" %s" % nonempty(l, last)[:-1])
86 86 if "BYPASS" not in os.environ:
87 87 exitcode = 1
88 88 del hits[0]
89 89 last = nonempty(l, last)
90 90
91 91 return exitcode
92 92
93 93 def readcommit(node):
94 94 return os.popen("hg export %s" % node).read()
95 95
96 96 if __name__ == "__main__":
97 97 exitcode = 0
98 98 node = os.environ.get("HG_NODE")
99 99
100 100 if node:
101 101 commit = readcommit(node)
102 102 exitcode = checkcommit(commit)
103 103 elif sys.argv[1:]:
104 104 for node in sys.argv[1:]:
105 105 exitcode |= checkcommit(readcommit(node), node)
106 106 else:
107 107 commit = sys.stdin.read()
108 108 exitcode = checkcommit(commit)
109 109 sys.exit(exitcode)
General Comments 0
You need to be logged in to leave comments. Login now