missingmaints: fix partial-row formatting
[git-dm.git] / stablefixes
blob7e5321e52202da925cd39018be214320afbe46ba
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 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 = [ ]
22 Fixes = [ ]
23 FixMap = { }
24 Reverts = [ ]
25 UpstreamMissing = [ ]
26 UpstreamMap = { }
27 Npatches = 0
30 # Patterns to snarf the info we're after.
31 # The {6,} in p_fixes is entirely to defend us against 8b9c6b28312cc,
32 # which reads "Fixes: 2.3.43".
34 p_fixes = re.compile(r'^\s+Fixes:\s+(commit\s+)?([0-9a-f]{6,}).*$')
35 p_revert = re.compile(r'This reverts commit ([0-9a-f]+)( +which is)?')
36 p_upstream = re.compile(r'commit ([0-9a-f]+) upstream', re.I)
37 p_upstream2 = re.compile(r'upstream commit ([0-9a-f]+)', re.I)
39 # Snarf the references to other patches.
41 def SaveFix(fixer, fixee):
42     for seen in Fixes:
43         if SameCommit(seen, fixer):
44             return
45     Fixes.append(fixer)
46     try:
47         FixMap[fixer].append(fixee)
48     except KeyError:
49         FixMap[fixer] = [ fixee ]
51 def FindRefs(patch):
52     #
53     # Look for a straightforward Fixes: line.
54     #
55     for line in patch.taglines:
56         m = p_fixes.match(line)
57         if m:
58             SaveFix(patch.commit, m.group(2))
59     #
60     # Or perhaps this commit is a revert?
61     #
62     whichis = False
63     m = p_revert.search(patch.changelog)
64     if m:
65         SaveFix(patch.commit, m.group(1))
66         Reverts.append(patch.commit)
67         whichis = m.group(2)
68     #
69     # In any case keep track of upstream patch corresponding to this one.  But be
70     # careful about "this reverts ... which is ... upstream"
71     #
72     if not whichis:
73         m = p_upstream.search(patch.changelog) or p_upstream2.search(patch.changelog)
74         if m:
75             Ids.append(m.group(1))
76             UpstreamMap[m.group(1)] = patch.commit
77         else:
78             UpstreamMissing.append(patch.commit)
81 # See if two commit IDs match.
83 def SameCommit(c1, c2):
84     l = min(len(c1), len(c2))
85     return c1[:l] == c2[:l]
88 # What's the URL of a patch in the stable tree?
90 SBase = 'https://git.kernel.org/cgit/linux/kernel/git/stable/' + \
91         'linux-stable.git/commit?id='
92 def StableURL(id):
93     return SBase + id
96 # Get the version for a commit
98 repo = '/k/git/kernel'
99 def find_version(commit):
100     command = ['git', 'describe', '--contains', commit, '--match', 'v*']
101     p = subprocess.Popen(command, cwd = repo, stdout = subprocess.PIPE,
102                          bufsize = 1)
103     desc = p.stdout.readline().decode('utf8')
104     p.wait()
105     #
106     # The description line has the form:
107     #
108     #      tag~N^M~n...
109     #
110     tilde = desc.find('~')
111     if tilde < 0:
112         return desc
113     return desc[:tilde]
115 def trim(commit):
116     return commit[:16]
119 # Go through the patch stream.
121 input = open(0, 'rb')
122 patch = gitlog.grabpatch(input)
123 while patch:
124     Npatches += 1
125     Ids.append(patch.commit)
126     FindRefs(patch)
127     patch = gitlog.grabpatch(input)
129 TableHdr = '''<tr><th rowspan=2>Type</th>
130                   <th colspan=2>Introduced</th>
131                   <th colspan=2>Fixed</th></tr>
132               <tr><th>Release</th><th>Commit</th><th>Release</th><th>Commit</th></tr>
136 # Now see how many fixes have been seen before.
138 out = open('stable-fixes.html', 'w')
139 out.write('<!-- Found %d patches, %d fixes, %d reverts -->\n' % (Npatches, len(Fixes), len(Reverts)))
140 out.write('<!-- %d had no upstream reference -->\n' % (len(UpstreamMissing)))
141 out.write('<table class="OddEven">\n')
142 out.write(TableHdr)
143 fixed_in_same = nfound = 0
145 # Go through all of the commit IDs we've seen (both stable and upstream)
146 # in reverse-time order.
148 def StablePatch(commit):
149     for id in Ids:
150         if SameCommit(commit, id):
151             try:
152                 return UpstreamMap[id]
153             except KeyError:
154                 return id
155     return None
157 Fixes.reverse()
158 for i in range(0, len(Fixes)):
159     if (i % 50) == 0:
160         print('Checking %d/%d, found %d   ' % (i, len(Fixes), nfound), end = '\r')
161         sys.stdout.flush()
162     fix = Fixes[i]
163     #
164     # We know we have a fix, but does it fix something that showed up in stable?
165     #
166     for fixed in FixMap[fix]:
167         fixed = StablePatch(fixed)
168         if fixed is None:
169             continue
170         v_id = find_version(fixed)
171         v_fixer = find_version(fix)
172         type = 'Fix'
173         if fix in Reverts:
174             type = 'Revert'
175         out.write('<tr><td>%s</td><td>%s</td>\n'
176                   '<td><a href="%s"><tt>%s</tt></a></td>\n'
177                   '<td>%s</td>\n'
178                   '<td><a href="%s"><tt>%s</tt></a></td></tr>\n'
179                   % (type, v_id, StableURL(fixed), trim(fixed),
180                      v_fixer, StableURL(fix), trim(fix)))
181         if v_id == v_fixer:
182             fixed_in_same += 1
183         nfound += 1
184 out.write('</table>')
185 out.write('<!-- Found %d refixes, %d in same release -->\n' % (nfound,
186                                                                fixed_in_same))
187 out.close()
188 print()