localrepo: define "features" on repository instances (API)
There are a handful of attributes/methods on repository instances that
describe the behavior of the repository. Furthermore, there is
an unbound set of repository descriptors that we may wish to expose.
For example, an extension may wish to add a descriptor and have
monkeypatched functions look for the presence of an attribute before
taking actions.
This commit introduces a "features" mechanism to allow repositories
to self-advertise an arbitrary set of strings that describe repository
behavior or capabilities.
We implement basic support for advertising a few features to give an
idea of what I want to use this for.
Differential Revision: https://phab.mercurial-scm.org/D4709
--- a/mercurial/bundle2.py Wed Sep 19 17:27:37 2018 -0700
+++ b/mercurial/bundle2.py Wed Sep 19 14:36:57 2018 -0700
@@ -1798,7 +1798,7 @@
"non-empty and does not use tree manifests"))
op.repo.requirements.add('treemanifest')
op.repo.svfs.options = localrepo.resolvestorevfsoptions(
- op.repo.ui, op.repo.requirements)
+ op.repo.ui, op.repo.requirements, op.repo.features)
op.repo._writerequirements()
extrakwargs = {}
targetphase = inpart.params.get('targetphase')
--- a/mercurial/localrepo.py Wed Sep 19 17:27:37 2018 -0700
+++ b/mercurial/localrepo.py Wed Sep 19 14:36:57 2018 -0700
@@ -478,6 +478,8 @@
# At this point, we know we should be capable of opening the repository.
# Now get on with doing that.
+ features = set()
+
# The "store" part of the repository holds versioned data. How it is
# accessed is determined by various requirements. The ``shared`` or
# ``relshared`` requirements indicate the store lives in the path contained
@@ -494,6 +496,8 @@
raise error.RepoError(_(b'.hg/sharedpath points to nonexistent '
b'directory %s') % sharedvfs.base)
+ features.add(repository.REPO_FEATURE_SHARED_STORAGE)
+
storebasepath = sharedvfs.base
cachepath = sharedvfs.join(b'cache')
else:
@@ -508,7 +512,7 @@
hgvfs.createmode = store.createmode
storevfs = store.vfs
- storevfs.options = resolvestorevfsoptions(ui, requirements)
+ storevfs.options = resolvestorevfsoptions(ui, requirements, features)
# The cache vfs is used to manage cache files.
cachevfs = vfsmod.vfs(cachepath, cacheaudited=True)
@@ -528,6 +532,7 @@
typ = fn(ui=ui,
intents=intents,
requirements=requirements,
+ features=features,
wdirvfs=wdirvfs,
hgvfs=hgvfs,
store=store,
@@ -564,6 +569,7 @@
sharedpath=storebasepath,
store=store,
cachevfs=cachevfs,
+ features=features,
intents=intents)
def gathersupportedrequirements(ui):
@@ -643,7 +649,7 @@
return storemod.basicstore(path, vfstype)
-def resolvestorevfsoptions(ui, requirements):
+def resolvestorevfsoptions(ui, requirements, features):
"""Resolve the options to pass to the store vfs opener.
The returned dict is used to influence behavior of the storage layer.
@@ -664,11 +670,11 @@
# opener options for it because those options wouldn't do anything
# meaningful on such old repos.
if b'revlogv1' in requirements or REVLOGV2_REQUIREMENT in requirements:
- options.update(resolverevlogstorevfsoptions(ui, requirements))
+ options.update(resolverevlogstorevfsoptions(ui, requirements, features))
return options
-def resolverevlogstorevfsoptions(ui, requirements):
+def resolverevlogstorevfsoptions(ui, requirements, features):
"""Resolve opener options specific to revlogs."""
options = {}
@@ -756,8 +762,10 @@
return filelog.narrowfilelog(self.svfs, path, self.narrowmatch())
-def makefilestorage(requirements, **kwargs):
+def makefilestorage(requirements, features, **kwargs):
"""Produce a type conforming to ``ilocalrepositoryfilestorage``."""
+ features.add(repository.REPO_FEATURE_REVLOG_FILE_STORAGE)
+
if repository.NARROW_REQUIREMENT in requirements:
return revlognarrowfilestorage
else:
@@ -831,7 +839,7 @@
def __init__(self, baseui, ui, origroot, wdirvfs, hgvfs, requirements,
supportedrequirements, sharedpath, store, cachevfs,
- intents=None):
+ features, intents=None):
"""Create a new local repository instance.
Most callers should use ``hg.repository()``, ``localrepo.instance()``,
@@ -873,6 +881,10 @@
cachevfs
``vfs.vfs`` used for cache files.
+ features
+ ``set`` of bytestrings defining features/capabilities of this
+ instance.
+
intents
``set`` of system strings indicating what this repo will be used
for.
@@ -891,6 +903,7 @@
self.sharedpath = sharedpath
self.store = store
self.cachevfs = cachevfs
+ self.features = features
self.filtername = None
--- a/mercurial/repository.py Wed Sep 19 17:27:37 2018 -0700
+++ b/mercurial/repository.py Wed Sep 19 14:36:57 2018 -0700
@@ -19,6 +19,13 @@
# we should move this to just "narrow" or similar.
NARROW_REQUIREMENT = 'narrowhg-experimental'
+# Local repository feature string.
+
+# Revlogs are being used for file storage.
+REPO_FEATURE_REVLOG_FILE_STORAGE = b'revlogfilestorage'
+# The storage part of the repository is shared from an external source.
+REPO_FEATURE_SHARED_STORAGE = b'sharedstore'
+
class ipeerconnection(interfaceutil.Interface):
"""Represents a "connection" to a repository.
@@ -1275,6 +1282,24 @@
requirements = interfaceutil.Attribute(
"""Set of requirements this repo uses.""")
+ features = interfaceutil.Attribute(
+ """Set of "features" this repository supports.
+
+ A "feature" is a loosely-defined term. It can refer to a feature
+ in the classical sense or can describe an implementation detail
+ of the repository. For example, a ``readonly`` feature may denote
+ the repository as read-only. Or a ``revlogfilestore`` feature may
+ denote that the repository is using revlogs for file storage.
+
+ The intent of features is to provide a machine-queryable mechanism
+ for repo consumers to test for various repository characteristics.
+
+ Features are similar to ``requirements``. The main difference is that
+ requirements are stored on-disk and represent requirements to open the
+ repository. Features are more run-time capabilities of the repository
+ and more granular capabilities (which may be derived from requirements).
+ """)
+
filtername = interfaceutil.Attribute(
"""Name of the repoview that is active on this repo.""")
--- a/mercurial/streamclone.py Wed Sep 19 17:27:37 2018 -0700
+++ b/mercurial/streamclone.py Wed Sep 19 14:36:57 2018 -0700
@@ -168,7 +168,7 @@
repo.requirements = requirements | (
repo.requirements - repo.supportedformats)
repo.svfs.options = localrepo.resolvestorevfsoptions(
- repo.ui, repo.requirements)
+ repo.ui, repo.requirements, repo.features)
repo._writerequirements()
if rbranchmap:
@@ -643,5 +643,5 @@
repo.requirements = set(requirements) | (
repo.requirements - repo.supportedformats)
repo.svfs.options = localrepo.resolvestorevfsoptions(
- repo.ui, repo.requirements)
+ repo.ui, repo.requirements, repo.features)
repo._writerequirements()