share: introduce config option to store requires in .hg/store
This introduces a config option which enabled stores the requirements on a
repository in store instead.
When enabled, `.hg/requires` will contain the `share-safe` requirement which
marks that the requirements are present in the store.
This is done so that repository requirements can be shared with shares made
using `hg share` command.
After this patch, `hg share` checks whether the source repository has
share-safe requirement, if yes, it does not copy the requirements.
Test for the new functionality is added and a test case in exitsing share tests
is also added.
Differential Revision: https://phab.mercurial-scm.org/D8633
--- a/mercurial/configitems.py Fri Aug 07 16:11:19 2020 +0530
+++ b/mercurial/configitems.py Tue Apr 14 21:07:09 2020 +0530
@@ -784,6 +784,9 @@
b'format', b'exp-use-side-data', default=False, experimental=True,
)
coreconfigitem(
+ b'format', b'exp-share-safe', default=False, experimental=True,
+)
+coreconfigitem(
b'format', b'internal-phase', default=False, experimental=True,
)
coreconfigitem(
--- a/mercurial/localrepo.py Fri Aug 07 16:11:19 2020 +0530
+++ b/mercurial/localrepo.py Tue Apr 14 21:07:09 2020 +0530
@@ -545,6 +545,26 @@
raise error.RepoError(_(b'repository %s not found') % path)
requirements = _readrequires(hgvfs, True)
+ shared = (
+ requirementsmod.SHARED_REQUIREMENT in requirements
+ or requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements
+ )
+ if shared:
+ sharedvfs = _getsharedvfs(hgvfs, requirements)
+
+ # if .hg/requires contains the sharesafe requirement, it means
+ # there exists a `.hg/store/requires` too and we should read it
+ # NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement
+ # is present. We never write SHARESAFE_REQUIREMENT for a repo if store
+ # is not present, refer checkrequirementscompat() for that
+ if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
+ if shared:
+ # This is a shared repo
+ storevfs = vfsmod.vfs(sharedvfs.join(b'store'))
+ else:
+ storevfs = vfsmod.vfs(hgvfs.join(b'store'))
+
+ requirements |= _readrequires(storevfs, False)
# The .hg/hgrc file may load extensions or contain config options
# that influence repository construction. Attempt to load it and
@@ -587,12 +607,7 @@
# accessed is determined by various requirements. If `shared` or
# `relshared` requirements are present, this indicates current repository
# is a share and store exists in path mentioned in `.hg/sharedpath`
- shared = (
- requirementsmod.SHARED_REQUIREMENT in requirements
- or requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements
- )
if shared:
- sharedvfs = _getsharedvfs(hgvfs, requirements)
storebasepath = sharedvfs.base
cachepath = sharedvfs.join(b'cache')
features.add(repository.REPO_FEATURE_SHARED_STORAGE)
@@ -1048,6 +1063,7 @@
requirementsmod.SPARSEREVLOG_REQUIREMENT,
requirementsmod.NODEMAP_REQUIREMENT,
bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT,
+ requirementsmod.SHARESAFE_REQUIREMENT,
}
_basesupported = supportedformats | {
b'store',
@@ -3329,6 +3345,11 @@
if ui.configbool(b'format', b'use-persistent-nodemap'):
requirements.add(requirementsmod.NODEMAP_REQUIREMENT)
+ # if share-safe is enabled, let's create the new repository with the new
+ # requirement
+ if ui.configbool(b'format', b'exp-share-safe'):
+ requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
+
return requirements
@@ -3362,6 +3383,16 @@
)
)
+ if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
+ ui.warn(
+ _(
+ b"ignoring enabled 'format.exp-share-safe' config because "
+ b"it is incompatible with disabled 'format.usestore'"
+ b" config\n"
+ )
+ )
+ dropped.add(requirementsmod.SHARESAFE_REQUIREMENT)
+
return dropped
@@ -3486,7 +3517,18 @@
b'layout',
)
- scmutil.writerequires(hgvfs, requirements)
+ # Filter the requirements into working copy and store ones
+ wcreq, storereq = scmutil.filterrequirements(requirements)
+ # write working copy ones
+ scmutil.writerequires(hgvfs, wcreq)
+ # If there are store requirements and the current repository
+ # is not a shared one, write stored requirements
+ # For new shared repository, we don't need to write the store
+ # requirements as they are already present in store requires
+ if storereq and b'sharedrepo' not in createopts:
+ scmutil.writerequires(hgvfs, wcreq)
+ storevfs = vfsmod.vfs(hgvfs.join(b'store'), cacheaudited=True)
+ scmutil.writerequires(storevfs, storereq)
# Write out file telling readers where to find the shared store.
if b'sharedrepo' in createopts:
--- a/mercurial/requirements.py Fri Aug 07 16:11:19 2020 +0530
+++ b/mercurial/requirements.py Tue Apr 14 21:07:09 2020 +0530
@@ -52,6 +52,11 @@
# relative to the current repository root path
RELATIVE_SHARED_REQUIREMENT = b'relshared'
+# A repository with share implemented safely. The repository has different
+# store and working copy requirements i.e. both `.hg/requires` and
+# `.hg/store/requires` are present.
+SHARESAFE_REQUIREMENT = b'exp-sharesafe'
+
# List of requirements which are working directory specific
# These requirements cannot be shared between repositories if they
# share the same store
@@ -60,8 +65,11 @@
# * SHARED_REQUIREMENT and RELATIVE_SHARED_REQUIREMENT are requirements which
# represents that the current working copy/repository shares store of another
# repo. Hence both of them should be stored in working copy
+# * SHARESAFE_REQUIREMENT needs to be stored in working dir to mark that rest of
+# the requirements are stored in store's requires
WORKING_DIR_REQUIREMENTS = {
SPARSE_REQUIREMENT,
SHARED_REQUIREMENT,
RELATIVE_SHARED_REQUIREMENT,
+ SHARESAFE_REQUIREMENT,
}
--- a/mercurial/scmutil.py Fri Aug 07 16:11:19 2020 +0530
+++ b/mercurial/scmutil.py Tue Apr 14 21:07:09 2020 +0530
@@ -1479,7 +1479,7 @@
Returns (wcreq, storereq)
"""
- if False:
+ if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
wc, store = set(), set()
for r in requirements:
if r in requirementsmod.WORKING_DIR_REQUIREMENTS:
--- a/mercurial/store.py Fri Aug 07 16:11:19 2020 +0530
+++ b/mercurial/store.py Tue Apr 14 21:07:09 2020 +0530
@@ -384,6 +384,7 @@
b'00changelog.i',
b'phaseroots',
b'obsstore',
+ b'requires',
]
@@ -455,7 +456,7 @@
yield x
def copylist(self):
- return [b'requires'] + _data
+ return _data
def write(self, tr):
pass
@@ -704,6 +705,7 @@
b'00manifest.i',
b'00changelog.d',
b'00changelog.i',
+ b'requires',
)
return [b'requires', b'00changelog.i'] + [b'store/' + f for f in d]
--- a/tests/test-journal-share.t Fri Aug 07 16:11:19 2020 +0530
+++ b/tests/test-journal-share.t Tue Apr 14 21:07:09 2020 +0530
@@ -1,3 +1,10 @@
+#testcases safe normal
+
+#if safe
+ $ echo "[format]" >> $HGRCPATH
+ $ echo "exp-share-safe = True" >> $HGRCPATH
+#endif
+
Journal extension test: tests the share extension support
$ cat >> testmocks.py << EOF
--- a/tests/test-narrow-share.t Fri Aug 07 16:11:19 2020 +0530
+++ b/tests/test-narrow-share.t Tue Apr 14 21:07:09 2020 +0530
@@ -1,4 +1,10 @@
#testcases flat tree
+#testcases safe normal
+
+#if safe
+ $ echo "[format]" >> $HGRCPATH
+ $ echo "exp-share-safe = True" >> $HGRCPATH
+#endif
$ . "$TESTDIR/narrow-library.sh"
--- a/tests/test-remotefilelog-share.t Fri Aug 07 16:11:19 2020 +0530
+++ b/tests/test-remotefilelog-share.t Tue Apr 14 21:07:09 2020 +0530
@@ -1,5 +1,12 @@
#require no-windows
+#testcases safe normal
+
+#if safe
+ $ echo "[format]" >> $HGRCPATH
+ $ echo "exp-share-safe = True" >> $HGRCPATH
+#endif
+
$ . "$TESTDIR/remotefilelog-library.sh"
$ cat >> $HGRCPATH <<EOF
--- a/tests/test-share-bookmarks.t Fri Aug 07 16:11:19 2020 +0530
+++ b/tests/test-share-bookmarks.t Tue Apr 14 21:07:09 2020 +0530
@@ -1,4 +1,10 @@
#testcases vfs svfs
+#testcases safe normal
+
+#if safe
+ $ echo "[format]" >> $HGRCPATH
+ $ echo "exp-share-safe = True" >> $HGRCPATH
+#endif
$ echo "[extensions]" >> $HGRCPATH
$ echo "share = " >> $HGRCPATH
@@ -284,3 +290,4 @@
$ hg init brokenrepo --config format.bookmarks-in-store=True --config format.usestore=false
ignoring enabled 'format.bookmarks-in-store' config beacuse it is incompatible with disabled 'format.usestore' config
+ ignoring enabled 'format.exp-share-safe' config because it is incompatible with disabled 'format.usestore' config (safe !)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-share-safe.t Tue Apr 14 21:07:09 2020 +0530
@@ -0,0 +1,69 @@
+setup
+
+ $ cat >> $HGRCPATH <<EOF
+ > [extensions]
+ > share =
+ > [format]
+ > exp-share-safe = True
+ > EOF
+
+prepare source repo
+
+ $ hg init source
+ $ cd source
+ $ cat .hg/requires
+ exp-sharesafe
+ $ cat .hg/store/requires
+ dotencode
+ fncache
+ generaldelta
+ revlogv1
+ sparserevlog
+ store
+ $ hg debugrequirements
+ dotencode
+ exp-sharesafe
+ fncache
+ generaldelta
+ revlogv1
+ sparserevlog
+ store
+
+ $ echo a > a
+ $ hg ci -Aqm "added a"
+ $ echo b > b
+ $ hg ci -Aqm "added b"
+ $ cd ..
+
+Create a shared repo and check the requirements are shared and read correctly
+ $ hg share source shared1
+ updating working directory
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd shared1
+ $ cat .hg/requires
+ exp-sharesafe
+ shared
+
+ $ hg debugrequirements -R ../source
+ dotencode
+ exp-sharesafe
+ fncache
+ generaldelta
+ revlogv1
+ sparserevlog
+ store
+
+ $ hg debugrequirements
+ dotencode
+ exp-sharesafe
+ fncache
+ generaldelta
+ revlogv1
+ shared
+ sparserevlog
+ store
+
+ $ echo c > c
+ $ hg ci -Aqm "added c"
+
+ $ hg unshare
--- a/tests/test-share.t Fri Aug 07 16:11:19 2020 +0530
+++ b/tests/test-share.t Tue Apr 14 21:07:09 2020 +0530
@@ -1,3 +1,10 @@
+#testcases safe normal
+
+#if safe
+ $ echo "[format]" >> $HGRCPATH
+ $ echo "exp-share-safe = True" >> $HGRCPATH
+#endif
+
$ echo "[extensions]" >> $HGRCPATH
$ echo "share = " >> $HGRCPATH
@@ -255,6 +262,7 @@
Test sharing a repository which was created with store requirement disable
$ hg init nostore --config format.usestore=false
+ ignoring enabled 'format.exp-share-safe' config because it is incompatible with disabled 'format.usestore' config (safe !)
$ hg share nostore sharednostore
abort: cannot create shared repository as source was created with 'format.usestore' config disabled
[255]