4 # Crank through the log looking at when developers did their first and
7 # git log | firstlast -v versiondb
9 import argparse, pickle
14 from utils import accumulator
19 p = argparse.ArgumentParser()
20 p.add_argument('-v', '--versiondb', help = 'Version database file',
21 required = False, default = 'committags.db')
22 p.add_argument('-c', '--config', help = 'Configuration file',
24 p.add_argument('-d', '--dbdir', help = 'Where to find the config database files',
25 required = False, default = '')
26 p.add_argument('-f', '--first', help = 'First version for detailed tracking',
27 required = False, default = '')
28 p.add_argument('-l', '--last', help = 'Last version for detailed tracking',
29 required = False, default = '')
30 p.add_argument('-m', '--minversions', required = False, default = 1, type = int,
31 help = 'How many versions an author contributes to for counting')
35 # Try to track the first directory a new developer touches.
39 def TrackFirstDirs(patch):
41 for file in patch.files:
42 split = file.split('/')
43 if split[0] in ['arch', 'drivers', 'fs']:
44 track = '/'.join(split[0:2])
59 print('\nDirectories touched by first commits:')
60 dirs = list(FirstDirs.keys())
61 dirs.sort(key = dirkey, reverse = True)
63 print('%5d: %s' % (FirstDirs[dir], dir))
66 # Let's also track who they worked for.
70 def TrackFirstEmpl(name):
79 def PrintFirstEmpls():
80 empls = list(FirstEmpls.keys())
81 empls.sort(key = emplkey, reverse = True)
84 print('%5d: %s' % (FirstEmpls[e], e))
86 # Make a quick sum of how many first timers were employed
90 if e not in [ '(Unknown)', '(None)' ]:
91 companies += FirstEmpls[e]
92 print('Companies: %d' % (companies))
100 def TrackCounts(v, patch):
102 DevsPerVersion[v].add(patch.author)
104 DevsPerVersion[v] = set()
105 DevsPerVersion[v].add(patch.author)
108 empl = patch.author.emailemployer(patch.email, patch.date)
109 except AttributeError:
112 EmplsPerVersion[v].add(empl)
114 EmplsPerVersion[v] = set()
115 EmplsPerVersion[v].add(empl)
117 # Version comparison stuff. Kernel-specific, obviously.
120 sys.stderr.write(gripe + '\n')
123 def versionmap(vers):
124 split = vers.split('.')
125 if not (2 <= len(split) <= 5):
126 die('funky version %s' % (vers))
127 if split[0] in ['v2', '2']:
129 if split[0] in ['v3', '3']:
130 return 100 + int(split[1])
131 if split[0] in ['v4', '4']:
132 return 120 + int(split[1])
133 if split[0] in ['v5', '5']:
134 return 150 + int(split[1])
135 die('Funky version %s' % (vers))
140 def SetTrackingVersions(args):
141 global T_First, T_Last
143 T_First = versionmap(args.first)
145 T_Last = versionmap(args.last)
147 def TrackingVersion(vers):
148 return T_First <= versionmap(vers) <= T_Last
151 # Count the number of last-patch authors that had the minimum number
154 def CountLasts(hackers):
157 if len(Versions[h.id]) >= args.minversions:
164 VDB = pickle.load(open(args.versiondb, 'rb'))
165 ConfigFile.ConfigFile(args.config, args.dbdir)
166 SetTrackingVersions(args)
168 Firsts = accumulator()
169 Lasts = accumulator()
170 Singles = accumulator()
171 Versions = accumulator()
173 # Read through the full patch stream and collect the relevant info.
175 input = open(0, 'rb') # Get a bytes version of stdin
176 patch = gitlog.grabpatch(input)
179 v = VDB[patch.commit]
181 print('Funky commit', patch.commit)
182 patch = gitlog.grabpatch(input)
184 TrackCounts(v, patch)
186 # The first patch we see is the last they committed, since git
187 # lists things in backwards order.
189 # ... except, of course, that life is not so simple, and git can
190 # present patches in different orders at different times, so we
191 # just have to compare versions.
195 if mapv < versionmap(patch.author.firstvers):
196 patch.author.firstvers = v
197 except AttributeError:
198 patch.author.firstvers = v
200 if mapv > versionmap(patch.author.lastvers):
201 patch.author.lastvers = v
202 except AttributeError:
203 patch.author.lastvers = v
204 patch.author.addpatch(patch)
205 Versions.append(patch.author.id, v, unique = True)
206 patch = gitlog.grabpatch(input)
209 # Pass over all the hackers we saw and collate stuff.
211 for h in database.AllHackers():
212 if len(h.patches) > 0 and len(Versions[h.id]) >= args.minversions:
213 Firsts.append(h.firstvers, h)
214 Lasts.append(h.lastvers, h)
215 if h.firstvers == h.lastvers:
216 Singles.incr(h.firstvers)
218 # Track details, but only for versions we care about
220 if TrackingVersion(h.firstvers):
224 empl = h.emailemployer(p.email, p.date)
225 except AttributeError:
226 print('No email on ', p.commit)
228 # if empl.name == '(Unknown)':
229 # print('UNK: %s %s' % (p.email, h.name))
230 TrackFirstEmpl(empl.name)
234 return versionmap(vers)
236 versions = list(Lasts.keys())
237 versions.sort(key = vkey)
238 if args.minversions <= 1:
239 print('VERS\tFirst\tLast\tSingle\tTotal\tEmpls')
241 print('VERS\tFirst\tLast\tTotal\tEmpls')
243 if args.minversions <= 1:
244 print('%s\t%d\t%d\t%d' % (v, len(Firsts[v]), len(Lasts[v]), Singles[v]),
247 print('%s\t%d\t%d' % (v, len(Firsts.get(v, [])),
248 CountLasts(Lasts.get(v, []))), end = '')
249 print('\t%d\t%d' % (len(DevsPerVersion[v]), len(EmplsPerVersion[v]) - 3))