share: introduce config option to store requires in .hg/store
authorPulkit Goyal <7895pulkit@gmail.com>
Tue, 14 Apr 2020 21:07:09 +0530
changeset 45483 d252f51ab032
parent 45482 9a99ab8217bd
child 45484 63eb1b5c580d
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
mercurial/configitems.py
mercurial/localrepo.py
mercurial/requirements.py
mercurial/scmutil.py
mercurial/store.py
tests/test-journal-share.t
tests/test-narrow-share.t
tests/test-remotefilelog-share.t
tests/test-share-bookmarks.t
tests/test-share-safe.t
tests/test-share.t
--- 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]