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
--- 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