patch: add a workingbackend dirstate layer on top of fsbackend
authorPatrick Mezard <pmezard@gmail.com>
Wed, 18 May 2011 23:48:17 +0200
changeset 14370 17cea10c343e
parent 14369 f8932d540088
child 14371 ec2aae8b375d
child 14377 f90d5641c78b
patch: add a workingbackend dirstate layer on top of fsbackend _updatedir() is no longer used by internalpatch() The change in test-mq-missingfiles.t comes from workingbackend not considering the missing 'b' file as changed, thus not calling addremove() on it.
hgext/record.py
mercurial/patch.py
tests/test-mq-missingfiles.t
--- a/hgext/record.py	Wed May 18 23:48:13 2011 +0200
+++ b/hgext/record.py	Wed May 18 23:48:17 2011 +0200
@@ -477,8 +477,7 @@
                 try:
                     ui.debug('applying patch\n')
                     ui.debug(fp.getvalue())
-                    patch.internalpatch(ui, repo, fp, 1, repo.root,
-                                        eolmode=None)
+                    patch.internalpatch(ui, repo, fp, 1, eolmode=None)
                 except patch.PatchError, err:
                     raise util.Abort(str(err))
             del fp
--- a/mercurial/patch.py	Wed May 18 23:48:13 2011 +0200
+++ b/mercurial/patch.py	Wed May 18 23:48:17 2011 +0200
@@ -478,6 +478,49 @@
     def setmode(self, fname, islink, isexec):
         util.setflags(self._join(fname), islink, isexec)
 
+class workingbackend(fsbackend):
+    def __init__(self, ui, repo, similarity):
+        super(workingbackend, self).__init__(ui, repo.root)
+        self.repo = repo
+        self.similarity = similarity
+        self.removed = set()
+        self.changed = set()
+        self.copied = []
+
+    def writelines(self, fname, lines, mode):
+        super(workingbackend, self).writelines(fname, lines, mode)
+        self.changed.add(fname)
+
+    def unlink(self, fname):
+        super(workingbackend, self).unlink(fname)
+        self.removed.add(fname)
+        self.changed.add(fname)
+
+    def copy(self, src, dst):
+        super(workingbackend, self).copy(src, dst)
+        self.copied.append((src, dst))
+        self.changed.add(dst)
+
+    def setmode(self, fname, islink, isexec):
+        super(workingbackend, self).setmode(fname, islink, isexec)
+        self.changed.add(fname)
+
+    def close(self):
+        wctx = self.repo[None]
+        addremoved = set(self.changed)
+        for src, dst in self.copied:
+            scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
+            addremoved.discard(src)
+        if (not self.similarity) and self.removed:
+            wctx.remove(sorted(self.removed))
+        if addremoved:
+            cwd = self.repo.getcwd()
+            if cwd:
+                addremoved = [util.pathto(self.repo.root, cwd, f)
+                              for f in addremoved]
+            scmutil.addremove(self.repo, addremoved, similarity=self.similarity)
+        return sorted(self.changed)
+
 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
@@ -1169,9 +1212,6 @@
     If 'eolmode' is 'strict', the patch content and patched file are
     read in binary mode. Otherwise, line endings are ignored when
     patching then normalized according to 'eolmode'.
-
-    Callers probably want to call '_updatedir' after this to
-    apply certain categories of changes not done by this function.
     """
     return _applydiff(ui, fp, patchfile, backend, changed, strip=strip,
                       eolmode=eolmode)
@@ -1311,7 +1351,7 @@
                          util.explainexit(code)[0])
     return fuzz
 
-def internalpatch(ui, repo, patchobj, strip, cwd, files=None, eolmode='strict',
+def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict',
                   similarity=0):
     """use builtin patch to apply <patchobj> to the working directory.
     returns whether patch was applied with fuzz factor."""
@@ -1324,7 +1364,7 @@
         raise util.Abort(_('unsupported line endings type: %s') % eolmode)
     eolmode = eolmode.lower()
 
-    backend = fsbackend(ui, cwd)
+    backend = workingbackend(ui, repo, similarity)
     try:
         fp = open(patchobj, 'rb')
     except TypeError:
@@ -1334,8 +1374,7 @@
     finally:
         if fp != patchobj:
             fp.close()
-        touched = _updatedir(ui, repo, files, similarity)
-        files.update(dict.fromkeys(touched))
+        files.update(dict.fromkeys(backend.close()))
     if ret < 0:
         raise PatchError(_('patch failed to apply'))
     return ret > 0
@@ -1364,7 +1403,7 @@
             finally:
                 touched = _updatedir(ui, repo, files, similarity)
                 files.update(dict.fromkeys(touched))
-        return internalpatch(ui, repo, patchname, strip, cwd, files, eolmode,
+        return internalpatch(ui, repo, patchname, strip, files, eolmode,
                              similarity)
     except PatchError, err:
         raise util.Abort(str(err))
--- a/tests/test-mq-missingfiles.t	Wed May 18 23:48:13 2011 +0200
+++ b/tests/test-mq-missingfiles.t	Wed May 18 23:48:17 2011 +0200
@@ -101,7 +101,6 @@
   applying changeb
   unable to find 'b' for patching
   1 out of 1 hunks FAILED -- saving rejects to file b.rej
-  b: No such file or directory
   patch failed, unable to continue (try -v)
   patch failed, rejects left in working dir
   errors during apply, please fix and refresh changeb