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
This commit is contained in:
Kostia Balytskyi 2020-03-12 03:19:40 -07:00 committed by Facebook GitHub Bot
parent 7fcd49242c
commit 30e39de418
4 changed files with 198 additions and 19 deletions

View File

@ -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!"),

View File

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

View File

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

View File

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