inittags: some updates
[git-dm.git] / stablefixes
blob4159d01d93d1ad92a75e75762c3225a124828c47
1 #!/usr/bin/python3
2 # -*- python -*-
4 # Read through a set of patches and see how many of them are fixes
5 # for prior patches.
7 #   git log --decorate master..stable | stablefixes
9 # As with most gitdm stuff, this has been developed to the point where it
10 # solved the immediate task and no further.  I hope it's useful, but can
11 # promise nothing.
13 # Copyright 2016 Eklektix, Inc.
14 # Copyright 2016 Jonathan Corbet <corbet@lwn.net>
16 # Redistributable under GPLv2
18 import gitlog
19 import sys, re, subprocess
21 Ids = set()
22 Fixes = [ ]
23 FixMap = { }
24 Reverts = [ ]
25 UpstreamMissing = [ ]
26 UpstreamMap = { }
27 Npatches = 0
30 # Track version tags as we go.
32 def canonID(id):
33     return id[:10]
35 PatchVersions = {}
37 def AddVersion(commit, version):
38     PatchVersions[canonID(commit)] = version
39 def GetVersion(commit):
40     try:
41         return PatchVersions[canonID(commit)]
42     except KeyError:
43         return 'unknown'
46 # Patterns to snarf the info we're after.
47 # The {6,} in p_fixes is entirely to defend us against 8b9c6b28312cc,
48 # which reads "Fixes: 2.3.43".
50 p_fixes = re.compile(r'^\s+Fixes:\s+(commit\s+)?([0-9a-f]{6,}).*$')
51 p_revert = re.compile(r'This reverts commit ([0-9a-f]+)( +which is)?')
52 p_upstream = re.compile(r'commit ([0-9a-f]+) upstream', re.I)
53 p_upstream2 = re.compile(r'upstream commit ([0-9a-f]+)', re.I)
55 # Snarf the references to other patches.
57 def SaveFix(fixer, fixee):
58     if fixer not in Fixes:
59         Fixes.append(fixer)
60     try:
61         if fixee not in FixMap[fixer]:
62             FixMap[fixer].append(fixee)
63     except KeyError:
64         FixMap[fixer] = [ fixee ]
66 def FindRefs(commit, patch):
67     #
68     # Look for a straightforward Fixes: line.
69     #
70     for line in patch.taglines:
71         m = p_fixes.match(line)
72         if m:
73             SaveFix(commit, canonID(m.group(2)))
74     #
75     # Or perhaps this commit is a revert?
76     #
77     whichis = False
78     m = p_revert.search(patch.changelog)
79     if m:
80         SaveFix(commit, canonID(m.group(1)))
81         Reverts.append(commit)
82         whichis = m.group(2)
83     #
84     # In any case keep track of upstream patch corresponding to this one.  But be
85     # careful about "this reverts ... which is ... upstream"
86     #
87     if not whichis:
88         m = p_upstream.search(patch.changelog) or p_upstream2.search(patch.changelog)
89         if m:
90             Ids.add(canonID(m.group(1)))
91             UpstreamMap[canonID(m.group(1))] = commit
92         else:
93             UpstreamMissing.append(commit)
95 # What's the URL of a patch in the stable tree?
97 SBase = 'https://git.kernel.org/cgit/linux/kernel/git/stable/' + \
98         'linux-stable.git/commit?id='
99 def StableURL(id):
100     return SBase + id
102 def trim(commit):
103     return commit[:16]
106 # Go through the patch stream.
108 release = 'unknown'
109 input = open(0, 'rb')
110 patch = gitlog.grabpatch(input)
111 while patch:
112     if patch.tag:
113         release = patch.tag
114     Npatches += 1
115     commit = canonID(patch.commit)
116     Ids.add(commit)
117     AddVersion(commit, release)
118     FindRefs(commit, patch)
119     patch = gitlog.grabpatch(input)
121 TableHdr = '''<tr><th rowspan=2>Type</th>
122                   <th colspan=2>Introduced</th>
123                   <th colspan=2>Fixed</th></tr>
124               <tr><th>Release</th><th>Commit</th><th>Release</th><th>Commit</th></tr>
128 # Now see how many fixes have been seen before.
130 out = open('stable-fixes.html', 'w')
131 out.write('<!-- Found %d patches, %d fixes, %d reverts -->\n' % (Npatches, len(Fixes), len(Reverts)))
132 out.write('<!-- %d had no upstream reference -->\n' % (len(UpstreamMissing)))
133 out.write('<table class="OddEven">\n')
134 out.write(TableHdr)
135 fixed_in_same = nfound = 0
137 # Go through all of the commit IDs we've seen (both stable and upstream)
138 # in reverse-time order.
140 def StablePatch(commit):
141     if commit in Ids:
142         try:
143             return UpstreamMap[commit]
144         except KeyError:
145             return commit
146     return None
148 Fixes.reverse()
149 buggy = set()
150 buggy_different = set()
151 for i in range(0, len(Fixes)):
152     if (i % 50) == 0:
153         print('Checking %d/%d, found %d   ' % (i, len(Fixes), nfound), end = '\r')
154         sys.stdout.flush()
155     fix = Fixes[i]
156     #
157     # We know we have a fix, but does it fix something that showed up in stable?
158     #
159     for fixed in FixMap[fix]:
160         fixed = StablePatch(fixed)
161         if fixed is None:
162             continue
163         buggy.add(fixed)
164         v_id = GetVersion(fixed)
165         v_fixer = GetVersion(fix)
166         type = 'Fix'
167         if fix in Reverts:
168             type = 'Revert'
169         out.write('<tr><td>%s</td><td>%s</td>\n'
170                   '<td><a href="%s"><tt>%s</tt></a></td>\n'
171                   '<td>%s</td>\n'
172                   '<td><a href="%s"><tt>%s</tt></a></td></tr>\n'
173                   % (type, v_id, StableURL(fixed), trim(fixed),
174                      v_fixer, StableURL(fix), trim(fix)))
175         if v_id == v_fixer:
176             fixed_in_same += 1
177         else:
178             buggy_different.add(fixed)
179         nfound += 1
180 out.write('</table>')
181 out.write('<!-- Found %d/%d/%d refixes, %d in same release -->\n' % (nfound, len(buggy),
182                                                                      len(buggy_different),
183                                                                      fixed_in_same))
184 out.close()
185 print()