6 from optparse import OptionParser
9 'general': ['build', 'tests', ],
10 'server': ['Monitor', 'MyPLC', 'PLCAPI', 'PLCRT', 'PLCWWW', 'PLEWWW', 'www-register-wizard',
11 'PXEService', 'drupal', 'plcmdline', ],
12 'node': ['linux-2.6', 'util-vserver', 'util-vserver-pl', 'chopstix-L0',
13 'BootCD', 'BootManager', 'BootstrapFS', 'VserverReference',
14 'DistributedRateLimiting', 'Mom', 'PingOfDeath',
15 'NodeManager', 'NodeManager-optin', 'NodeManager-topo',
16 'NodeUpdate', 'CoDemux',
17 'nodeconfig', 'pl_sshd',
18 'libnl', 'pypcilib', 'pyplnet', ],
19 'wifi': ['madwifi', 'PlanetBridge', 'hostapd', ],
20 'emulation': ['dummynet_image', 'ipfw', ],
21 'netflow': ['fprobe-ulog', 'pf2gui', 'pf2monitor', 'pf2slice', 'iproute2', 'iptables', 'silk', ],
22 'sfa': ['sfa', 'xmlrspecs', 'pyopenssl', ],
23 'vsys': ['vsys', 'vsys-scripts', 'vsys-wrappers', 'inotify-tools'],
24 'deprecated': ['proper', 'libhttpd++', 'oombailout', 'ulogd', 'patchdep', 'pdelta',
25 'sandbox', 'playground', 'infrastructure', 'util-python', 'vnetspec',
29 epoch = '{2007-07-01}'
34 def __init__(self, name, options):
36 self.options = options
37 self.user_commits = []
39 self.current_rev = None
40 self.current_user = None
42 valid = re.compile(r'\Ar[0-9]+ \|')
43 tagging = re.compile(r'\ATagging|\ASetting tag')
49 def record(self, user, rev):
51 self.user_revs[user].append(rev)
53 self.user_revs[user] = [rev]
54 self.current_rev = rev
55 self.current_user = user
58 if (not self.current_user) or (not self.current_rev):
60 user_list = self.user_revs[self.current_user]
61 if len(user_list) >= 1 and user_list[-1] == self.current_rev:
65 cmd = "svn log -r %s:%s http://svn.planet-lab.org/svn/%s " % (
66 self.options.fromv, self.options.tov, self.name)
67 if self.options.verbose:
71 if not self.valid.match(line):
72 # mostly ignore commit body, except for ignoring the current commit if -i is set
73 if self.options.ignore_tags and self.tagging.match(line):
74 # roll back these changes
77 fields = line.split('|')
78 fields = [field.strip() for field in fields]
79 [rev, user, ctime, size] = fields[:4]
80 self.record(user, rev)
81 # translate into a list of tuples
82 user_commits = [(user, len(revs))
83 for (user, revs) in list(self.user_revs.items())]
84 user_commits.sort(key=self.sort_key)
85 self.user_commits = user_commits
88 if len(self.user_commits) == 0:
91 (self.name, self.options.fromv, self.options.tov), end=' ')
92 if self.options.ignore_tags:
93 print(' - Ignored tag commits')
96 for (u, c) in self.user_commits:
102 def __init__(self, options):
103 # key=user, value=commits
104 self.options = options
105 self.user_commits_dict = {}
106 self.user_commits = []
108 def merge(self, modulehistory):
109 for (u, c) in modulehistory.user_commits:
111 self.user_commits_dict[u] += c
113 self.user_commits_dict[u] = c
116 user_commits = [(u, c)
117 for (u, c) in list(self.user_commits_dict.items())]
118 user_commits.sort(ModuleHistory.sort)
119 self.user_commits = user_commits
122 print('Overall', end=' ')
123 if self.options.ignore_tags:
124 print(' - Ignored tag commits')
127 for (u, c) in self.user_commits:
133 def __init__(self, map):
136 def categories(self):
137 return list(self.map.keys())
139 def all_modules(self, categories=None):
141 categories = self.categories()
142 elif not isinstance(categories, list):
143 categories = [categories]
145 for category in categories:
146 result += self.map[category]
149 def locate(self, keywords):
153 result += self.map[kw]
158 def list(self, scope):
159 for (cat, mod_list) in list(self.map.items()):
166 usage = "%prog [module_or_category ...]"
167 parser = OptionParser(usage=usage)
168 parser.add_option("-f", "--from", action="store", dest='fromv',
169 default=epoch, help="The revision to start from, default %s" % epoch)
170 parser.add_option("-t", "--to", action="store", dest='tov',
171 default='HEAD', help="The revision to end with, default HEAD")
172 parser.add_option("-n", "--no-aggregate", action='store_false', dest='aggregate', default=True,
173 help='Do not aggregate over modules')
174 parser.add_option("-v", "--verbose", action='store_true', dest='verbose', default=False,
175 help='Run in verbose/debug mode')
176 parser.add_option("-i", "--ignore-tags", action='store_true', dest='ignore_tags',
177 help='ignore commits related to tagging')
178 parser.add_option("-l", "--list", action='store_true', dest='list_modules',
179 help='list available modules and categories')
180 # pass this to the invoked shell if any
181 (options, args) = parser.parse_args()
183 map = Modules(modules_map)
185 modules = map.all_modules()
187 modules = map.locate(args)
189 if options.list_modules:
193 if len(modules) <= 1:
194 options.aggregate = False
196 aggregate = Aggregate(options)
197 for module in modules:
198 history = ModuleHistory(module, options)
201 aggregate.merge(history)
203 if options.aggregate:
208 if __name__ == '__main__':