Mercurial > hg
view contrib/check-commit @ 42051:f4147ca63d39
uncommit: abort if an explicitly given file cannot be uncommitted (BC)
I've gotten burned several times by this in the last few days. The former tests
look simple enough, but if a good file and a bad file are given, the bad files
are silently ignored. Some commands like `forget` will warn about bogus files,
but that would likely get lost in the noise of an interactive uncommit. The
commit command aborts if a bad file is given, so this seems more consistent for
commands that alter the repository.
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Fri, 29 Mar 2019 21:53:15 -0400 |
parents | 45a4789d3ff2 |
children | 24a07347aa60 |
line wrap: on
line source
#!/usr/bin/env python # # Copyright 2014 Matt Mackall <mpm@selenic.com> # # A tool/hook to run basic sanity checks on commits/patches for # submission to Mercurial. Install by adding the following to your # .hg/hgrc: # # [hooks] # pretxncommit = contrib/check-commit # # The hook can be temporarily bypassed with: # # $ BYPASS= hg commit # # See also: https://mercurial-scm.org/wiki/ContributingChanges from __future__ import absolute_import, print_function import os import re import sys commitheader = r"^(?:# [^\n]*\n)*" afterheader = commitheader + r"(?!#)" beforepatch = afterheader + r"(?!\n(?!@@))" errors = [ (beforepatch + r".*[(]bc[)]", "(BC) needs to be uppercase"), (beforepatch + r".*[(]issue \d\d\d", "no space allowed between issue and number"), (beforepatch + r".*[(]bug(\d|\s)", "use (issueDDDD) instead of bug"), (commitheader + r"# User [^@\n]+\n", "username is not an email address"), (commitheader + r"(?!merge with )[^#]\S+[^:] ", "summary line doesn't start with 'topic: '"), (afterheader + r"[A-Z][a-z]\S+", "don't capitalize summary lines"), (afterheader + r"^\S+: *[A-Z][a-z]\S+", "don't capitalize summary lines"), (afterheader + r"\S*[^A-Za-z0-9-_]\S*: ", "summary keyword should be most user-relevant one-word command or topic"), (afterheader + r".*\.\s*\n", "don't add trailing period on summary line"), (afterheader + r".{79,}", "summary line too long (limit is 78)"), # Forbid "_" in function name. # # We skip the check for cffi related functions. They use names mapping the # name of the C function. C function names may contain "_". (r"\n\+[ \t]+def (?!cffi)[a-z]+_[a-z]", "adds a function with foo_bar naming"), ] word = re.compile(r'\S') def nonempty(first, second): if word.search(first): return first return second def checkcommit(commit, node=None): exitcode = 0 printed = node is None hits = [] signtag = (afterheader + r'Added (tag [^ ]+|signature) for changeset [a-f0-9]{12}') if re.search(signtag, commit): return 0 for exp, msg in errors: for m in re.finditer(exp, commit): end = m.end() trailing = re.search(r'(\\n)+$', exp) if trailing: end -= len(trailing.group()) / 2 hits.append((end, exp, msg)) if hits: hits.sort() pos = 0 last = '' for n, l in enumerate(commit.splitlines(True)): pos += len(l) while len(hits): end, exp, msg = hits[0] if pos < end: break if not printed: printed = True print("node: %s" % node) print("%d: %s" % (n, msg)) print(" %s" % nonempty(l, last)[:-1]) if "BYPASS" not in os.environ: exitcode = 1 del hits[0] last = nonempty(l, last) return exitcode def readcommit(node): return os.popen("hg export %s" % node).read() if __name__ == "__main__": exitcode = 0 node = os.environ.get("HG_NODE") if node: commit = readcommit(node) exitcode = checkcommit(commit) elif sys.argv[1:]: for node in sys.argv[1:]: exitcode |= checkcommit(readcommit(node), node) else: commit = sys.stdin.read() exitcode = checkcommit(commit) sys.exit(exitcode)