Mercurial > public > mercurial-scm > hg-stable
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 |