contrib/check-commit
author timeless <timeless@mozdev.org>
Tue, 12 Jan 2016 08:50:15 +0000
changeset 27782 7291c8165e33
parent 27781 2af351bd289c
child 27783 1d095371de47
permissions -rwxr-xr-x
check-commit: try to fix multiline handling The old code did not understand the difference between the first line of the summary, and a random line in the summary that happened to include a #, or a random line in the changes that happened to include it. 965788d9ae09 is an example where it fails
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
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    18
import re, sys, os
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    19
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    20
commitheader = r"^(?:# [^\n]*\n)*"
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    21
afterheader = commitheader + r"(?!#)"
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    22
beforepatch = afterheader + r"(?!\n(?!@@))"
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    23
22043
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    24
errors = [
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    25
    (beforepatch + r".*[(]bc[)]", "(BC) needs to be uppercase"),
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    26
    (beforepatch + r".*[(]issue \d\d\d", "no space allowed between issue and number"),
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    27
    (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
    28
    (commitheader + r"# User [^@\n]+\n", "username is not an email address"),
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    29
    (commitheader + r"(?!merge with )[^#]\S+[^:] ",
22043
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    30
     "summary line doesn't start with 'topic: '"),
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    31
    (afterheader + r"[A-Z][a-z]\S+", "don't capitalize summary lines"),
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    32
    (afterheader + r"[^\n]*: *[A-Z][a-z]\S+", "don't capitalize summary lines"),
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    33
    (afterheader + r"\S*[^A-Za-z0-9-]\S*: ",
27692
e0465035def9 check-commit: try to curb bad commit summary keywords
Matt Mackall <mpm@selenic.com>
parents: 27199
diff changeset
    34
     "summary keyword should be most user-relevant one-word command or topic"),
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    35
    (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
    36
    (afterheader + r".{79,}", "summary line too long (limit is 78)"),
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    37
    (r"\n\+\n \n", "adds double empty line"),
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    38
    (r"\n \n\+\n", "adds double empty line"),
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    39
    (r"\n\+[ \t]+def [a-z]+_[a-z]", "adds a function with foo_bar naming"),
22043
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    40
]
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    41
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    42
word = re.compile('\S')
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    43
def nonempty(first, second):
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    44
    if word.search(first):
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    45
        return first
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    46
    return second
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    47
27781
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    48
def checkcommit(commit, node = None):
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    49
    exitcode = 0
27781
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    50
    printed = node is None
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    51
    for exp, msg in errors:
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    52
        m = re.search(exp, commit)
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    53
        if m:
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    54
            pos = 0
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    55
            end = m.end()
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    56
            trailing = re.search(r'(\\n)+$', exp)
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    57
            if trailing:
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    58
                end -= len(trailing.group()) / 2
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    59
            last = ''
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    60
            for n, l in enumerate(commit.splitlines(True)):
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    61
                pos += len(l)
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    62
                if pos < end:
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    63
                    last = nonempty(l, last)
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    64
                else:
27781
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    65
                    if not printed:
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    66
                        printed = True
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    67
                        print "node: %s" % node
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    68
                    print "%d: %s" % (n, msg)
27782
7291c8165e33 check-commit: try to fix multiline handling
timeless <timeless@mozdev.org>
parents: 27781
diff changeset
    69
                    print " %s" % nonempty(l, last)[:-1]
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    70
                    if "BYPASS" not in os.environ:
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    71
                        exitcode = 1
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    72
                    break
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    73
    return exitcode
22043
1274ff3f20a8 contrib: add check-commit hook script to sanity-check commits
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    74
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    75
def readcommit(node):
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    76
    return os.popen("hg export %s" % node).read()
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    77
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    78
if __name__ == "__main__":
27781
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    79
    exitcode = 0
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    80
    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
    81
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    82
    if node:
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    83
        commit = readcommit(node)
27781
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    84
        exitcode = checkcommit(commit)
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    85
    elif sys.argv[1:]:
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    86
        for node in sys.argv[1:]:
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    87
            exitcode |= checkcommit(readcommit(node), node)
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    88
    else:
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    89
        commit = sys.stdin.read()
27781
2af351bd289c check-commit: support REVs as commandline arguments
timeless <timeless@mozdev.org>
parents: 27780
diff changeset
    90
        exitcode = checkcommit(commit)
27780
f47185f09533 check-commit: modularize
timeless <timeless@mozdev.org>
parents: 27779
diff changeset
    91
    sys.exit(exitcode)