Patchwork D1241: overlayworkingctx: add _auditconflicts to write()

login
register
mail settings
Submitter phabricator
Date Dec. 8, 2017, 8:39 p.m.
Message ID <823f2c0a6369a24f1f7340428b56851d@localhost.localdomain>
Download mbox | patch
Permalink /patch/26124/
State Not Applicable
Headers show

Comments

phabricator - Dec. 8, 2017, 8:39 p.m.
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG72fbdd373de8: overlayworkingctx: add _auditconflicts to write() (authored by phillco, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D1241?vs=4233&id=4270

REVISION DETAIL
  https://phab.mercurial-scm.org/D1241

AFFECTED FILES
  mercurial/context.py

CHANGE DETAILS




To: phillco, #hg-reviewers, durin42
Cc: durin42, mercurial-devel

Patch

diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -2092,9 +2092,50 @@ 
         except error.ManifestLookupError:
             return False
 
+    def _auditconflicts(self, path):
+        """Replicates conflict checks done by wvfs.write().
+
+        Since we never write to the filesystem and never call `applyupdates` in
+        IMM, we'll never check that a path is actually writable -- e.g., because
+        it adds `a/foo`, but `a` is actually a file in the other commit.
+        """
+        def fail(path, component):
+            # p1() is the base and we're receiving "writes" for p2()'s
+            # files.
+            if 'l' in self.p1()[component].flags():
+                raise error.Abort("error: %s conflicts with symlink %s "
+                                  "in %s." % (path, component,
+                                              self.p1().rev()))
+            else:
+                raise error.Abort("error: '%s' conflicts with file '%s' in "
+                                  "%s." % (path, component,
+                                           self.p1().rev()))
+
+        # Test that each new directory to be created to write this path from p2
+        # is not a file in p1.
+        components = path.split('/')
+        for i in xrange(len(components)):
+            component = "/".join(components[0:i])
+            if component in self.p1():
+                fail(path, component)
+
+        # Test the other direction -- that this path from p2 isn't a directory
+        # in p1 (test that p1 doesn't any paths matching `path/*`).
+        match = matchmod.match('/', '', [path + '/'], default=b'relpath')
+        matches = self.p1().manifest().matches(match)
+        if len(matches) > 0:
+            if len(matches) == 1 and matches.keys()[0] == path:
+                return
+            raise error.Abort("error: file '%s' cannot be written because "
+                              " '%s/' is a folder in %s (containing %d "
+                              "entries: %s)"
+                              % (path, path, self.p1(), len(matches),
+                                 ', '.join(matches.keys())))
+
     def write(self, path, data, flags=''):
         if data is None:
             raise error.ProgrammingError("data must be non-None")
+        self._auditconflicts(path)
         self._markdirty(path, exists=True, data=data, date=util.makedate(),
                         flags=flags)