push: add '--force' support

Summary:
force pushrebase is basically a plain push, this diff also adds check
for mutation metadata. To keep public commit data clean, server prefer to
remove mutation metadata.

Reviewed By: quark-zju

Differential Revision: D52141515

fbshipit-source-id: aeca404b482cc94d538aa051d89f5ac202a0a5b1
This commit is contained in:
Zhaolong Zhu 2023-12-15 12:48:28 -08:00 committed by Facebook GitHub Bot
parent 4ea1e5038f
commit 90de8eba1d
2 changed files with 72 additions and 51 deletions

View File

@ -323,11 +323,8 @@ Test a non-forward push
~
$ hgedenapi push --force -r . --to master_bookmark_2 --non-forward-move --pushvar NON_FAST_FORWARD=true
fallback reason: --force is not supported by EdenApi push yet
pushing rev 589551466f25 to destination mononoke://$LOCALIP:$LOCAL_PORT/repo bookmark master_bookmark_2
searching for changes
no changes found
updating bookmark master_bookmark_2
pushing rev 589551466f25 to destination https://localhost:*/edenapi/ bookmark master_bookmark_2 (glob)
moving remote bookmark master_bookmark_2 from eb388b759fde to 589551466f25
$ log -r "20::"
o merge 10 and 12 [public;rev=25;eb388b759fde] default/master_bookmark
@ -391,13 +388,15 @@ Test non-fast-forward force pushrebase
~
-- we don't need to pass --pushvar NON_FAST_FORWARD if we're doing a force pushrebase
$ hgedenapi push -r . -f --to newbook
fallback reason: --force is not supported by EdenApi push yet
pushing rev 4899f9112d9b to destination mononoke://$LOCALIP:$LOCAL_PORT/repo bookmark newbook
searching for changes
adding changesets
adding manifests
adding file changes
updating bookmark newbook
pushing rev 4899f9112d9b to destination https://localhost:*/edenapi/ bookmark newbook (glob)
edenapi: queue 1 commit for upload
edenapi: queue 1 file for upload
edenapi: uploaded 1 file
edenapi: queue 1 tree for upload
edenapi: uploaded 1 tree
edenapi: uploading commit '4899f9112d9b79c3ecbc343169db37fbe1efdd20'...
edenapi: uploaded 1 changeset
moving remote bookmark newbook from 7a037594e202 to 4899f9112d9b
-- "20 draft newbook" gets moved to 26 and 20 gets hidden.
$ log -r "20::"
@ 26 [public;rev=27;4899f9112d9b] default/newbook
@ -428,27 +427,20 @@ Test non-fast-forward force pushrebase
4899f9112d9b79c3ecbc343169db37fbe1efdd20
$ cd ../repo2
-- Check that a force pushrebase with mutation markers is a fail
Check that a force pushrebase with mutation markers.
$ echo SPARTACUS > sum_ego && hg ci -qAm 27
$ echo SPARTACUS! > sum_ego && hg amend --config mutation.enabled=true --config mutation.record=true
$ hgedenapi push -r . -f --to newbook
fallback reason: --force is not supported by EdenApi push yet
pushing rev * to destination mononoke://$LOCALIP:$LOCAL_PORT/repo bookmark newbook (glob)
searching for changes
remote: Command failed
remote: Error:
remote: Forced push blocked because it contains mutation metadata.
remote: You can remove the metadata from a commit with `hg amend --config mutation.record=false`.
remote: For more help, please contact the Source Control team at https://fburl.com/27qnuyl2
remote:
remote: Root cause:
remote: Forced push blocked because it contains mutation metadata.
remote: You can remove the metadata from a commit with `hg amend --config mutation.record=false`.
remote: For more help, please contact the Source Control team at https://fburl.com/27qnuyl2
remote:
remote: Debug context:
remote: "Forced push blocked because it contains mutation metadata.\nYou can remove the metadata from a commit with `hg amend --config mutation.record=false`.\nFor more help, please contact the Source Control team at https://fburl.com/27qnuyl2"
abort: unexpected EOL, expected netstring digit
$ hgedenapi push -r . -f --to newbook --config push.check-mutation=true
pushing rev * to destination https://localhost:*/edenapi/ bookmark newbook (glob)
edenapi: queue 1 commit for upload
edenapi: queue 1 file for upload
edenapi: uploaded 1 file
edenapi: queue 1 tree for upload
edenapi: uploaded 1 tree
edenapi: uploading commit '*'... (glob)
edenapi: uploaded 1 changeset
abort: forced push blocked because commit * contains mutation metadata (glob)
(use 'hg amend --config mutation.record=false' to remove the metadata)
[255]
Check that we can replace a file with a directory

View File

@ -12,6 +12,9 @@ from .i18n import _
from .node import bin, hex, nullhex, short
MUTATION_KEYS = {"mutpred", "mutuser", "mutdate", "mutop", "mutsplit"}
def get_edenapi_for_dest(repo, _dest):
"""Get an EdenApi instance for the given destination."""
if not repo.ui.configbool("push", "edenapi"):
@ -32,11 +35,6 @@ def get_edenapi_for_dest(repo, _dest):
def push(repo, dest, head_node, remote_bookmark, force=False, opargs=None):
"""Push via EdenApi (HTTP)"""
if force:
raise error.UnsupportedEdenApiPush(
_("--force is not supported by EdenApi push yet")
)
ui = repo.ui
edenapi = get_edenapi_for_dest(repo, dest)
opargs = opargs or {}
@ -78,30 +76,36 @@ def push(repo, dest, head_node, remote_bookmark, force=False, opargs=None):
% remote_bookmark
)
if repo[head_node].phase() == phases.public:
# if the head is already a public commit, then do a plain push (no pushrebase)
plain_push(repo, edenapi, remote_bookmark, head_node, bookmark_node, opargs)
if force or repo[head_node].phase() == phases.public:
# if the head is already a public commit or force is set, then do a plain
# push (no pushrebase)
plain_push(
repo, edenapi, remote_bookmark, head_node, bookmark_node, force, opargs
)
else:
# update the exiting bookmark with push rebase
return push_rebase(repo, dest, head_node, draft_nodes, remote_bookmark, opargs)
def plain_push(repo, edenapi, bookmark, to_node, from_node, opargs=None):
def plain_push(repo, edenapi, bookmark, to_node, from_node, force, opargs=None):
"""Plain push without rebasing."""
pushvars = parse_pushvars(opargs.get("pushvars"))
# setbookmark api server logic does not check if it's a non fast-forward move,
# let's check it in the client side as a workaround for now
is_ancestor = repo.dageval(lambda: isancestor(from_node, to_node))
if not is_ancestor:
if not is_true(pushvars.get("NON_FAST_FORWARD")):
raise error.Abort(
_(
"non-fast-forward push to remote bookmark %s from %s to %s "
"(set pushvar NON_FAST_FORWARD=true for a non-fast-forward move)"
if force:
check_mutation_metadata(repo, to_node)
else:
# setbookmark api server logic does not check if it's a non fast-forward move,
# let's check it in the client side as a workaround for now
is_ancestor = repo.dageval(lambda: isancestor(from_node, to_node))
if not is_ancestor:
if not is_true(pushvars.get("NON_FAST_FORWARD")):
raise error.Abort(
_(
"non-fast-forward push to remote bookmark %s from %s to %s "
"(set pushvar NON_FAST_FORWARD=true for a non-fast-forward move)"
)
% (bookmark, short(from_node), short(to_node)),
)
% (bookmark, short(from_node), short(to_node)),
)
repo.ui.status(
_("moving remote bookmark %s from %s to %s\n")
@ -256,5 +260,30 @@ def parse_pushvars(pushvars_strs: Optional[List[str]]) -> List[Tuple[str, str]]:
return pushvars
def check_mutation_metadata(repo, to_node):
"""Check if the given commits have mutation metadata. If so, abort."""
# context: https://github.com/facebook/sapling/blob/fb09c14ae6d1a134259f66d9997d1af21c605c07/eden/mononoke/repo_client/unbundle/src/resolver.rs#L616
# this logic is probably not be needed nowadays (disabled by default), but we
# keep it here just in case.
if not repo.ui.configbool("push", "check-mutation"):
return
draft_nodes = repo.dageval(lambda: only([to_node], public()))
for node in draft_nodes:
ctx = repo[node]
if ctx.extra().keys() & MUTATION_KEYS:
hint = _(
"use 'hg amend --config mutation.record=false' to remove the metadata"
)
support = repo.ui.config("ui", "supportcontact")
if support:
hint += _(" or contact %s for help") % support
raise error.Abort(
_("forced push blocked because commit %s contains mutation metadata")
% ctx,
hint=hint,
)
def is_true(s: Optional[str]) -> bool:
return s == "true" or s == "True"