comparison mercurial/patch.py @ 14611:adbf5e7df96d

import: add --bypass option This feature is more a way to test patching without a working directory than something people asked about. Adding a --rev option to specify the parent patch revision would make it a little more useful. What this change introduces is patch.repobackend class which let patches be applied against repository revisions. The caller must supply a filestore object to receive patched content, which can be turned into a memctx with patch.makememctx() helper.
author Patrick Mezard <pmezard@gmail.com>
date Tue, 14 Jun 2011 23:26:35 +0200
parents f53dc0787424
children 7ddf9a607b75
comparison
equal deleted inserted replaced
14610:5d6244930559 14611:adbf5e7df96d
9 import cStringIO, email.Parser, os, errno, re 9 import cStringIO, email.Parser, os, errno, re
10 import tempfile, zlib, shutil 10 import tempfile, zlib, shutil
11 11
12 from i18n import _ 12 from i18n import _
13 from node import hex, nullid, short 13 from node import hex, nullid, short
14 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding 14 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, error
15 import context
15 16
16 gitre = re.compile('diff --git a/(.*) b/(.*)') 17 gitre = re.compile('diff --git a/(.*) b/(.*)')
17 18
18 class PatchError(Exception): 19 class PatchError(Exception):
19 pass 20 pass
508 return self.opener.read(fn), mode, copied 509 return self.opener.read(fn), mode, copied
509 510
510 def close(self): 511 def close(self):
511 if self.opener: 512 if self.opener:
512 shutil.rmtree(self.opener.base) 513 shutil.rmtree(self.opener.base)
514
515 class repobackend(abstractbackend):
516 def __init__(self, ui, repo, ctx, store):
517 super(repobackend, self).__init__(ui)
518 self.repo = repo
519 self.ctx = ctx
520 self.store = store
521 self.changed = set()
522 self.removed = set()
523 self.copied = {}
524
525 def _checkknown(self, fname):
526 if fname not in self.ctx:
527 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
528
529 def getfile(self, fname):
530 try:
531 fctx = self.ctx[fname]
532 except error.LookupError:
533 raise IOError()
534 flags = fctx.flags()
535 return fctx.data(), ('l' in flags, 'x' in flags)
536
537 def setfile(self, fname, data, mode, copysource):
538 if copysource:
539 self._checkknown(copysource)
540 if data is None:
541 data = self.ctx[fname].data()
542 self.store.setfile(fname, data, mode, copysource)
543 self.changed.add(fname)
544 if copysource:
545 self.copied[fname] = copysource
546
547 def unlink(self, fname):
548 self._checkknown(fname)
549 self.removed.add(fname)
550
551 def exists(self, fname):
552 return fname in self.ctx
553
554 def close(self):
555 return self.changed | self.removed
513 556
514 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1 557 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
515 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@') 558 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
516 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)') 559 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
517 eolmodes = ['strict', 'crlf', 'lf', 'auto'] 560 eolmodes = ['strict', 'crlf', 'lf', 'auto']
1330 if code: 1373 if code:
1331 raise PatchError(_("patch command failed: %s") % 1374 raise PatchError(_("patch command failed: %s") %
1332 util.explainexit(code)[0]) 1375 util.explainexit(code)[0])
1333 return fuzz 1376 return fuzz
1334 1377
1335 def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict', 1378 def patchbackend(ui, backend, patchobj, strip, files=None, eolmode='strict'):
1336 similarity=0):
1337 """use builtin patch to apply <patchobj> to the working directory.
1338 returns whether patch was applied with fuzz factor."""
1339
1340 if files is None: 1379 if files is None:
1341 files = set() 1380 files = set()
1342 if eolmode is None: 1381 if eolmode is None:
1343 eolmode = ui.config('patch', 'eol', 'strict') 1382 eolmode = ui.config('patch', 'eol', 'strict')
1344 if eolmode.lower() not in eolmodes: 1383 if eolmode.lower() not in eolmodes:
1345 raise util.Abort(_('unsupported line endings type: %s') % eolmode) 1384 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
1346 eolmode = eolmode.lower() 1385 eolmode = eolmode.lower()
1347 1386
1348 store = filestore() 1387 store = filestore()
1349 backend = workingbackend(ui, repo, similarity)
1350 try: 1388 try:
1351 fp = open(patchobj, 'rb') 1389 fp = open(patchobj, 'rb')
1352 except TypeError: 1390 except TypeError:
1353 fp = patchobj 1391 fp = patchobj
1354 try: 1392 try:
1360 files.update(backend.close()) 1398 files.update(backend.close())
1361 store.close() 1399 store.close()
1362 if ret < 0: 1400 if ret < 0:
1363 raise PatchError(_('patch failed to apply')) 1401 raise PatchError(_('patch failed to apply'))
1364 return ret > 0 1402 return ret > 0
1403
1404 def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict',
1405 similarity=0):
1406 """use builtin patch to apply <patchobj> to the working directory.
1407 returns whether patch was applied with fuzz factor."""
1408 backend = workingbackend(ui, repo, similarity)
1409 return patchbackend(ui, backend, patchobj, strip, files, eolmode)
1410
1411 def patchrepo(ui, repo, ctx, store, patchobj, strip, files=None,
1412 eolmode='strict'):
1413 backend = repobackend(ui, repo, ctx, store)
1414 return patchbackend(ui, backend, patchobj, strip, files, eolmode)
1415
1416 def makememctx(repo, parents, text, user, date, branch, files, store,
1417 editor=None):
1418 def getfilectx(repo, memctx, path):
1419 data, (islink, isexec), copied = store.getfile(path)
1420 return context.memfilectx(path, data, islink=islink, isexec=isexec,
1421 copied=copied)
1422 extra = {}
1423 if branch:
1424 extra['branch'] = encoding.fromlocal(branch)
1425 ctx = context.memctx(repo, parents, text, files, getfilectx, user,
1426 date, extra)
1427 if editor:
1428 ctx._text = editor(repo, ctx, [])
1429 return ctx
1365 1430
1366 def patch(ui, repo, patchname, strip=1, files=None, eolmode='strict', 1431 def patch(ui, repo, patchname, strip=1, files=None, eolmode='strict',
1367 similarity=0): 1432 similarity=0):
1368 """Apply <patchname> to the working directory. 1433 """Apply <patchname> to the working directory.
1369 1434