diff --git a/hgext/phabricator.py b/hgext/phabricator.py --- a/hgext/phabricator.py +++ b/hgext/phabricator.py @@ -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): diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -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') diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -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 diff --git a/tests/test-share-safe.t b/tests/test-share-safe.t --- a/tests/test-share-safe.t +++ b/tests/test-share-safe.t @@ -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 <> .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