contrib/check-commit
author Valentin Gatien-Baron <vgatien-baron@janestreet.com>
Wed, 22 Jan 2020 14:11:11 -0500
branchstable
changeset 44134 e96ed3a61899
parent 43659 99e231afc29c
child 45830 c102b704edb5
permissions -rwxr-xr-x
recover: fix typos Differential Revision: https://phab.mercurial-scm.org/D7971
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
22043
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     1
#!/usr/bin/env python
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     2
#
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     3
# Copyright 2014 Matt Mackall <mpm@selenic.com>
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     4
#
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     5
# A tool/hook to run basic sanity checks on commits/patches for
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     6
# submission to Mercurial. Install by adding the following to your
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     7
# .hg/hgrc:
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     8
#
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     9
# [hooks]
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    10
# pretxncommit = contrib/check-commit
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    11
#
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    12
# The hook can be temporarily bypassed with:
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    13
#
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    14
# $ BYPASS= hg commit
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    15
#
26421
4b0fc75f9403 urls: bulk-change primary website URLs
Matt Mackall <mpm@selenic.com>
parents: 25643
diff changeset
    16
# See also: https://mercurial-scm.org/wiki/ContributingChanges
22043
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    17
29164
91f35b1a34cf py3: make contrib/check-commit use print_function
Pulkit Goyal <7895pulkit@gmail.com>
parents: 29163
diff changeset
    18
from __future__ import absolute_import, print_function
29163
bf7fd815b083 py3: make contrib/check-commit use absolute_import
Pulkit Goyal <7895pulkit@gmail.com>
parents: 28043
diff changeset
    19
bf7fd815b083 py3: make contrib/check-commit use absolute_import
Pulkit Goyal <7895pulkit@gmail.com>
parents: 28043
diff changeset
    20
import os
bf7fd815b083 py3: make contrib/check-commit use absolute_import
Pulkit Goyal <7895pulkit@gmail.com>
parents: 28043
diff changeset
    21
import re
bf7fd815b083 py3: make contrib/check-commit use absolute_import
Pulkit Goyal <7895pulkit@gmail.com>
parents: 28043
diff changeset
    22
import sys
22043
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    23
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    24
commitheader = r"^(?:# [^\n]*\n)*"
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    25
afterheader = commitheader + r"(?!#)"
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    26
beforepatch = afterheader + r"(?!\n(?!@@))"
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    27
22043
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    28
errors = [
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    29
    (beforepatch + r".*[(]bc[)]", "(BC) needs to be uppercase"),
43659
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    30
    (
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    31
        beforepatch + r".*[(]issue \d\d\d",
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    32
        "no space allowed between issue and number",
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    33
    ),
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    34
    (beforepatch + r".*[(]bug(\d|\s)", "use (issueDDDD) instead of bug"),
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    35
    (commitheader + r"# User [^@\n]+\n", "username is not an email address"),
43659
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    36
    (
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    37
        commitheader + r"(?!merge with )[^#]\S+[^:] ",
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    38
        "summary line doesn't start with 'topic: '",
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    39
    ),
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    40
    (afterheader + r"[A-Z][a-z]\S+", "don't capitalize summary lines"),
40952
811f772b44aa check-commit: disallow capitalization only right after topic
Martin von Zweigbergk <martinvonz@google.com>
parents: 40308
diff changeset
    41
    (afterheader + r"^\S+: *[A-Z][a-z]\S+", "don't capitalize summary lines"),
43659
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    42
    (
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    43
        afterheader + r"\S*[^A-Za-z0-9-_]\S*: ",
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    44
        "summary keyword should be most user-relevant one-word command or topic",
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    45
    ),
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    46
    (afterheader + r".*\.\s*\n", "don't add trailing period on summary line"),
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    47
    (afterheader + r".{79,}", "summary line too long (limit is 78)"),
22043
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    48
]
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    49
41539
45a4789d3ff2 check-commit: use raw string for regular expression
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40952
diff changeset
    50
word = re.compile(r'\S')
43659
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    51
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    52
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    53
def nonempty(first, second):
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    54
    if word.search(first):
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    55
        return first
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    56
    return second
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    57
43659
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    58
28043
ac4684c21f73 check-commit: omit whitespace
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 28042
diff changeset
    59
def checkcommit(commit, node=None):
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    60
    exitcode = 0
27781
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    61
    printed = node is None
27783
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    62
    hits = []
43659
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    63
    signtag = (
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    64
        afterheader + r'Added (tag [^ ]+|signature) for changeset [a-f0-9]{12}'
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    65
    )
30843
2fb3ae89e4e1 contrib: fix check-commit to not reject commits from `hg sign` and `hg tag`
Augie Fackler <augie@google.com>
parents: 30061
diff changeset
    66
    if re.search(signtag, commit):
2fb3ae89e4e1 contrib: fix check-commit to not reject commits from `hg sign` and `hg tag`
Augie Fackler <augie@google.com>
parents: 30061
diff changeset
    67
        return 0
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    68
    for exp, msg in errors:
28012
897b2fcf079f check-commit: scan for multiple instances of error patterns
Matt Mackall <mpm@selenic.com>
parents: 27783
diff changeset
    69
        for m in re.finditer(exp, commit):
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    70
            end = m.end()
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    71
            trailing = re.search(r'(\\n)+$', exp)
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    72
            if trailing:
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    73
                end -= len(trailing.group()) / 2
27783
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    74
            hits.append((end, exp, msg))
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    75
    if hits:
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    76
        hits.sort()
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    77
        pos = 0
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    78
        last = ''
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    79
        for n, l in enumerate(commit.splitlines(True)):
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    80
            pos += len(l)
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    81
            while len(hits):
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    82
                end, exp, msg = hits[0]
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    83
                if pos < end:
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    84
                    break
27783
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    85
                if not printed:
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    86
                    printed = True
29164
91f35b1a34cf py3: make contrib/check-commit use print_function
Pulkit Goyal <7895pulkit@gmail.com>
parents: 29163
diff changeset
    87
                    print("node: %s" % node)
91f35b1a34cf py3: make contrib/check-commit use print_function
Pulkit Goyal <7895pulkit@gmail.com>
parents: 29163
diff changeset
    88
                print("%d: %s" % (n, msg))
91f35b1a34cf py3: make contrib/check-commit use print_function
Pulkit Goyal <7895pulkit@gmail.com>
parents: 29163
diff changeset
    89
                print(" %s" % nonempty(l, last)[:-1])
27783
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    90
                if "BYPASS" not in os.environ:
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    91
                    exitcode = 1
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    92
                del hits[0]
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    93
            last = nonempty(l, last)
1d095371de47 check-commit: sort errors by line number
timeless <timeless@mozdev.org>
parents: 27782
diff changeset
    94
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    95
    return exitcode
22043
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    96
43659
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
    97
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    98
def readcommit(node):
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    99
    return os.popen("hg export %s" % node).read()
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
   100
43659
99e231afc29c black: blacken scripts
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43112
diff changeset
   101
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
   102
if __name__ == "__main__":
27781
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
   103
    exitcode = 0
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
   104
    node = os.environ.get("HG_NODE")
22043
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   105
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
   106
    if node:
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
   107
        commit = readcommit(node)
27781
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
   108
        exitcode = checkcommit(commit)
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
   109
    elif sys.argv[1:]:
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
   110
        for node in sys.argv[1:]:
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
   111
            exitcode |= checkcommit(readcommit(node), node)
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
   112
    else:
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
   113
        commit = sys.stdin.read()
27781
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
   114
        exitcode = checkcommit(commit)
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
   115
    sys.exit(exitcode)