+++ /dev/null
-#!/usr/bin/python3
-
-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', 'hostapd', ],
- 'emulation': ['dummynet_image', 'ipfw', ],
- 'netflow': ['fprobe-ulog', 'pf2gui', 'pf2monitor', 'pf2slice', 'iproute2', 'iptables', 'silk', ],
- 'sfa': ['sfa', '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(r'\Ar[0-9]+ \|')
- tagging = re.compile(r'\ATagging|\ASetting tag')
-
- @staticmethod
- def sort_key(u, c):
- return c
-
- 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 list(self.user_revs.items())]
- user_commits.sort(key=self.sort_key)
- 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), end=' ')
- 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 list(self.user_commits_dict.items())]
- user_commits.sort(ModuleHistory.sort)
- self.user_commits = user_commits
-
- def show(self):
- print('Overall', end=' ')
- 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 list(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 kw in self.map:
- result += self.map[kw]
- else:
- result += [kw]
- return result
-
- def list(self, scope):
- for (cat, mod_list) in list(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()