1188 if i < 0: |
1188 if i < 0: |
1189 i = s.find(' ') |
1189 i = s.find(' ') |
1190 if i < 0: |
1190 if i < 0: |
1191 return s |
1191 return s |
1192 return s[:i] |
1192 return s[:i] |
|
1193 |
|
1194 def parsepatch(fp): |
|
1195 """patch -> [] of headers -> [] of hunks """ |
|
1196 class parser(object): |
|
1197 """patch parsing state machine""" |
|
1198 def __init__(self): |
|
1199 self.fromline = 0 |
|
1200 self.toline = 0 |
|
1201 self.proc = '' |
|
1202 self.header = None |
|
1203 self.context = [] |
|
1204 self.before = [] |
|
1205 self.hunk = [] |
|
1206 self.headers = [] |
|
1207 |
|
1208 def addrange(self, limits): |
|
1209 fromstart, fromend, tostart, toend, proc = limits |
|
1210 self.fromline = int(fromstart) |
|
1211 self.toline = int(tostart) |
|
1212 self.proc = proc |
|
1213 |
|
1214 def addcontext(self, context): |
|
1215 if self.hunk: |
|
1216 h = recordhunk(self.header, self.fromline, self.toline, |
|
1217 self.proc, self.before, self.hunk, context) |
|
1218 self.header.hunks.append(h) |
|
1219 self.fromline += len(self.before) + h.removed |
|
1220 self.toline += len(self.before) + h.added |
|
1221 self.before = [] |
|
1222 self.hunk = [] |
|
1223 self.proc = '' |
|
1224 self.context = context |
|
1225 |
|
1226 def addhunk(self, hunk): |
|
1227 if self.context: |
|
1228 self.before = self.context |
|
1229 self.context = [] |
|
1230 self.hunk = hunk |
|
1231 |
|
1232 def newfile(self, hdr): |
|
1233 self.addcontext([]) |
|
1234 h = header(hdr) |
|
1235 self.headers.append(h) |
|
1236 self.header = h |
|
1237 |
|
1238 def addother(self, line): |
|
1239 pass # 'other' lines are ignored |
|
1240 |
|
1241 def finished(self): |
|
1242 self.addcontext([]) |
|
1243 return self.headers |
|
1244 |
|
1245 transitions = { |
|
1246 'file': {'context': addcontext, |
|
1247 'file': newfile, |
|
1248 'hunk': addhunk, |
|
1249 'range': addrange}, |
|
1250 'context': {'file': newfile, |
|
1251 'hunk': addhunk, |
|
1252 'range': addrange, |
|
1253 'other': addother}, |
|
1254 'hunk': {'context': addcontext, |
|
1255 'file': newfile, |
|
1256 'range': addrange}, |
|
1257 'range': {'context': addcontext, |
|
1258 'hunk': addhunk}, |
|
1259 'other': {'other': addother}, |
|
1260 } |
|
1261 |
|
1262 p = parser() |
|
1263 |
|
1264 state = 'context' |
|
1265 for newstate, data in scanpatch(fp): |
|
1266 try: |
|
1267 p.transitions[state][newstate](p, data) |
|
1268 except KeyError: |
|
1269 raise PatchError('unhandled transition: %s -> %s' % |
|
1270 (state, newstate)) |
|
1271 state = newstate |
|
1272 return p.finished() |
1193 |
1273 |
1194 def pathtransform(path, strip, prefix): |
1274 def pathtransform(path, strip, prefix): |
1195 '''turn a path from a patch into a path suitable for the repository |
1275 '''turn a path from a patch into a path suitable for the repository |
1196 |
1276 |
1197 prefix, if not empty, is expected to be normalized with a / at the end. |
1277 prefix, if not empty, is expected to be normalized with a / at the end. |