##// END OF EJS Templates
py3: make contrib/check-commit use absolute_import
Pulkit Goyal -
r29163:bf7fd815 default
parent child Browse files
Show More
@@ -1,98 +1,102
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 import re, sys, os
18 from __future__ import absolute_import
19
20 import os
21 import re
22 import sys
19
23
20 commitheader = r"^(?:# [^\n]*\n)*"
24 commitheader = r"^(?:# [^\n]*\n)*"
21 afterheader = commitheader + r"(?!#)"
25 afterheader = commitheader + r"(?!#)"
22 beforepatch = afterheader + r"(?!\n(?!@@))"
26 beforepatch = afterheader + r"(?!\n(?!@@))"
23
27
24 errors = [
28 errors = [
25 (beforepatch + r".*[(]bc[)]", "(BC) needs to be uppercase"),
29 (beforepatch + r".*[(]bc[)]", "(BC) needs to be uppercase"),
26 (beforepatch + r".*[(]issue \d\d\d",
30 (beforepatch + r".*[(]issue \d\d\d",
27 "no space allowed between issue and number"),
31 "no space allowed between issue and number"),
28 (beforepatch + r".*[(]bug(\d|\s)", "use (issueDDDD) instead of bug"),
32 (beforepatch + r".*[(]bug(\d|\s)", "use (issueDDDD) instead of bug"),
29 (commitheader + r"# User [^@\n]+\n", "username is not an email address"),
33 (commitheader + r"# User [^@\n]+\n", "username is not an email address"),
30 (commitheader + r"(?!merge with )[^#]\S+[^:] ",
34 (commitheader + r"(?!merge with )[^#]\S+[^:] ",
31 "summary line doesn't start with 'topic: '"),
35 "summary line doesn't start with 'topic: '"),
32 (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"),
33 (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"),
34 (afterheader + r"\S*[^A-Za-z0-9-]\S*: ",
38 (afterheader + r"\S*[^A-Za-z0-9-]\S*: ",
35 "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"),
36 (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"),
37 (afterheader + r".{79,}", "summary line too long (limit is 78)"),
41 (afterheader + r".{79,}", "summary line too long (limit is 78)"),
38 (r"\n\+\n( |\+)\n", "adds double empty line"),
42 (r"\n\+\n( |\+)\n", "adds double empty line"),
39 (r"\n \n\+\n", "adds double empty line"),
43 (r"\n \n\+\n", "adds double empty line"),
40 (r"\n\+[ \t]+def [a-z]+_[a-z]", "adds a function with foo_bar naming"),
44 (r"\n\+[ \t]+def [a-z]+_[a-z]", "adds a function with foo_bar naming"),
41 ]
45 ]
42
46
43 word = re.compile('\S')
47 word = re.compile('\S')
44 def nonempty(first, second):
48 def nonempty(first, second):
45 if word.search(first):
49 if word.search(first):
46 return first
50 return first
47 return second
51 return second
48
52
49 def checkcommit(commit, node=None):
53 def checkcommit(commit, node=None):
50 exitcode = 0
54 exitcode = 0
51 printed = node is None
55 printed = node is None
52 hits = []
56 hits = []
53 for exp, msg in errors:
57 for exp, msg in errors:
54 for m in re.finditer(exp, commit):
58 for m in re.finditer(exp, commit):
55 end = m.end()
59 end = m.end()
56 trailing = re.search(r'(\\n)+$', exp)
60 trailing = re.search(r'(\\n)+$', exp)
57 if trailing:
61 if trailing:
58 end -= len(trailing.group()) / 2
62 end -= len(trailing.group()) / 2
59 hits.append((end, exp, msg))
63 hits.append((end, exp, msg))
60 if hits:
64 if hits:
61 hits.sort()
65 hits.sort()
62 pos = 0
66 pos = 0
63 last = ''
67 last = ''
64 for n, l in enumerate(commit.splitlines(True)):
68 for n, l in enumerate(commit.splitlines(True)):
65 pos += len(l)
69 pos += len(l)
66 while len(hits):
70 while len(hits):
67 end, exp, msg = hits[0]
71 end, exp, msg = hits[0]
68 if pos < end:
72 if pos < end:
69 break
73 break
70 if not printed:
74 if not printed:
71 printed = True
75 printed = True
72 print "node: %s" % node
76 print "node: %s" % node
73 print "%d: %s" % (n, msg)
77 print "%d: %s" % (n, msg)
74 print " %s" % nonempty(l, last)[:-1]
78 print " %s" % nonempty(l, last)[:-1]
75 if "BYPASS" not in os.environ:
79 if "BYPASS" not in os.environ:
76 exitcode = 1
80 exitcode = 1
77 del hits[0]
81 del hits[0]
78 last = nonempty(l, last)
82 last = nonempty(l, last)
79
83
80 return exitcode
84 return exitcode
81
85
82 def readcommit(node):
86 def readcommit(node):
83 return os.popen("hg export %s" % node).read()
87 return os.popen("hg export %s" % node).read()
84
88
85 if __name__ == "__main__":
89 if __name__ == "__main__":
86 exitcode = 0
90 exitcode = 0
87 node = os.environ.get("HG_NODE")
91 node = os.environ.get("HG_NODE")
88
92
89 if node:
93 if node:
90 commit = readcommit(node)
94 commit = readcommit(node)
91 exitcode = checkcommit(commit)
95 exitcode = checkcommit(commit)
92 elif sys.argv[1:]:
96 elif sys.argv[1:]:
93 for node in sys.argv[1:]:
97 for node in sys.argv[1:]:
94 exitcode |= checkcommit(readcommit(node), node)
98 exitcode |= checkcommit(readcommit(node), node)
95 else:
99 else:
96 commit = sys.stdin.read()
100 commit = sys.stdin.read()
97 exitcode = checkcommit(commit)
101 exitcode = checkcommit(commit)
98 sys.exit(exitcode)
102 sys.exit(exitcode)
General Comments 0
You need to be logged in to leave comments. Login now