Convert gitdm to Python 3
[git-dm.git] / gitlog.py
blob71efee1da2deca7932400f0cacfde365c7579277
2 # Stuff for dealing with the git log output.
4 # Someday this will be the only version of grabpatch, honest.
6 import re, rfc822, datetime
7 from patterns import patterns
8 import database
12 # Input file handling. Someday it would be good to make this smarter
13 # so that it handles running git with the right options and such.
15 # Someday.
17 SavedLine = ''
19 def getline(input):
20 global SavedLine
21 if SavedLine:
22 ret = SavedLine
23 SavedLine = ''
24 return ret
25 l = input.readline()
26 if l:
27 return l.rstrip()
28 return None
30 def SaveLine(line):
31 global SavedLine
32 SavedLine = line
35 # A simple state machine based on where we are in the patch. The
36 # first stuff we get is the header.
38 S_HEADER = 0
40 # Then comes the single-line description.
42 S_DESC = 1
44 # ...the full changelog...
46 S_CHANGELOG = 2
48 # ...the tag section....
50 S_TAGS = 3
52 # ...the numstat section.
54 S_NUMSTAT = 4
56 S_DONE = 5
59 # The functions to handle each of these states.
61 def get_header(patch, line, input):
62 if line == '':
63 if patch.author == '':
64 print 'Funky auth line in', patch.commit
65 patch.author = database.LookupStoreHacker('Unknown',
66 'unknown@hacker.net')
67 return S_DESC
68 m = patterns['author'].match(line)
69 if m:
70 patch.email = database.RemapEmail(m.group(2))
71 patch.author = database.LookupStoreHacker(m.group(1), patch.email)
72 else:
73 m = patterns['date'].match(line)
74 if m:
75 dt = rfc822.parsedate(m.group(2))
76 patch.date = datetime.date(dt[0], dt[1], dt[2])
77 return S_HEADER
79 def get_desc(patch, line, input):
80 if not line:
81 print 'Missing desc in', patch.commit
82 return S_CHANGELOG
83 patch.desc = line
84 line = getline(input)
85 while line:
86 patch.desc += line
87 line = getline(input)
88 return S_CHANGELOG
90 tagline = re.compile(r'^\s+(([-a-z]+-by)|cc|fixes):.*$', re.I)
91 def get_changelog(patch, line, input):
92 if not line:
93 if patch.templog:
94 patch.changelog += patch.templog
95 patch.templog = ''
96 if patterns['commit'].match(line):
97 # No changelog at all - usually a Linus tag
98 SaveLine(line)
99 return S_DONE
100 elif tagline.match(line):
101 if patch.templog:
102 patch.changelog += patch.templog
103 return get_tag(patch, line, input)
104 else:
105 patch.templog += line + '\n'
106 return S_CHANGELOG
108 def get_tag(patch, line, input):
110 # Some people put blank lines in the middle of tags.
112 if not line:
113 return S_TAGS
115 # A new commit line says we've gone too far.
117 if patterns['commit'].match(line):
118 SaveLine(line)
119 return S_DONE
121 # Check for a numstat line
123 if patterns['numstat'].match(line):
124 return get_numstat(patch, line, input)
126 # Look for interesting tags
128 m = patterns['signed-off-by'].match(line)
129 if m:
130 patch.signoffs.append(m.group(2))
131 else:
133 # Look for other tags indicating that somebody at least
134 # looked at the patch.
136 for tag in ('acked-by', 'reviewed-by', 'tested-by'):
137 if patterns[tag].match(line):
138 patch.othertags += 1
139 break
140 patch.taglines.append(line)
141 return S_TAGS
143 def get_numstat(patch, line, input):
144 m = patterns['numstat'].match(line)
145 if not m:
146 return S_DONE
147 try:
148 patch.addfile(int(m.group(1)), int(m.group(2)), m.group(3))
150 # Binary files just have "-" in the line fields. In this case, set
151 # the counts to zero so that we at least track that the file was
152 # touched.
154 except ValueError:
155 patch.addfile(0, 0, m.group(3))
156 return S_NUMSTAT
158 grabbers = [ get_header, get_desc, get_changelog, get_tag, get_numstat ]
162 # A variant on the gitdm patch class.
164 class patch:
165 def __init__(self, commit):
166 self.commit = commit
167 self.desc = ''
168 self.changelog = ''
169 self.templog = ''
170 self.author = ''
171 self.signoffs = [ ]
172 self.othertags = 0
173 self.added = self.removed = 0
174 self.files = [ ]
175 self.taglines = [ ]
177 def addfile(self, added, removed, file):
178 self.added += added
179 self.removed += removed
180 self.files.append(file)
182 def grabpatch(input):
184 # If it's not a patch something is screwy.
186 line = getline(input)
187 if line is None:
188 return None
189 m = patterns['commit'].match(line)
190 if not m:
191 print 'noncommit', line
192 return None
193 p = patch(m.group(1))
194 state = S_HEADER
196 # Crank through the patch.
198 while state != S_DONE:
199 line = getline(input)
200 if line is None:
201 if state != S_NUMSTAT:
202 print 'Ran out of patch', state
203 return None
204 return p
205 state = grabbers[state](p, line, input)
206 return p