Move the long option name enum from cl.h into main.c, because it is
[svn.git] / tools / hook-scripts / log-police.py
blob1f056f01d91d40d85c86aaac4d13ece0fd66fb5c
1 #!/usr/bin/env python
3 # log-police.py: Ensure that log messages end with a single newline.
4 # See usage() function for details, or just run with no arguments.
6 import os
7 import sys
8 import getopt
9 try:
10 my_getopt = getopt.gnu_getopt
11 except AttributeError:
12 my_getopt = getopt.getopt
14 import svn
15 import svn.fs
16 import svn.repos
17 import svn.core
20 # Pretend we have true booleans on older python versions
21 try:
22 True
23 except:
24 True = 1
25 False = 0
28 def fix_log_message(log_message):
29 """Return a fixed version of LOG_MESSAGE. By default, this just
30 means ensuring that the result ends with exactly one newline and no
31 other whitespace. But if you want to do other kinds of fixups, this
32 function is the place to implement them -- all log message fixing in
33 this script happens here."""
34 return log_message.rstrip() + "\n"
37 def fix_txn(fs, txn_name):
38 "Fix up the log message for txn TXN_NAME in FS. See fix_log_message()."
39 txn = svn.fs.svn_fs_open_txn(fs, txn_name)
40 log_message = svn.fs.svn_fs_txn_prop(txn, "svn:log")
41 if log_message is not None:
42 new_message = fix_log_message(log_message)
43 if new_message != log_message:
44 svn.fs.svn_fs_change_txn_prop(txn, "svn:log", new_message)
47 def fix_rev(fs, revnum):
48 "Fix up the log message for revision REVNUM in FS. See fix_log_message()."
49 log_message = svn.fs.svn_fs_revision_prop(fs, revnum, 'svn:log')
50 if log_message is not None:
51 new_message = fix_log_message(log_message)
52 if new_message != log_message:
53 svn.fs.svn_fs_change_rev_prop(fs, revnum, "svn:log", new_message)
56 def usage_and_exit(error_msg=None):
57 """Write usage information and exit. If ERROR_MSG is provide, that
58 error message is printed first (to stderr), the usage info goes to
59 stderr, and the script exits with a non-zero status. Otherwise,
60 usage info goes to stdout and the script exits with a zero status."""
61 import os.path
62 stream = error_msg and sys.stderr or sys.stdout
63 if error_msg:
64 stream.write("ERROR: %s\n\n" % error_msg)
65 stream.write("USAGE: %s [-t TXN_NAME | -r REV_NUM | --all-revs] REPOS\n"
66 % (os.path.basename(sys.argv[0])))
67 stream.write("""
68 Ensure that log messages end with exactly one newline and no other
69 whitespace characters. Use as a pre-commit hook by passing '-t TXN_NAME';
70 fix up a single revision by passing '-r REV_NUM'; fix up all revisions by
71 passing '--all-revs'. (When used as a pre-commit hook, may modify the
72 svn:log property on the txn.)
73 """)
74 sys.exit(error_msg and 1 or 0)
77 def main(ignored_pool, argv):
78 repos_path = None
79 txn_name = None
80 rev_name = None
81 all_revs = False
83 try:
84 opts, args = my_getopt(argv[1:], 't:r:h?', ["help", "all-revs"])
85 except:
86 usage_and_exit("problem processing arguments / options.")
87 for opt, value in opts:
88 if opt == '--help' or opt == '-h' or opt == '-?':
89 usage_and_exit()
90 elif opt == '-t':
91 txn_name = value
92 elif opt == '-r':
93 rev_name = value
94 elif opt == '--all-revs':
95 all_revs = True
96 else:
97 usage_and_exit("unknown option '%s'." % opt)
99 if txn_name is not None and rev_name is not None:
100 usage_and_exit("cannot pass both -t and -r.")
101 if txn_name is not None and all_revs:
102 usage_and_exit("cannot pass --all-revs with -t.")
103 if rev_name is not None and all_revs:
104 usage_and_exit("cannot pass --all-revs with -r.")
105 if rev_name is None and txn_name is None and not all_revs:
106 usage_and_exit("must provide exactly one of -r, -t, or --all-revs.")
107 if len(args) != 1:
108 usage_and_exit("only one argument allowed (the repository).")
110 repos_path = svn.core.svn_path_canonicalize(args[0])
112 # A non-bindings version of this could be implemented by calling out
113 # to 'svnlook getlog' and 'svnadmin setlog'. However, using the
114 # bindings results in much simpler code.
116 fs = svn.repos.svn_repos_fs(svn.repos.svn_repos_open(repos_path))
117 if txn_name is not None:
118 fix_txn(fs, txn_name)
119 elif rev_name is not None:
120 fix_rev(fs, int(rev_name))
121 elif all_revs:
122 # Do it such that if we're running on a live repository, we'll
123 # catch up even with commits that came in after we started.
124 last_youngest = 0
125 while True:
126 youngest = svn.fs.svn_fs_youngest_rev(fs)
127 if youngest >= last_youngest:
128 for this_rev in range(last_youngest, youngest + 1):
129 fix_rev(fs, this_rev)
130 last_youngest = youngest + 1
131 else:
132 break
135 if __name__ == '__main__':
136 sys.exit(svn.core.run_app(main, sys.argv))