2 # Stuff for dealing with the git log output.
4 # Someday this will be the only version of grabpatch, honest.
7 from email
.utils
import parsedate
8 from patterns
import patterns
13 # Input file handling. Someday it would be good to make this smarter
14 # so that it handles running git with the right options and such.
30 # In theory everything coming out of git is utf8. In practice,
31 # there's some funky stuff in the kernel repo. So...fall back
32 # to latin1 if a utf8 decode fails; that doesn't work for everything
33 # but it's about as good as we can do.
37 except UnicodeDecodeError:
38 l
= l
.decode('latin1')
46 # A simple state machine based on where we are in the patch. The
47 # first stuff we get is the header.
51 # Then comes the single-line description.
55 # ...the full changelog...
59 # ...the tag section....
63 # ...the numstat section.
70 # The functions to handle each of these states.
72 def get_header(patch
, line
, input):
74 if patch
.author
== '':
75 print('Funky auth line in', patch
.commit
)
76 patch
.author
= database
.LookupStoreHacker('Unknown',
79 m
= patterns
['author'].match(line
)
81 patch
.email
= database
.RemapEmail(m
.group(2))
82 patch
.author
= database
.LookupStoreHacker(m
.group(1), patch
.email
)
84 m
= patterns
['date'].match(line
)
86 dt
= parsedate(m
.group(2))
87 patch
.date
= datetime
.date(dt
[0], dt
[1], dt
[2])
90 def get_desc(patch
, line
, input):
92 print('Missing desc in', patch
.commit
)
101 tagline
= re
.compile(r
'^\s+(([-a-z]+-by)|cc|fixes):.*$', re
.I
)
102 def get_changelog(patch
, line
, input):
105 patch
.changelog
+= patch
.templog
107 if patterns
['commit'].match(line
):
108 # No changelog at all - usually a Linus tag
111 elif tagline
.match(line
):
113 patch
.changelog
+= patch
.templog
114 return get_tag(patch
, line
, input)
116 patch
.templog
+= line
+ '\n'
119 def get_tag(patch
, line
, input):
121 # Some people put blank lines in the middle of tags.
126 # A new commit line says we've gone too far.
128 if patterns
['commit'].match(line
):
132 # Check for a numstat line
134 if patterns
['numstat'].match(line
):
135 return get_numstat(patch
, line
, input)
137 # Look for interesting tags
139 m
= patterns
['signed-off-by'].match(line
)
141 patch
.signoffs
.append(m
.group(2))
144 # Look for other tags indicating that somebody at least
145 # looked at the patch.
147 for tag
in ('acked-by', 'reviewed-by', 'tested-by'):
148 if patterns
[tag
].match(line
):
151 patch
.taglines
.append(line
)
154 def get_numstat(patch
, line
, input):
155 m
= patterns
['numstat'].match(line
)
159 patch
.addfile(int(m
.group(1)), int(m
.group(2)), m
.group(3))
161 # Binary files just have "-" in the line fields. In this case, set
162 # the counts to zero so that we at least track that the file was
166 patch
.addfile(0, 0, m
.group(3))
169 grabbers
= [ get_header
, get_desc
, get_changelog
, get_tag
, get_numstat
]
173 # A variant on the gitdm patch class.
176 def __init__(self
, commit
):
184 self
.added
= self
.removed
= 0
188 def addfile(self
, added
, removed
, file):
190 self
.removed
+= removed
191 self
.files
.append(file)
193 tag
= re
.compile('\(tag: (v[.\d]+)\)')
194 def grabpatch(input):
196 # If it's not a patch something is screwy.
198 line
= getline(input)
201 m
= patterns
['commit'].match(line
)
203 print('noncommit', line
)
205 p
= patch(m
.group(1))
208 # Look for a tag on this line. BUG fails with two tags
212 patch
.tag
= m
.group(1)
216 # Crank through the patch.
218 while state
!= S_DONE
:
219 line
= getline(input)
221 if state
!= S_NUMSTAT
:
222 print('Ran out of patch', state
)
225 state
= grabbers
[state
](p
, line
, input)