comparison mercurial/patch.py @ 14389:909ac6b9636b

patch: stop modifying gitpatch objects gitpatch objects emitted by iterhunks() were referencing file paths unmodified from the input patch. _applydif() made them usable by modifying the gitpatch objects in-place with specified path strip level. The same modified objects were then reused by iterhunks() generator. _applydiff() now copies and update the paths which completely decouples both routines. As a side effect, the "git" event now receives only metadata about copies/renames to perform the necessary copies ahead of time. Other actions are handled in the "file" event.
author Patrick Mezard <pmezard@gmail.com>
date Thu, 19 May 2011 22:44:01 +0200
parents 37c997d21752
children ce77c275bec3
comparison
equal deleted inserted replaced
14388:37c997d21752 14389:909ac6b9636b
1155 continue 1155 continue
1156 if gitpatches is None: 1156 if gitpatches is None:
1157 # scan whole input for git metadata 1157 # scan whole input for git metadata
1158 gitpatches = [('b/' + gp.path, gp) for gp 1158 gitpatches = [('b/' + gp.path, gp) for gp
1159 in scangitpatch(lr, x)] 1159 in scangitpatch(lr, x)]
1160 yield 'git', [g[1] for g in gitpatches] 1160 yield 'git', [g[1] for g in gitpatches
1161 if g[1].op in ('COPY', 'RENAME')]
1161 gitpatches.reverse() 1162 gitpatches.reverse()
1162 afile = 'a/' + m.group(1) 1163 afile = 'a/' + m.group(1)
1163 bfile = 'b/' + m.group(2) 1164 bfile = 'b/' + m.group(2)
1164 while bfile != gitpatches[-1][0]: 1165 while bfile != gitpatches[-1][0]:
1165 gp = gitpatches.pop()[1] 1166 gp = gitpatches.pop()[1]
1218 """ 1219 """
1219 return _applydiff(ui, fp, patchfile, backend, changed, strip=strip, 1220 return _applydiff(ui, fp, patchfile, backend, changed, strip=strip,
1220 eolmode=eolmode) 1221 eolmode=eolmode)
1221 1222
1222 def _applydiff(ui, fp, patcher, backend, changed, strip=1, eolmode='strict'): 1223 def _applydiff(ui, fp, patcher, backend, changed, strip=1, eolmode='strict'):
1224
1225 def pstrip(p):
1226 return pathstrip(p, strip - 1)[1]
1227
1223 rejects = 0 1228 rejects = 0
1224 err = 0 1229 err = 0
1225 current_file = None 1230 current_file = None
1226 1231
1227 for state, values in iterhunks(fp): 1232 for state, values in iterhunks(fp):
1237 if current_file: 1242 if current_file:
1238 rejects += current_file.close() 1243 rejects += current_file.close()
1239 current_file = None 1244 current_file = None
1240 afile, bfile, first_hunk, gp = values 1245 afile, bfile, first_hunk, gp = values
1241 if gp: 1246 if gp:
1242 changed[gp.path] = gp 1247 path = pstrip(gp.path)
1248 changed[path] = gp
1243 if gp.op == 'DELETE': 1249 if gp.op == 'DELETE':
1244 backend.unlink(gp.path) 1250 backend.unlink(path)
1245 continue 1251 continue
1246 if gp.op == 'RENAME': 1252 if gp.op == 'RENAME':
1247 backend.unlink(gp.oldpath) 1253 backend.unlink(pstrip(gp.oldpath))
1248 if gp.mode and not first_hunk: 1254 if gp.mode and not first_hunk:
1249 if gp.op == 'ADD': 1255 if gp.op == 'ADD':
1250 # Added files without content have no hunk and must be created 1256 # Added files without content have no hunk and must be created
1251 backend.writelines(gp.path, [], gp.mode) 1257 backend.writelines(path, [], gp.mode)
1252 else: 1258 else:
1253 backend.setmode(gp.path, gp.mode[0], gp.mode[1]) 1259 backend.setmode(path, gp.mode[0], gp.mode[1])
1254 if not first_hunk: 1260 if not first_hunk:
1255 continue 1261 continue
1256 try: 1262 try:
1257 mode = gp and gp.mode or None 1263 mode = gp and gp.mode or None
1258 current_file, missing = selectfile(backend, afile, bfile, 1264 current_file, missing = selectfile(backend, afile, bfile,
1264 current_file = None 1270 current_file = None
1265 rejects += 1 1271 rejects += 1
1266 continue 1272 continue
1267 elif state == 'git': 1273 elif state == 'git':
1268 for gp in values: 1274 for gp in values:
1269 gp.path = pathstrip(gp.path, strip - 1)[1] 1275 backend.copy(pstrip(gp.oldpath), pstrip(gp.path))
1270 if gp.oldpath:
1271 gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
1272 if gp.op in ('COPY', 'RENAME'):
1273 backend.copy(gp.oldpath, gp.path)
1274 else: 1276 else:
1275 raise util.Abort(_('unsupported parser state: %s') % state) 1277 raise util.Abort(_('unsupported parser state: %s') % state)
1276 1278
1277 if current_file: 1279 if current_file:
1278 rejects += current_file.close() 1280 rejects += current_file.close()
1385 backend = fsbackend(ui, repo.root) 1387 backend = fsbackend(ui, repo.root)
1386 fp = open(patchpath, 'rb') 1388 fp = open(patchpath, 'rb')
1387 try: 1389 try:
1388 changed = set() 1390 changed = set()
1389 for state, values in iterhunks(fp): 1391 for state, values in iterhunks(fp):
1390 if state == 'hunk': 1392 if state == 'file':
1391 continue
1392 elif state == 'file':
1393 afile, bfile, first_hunk, gp = values 1393 afile, bfile, first_hunk, gp = values
1394 if gp: 1394 if gp:
1395 changed.add(gp.path) 1395 changed.add(pathstrip(gp.path, strip - 1)[1])
1396 if gp.op == 'RENAME': 1396 if gp.op == 'RENAME':
1397 changed.add(gp.oldpath) 1397 changed.add(pathstrip(gp.oldpath, strip - 1)[1])
1398 if not first_hunk: 1398 if not first_hunk:
1399 continue 1399 continue
1400 current_file, missing = selectfile(backend, afile, bfile, 1400 current_file, missing = selectfile(backend, afile, bfile,
1401 first_hunk, strip) 1401 first_hunk, strip)
1402 changed.add(current_file) 1402 changed.add(current_file)
1403 elif state == 'git': 1403 elif state not in ('hunk', 'git'):
1404 for gp in values:
1405 gp.path = pathstrip(gp.path, strip - 1)[1]
1406 if gp.oldpath:
1407 gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
1408 else:
1409 raise util.Abort(_('unsupported parser state: %s') % state) 1404 raise util.Abort(_('unsupported parser state: %s') % state)
1410 return changed 1405 return changed
1411 finally: 1406 finally:
1412 fp.close() 1407 fp.close()
1413 1408