diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -781,6 +781,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 @@ -449,6 +449,11 @@ # The repository use persistent nodemap for the changelog and the manifest. NODEMAP_REQUIREMENT = b'persistent-nodemap' +# 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' + # Functions receiving (ui, features) that extensions can register to impact # the ability to load repositories with custom requirements. Only # functions defined in loaded extensions are called. @@ -529,6 +534,26 @@ raise requirements = set() + # if .hg/requires contains the exp-sharesafe requirement, it means + # there exists a `.hg/store/requires` too and we should read it + # TODO: make this code more stricter by checking whether store exists + # and other checks + if SHARESAFE_REQUIREMENT in requirements: + if hgvfs.exists(b'sharedpath'): + # This is a shared repo + sharedpath = hgvfs.read(b'sharedpath').rstrip(b'\n') + if b'relshared' in requirements: + sharedpath = hgvfs.join(sharedpath) + storevfs = vfsmod.vfs(vfsmod.vfs(sharedpath).join(b'store')) + else: + storevfs = vfsmod.vfs(hgvfs.join(b'store'), cacheaudited=True) + try: + store_requirements = set(storevfs.read(b'requires').splitlines()) + requirements |= store_requirements + except IOError as e: + if e.errno != errno.ENOENT: + raise + # 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. @@ -1034,6 +1059,7 @@ SPARSEREVLOG_REQUIREMENT, NODEMAP_REQUIREMENT, bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT, + SHARESAFE_REQUIREMENT, } _basesupported = supportedformats | { b'store', @@ -1303,7 +1329,12 @@ self._writerequirements() def _writerequirements(self): - scmutil.writerequires(self.vfs, self.requirements) + if SHARESAFE_REQUIREMENT in self.requirements: + with self.lock(): + scmutil.writerequires(self.svfs, self.requirements) + scmutil.writerequires(self.vfs, set([SHARESAFE_REQUIREMENT])) + else: + scmutil.writerequires(self.vfs, self.requirements) # Don't cache auditor/nofsauditor, or you'll end up with reference cycle: # self -> auditor -> self._checknested -> self @@ -3638,6 +3669,11 @@ if ui.configbool(b'format', b'use-persistent-nodemap'): requirements.add(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(SHARESAFE_REQUIREMENT) + return requirements @@ -3761,7 +3797,20 @@ b'layout', ) - scmutil.writerequires(hgvfs, requirements) + if SHARESAFE_REQUIREMENT in requirements and b'store' in requirements: + if b'sharedrepo' in createopts: + req = set([b'shared', SHARESAFE_REQUIREMENT]) + if b'relshared' in requirements: + req.add(b'relshared') + scmutil.writerequires(hgvfs, req) + else: + scmutil.writerequires(hgvfs, set([SHARESAFE_REQUIREMENT])) + storevfs = vfsmod.vfs(hgvfs.join(b'store'), cacheaudited=True) + scmutil.writerequires( + storevfs, requirements - set([SHARESAFE_REQUIREMENT]) + ) + else: + scmutil.writerequires(hgvfs, requirements) # Write out file telling readers where to find the shared store. if b'sharedrepo' in createopts: diff --git a/mercurial/store.py b/mercurial/store.py --- a/mercurial/store.py +++ b/mercurial/store.py @@ -375,7 +375,7 @@ _data = ( b'bookmarks narrowspec data meta 00manifest.d 00manifest.i' - b' 00changelog.d 00changelog.i phaseroots obsstore' + b' 00changelog.d 00changelog.i phaseroots obsstore requires' ) @@ -447,7 +447,7 @@ yield x def copylist(self): - return [b'requires'] + _data.split() + return _data.split() def write(self, tr): pass @@ -687,7 +687,7 @@ def copylist(self): d = ( b'bookmarks narrowspec data meta dh fncache phaseroots obsstore' - b' 00manifest.d 00manifest.i 00changelog.d 00changelog.i' + b' 00manifest.d 00manifest.i 00changelog.d 00changelog.i requires' ) return [b'requires', b'00changelog.i'] + [ b'store/' + f for f in d.split() 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 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