Index: svnadmin/hooks/scripts/log-police.py =================================================================== --- svnadmin/hooks/scripts/log-police.py (revision 286375) +++ svnadmin/hooks/scripts/log-police.py (revision 286376) @@ -1,160 +1,166 @@ #!/usr/local/bin/python # $FreeBSD$ # http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/log-police.py # log-police.py: Ensure that log messages end with a single newline. # See usage() function for details, or just run with no arguments. import os import sys import getopt try: my_getopt = getopt.gnu_getopt except AttributeError: my_getopt = getopt.getopt import svn import svn.fs import svn.repos import svn.core # Pretend we have true booleans on older python versions # How old??? try: True except: True = 1 False = 0 def fix_log_message(log_message): "Return a fixed version of LOG_MESSAGE." s = "" # Pretend leading blanks are adjacent to another lastblank = True for line in log_message.splitlines(): line = line.rstrip() # Remove duplicate blank lines if line == "": if lastblank: continue lastblank = True else: lastblank = False # should check last paragraph for known headers. if line == "PR:": continue if line == "Submitted by:": continue if line == "Reviewed by:": continue if line == "Approved by:": continue if line == "Obtained from:": continue if line == "MFC after:": continue if line == "Relnotes:": continue if line == "Security:": continue if line == "Sponsored by:": continue s = s + line + "\n" s = s.rstrip() + "\n" return s def fix_txn(fs, txn_name): "Fix up the log message for txn TXN_NAME in FS. See fix_log_message()." txn = svn.fs.svn_fs_open_txn(fs, txn_name) log_message = svn.fs.svn_fs_txn_prop(txn, "svn:log") if log_message is not None: new_message = fix_log_message(log_message) + if new_message.strip() == "": + sys.stderr.write("Log message required\n") + sys.exit(1) if new_message != log_message: svn.fs.svn_fs_change_txn_prop(txn, "svn:log", new_message) + else: + sys.stderr.write("Log message required\n") + sys.exit(1) def fix_rev(fs, revnum): "Fix up the log message for revision REVNUM in FS. See fix_log_message()." log_message = svn.fs.svn_fs_revision_prop(fs, revnum, 'svn:log') if log_message is not None: new_message = fix_log_message(log_message) if new_message != log_message: svn.fs.svn_fs_change_rev_prop(fs, revnum, "svn:log", new_message) def usage_and_exit(error_msg=None): """Write usage information and exit. If ERROR_MSG is provide, that error message is printed first (to stderr), the usage info goes to stderr, and the script exits with a non-zero status. Otherwise, usage info goes to stdout and the script exits with a zero status.""" import os.path stream = error_msg and sys.stderr or sys.stdout if error_msg: stream.write("ERROR: %s\n\n" % error_msg) stream.write("USAGE: %s [-t TXN_NAME | -r REV_NUM | --all-revs] REPOS\n" % (os.path.basename(sys.argv[0]))) stream.write(""" Ensure that log messages end with exactly one newline and no other whitespace characters. Use as a pre-commit hook by passing '-t TXN_NAME'; fix up a single revision by passing '-r REV_NUM'; fix up all revisions by passing '--all-revs'. (When used as a pre-commit hook, may modify the svn:log property on the txn.) """) sys.exit(error_msg and 1 or 0) def main(ignored_pool, argv): repos_path = None txn_name = None rev_name = None all_revs = False try: opts, args = my_getopt(argv[1:], 't:r:h?', ["help", "all-revs"]) except: usage_and_exit("problem processing arguments / options.") for opt, value in opts: if opt == '--help' or opt == '-h' or opt == '-?': usage_and_exit() elif opt == '-t': txn_name = value elif opt == '-r': rev_name = value elif opt == '--all-revs': all_revs = True else: usage_and_exit("unknown option '%s'." % opt) if txn_name is not None and rev_name is not None: usage_and_exit("cannot pass both -t and -r.") if txn_name is not None and all_revs: usage_and_exit("cannot pass --all-revs with -t.") if rev_name is not None and all_revs: usage_and_exit("cannot pass --all-revs with -r.") if rev_name is None and txn_name is None and not all_revs: usage_and_exit("must provide exactly one of -r, -t, or --all-revs.") if len(args) != 1: usage_and_exit("only one argument allowed (the repository).") repos_path = svn.core.svn_path_canonicalize(args[0]) # A non-bindings version of this could be implemented by calling out # to 'svnlook getlog' and 'svnadmin setlog'. However, using the # bindings results in much simpler code. fs = svn.repos.svn_repos_fs(svn.repos.svn_repos_open(repos_path)) if txn_name is not None: fix_txn(fs, txn_name) elif rev_name is not None: fix_rev(fs, int(rev_name)) elif all_revs: # Do it such that if we're running on a live repository, we'll # catch up even with commits that came in after we started. last_youngest = 0 while True: youngest = svn.fs.svn_fs_youngest_rev(fs) if youngest >= last_youngest: for this_rev in range(last_youngest, youngest + 1): fix_rev(fs, this_rev) last_youngest = youngest + 1 else: break if __name__ == '__main__': sys.exit(svn.core.run_app(main, sys.argv))