localrepo: read requirements file in makelocalrepository()
authorGregory Szorc <gregory.szorc@gmail.com>
Wed, 12 Sep 2018 14:45:52 -0700
changeset 39692 6a3162ed881d
parent 39691 2f067e365532
child 39693 5b8e9b2060ef
localrepo: read requirements file in makelocalrepository() Previously, scmutil.readrequires() loaded the requirements file and validated its content against what was supported. Requirements translate to repository features and are critical to our plans to dynamically create local repository types. So, we must load them in makelocalrepository() before a repository instance is constructed. This commit moves the reading of the .hg/requires file to makelocalrepository(). Because scmutil.readrequires() was performing I/O and validation, we inlined the validation into localrepository.__init__ and removed scmutil.readrequires(). I plan to remove scmutil.readrequires() in a future commit (we can't do it now because statichttprepo uses it). Differential Revision: https://phab.mercurial-scm.org/D4568
mercurial/localrepo.py
--- a/mercurial/localrepo.py	Wed Sep 12 12:36:07 2018 -0700
+++ b/mercurial/localrepo.py	Wed Sep 12 14:45:52 2018 -0700
@@ -408,6 +408,18 @@
 
         raise error.RepoError(_(b'repository %s not found') % path)
 
+    # .hg/requires file contains a newline-delimited list of
+    # features/capabilities the opener (us) must have in order to use
+    # the repository. This file was introduced in Mercurial 0.9.2,
+    # which means very old repositories may not have one. We assume
+    # a missing file translates to no requirements.
+    try:
+        requirements = set(hgvfs.read(b'requires').splitlines())
+    except IOError as e:
+        if e.errno != errno.ENOENT:
+            raise
+        requirements = set()
+
     # The .hg/hgrc file may load extensions or contain config options
     # that influence repository construction. Attempt to load it and
     # process any new extensions that it may have pulled in.
@@ -424,6 +436,7 @@
         origroot=path,
         wdirvfs=wdirvfs,
         hgvfs=hgvfs,
+        requirements=requirements,
         intents=intents)
 
 @interfaceutil.implementer(repository.completelocalrepository)
@@ -476,7 +489,8 @@
         'bisect.state',
     }
 
-    def __init__(self, baseui, ui, origroot, wdirvfs, hgvfs, intents=None):
+    def __init__(self, baseui, ui, origroot, wdirvfs, hgvfs, requirements,
+                 intents=None):
         """Create a new local repository instance.
 
         Most callers should use ``hg.repository()``, ``localrepo.instance()``,
@@ -500,6 +514,9 @@
         hgvfs
            ``vfs.vfs`` rooted at .hg/
 
+        requirements
+           ``set`` of bytestrings representing repository opening requirements.
+
         intents
            ``set`` of system strings indicating what this repo will be used
            for.
@@ -545,12 +562,23 @@
             if engine.revlogheader():
                 self.supported.add('exp-compression-%s' % name)
 
-        try:
-            self.requirements = scmutil.readrequires(self.vfs, self.supported)
-        except IOError as inst:
-            if inst.errno != errno.ENOENT:
-                raise
-            self.requirements = set()
+        # Validate that all seen repository requirements are supported.
+        missingrequirements = []
+        for r in requirements:
+            if r not in self.supported:
+                if not r or not r[0:1].isalnum():
+                    raise error.RequirementError(
+                        _(".hg/requires file is corrupt"))
+                missingrequirements.append(r)
+        missingrequirements.sort()
+        if missingrequirements:
+            raise error.RequirementError(
+                _("repository requires features unknown to this Mercurial: %s")
+                % " ".join(missingrequirements),
+                hint=_("see https://mercurial-scm.org/wiki/MissingRequirement"
+                       " for more information"))
+
+        self.requirements = requirements
 
         cachepath = self.vfs.join('cache')
         self.sharedpath = self.path