mercurial/localrepo.py
changeset 33436 9bb4decd43b0
parent 33403 1bb209d08a34
child 33437 0720e6265c8a
--- a/mercurial/localrepo.py	Tue Jul 11 12:27:58 2017 +0200
+++ b/mercurial/localrepo.py	Tue Jul 11 12:38:17 2017 +0200
@@ -300,6 +300,26 @@
     # only functions defined in module of enabled extensions are invoked
     featuresetupfuncs = set()
 
+    # list of prefix for file which can be written without 'wlock'
+    # Extensions should extend this list when needed
+    _wlockfreeprefix = {
+        # We migh consider requiring 'wlock' for the next
+        # two, but pretty much all the existing code assume
+        # wlock is not needed so we keep them excluded for
+        # now.
+        'hgrc',
+        'requires',
+        # XXX cache is a complicatged business someone
+        # should investigate this in depth at some point
+        'cache/',
+        # XXX shouldn't be dirstate covered by the wlock?
+        'dirstate',
+        # XXX bisect was still a bit too messy at the time
+        # this changeset was introduced. Someone should fix
+        # the remainig bit and drop this line
+        'bisect.state',
+    }
+
     def __init__(self, baseui, path, create=False):
         self.requirements = set()
         self.filtername = None
@@ -319,10 +339,13 @@
         self.auditor = pathutil.pathauditor(self.root, self._checknested)
         self.nofsauditor = pathutil.pathauditor(self.root, self._checknested,
                                                 realfs=False)
-        self.vfs = vfsmod.vfs(self.path)
         self.baseui = baseui
         self.ui = baseui.copy()
         self.ui.copy = baseui.copy # prevent copying repo configuration
+        self.vfs = vfsmod.vfs(self.path)
+        if (self.ui.configbool('devel', 'all-warnings') or
+            self.ui.configbool('devel', 'check-locks')):
+            self.vfs.audit = self._getvfsward(self.vfs.audit)
         # A list of callback to shape the phase if no data were found.
         # Callback are in the form: func(repo, roots) --> processed root.
         # This list it to be filled by extension during repo setup
@@ -441,6 +464,38 @@
         # Signature to cached matcher instance.
         self._sparsematchercache = {}
 
+    def _getvfsward(self, origfunc):
+        """build a ward for self.vfs"""
+        rref = weakref.ref(self)
+        def checkvfs(path, mode=None):
+            ret = origfunc(path, mode=mode)
+            repo = rref()
+            if (repo is None
+                or not util.safehasattr(repo, '_wlockref')
+                or not util.safehasattr(repo, '_lockref')):
+                return
+            if mode in (None, 'r', 'rb'):
+                return
+            if path.startswith(repo.path):
+                # truncate name relative to the repository (.hg)
+                path = path[len(repo.path) + 1:]
+            if path.startswith('journal.'):
+                # journal is covered by 'lock'
+                if repo._currentlock(repo._lockref) is None:
+                    repo.ui.develwarn('write with no lock: "%s"' % path,
+                                      stacklevel=2)
+            elif repo._currentlock(repo._wlockref) is None:
+                # rest of vfs files are covered by 'wlock'
+                #
+                # exclude special files
+                for prefix in self._wlockfreeprefix:
+                    if path.startswith(prefix):
+                        return
+                repo.ui.develwarn('write with no wlock: "%s"' % path,
+                                  stacklevel=2)
+            return ret
+        return checkvfs
+
     def close(self):
         self._writecaches()