Patchwork D8633: share: introduce config option to store requires in .hg/store

login
register
mail settings
Submitter phabricator
Date June 15, 2020, 3:04 p.m.
Message ID <differential-rev-PHID-DREV-7tmt46rgtygb725elmw2-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/46505/
State New
Headers show

Comments

phabricator - June 15, 2020, 3:04 p.m.
pulkit created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This introduces a config option which enabled stores the requirements on a
  repository in store instead.
  
  When enabled, `.hg/requires` will contain the exp-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
  exp-share-safe requirement, if yes, it does not copy the requirements.
  
  Logic to read requirements from store of source of shared repository is also
  added.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D8633

AFFECTED FILES
  mercurial/configitems.py
  mercurial/localrepo.py
  mercurial/store.py
  tests/test-share-safe.t

CHANGE DETAILS




To: pulkit, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

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 <<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
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/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,23 @@ 
             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
+            sharedsource = hgvfs.read(b'sharedpath')
+            storevfs = vfsmod.vfs(vfsmod.vfs(sharedsource).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:
+            pass
+
     # 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 +1056,7 @@ 
         SPARSEREVLOG_REQUIREMENT,
         NODEMAP_REQUIREMENT,
         bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT,
+        SHARESAFE_REQUIREMENT,
     }
     _basesupported = supportedformats | {
         b'store',
@@ -1302,7 +1325,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
@@ -3637,6 +3665,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
 
 
@@ -3760,7 +3793,19 @@ 
             b'layout',
         )
 
-    scmutil.writerequires(hgvfs, requirements)
+    if SHARESAFE_REQUIREMENT in requirements and b'store' in requirements:
+        # TODO: make sure that the source repository has sharesafe requirement # too
+        if 'sharedrepo' in createopts:
+            req = 'shared' if 'shared' in requirements else 'relshared'
+            scmutil.writerequires(hgvfs, set([SHARESAFE_REQUIREMENT, 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/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(