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

Mads Kiilerich mads at kiilerich.com
Wed Oct 12 20:39:31 EDT 2016


# HG changeset patch
# User Mads Kiilerich <madski at unity3d.com>
# Date 1476319116 -7200
#      Thu Oct 13 02:38:36 2016 +0200
# Node ID bc711933bb8cfacfe87d83fe945cf566d79439d8
# Parent  6affe6ed5cc5b945961fb8f1fe2dc18e711089c2
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 "API" for repository types could use some cleanup, but this seems to be one
way 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