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
--- 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