[PATCH 3 of 3 v2] repos: introduce '-R readonly:PATH' for doing local operations "read only"

Mads Kiilerich mads at kiilerich.com
Fri Oct 14 01:17:30 UTC 2016


# HG changeset patch
# User Mads Kiilerich <madski at unity3d.com>
# Date 1476402795 -7200
#      Fri Oct 14 01:53:15 2016 +0200
# Node ID 491da143c2f310da732c2e1005e7f8394134a51d
# Parent  24a7ccfb932b134da24e58817991943c8bbd63fa
repos: introduce '-R readonly:PATH' for doing local operations "read only"

When used as 'readonly:.', this is pretty much like if the repository was owned
by another user and the current user didn't have write access to anything in
.hg .

Using this feature will for example allow multiple simultaneous pushes,
pushes without phase changes, and will provide a "safe" way to run commands ...
assuming this and our use of VFS is complete and correct.

The existing "API" for repository types could use some cleanup - it requires
modules with special undefined duck typing. This patch seems to do what is
needed.

The existing VFS class hierarchy has a "readonlyvfs" class, but whatever it is,
it doesn't seem suitable for this use; it doesn't seem to be a reusable class
or mixin.

diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -31,6 +31,7 @@ from . import (
     merge as mergemod,
     node,
     phases,
+    readonlyrepo,
     repoview,
     scmutil,
     sshpeer,
@@ -112,6 +113,7 @@ schemes = {
     'https': httppeer,
     'ssh': sshpeer,
     'static-http': statichttprepo,
+    'readonly': lambda path: readonlyrepo,
 }
 
 def _peerlookup(path):
diff --git a/mercurial/readonlyrepo.py b/mercurial/readonlyrepo.py
new file mode 100644
--- /dev/null
+++ b/mercurial/readonlyrepo.py
@@ -0,0 +1,57 @@
+# readonlyrepo.py - a local repository class for mercurial that can't write
+# or lock the repository
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from __future__ import absolute_import
+
+import errno
+
+from .i18n import _
+from . import (
+    localrepo,
+    scmutil,
+    util,
+)
+
+# created, not thrown yet
+readonlyexception = IOError(errno.EACCES, _('this is a "readonly" repository'))
+
+# this seems to be a necessary part of the repository type API
+islocal = localrepo.islocal
+
+class readonlyvfs(scmutil.vfs):
+    """A VFS that only can be called with read modes - writing will fail with
+    an IO error as if the user didn't have write access"""
+
+    def __call__(self, path, mode='r', *args, **kw):
+        if mode not in ('r', 'rb'):
+            raise readonlyexception
+        return super(readonlyvfs, self).__call__(path, mode, *args, **kw)
+
+class readonlyrepo(localrepo.localrepository):
+    """A repository that is local but read only, as if the user didn't have
+    file system write access."""
+
+    def __init__(self, baseui, path=None, create=False):
+        # we know the "scheme" for path is "readonly" but do not want to extend
+        # the file/bundle hack in the "url" parser - just strip it here
+        assert path.startswith('readonly:'), path
+        path = path[len('readonly:'):]
+
+        super(readonlyrepo, self).__init__(baseui, path=path, create=False)
+
+        assert self.vfs.__class__ is scmutil.vfs
+        self.vfs.__class__ = readonlyvfs
+        assert self.wvfs.__class__ is scmutil.vfs
+        self.wvfs.__class__ = readonlyvfs
+
+    def lock(self, wait=True):
+        raise readonlyexception
+
+    def wlock(self, wait=True):
+        raise readonlyexception
+
+def instance(ui, path, create):
+    return readonlyrepo(ui, util.urllocalpath(path), create=False)
diff --git a/tests/test-phases-exchange.t b/tests/test-phases-exchange.t
--- a/tests/test-phases-exchange.t
+++ b/tests/test-phases-exchange.t
@@ -1197,25 +1197,27 @@ publish changesets as plain push does
   |
   ~
 
-  $ hg -R Upsilon push Pi -r 7
+  $ hg -R readonly:Upsilon push Pi -r 7
   pushing to Pi
   searching for changes
   no changes found
+  cannot lock source repo, skipping local public phase update
   [1]
   $ hgph Upsilon -r 'min(draft())'
-  o  8 draft a-F - b740e3e5c05d
+  o  2 draft a-C - 54acac6f23ab
   |
   ~
 
-  $ hg -R Upsilon push Pi -r 8
+  $ hg -R readonly:Upsilon push Pi -r 8
   pushing to Pi
   searching for changes
   adding changesets
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
+  cannot lock source repo, skipping local public phase update
 
   $ hgph Upsilon -r 'min(draft())'
-  o  9 draft a-G - 3e27b6f1eee1
+  o  2 draft a-C - 54acac6f23ab
   |
   ~



More information about the Mercurial-devel mailing list