2 # -*- mode:python; var: python-guess-indent:false; python-indent:4; -*-
7 from optparse import OptionParser
10 'general' : [ 'build', 'tests', ],
11 'server' : ['Monitor', 'MyPLC', 'PLCAPI', 'PLCRT', 'PLCWWW', 'PLEWWW', 'www-register-wizard',
12 'PXEService', 'drupal', 'plcmdline',],
13 'node': [ 'linux-2.6', 'util-vserver', 'util-vserver-pl', 'chopstix-L0',
14 'BootCD', 'BootManager', 'BootstrapFS', 'VserverReference',
15 'DistributedRateLimiting', 'Mom', 'PingOfDeath',
16 'NodeManager', 'NodeManager-optin', 'NodeManager-topo',
17 'NodeUpdate', 'CoDemux',
18 'nodeconfig', 'pl_sshd',
19 'libnl', 'pypcilib', 'pyplnet',],
20 'wifi' : ['madwifi', 'PlanetBridge', 'hostapd',],
21 'emulation': ['dummynet_image', 'ipfw', ],
22 'netflow' : [ 'fprobe-ulog', 'pf2gui', 'pf2monitor', 'pf2slice', 'iproute2', 'iptables', 'silk',],
23 'sfa' : [ 'sfa', 'xmlrspecs', 'pyopenssl', ],
24 'vsys' : ['vsys', 'vsys-scripts', 'vsys-wrappers', 'inotify-tools'],
25 'deprecated' : [ 'proper', 'libhttpd++', 'oombailout', 'ulogd', 'patchdep', 'pdelta',
26 'sandbox', 'playground', 'infrastructure', 'util-python', 'vnetspec',
34 def __init__ (self, name, options):
40 self.current_user=None
42 valid=re.compile('\Ar[0-9]+ \|')
43 tagging=re.compile('\ATagging|\ASetting tag')
46 def sort ( (u1,c1), (u2,c2) ): return c2-c1
48 def record(self,user,rev):
50 self.user_revs[user].append(rev)
52 self.user_revs[user] = [rev]
54 self.current_user=user
57 if (not self.current_user) or (not self.current_rev): return
58 user_list=self.user_revs[self.current_user]
59 if len(user_list) >= 1 and user_list[-1] == self.current_rev:
63 cmd = "svn log -r %s:%s http://svn.planet-lab.org/svn/%s " % (self.options.fromv,self.options.tov,self.name)
64 if self.options.verbose:
68 if not self.valid.match(line):
69 # mostly ignore commit body, except for ignoring the current commit if -i is set
70 if self.options.ignore_tags and self.tagging.match(line):
71 # roll back these changes
74 fields = line.split('|')
75 fields = [field.strip() for field in fields]
76 [rev,user,ctime,size] = fields[:4]
78 # translate into a list of tuples
79 user_commits = [ (user,len(revs)) for (user,revs) in self.user_revs.items() ]
80 user_commits.sort(self.sort)
81 self.user_commits=user_commits
84 if len(self.user_commits) ==0: return
85 print '%s [%s-%s]'%(self.name,self.options.fromv,self.options.tov),
86 if self.options.ignore_tags:
87 print ' - Ignored tag commits'
90 for (u,c) in self.user_commits:
95 def __init__ (self,options):
96 # key=user, value=commits
98 self.user_commits_dict={}
101 def merge (self, modulehistory):
102 for (u,c) in modulehistory.user_commits:
104 self.user_commits_dict[u] += c
106 self.user_commits_dict[u] = c
109 user_commits = [ (u,c) for (u,c) in self.user_commits_dict.items() ]
110 user_commits.sort(ModuleHistory.sort)
111 self.user_commits=user_commits
115 if self.options.ignore_tags:
116 print ' - Ignored tag commits'
119 for (u,c) in self.user_commits:
124 def __init__ (self,map):
127 def categories(self):
128 return self.map.keys()
130 def all_modules(self,categories=None):
131 if not categories: categories=self.categories()
132 elif not isinstance(categories,list): categories=[categories]
134 for category in categories:
135 result += self.map[category]
138 def locate (self,keywords):
141 if self.map.has_key(kw):
142 result += self.map[kw]
147 def list(self,scope):
148 for (cat,mod_list) in self.map.items():
154 usage="%prog [module_or_category ...]"
155 parser = OptionParser(usage=usage)
156 parser.add_option("-f", "--from", action = "store", dest='fromv',
157 default = epoch, help = "The revision to start from, default %s"%epoch)
158 parser.add_option("-t", "--to", action = "store", dest='tov',
159 default = 'HEAD', help = "The revision to end with, default HEAD")
160 parser.add_option("-n","--no-aggregate", action='store_false',dest='aggregate',default=True,
161 help='Do not aggregate over modules')
162 parser.add_option("-v","--verbose", action='store_true',dest='verbose',default=False,
163 help='Run in verbose/debug mode')
164 parser.add_option("-i","--ignore-tags",action='store_true',dest='ignore_tags',
165 help='ignore commits related to tagging')
166 parser.add_option("-l","--list",action='store_true',dest='list_modules',
167 help='list available modules and categories')
168 # pass this to the invoked shell if any
169 (options, args) = parser.parse_args()
171 map=Modules(modules_map)
173 modules=map.all_modules()
175 modules=map.locate(args)
177 if options.list_modules:
182 options.aggregate=False
184 aggregate = Aggregate(options)
185 for module in modules:
186 history=ModuleHistory(module,options)
189 aggregate.merge(history)
191 if options.aggregate:
195 if __name__ == '__main__':