Warning, file /sdk/kde-dev-scripts/svn2log.py was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 #!/usr/bin/python 0002 # -*- coding: UTF-8 -*- 0003 # 0004 # Copyright (c) 2003 The University of Wroclaw. 0005 # All rights reserved. 0006 # 0007 # Redistribution and use in source and binary forms, with or without 0008 # modification, are permitted provided that the following conditions 0009 # are met: 0010 # 1. Redistributions of source code must retain the above copyright 0011 # notice, this list of conditions and the following disclaimer. 0012 # 2. Redistributions in binary form must reproduce the above copyright 0013 # notice, this list of conditions and the following disclaimer in the 0014 # documentation and/or other materials provided with the distribution. 0015 # 3. The name of the University may not be used to endorse or promote 0016 # products derived from this software without specific prior 0017 # written permission. 0018 # 0019 # THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY ``AS IS'' AND ANY EXPRESS OR 0020 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 0021 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 0022 # NO EVENT SHALL THE UNIVERSITY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 0023 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 0024 # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 0025 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 0026 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 0027 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 0028 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 0029 # 0030 0031 import sys 0032 import os 0033 import time 0034 import re 0035 import getopt 0036 import string 0037 import codecs 0038 import locale 0039 0040 from xml.utils import qp_xml 0041 0042 kill_prefix_rx = None 0043 default_domain = "localhost" 0044 exclude = [] 0045 users = { } 0046 reloc = { } 0047 max_join_delta = 3 * 60 0048 list_format = False 0049 0050 date_rx = re.compile(r"^(\d+-\d+-\d+T\d+:\d+:\d+)") 0051 0052 def die(msg): 0053 sys.stderr.write(msg + "\n") 0054 sys.exit(1) 0055 0056 def attr(e, n): 0057 return e.attrs[("", n)] 0058 0059 def has_child(e, n): 0060 for c in e.children: 0061 if c.name == n: return 1 0062 return 0 0063 0064 def child(e, n): 0065 for c in e.children: 0066 if c.name == n: return c 0067 die("<%s> doesn't have <%s> child" % (e.name, n)) 0068 0069 def convert_path(n): 0070 for src in reloc.keys(): 0071 n = string.replace(n, src, reloc[src]) 0072 if kill_prefix_rx != None: 0073 if kill_prefix_rx.search(n): 0074 n = kill_prefix_rx.sub("", n) 0075 else: 0076 return None 0077 if n.startswith("/"): n = n[1:] 0078 if n == "": n = "/" 0079 for pref in exclude: 0080 if n.startswith(pref): 0081 return None 0082 return n 0083 0084 def convert_user(u): 0085 if users.has_key(u): 0086 return users[u] 0087 else: 0088 return u 0089 0090 def wrap_text_line(str, pref, width): 0091 ret = u"" 0092 line = u"" 0093 first_line = True 0094 for word in str.split(): 0095 if line == u"": 0096 line = word 0097 else: 0098 if len(line + u" " + word) > width: 0099 if first_line: 0100 ret += line + u"\n" 0101 first_line = False 0102 line = word 0103 else: 0104 ret += pref + line + u"\n" 0105 line = word 0106 else: 0107 line += u" " + word 0108 if first_line: 0109 ret += line + u"\n" 0110 else: 0111 ret += pref + line + u"\n" 0112 return ret 0113 0114 def wrap_text(str, pref, width): 0115 if not list_format: 0116 return wrap_text_line(str,pref,width) 0117 else: 0118 items = re.split(r"\-\s+",str) 0119 ret = wrap_text_line(items[0],pref,width) 0120 for item in items[1:]: 0121 ret += pref + u"- " + wrap_text_line(item,pref+" ",width) 0122 return ret 0123 0124 class Entry: 0125 def __init__(self, tm, rev, author, msg): 0126 self.tm = tm 0127 self.rev = rev 0128 self.author = author 0129 self.msg = msg 0130 self.beg_tm = tm 0131 self.beg_rev = rev 0132 0133 def join(self, other): 0134 self.tm = other.tm 0135 self.rev = other.rev 0136 self.msg += other.msg 0137 0138 def dump(self, out): 0139 sys.stderr.write(self.rev+"\n") 0140 if self.rev != self.beg_rev: 0141 out.write("%s [r%s-%s] %s\n\n" % \ 0142 (time.strftime("%Y-%m-%d %H:%M +0000", time.localtime(self.beg_tm)), \ 0143 self.rev, self.beg_rev, convert_user(self.author))) 0144 else: 0145 out.write(u"%s [r%s] %s\n\n" % \ 0146 (time.strftime("%Y-%m-%d %H:%M +0000", time.localtime(self.beg_tm)), \ 0147 self.rev, convert_user(self.author))) 0148 out.write(self.msg) 0149 0150 def can_join(self, other): 0151 return self.author == other.author and abs(self.tm - other.tm) < max_join_delta 0152 0153 def process_entry(e): 0154 rev = attr(e, "revision") 0155 if has_child(e, "author"): 0156 author = child(e, "author").textof() 0157 else: 0158 author = "anonymous" 0159 m = date_rx.search(child(e, "date").textof()) 0160 msg = child(e, "msg").textof() 0161 if m: 0162 tm = time.mktime(time.strptime(m.group(1), "%Y-%m-%dT%H:%M:%S")) 0163 else: 0164 die("evil date: %s" % child(e, "date").textof()) 0165 paths = [] 0166 for path in child(e, "paths").children: 0167 if path.name != "path": die("<paths> has non-<path> child") 0168 nam = convert_path(path.textof()) 0169 if nam != None: 0170 if attr(path, "action") == "D": 0171 paths.append(nam + " (removed)") 0172 elif attr(path, "action") == "A": 0173 paths.append(nam + " (added)") 0174 else: 0175 paths.append(nam) 0176 0177 if msg.startswith("SVN_SILENT"): 0178 return None 0179 0180 if msg.startswith("CVS_SILENT"): 0181 return None 0182 0183 if msg.startswith("This commit was manufactured by cvs2svn to create branch"): 0184 return None 0185 0186 if paths != []: 0187 return Entry(tm, rev, author, "\t* %s\n" % wrap_text(", ".join(paths) + ": " + msg, "\t ", 65)) 0188 0189 return None 0190 0191 def process(fin, fout): 0192 parser = qp_xml.Parser() 0193 root = parser.parse(fin) 0194 0195 if root.name != "log": die("root is not <log>") 0196 0197 cur = None 0198 0199 for logentry in root.children: 0200 if logentry.name != "logentry": die("non <logentry> <log> child") 0201 e = process_entry(logentry) 0202 if e != None: 0203 if cur != None: 0204 if cur.can_join(e): 0205 cur.join(e) 0206 else: 0207 cur.dump(fout) 0208 cur = e 0209 else: cur = e 0210 0211 if cur != None: cur.dump(fout) 0212 0213 def usage(): 0214 sys.stderr.write(\ 0215 """Usage: %s [OPTIONS] [FILE] 0216 Convert specified subversion xml logfile to GNU-style ChangeLog. 0217 0218 Options: 0219 -p, --prefix=REGEXP set root directory of project (it will be striped off 0220 from ChangeLog entries, paths outside it will be 0221 ignored) 0222 -x, --exclude=DIR exclude DIR from ChangeLog (relative to prefix) 0223 -o, --output set output file (defaults to 'ChangeLog') 0224 -d, --domain=DOMAIN set default domain for logins not listed in users file 0225 -u, --users=FILE read logins from specified file 0226 --users-charset=ENCODING 0227 specify encoding of users. defaults to ISO8859-1 0228 -F, --list-format format commit logs with enumerated change list (items 0229 prefixed by '- ') 0230 -r, --relocate=X=Y before doing any other operations on paths, replace 0231 X with Y (useful for directory moves) 0232 -D, --delta=SECS when log entries differ by less then SECS seconds and 0233 have the same author -- they are merged, it defaults 0234 to 180 seconds 0235 -h, --help print this information 0236 0237 Users file is used to map svn logins to real names to appear in ChangeLog. 0238 If login is not found in users file "login <login@domain>" is used. 0239 0240 Example users file: 0241 john John X. Foo <jfoo@example.org> 0242 mark Marcus Blah <mb@example.org> 0243 0244 Typical usage of this script is something like this: 0245 0246 svn log -v --xml | %s -p '/foo/(branches/[^/]+|trunk)' -u aux/users 0247 0248 Please send bug reports and comments to author: 0249 Michal Moskal <malekith@pld-linux.org> 0250 0251 """ % (sys.argv[0], sys.argv[0])) 0252 0253 def utf_open(name, mode): 0254 return codecs.open(name, mode, encoding="utf-8", errors="replace") 0255 0256 def process_opts(): 0257 try: 0258 opts, args = getopt.gnu_getopt(sys.argv[1:], "o:u:p:x:d:r:d:D:Fh", 0259 ["users-charset=", "users=", "prefix=", "domain=", "delta=", 0260 "exclude=", "help", "output=", "relocate=", 0261 "list-format"]) 0262 except getopt.GetoptError: 0263 usage() 0264 sys.exit(2) 0265 fin = sys.stdin 0266 fout = None 0267 users_file = None 0268 users_charset = 'ISO8859-1' 0269 global kill_prefix_rx, exclude, users, default_domain, reloc, max_join_delta, list_format 0270 for o, a in opts: 0271 if o in ("--prefix", "-p"): 0272 kill_prefix_rx = re.compile("^" + a) 0273 elif o in ("--exclude", "-x"): 0274 exclude.append(a) 0275 elif o in ("--help", "-h"): 0276 usage() 0277 sys.exit(0) 0278 elif o in ("--output", "-o"): 0279 fout = utf_open(a, "w") 0280 elif o in ("--domain", "-d"): 0281 default_domain = a 0282 elif o in ("--users", "-u"): 0283 users_file = a 0284 elif o in ("--users-charset"): 0285 users_charset = a 0286 elif o in ("--relocate", "-r"): 0287 (src, target) = a.split("=") 0288 reloc[src] = target 0289 elif o in ("--delta", "-D"): 0290 max_join_delta = int(a) 0291 elif o in ("--list-format", "-F"): 0292 list_format = True 0293 else: 0294 usage() 0295 sys.exit(2) 0296 0297 if len(args) > 1: 0298 usage() 0299 sys.exit(2) 0300 if len(args) == 1: 0301 fin = open(args[0], "r") 0302 if fout == None: 0303 fout = utf_open("ChangeLog", "w") 0304 0305 if users_file != None: 0306 f = utf_open(users_file, "r") 0307 for line in f.xreadlines(): 0308 w = line.split() 0309 if len(line) < 1 or line[0] == '#' or len(w) < 2: 0310 continue 0311 users[w[0]] = " ".join(w[1:]).decode(users_charset) 0312 process(fin, fout) 0313 0314 if __name__ == "__main__": 0315 os.environ['TZ'] = 'UTC' 0316 try: 0317 time.tzset() 0318 except AttributeError: 0319 pass 0320 process_opts() 0321 0322 # vim:ts=2:sw=2:et