From 30e39de41898297de0b45452d1d4b6c7bd51b783 Mon Sep 17 00:00:00 2001 From: Kostia Balytskyi Date: Thu, 12 Mar 2020 03:19:40 -0700 Subject: [PATCH] edenscm: add a separate path type for infinitepush writes Summary: In order to support gradual rollout of infinitepush for other backends (e.g. Mononoke), we need the ability to route read vs write requests separately. To achieve this, we need a separate path type: `infinitepush-write` (kind of like `default-push`, the naming inconsistency is a little unfortunate, as I don't want to use `infinitepush-push`). The desired behavior of the new path type is as follows: - takes precedence over `infinitepush` path when the user does `hg push -r . --to scratch/bla` - replaces `infinitepush` path when the user does `hg push infinitepush -r . --to scratch/bla` - absence of this path means draft pushes will go to `infinitepush` path - draft pulls always go to `infinitepush` path, and *there's no fallback to `infinitepush-write`* - commit cloud always talks to `infinitepush-write`, if it is present (meaning that commit cloud pulls do go to `infinitepush-write` path - this is done, as commitcloud uses infinitepush paths to also check whether something is backed up - and also, commitcloud may need to pull very soon after something has been pushed Reviewed By: quark-zju Differential Revision: D20368158 fbshipit-source-id: 59db174cebbf2b48765dff37bc93aad176c2d7c1 --- eden/scm/edenscm/hgext/commitcloud/util.py | 9 +- eden/scm/edenscm/hgext/infinitepush/client.py | 65 ++++++--- .../edenscm/hgext/infinitepush/constants.py | 9 ++ eden/scm/tests/test-infinitepush-write.t | 134 ++++++++++++++++++ 4 files changed, 198 insertions(+), 19 deletions(-) create mode 100644 eden/scm/tests/test-infinitepush-write.t diff --git a/eden/scm/edenscm/hgext/commitcloud/util.py b/eden/scm/edenscm/hgext/commitcloud/util.py index 55a4c14b53..1f6f5a6560 100644 --- a/eden/scm/edenscm/hgext/commitcloud/util.py +++ b/eden/scm/edenscm/hgext/commitcloud/util.py @@ -10,7 +10,7 @@ import os from edenscm.mercurial import commands, encoding, error, pycompat, util from edenscm.mercurial.i18n import _ -from . import error as ccerror +from . import dependencies, error as ccerror SERVICE = "commitcloud" @@ -67,7 +67,12 @@ def getreponame(repo): def getremotepath(repo, dest): # If dest is empty, pass in None to get the default path. - path = repo.ui.paths.getpath(dest or None, default=("infinitepush", "default")) + pathname = dependencies.infinitepush.constants.pathname + path = repo.ui.paths.getpath( + dest or None, + default=(pathname.infinitepushwrite, pathname.infinitepush, pathname.default), + ) + if not path: raise error.Abort( _("default repository not configured!"), diff --git a/eden/scm/edenscm/hgext/infinitepush/client.py b/eden/scm/edenscm/hgext/infinitepush/client.py index 6532883064..ba00feb605 100644 --- a/eden/scm/edenscm/hgext/infinitepush/client.py +++ b/eden/scm/edenscm/hgext/infinitepush/client.py @@ -32,6 +32,7 @@ from edenscm.mercurial import ( from edenscm.mercurial.i18n import _ from . import bookmarks, constants +from .constants import pathname _maybehash = re.compile(r"^[a-f0-9]+$").search @@ -109,16 +110,35 @@ def preparepush(ui, dest): # Mercurial and Mononoke), then infinite pushes without a path OR with a # path of "default" will be routed to both of them. Put it another way: when # you do a scratch push, "default" means the infinitepush path. - if dest == "default": + if dest == pathname.default: try: - return (True, ui.paths.getpath("infinitepush")) + return (True, ui.paths.getpath(pathname.infinitepushwrite)) except error.RepoError: # Fallthrough to the next block. pass - if dest is None or dest in ("default", "infinitepush"): + try: + return (True, ui.paths.getpath(pathname.infinitepush)) + except error.RepoError: + # Fallthrough to the next block. + pass + + if dest == pathname.infinitepush: + try: + return (True, ui.paths.getpath(pathname.infinitepushwrite)) + except error.RepoError: + # Fallthrough to the next block. + pass + + if dest in {None, pathname.default, pathname.infinitepush}: path = ui.paths.getpath( - dest, default=("infinitepush", "default-push", "default") + dest, + default=( + pathname.infinitepushwrite, + pathname.infinitepush, + pathname.defaultpush, + pathname.default, + ), ) return (True, path) @@ -201,11 +221,13 @@ def _push(orig, ui, repo, dest=None, *args, **opts): # the default infinitepush destination. if replicate: try: - otherpath = repo.ui.paths.getpath("infinitepush-other") + otherpath = repo.ui.paths.getpath(pathname.infinitepushother) except error.RepoError: pass else: - path = ui.paths.getpath(dest, default=("default-push", "default")) + path = ui.paths.getpath( + dest, default=(pathname.defaultpush, pathname.default) + ) # Copy-paste from `push` command if not path: @@ -276,7 +298,7 @@ def _bookmarks(orig, ui, repo, *names, **opts): pattern = opts.get("list_remote") delete = opts.get("delete") remotepath = opts.get("remote_path") - path = ui.paths.getpath(remotepath or None, default=("default")) + path = ui.paths.getpath(remotepath or None, default=(pathname.default,)) if pattern: destpath = path.pushloc or path.loc @@ -306,7 +328,7 @@ def _bookmarks(orig, ui, repo, *names, **opts): if len(scratch_bms) > 0: if remotepath == "": - remotepath = "default" + remotepath = pathname.default bookmarks.deleteremotebookmarks(ui, repo, remotepath, scratch_bms) if len(other_bms) > 0 or len(scratch_bms) == 0: @@ -357,8 +379,10 @@ def _resetinfinitepushpath(ui, **opts): """ overrides = {} - infinitepushpath = "infinitepush" - infinitepushbookmarkpath = "infinitepushbookmark" + defaultpath = pathname.default + infinitepushpath = pathname.infinitepush + infinitepushwritepath = pathname.infinitepushwrite + infinitepushbookmarkpath = pathname.infinitepushbookmark pullingsinglecommithash = False if opts.get("rev"): @@ -374,16 +398,23 @@ def _resetinfinitepushpath(ui, **opts): path = None if path is not None: - overrides[("paths", "default")] = ui.paths[path].loc + overrides[("paths", defaultpath)] = ui.paths[path].loc overrides[("paths", infinitepushpath)] = "!" + overrides[("paths", infinitepushwritepath)] = "!" overrides[("paths", infinitepushbookmarkpath)] = "!" with ui.configoverride(overrides, "infinitepush"): - loc, sub = ui.configsuboptions("paths", "default") - ui.paths["default"] = uimod.path(ui, "default", rawloc=loc, suboptions=sub) - if infinitepushpath in ui.paths: - del ui.paths[infinitepushpath] - if infinitepushbookmarkpath in ui.paths: - del ui.paths[infinitepushbookmarkpath] + loc, sub = ui.configsuboptions("paths", defaultpath) + ui.paths[defaultpath] = uimod.path( + ui, defaultpath, rawloc=loc, suboptions=sub + ) + for p in [ + infinitepushpath, + infinitepushbookmarkpath, + infinitepushwritepath, + ]: + if p not in ui.paths: + continue + del ui.paths[p] yield else: yield diff --git a/eden/scm/edenscm/hgext/infinitepush/constants.py b/eden/scm/edenscm/hgext/infinitepush/constants.py index a211583f58..4bfdd4791c 100644 --- a/eden/scm/edenscm/hgext/infinitepush/constants.py +++ b/eden/scm/edenscm/hgext/infinitepush/constants.py @@ -7,3 +7,12 @@ scratchbranchparttype = "b2x:infinitepush" scratchbookmarksparttype = "b2x:infinitepushscratchbookmarks" scratchmutationparttype = "b2x:infinitepushmutation" pushrebaseparttype = "b2x:rebase" + + +class pathname(object): + default = "default" + defaultpush = "default-push" + infinitepush = "infinitepush" + infinitepushother = "infinitepush-other" + infinitepushbookmark = "infinitepushbookmark" + infinitepushwrite = "infinitepush-write" diff --git a/eden/scm/tests/test-infinitepush-write.t b/eden/scm/tests/test-infinitepush-write.t new file mode 100644 index 0000000000..4002d6e4fb --- /dev/null +++ b/eden/scm/tests/test-infinitepush-write.t @@ -0,0 +1,134 @@ +#chg-compatible + + $ enable remotefilelog + $ enable treemanifest + +Setup the test + $ . "$TESTDIR/library.sh" + $ . "$TESTDIR/infinitepush/library.sh" + $ setupcommon + $ enable infinitepush pushrebase + $ cat >> "$HGRCPATH" << EOF + > [treemanifest] + > sendtrees=True + > treeonly=True + > EOF + $ cp "$HGRCPATH" "$TESTTMP/defaulthgrc" + + $ hg init repo1 + $ cd repo1 + $ setupserver + $ cat >> .hg/hgrc << EOF + > [treemanifest] + > server=true + > EOF + $ cd .. + + $ hg init repo2 + $ cd repo2 + $ setupserver + $ cat >> .hg/hgrc << EOF + > [treemanifest] + > server=true + > EOF + $ cd .. + + $ hg init repo3 + $ cd repo3 + $ setupserver + $ cat >> .hg/hgrc << EOF + > [treemanifest] + > server=true + > EOF + $ cd .. + +Check that we push to the write path if it is present + $ hg clone ssh://user@dummy/repo1 client -q + $ cp "$TESTTMP/defaulthgrc" "$HGRCPATH" + $ cat >> "$HGRCPATH" << EOF + > [paths] + > default-push=ssh://user@dummy/repo3 + > infinitepush=ssh://user@dummy/repo1 + > infinitepush-write=ssh://user@dummy/repo2 + > [remotefilelog] + > fallbackpath=ssh://user@dummy/repo2 + > [infinitepush] + > branchpattern=re:scratch/.+ + > EOF + $ cd client + $ mkcommit initialcommit + $ hg push -r . --to scratch/test123 --create + pushing to ssh://user@dummy/repo2 + searching for changes + remote: pushing 1 commit: + remote: 67145f466344 initialcommit + $ mkcommit morecommit + $ hg push infinitepush -r . --to scratch/test123 + pushing to ssh://user@dummy/repo2 + searching for changes + remote: pushing 2 commits: + remote: 67145f466344 initialcommit + remote: 6b2f28e02245 morecommit + $ mkcommit anothercommit + $ hg push default -r . --to scratch/test123 + pushing to ssh://user@dummy/repo2 + searching for changes + remote: pushing 3 commits: + remote: 67145f466344 initialcommit + remote: 6b2f28e02245 morecommit + remote: 17528c345014 anothercommit + +-- check that we fallback to non-write path, when write path is not there + $ mkcommit yetanothercommit + $ hg push -r . --to scratch/test123 --create --config paths.infinitepush-write= + pushing to ssh://user@dummy/repo1 + searching for changes + remote: pushing 4 commits: + remote: 67145f466344 initialcommit + remote: 6b2f28e02245 morecommit + remote: 17528c345014 anothercommit + remote: 8785135185c9 yetanothercommit + $ cd .. + +Check that we pull/update from the read path, regardless of the write path presence + $ hg clone ssh://user@dummy/repo1 client2 -q + $ cp "$TESTTMP/defaulthgrc" "$HGRCPATH" + $ cat >> "$HGRCPATH" << EOF + > [paths] + > infinitepush=ssh://user@dummy/repo2 + > infinitepush-write=ssh://user@dummy/repo3 + > [remotefilelog] + > fallbackpath=ssh://user@dummy/repo2 + > [infinitepush] + > branchpattern=re:scratch/.+ + > EOF + $ cd client2 + $ mkcommit initialcommit + $ hg pull -r 67145f466344 + pulling from ssh://user@dummy/repo2 + no changes found + adding changesets + adding manifests + adding file changes + added 0 changesets with 0 changes to 1 files + $ hg update -r 6b2f28e02245 + '6b2f28e02245' does not exist locally - looking for it remotely... + pulling from ssh://user@dummy/repo2 + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 2 files + '6b2f28e02245' found remotely + pull finished in * (glob) + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + +-- check that we can pull from read path, when write path is not present + $ hg pull -r 8785135185c9 --config paths.infinitepush-write= --config paths.infinitepush=ssh://user@dummy/repo1 + pulling from ssh://user@dummy/repo1 + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 4 files + $ cd ..