From 8cb8a2e3a7bdb4791a9bdacbcf6afb88dd206740 Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Sat, 18 Jul 2009 15:15:55 +0000 Subject: [PATCH] revised version of Stephen's svnloghistory.py --- module-log.py | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100755 module-log.py diff --git a/module-log.py b/module-log.py new file mode 100755 index 00000000..5ab53747 --- /dev/null +++ b/module-log.py @@ -0,0 +1,196 @@ +#!/usr/bin/python +# -*- mode:python; var: python-guess-indent:false; python-indent:4; -*- + +import os +import sys +import re +from optparse import OptionParser + +modules_map = { + 'general' : [ 'build', 'tests', ], + 'server' : ['Monitor', 'MyPLC', 'PLCAPI', 'PLCRT', 'PLCWWW', 'PLEWWW', 'www-register-wizard', + 'PXEService', 'drupal', 'plcmdline',], + 'node': [ 'linux-2.6', 'util-vserver', 'util-vserver-pl', 'chopstix-L0', + 'BootCD', 'BootManager', 'BootstrapFS', 'VserverReference', + 'DistributedRateLimiting', 'Mom', 'PingOfDeath', + 'NodeManager', 'NodeManager-optin', 'NodeManager-topo', + 'NodeUpdate', 'CoDemux', + 'nodeconfig', 'pl_sshd', + 'libnl', 'pypcilib', 'pyplnet',], + 'wifi' : ['madwifi', 'PlanetBridge', 'wifiadmin', 'hostapd',], + 'emulation': ['dummynet_image', 'ipfw', ], + 'netflow' : [ 'fprobe-ulog', 'pf2gui', 'pf2monitor', 'pf2slice', 'iproute2', 'iptables', 'silk',], + 'sfa' : [ 'geniwrapper', 'xmlrspecs', 'pyopenssl', ], + 'vsys' : ['vsys', 'vsys-scripts', 'vsys-wrappers', 'inotify-tools'], + 'deprecated' : [ 'proper', 'libhttpd++', 'oombailout', 'ulogd', 'patchdep', 'pdelta', + 'sandbox', 'playground', 'infrastructure', 'util-python', 'vnetspec', + ], + } + +epoch='{2007-07-01}' + +class ModuleHistory: + + def __init__ (self, name, options): + self.name=name + self.options=options + self.user_commits=[] + self.user_revs={} + self.current_rev=None + self.current_user=None + + valid=re.compile('\Ar[0-9]+ \|') + tagging=re.compile('\ATagging|\ASetting tag') + + @staticmethod + def sort ( (u1,c1), (u2,c2) ): return c2-c1 + + def record(self,user,rev): + try: + self.user_revs[user].append(rev) + except: + self.user_revs[user] = [rev] + self.current_rev=rev + self.current_user=user + + def ignore(self): + if (not self.current_user) or (not self.current_rev): return + user_list=self.user_revs[self.current_user] + if len(user_list) >= 1 and user_list[-1] == self.current_rev: + user_list.pop() + + def scan (self): + cmd = "svn log -r %s:%s http://svn.planet-lab.org/svn/%s " % (self.options.fromv,self.options.tov,self.name) + if self.options.verbose: + print 'running',cmd + f = os.popen(cmd) + for line in f: + if not self.valid.match(line): + # mostly ignore commit body, except for ignoring the current commit if -i is set + if self.options.ignore_tags and self.tagging.match(line): + # roll back these changes + self.ignore() + continue + fields = line.split('|') + fields = [field.strip() for field in fields] + [rev,user,ctime,size] = fields[:4] + self.record(user,rev) + # translate into a list of tuples + user_commits = [ (user,len(revs)) for (user,revs) in self.user_revs.items() ] + user_commits.sort(self.sort) + self.user_commits=user_commits + + def show(self): + if len(self.user_commits) ==0: return + print '%s [%s-%s]'%(self.name,self.options.fromv,self.options.tov), + if self.options.ignore_tags: + print ' - Ignored tag commits' + else: + print '' + for (u,c) in self.user_commits: + print "\t",u,c + +class Aggregate: + + def __init__ (self,options): + # key=user, value=commits + self.options=options + self.user_commits_dict={} + self.user_commits=[] + + def merge (self, modulehistory): + for (u,c) in modulehistory.user_commits: + try: + self.user_commits_dict[u] += c + except: + self.user_commits_dict[u] = c + + def sort (self): + user_commits = [ (u,c) for (u,c) in self.user_commits_dict.items() ] + user_commits.sort(ModuleHistory.sort) + self.user_commits=user_commits + + def show(self): + print 'Overall', + if self.options.ignore_tags: + print ' - Ignored tag commits' + else: + print '' + for (u,c) in self.user_commits: + print "\t",u,c + +class Modules: + + def __init__ (self,map): + self.map=map + + def categories(self): + return self.map.keys() + + def all_modules(self,categories=None): + if not categories: categories=self.categories() + elif not isinstance(categories,list): categories=[categories] + result=[] + for category in categories: + result += self.map[category] + return result + + def locate (self,keywords): + result=[] + for kw in keywords: + if self.map.has_key(kw): + result += self.map[kw] + else: + result += [kw] + return result + + def list(self,scope): + for (cat,mod_list) in self.map.items(): + for mod in mod_list: + if mod in scope: + print cat,mod + +def main (): + usage="%prog [module_or_category ...]" + parser = OptionParser(usage=usage) + parser.add_option("-f", "--from", action = "store", dest='fromv', + default = epoch, help = "The revision to start from, default %s"%epoch) + parser.add_option("-t", "--to", action = "store", dest='tov', + default = 'HEAD', help = "The revision to end with, default HEAD") + parser.add_option("-n","--no-aggregate", action='store_false',dest='aggregate',default=True, + help='Do not aggregate over modules') + parser.add_option("-v","--verbose", action='store_true',dest='verbose',default=False, + help='Run in verbose/debug mode') + parser.add_option("-i","--ignore-tags",action='store_true',dest='ignore_tags', + help='ignore commits related to tagging') + parser.add_option("-l","--list",action='store_true',dest='list_modules', + help='list available modules and categories') + # pass this to the invoked shell if any + (options, args) = parser.parse_args() + + map=Modules(modules_map) + if not args: + modules=map.all_modules() + else: + modules=map.locate(args) + + if options.list_modules: + map.list(modules) + return + + if len(modules) <=1: + options.aggregate=False + + aggregate = Aggregate(options) + for module in modules: + history=ModuleHistory(module,options) + history.scan() + history.show() + aggregate.merge(history) + + if options.aggregate: + aggregate.sort() + aggregate.show() + +if __name__ == '__main__': + main() -- 2.43.0