localrepo: load the share source .hg/hgrc also in share-safe mode (API)
authorPulkit Goyal <7895pulkit@gmail.com>
Thu, 02 Jul 2020 16:23:36 +0530
changeset 45485 b71858b42963
parent 45484 63eb1b5c580d
child 45486 ac7a3da0dbb6
localrepo: load the share source .hg/hgrc also in share-safe mode (API) The second part of the Share Safe Plan is to share source repo config also. This patch adds logic to load the source repo .hg/hgrc if we are in share safe mode. On unshare, we copy and prepend source config to current repo so that config which was shared is persisted. A test is added to show that now if we enable a hook on the source repo, that also runs on the shared repositories. API change as a new optional argument sharedvfs added to localrepo.loadhgrc() Differential Revision: https://phab.mercurial-scm.org/D8656
hgext/phabricator.py
mercurial/hg.py
mercurial/localrepo.py
tests/test-share-safe.t
--- a/hgext/phabricator.py	Fri Aug 07 17:42:15 2020 +0530
+++ b/hgext/phabricator.py	Thu Jul 02 16:23:36 2020 +0530
@@ -167,7 +167,7 @@
 
 
 @eh.wrapfunction(localrepo, "loadhgrc")
-def _loadhgrc(orig, ui, wdirvfs, hgvfs, requirements):
+def _loadhgrc(orig, ui, wdirvfs, hgvfs, requirements, **opts):
     """Load ``.arcconfig`` content into a ui instance on repository open.
     """
     result = False
@@ -201,7 +201,9 @@
     if cfg:
         ui.applyconfig(cfg, source=wdirvfs.join(b".arcconfig"))
 
-    return orig(ui, wdirvfs, hgvfs, requirements) or result  # Load .hg/hgrc
+    return (
+        orig(ui, wdirvfs, hgvfs, requirements, **opts) or result
+    )  # Load .hg/hgrc
 
 
 def vcrcommand(name, flags, spec, helpcategory=None, optionalrepo=False):
--- a/mercurial/hg.py	Fri Aug 07 17:42:15 2020 +0530
+++ b/mercurial/hg.py	Thu Jul 02 16:23:36 2020 +0530
@@ -332,6 +332,28 @@
     return r
 
 
+def _prependsourcehgrc(repo):
+    """ copies the source repo config and prepend it in current repo .hg/hgrc
+    on unshare. This is only done if the share was perfomed using share safe
+    method where we share config of source in shares"""
+    srcvfs = vfsmod.vfs(repo.sharedpath)
+    dstvfs = vfsmod.vfs(repo.path)
+
+    if not srcvfs.exists(b'hgrc'):
+        return
+
+    currentconfig = b''
+    if dstvfs.exists(b'hgrc'):
+        currentconfig = dstvfs.read(b'hgrc')
+
+    with dstvfs(b'hgrc', b'wb') as fp:
+        sourceconfig = srcvfs.read(b'hgrc')
+        fp.write(b"# Config copied from shared source\n")
+        fp.write(sourceconfig)
+        fp.write(b'\n')
+        fp.write(currentconfig)
+
+
 def unshare(ui, repo):
     """convert a shared repository to a normal one
 
@@ -350,6 +372,11 @@
         # fail
         destlock = copystore(ui, repo, repo.path)
         with destlock or util.nullcontextmanager():
+            if requirements.SHARESAFE_REQUIREMENT in repo.requirements:
+                # we were sharing .hg/hgrc of the share source with the current
+                # repo. We need to copy that while unsharing otherwise it can
+                # disable hooks and other checks
+                _prependsourcehgrc(repo)
 
             sharefile = repo.vfs.join(b'sharedpath')
             util.rename(sharefile, sharefile + b'.old')
--- a/mercurial/localrepo.py	Fri Aug 07 17:42:15 2020 +0530
+++ b/mercurial/localrepo.py	Thu Jul 02 16:23:36 2020 +0530
@@ -569,7 +569,7 @@
     # 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.
-    if loadhgrc(ui, wdirvfs, hgvfs, requirements):
+    if loadhgrc(ui, wdirvfs, hgvfs, requirements, sharedvfs):
         afterhgrcload(ui, wdirvfs, hgvfs, requirements)
         extensions.loadall(ui)
         extensions.populateui(ui)
@@ -697,7 +697,7 @@
     )
 
 
-def loadhgrc(ui, wdirvfs, hgvfs, requirements):
+def loadhgrc(ui, wdirvfs, hgvfs, requirements, sharedvfs=None):
     """Load hgrc files/content into a ui instance.
 
     This is called during repository opening to load any additional
@@ -708,9 +708,20 @@
     Extensions should monkeypatch this function to modify how per-repo
     configs are loaded. For example, an extension may wish to pull in
     configs from alternate files or sources.
+
+    sharedvfs is vfs object pointing to source repo if the current one is a
+    shared one
     """
     if not rcutil.use_repo_hgrc():
         return False
+
+    # first load config from shared source if we has to
+    if requirementsmod.SHARESAFE_REQUIREMENT in requirements and sharedvfs:
+        try:
+            ui.readconfig(sharedvfs.join(b'hgrc'), root=sharedvfs.base)
+        except IOError:
+            pass
+
     try:
         ui.readconfig(hgvfs.join(b'hgrc'), root=wdirvfs.base)
         return True
--- a/tests/test-share-safe.t	Fri Aug 07 17:42:15 2020 +0530
+++ b/tests/test-share-safe.t	Thu Jul 02 16:23:36 2020 +0530
@@ -66,4 +66,112 @@
   $ echo c > c
   $ hg ci -Aqm "added c"
 
+Check that config of the source repository is also loaded
+
+  $ hg showconfig ui.curses
+  [1]
+
+  $ echo "[ui]" >> ../source/.hg/hgrc
+  $ echo "curses=true" >> ../source/.hg/hgrc
+
+  $ hg showconfig ui.curses
+  true
+
+However, local .hg/hgrc should override the config set by share source
+
+  $ echo "[ui]" >> .hg/hgrc
+  $ echo "curses=false" >> .hg/hgrc
+
+  $ hg showconfig ui.curses
+  false
+
+Testing that hooks set in source repository also runs in shared repo
+
+  $ cd ../source
+  $ cat <<EOF >> .hg/hgrc
+  > [extensions]
+  > hooklib=
+  > [hooks]
+  > pretxnchangegroup.reject_merge_commits = \
+  >   python:hgext.hooklib.reject_merge_commits.hook
+  > EOF
+
+  $ cd ..
+  $ hg clone source cloned
+  updating to branch default
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd cloned
+  $ hg up 0
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ echo bar > bar
+  $ hg ci -Aqm "added bar"
+  $ hg merge
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m "merge commit"
+
+  $ hg push ../source
+  pushing to ../source
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  error: pretxnchangegroup.reject_merge_commits hook failed: bcde3522682d rejected as merge on the same branch. Please consider rebase.
+  transaction abort!
+  rollback completed
+  abort: bcde3522682d rejected as merge on the same branch. Please consider rebase.
+  [255]
+
+  $ hg push ../shared1
+  pushing to ../shared1
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  error: pretxnchangegroup.reject_merge_commits hook failed: bcde3522682d rejected as merge on the same branch. Please consider rebase.
+  transaction abort!
+  rollback completed
+  abort: bcde3522682d rejected as merge on the same branch. Please consider rebase.
+  [255]
+
+Test that if share source config is untrusted, we dont read it
+
+  $ cd ../shared1
+
+  $ cat << EOF > $TESTTMP/untrusted.py
+  > from mercurial import scmutil, util
+  > def uisetup(ui):
+  >     class untrustedui(ui.__class__):
+  >         def _trusted(self, fp, f):
+  >             if util.normpath(fp.name).endswith(b'source/.hg/hgrc'):
+  >                 return False
+  >             return super(untrustedui, self)._trusted(fp, f)
+  >     ui.__class__ = untrustedui
+  > EOF
+
+  $ hg showconfig hooks
+  hooks.pretxnchangegroup.reject_merge_commits=python:hgext.hooklib.reject_merge_commits.hook
+
+  $ hg showconfig hooks --config extensions.untrusted=$TESTTMP/untrusted.py
+  [1]
+
+Unsharing works
+
   $ hg unshare
+
+Test that source config is added to the shared one after unshare, and the config
+of current repo is still respected over the config which came from source config
+  $ cd ../cloned
+  $ hg push ../shared1
+  pushing to ../shared1
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  error: pretxnchangegroup.reject_merge_commits hook failed: bcde3522682d rejected as merge on the same branch. Please consider rebase.
+  transaction abort!
+  rollback completed
+  abort: bcde3522682d rejected as merge on the same branch. Please consider rebase.
+  [255]
+  $ hg showconfig ui.curses -R ../shared1
+  false