Hacky stuff for 4.0
[git-dm.git] / firstlast
blob2b07952ee10943d4e49dcbc623eea58de84d7b81
1 #!/usr/bin/pypy
2 # -*- python -*-
4 # Crank through the log looking at when developers did their first and
5 # last patches.
7 # git log | firstlast -v versiondb
9 import argparse, pickle
10 import sys
11 import gitlog
12 import database
13 import ConfigFile
14 from utils import accumulator
16 # Arg processing
18 def SetupArgs():
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',
23                    required = True)
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')
32     return p.parse_args()
35 # Try to track the first directory a new developer touches.
37 FirstDirs = { }
39 def TrackFirstDirs(patch):
40     dirs = [ ]
41     for file in patch.files:
42         split = file.split('/')
43         if split[0] in ['arch', 'drivers', 'fs']:
44             track = '/'.join(split[0:2])
45         else:
46             track = split[0]
47         if track not in dirs:
48             dirs.append(track)
49     for dir in dirs:
50         try:
51             FirstDirs[dir] += 1
52         except KeyError:
53             FirstDirs[dir] = 1
55 def cmpdirs(d1, d2):
56     return FirstDirs[d2] - FirstDirs[d1]
58 def PrintFirstDirs():
59     print '\nDirectories touched by first commits:'
60     dirs = FirstDirs.keys()
61     dirs.sort(cmpdirs)
62     for dir in dirs[:20]:
63         print '%5d: %s' % (FirstDirs[dir], dir)
66 # Let's also track who they worked for.
68 FirstEmpls = { }
70 def TrackFirstEmpl(name):
71     try:
72         FirstEmpls[name] += 1
73     except KeyError:
74         FirstEmpls[name] = 1
76 def cmpempls(e1, e2):
77     return FirstEmpls[e2] - FirstEmpls[e1]
79 def PrintFirstEmpls():
80     empls = FirstEmpls.keys()
81     empls.sort(cmpempls)
82     print '\nEmployers:'
83     for e in empls[:30]:
84         print '%5d: %s' % (FirstEmpls[e], e)
85     #
86     # We "know" that unknown/none are always the top two...
87     #
88     companies = 0
89     for e in empls[2:]:
90         companies += FirstEmpls[e]
91     print 'Companies: %d' % (companies)
94 # Version comparison stuff.  Kernel-specific, obviously.
96 def die(gripe):
97     sys.stderr.write(gripe + '\n')
98     sys.exit(1)
100 def versionmap(vers):
101     split = vers.split('.')
102     if not (2 <= len(split) <= 4):
103         die('funky version %s' % (vers))
104     if split[0] in ['v2', '2']:
105         return int(split[2])
106     if split[0] in ['v3', '3']:
107         return 100 + int(split[1])
108     if split[0] in ['v4', '4']:
109         return 120 + int(split[1])
110     die('Funky version %s' % (vers))
112 T_First = 0
113 T_Last = 999999
115 def SetTrackingVersions(args):
116     global T_First, T_Last
117     if args.first:
118         T_First = versionmap(args.first)
119     if args.last:
120         T_Last = versionmap(args.last)
122 def TrackingVersion(vers):
123     return T_First <= versionmap(vers) <= T_Last
126 # Main program.
128 args = SetupArgs()
129 VDB = pickle.load(open(args.versiondb, 'r'))
130 ConfigFile.ConfigFile(args.config, args.dbdir)
131 SetTrackingVersions(args)
133 Firsts = accumulator()
134 Lasts = accumulator()
135 Singles = accumulator()
136 Versions = accumulator()
138 # Read through the full patch stream and collect the relevant info.
140 patch = gitlog.grabpatch(sys.stdin)
141 while patch:
142     try:
143         v = VDB[patch.commit]
144     except KeyError:
145         print 'Funky commit', patch.commit
146         patch = gitlog.grabpatch(sys.stdin)
147         continue
148     #
149     # The first patch we see is the last they committed, since git
150     # lists things in backwards order.
151     #
152     if len(patch.author.patches) == 0:
153         patch.author.lastvers = v
154         Lasts.append(v, patch.author)
155     patch.author.firstvers = v
156     patch.author.addpatch(patch)
157     Versions.append(patch.author.id, v, unique = True)
158     patch = gitlog.grabpatch(sys.stdin)
161 # Pass over all the hackers we saw and collate stuff.
163 for h in database.AllHackers():
164     if len(h.patches) > 0 and len(Versions[h.id]) >= args.minversions:
165         Firsts.append(h.firstvers, h)
166         if h.firstvers == h.lastvers:
167             Singles.incr(h.firstvers)
168         #
169         # Track details, but only for versions we care about
170         #
171         if TrackingVersion(h.firstvers):
172             p = h.patches[-1]
173             TrackFirstDirs(p)
174             try:
175                 empl = h.emailemployer(p.email, p.date)
176             except AttributeError:
177                 print 'No email on ', p.commit
178                 continue
179             if empl.name == '(Unknown)':
180                 print 'UNK: %s %s' % (p.email, h.name)
181             TrackFirstEmpl(empl.name)
183 versions = Lasts.keys()
185 def cmpvers(v1, v2):
186     return versionmap(v1) - versionmap(v2)  # reverse sort
187 versions.sort(cmpvers)
188 for v in versions:
189     if args.minversions <= 1:
190         print v, len(Firsts[v]), len(Lasts[v]), Singles[v]
191     else:
192         print v, len(Firsts.get(v, [])), len(Lasts.get(v, []))
193 PrintFirstDirs()
194 PrintFirstEmpls()