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 |