diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -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( diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -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: diff --git a/mercurial/requirements.py b/mercurial/requirements.py --- a/mercurial/requirements.py +++ b/mercurial/requirements.py @@ -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, } diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -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: diff --git a/mercurial/store.py b/mercurial/store.py --- a/mercurial/store.py +++ b/mercurial/store.py @@ -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] diff --git a/tests/test-journal-share.t b/tests/test-journal-share.t --- a/tests/test-journal-share.t +++ b/tests/test-journal-share.t @@ -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 diff --git a/tests/test-narrow-share.t b/tests/test-narrow-share.t --- a/tests/test-narrow-share.t +++ b/tests/test-narrow-share.t @@ -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" diff --git a/tests/test-remotefilelog-share.t b/tests/test-remotefilelog-share.t --- a/tests/test-remotefilelog-share.t +++ b/tests/test-remotefilelog-share.t @@ -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 <> $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 !) diff --git a/tests/test-share-safe.t b/tests/test-share-safe.t new file mode 100644 --- /dev/null +++ b/tests/test-share-safe.t @@ -0,0 +1,69 @@ +setup + + $ cat >> $HGRCPATH < [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 diff --git a/tests/test-share.t b/tests/test-share.t --- a/tests/test-share.t +++ b/tests/test-share.t @@ -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]