pushrebase: also update remote names

Summary:
For now pushrebase updates working parent, bookmarks. This diff makes it also
update remote bookmarks.

This resolves a race condtion in remotenames where the pulled remote bookmarks
have unknown nodes and cause update to silently fail.

Reviewed By: sfilipco

Differential Revision: D18601035

fbshipit-source-id: 6f2c318cbf6b29a6427eeac6d374c1fb13e6155c
This commit is contained in:
Jun Wu 2019-12-06 11:36:38 -08:00 committed by Facebook Github Bot
parent ae2be4c73f
commit 3f9123d94f
3 changed files with 64 additions and 7 deletions

View File

@ -462,6 +462,41 @@ def _push(orig, ui, repo, *args, **opts):
result = orig(ui, repo, *args, **opts)
if onto and tracker.replacementsreceived:
# move remote bookmark
#
# Note: 'remotenames' also uses 'listkeys' to update remote
# bookmarks after push. However, that's racy and does not always
# succeed.
try:
rmarks = repo.names["remotebookmarks"]
except KeyError:
# remotenames is not enabled.
pass
else:
from .. import remotenames
# convert to full name (ex. 'master' -> 'remote/master')
fullname = remotenames.hoist2fullname(repo, onto)
nodes = rmarks.namemap(repo, fullname)
if nodes:
rebasednodes = list(tracker.mapping.values())
# The server does not tell us the explicit new location of
# the 'onto' remote boookmark, but we can infer that from
# the rebased commits.
newnode = next(repo.nodes("max(%ln)", rebasednodes), None)
# remotenames might have moved the bookmark already. If
# newnode is already in nodes, there is no need to update
# it again.
if newnode is not None and newnode not in nodes:
if not ui.quiet:
# When we do update it in this code path, print
# a message. This is used in tests.
ui.write_err(
_("moving remote bookmark %r to %s\n")
% (fullname, short(newnode))
)
remotenames.setremotebookmark(repo, fullname, newnode)
# move working copy parent
if wnode in tracker.mapping:
hg.update(repo, tracker.mapping[wnode])

View File

@ -98,6 +98,10 @@ configitem("remotenames", "transitionbookmarks", default=[])
configitem("remotenames", "transitionmessage", default=None)
configitem("remotenames", "upstream", default=[])
# Perform a pull of remotenames for "push" command. This is racy and does not
# always update remote bookmarks! The config option exists for testing purpose.
configitem("remotenames", "racy-pull-on-push", default=True)
namespacepredicate = registrar.namespacepredicate()
templatekeyword = registrar.templatekeyword()
revsetpredicate = registrar.revsetpredicate()
@ -138,7 +142,13 @@ def expush(orig, repo, remote, *args, **kwargs):
remotebookmarks = _listremotebookmarks(remote, remotebookmarkskeys)
else:
remotebookmarks = remote.listkeys("bookmarks")
pullremotenames(repo, remote, remotebookmarks)
# ATTENTION: This might get commits that are unknown to the local repo!
# The correct approach is to get the remote names within "orig". But
# that requires some complicated server-side changes.
# internal config: remotenames.racy-pull-on-push
if repo.ui.configbool("remotenames", "racy-pull-on-push"):
pullremotenames(repo, remote, remotebookmarks)
return res
@ -2072,17 +2082,27 @@ def precachedistance(repo):
@command("debugremotebookmark")
def debugremotebookmark(ui, repo, name, rev):
"""Change a remote bookmark under the 'debugremote' namespace."""
data = {} # {'remote': {'master': '<commit hash>'}}
node = scmutil.revsingle(repo, rev).node()
setremotebookmark(repo, "debugremote/%s" % name, node)
def setremotebookmark(repo, fullname, newnode):
"""Update a single remote bookmark"""
with repo.wlock(), repo.lock(), repo.transaction("debugremotebookmark"):
data = {} # {'remote': {'master': '<commit hash>'}}
for hexnode, _nametype, remote, rname in readremotenames(repo):
data.setdefault(remote, {})[rname] = hexnode
hexnode = scmutil.revsingle(repo, rev).hex()
data.setdefault("debugremote", {})[name] = hexnode
remote, name = fullname.split("/", 1)
data.setdefault(remote, {})[name] = hex(newnode)
saveremotenames(repo, data)
def hoist2fullname(repo, hoistname):
"""Convert a hoisted name (ex. 'master') to full name (ex. 'remote/master')"""
fullname = "%s/%s" % (repo.ui.config("remotenames", "hoist"), hoistname)
return fullname
#########
# revsets
#########

View File

@ -81,7 +81,8 @@ Test that pushing to a remotename gets rebased
o 0 "initial" default/master
$ hg push --to master
(disable remotenames.racy-pull-on-push so we can check pushrebase's fallback behavior on updating remotenames)
$ hg push --to master --config remotenames.racy-pull-on-push=0
pushing rev 5c3cfb78df2f to destination ssh://user@dummy/server bookmark master
searching for changes
remote: pushing 1 changeset:
@ -92,6 +93,7 @@ Test that pushing to a remotename gets rebased
adding file changes
added 2 changesets with 1 changes to 2 files
updating bookmark master
moving remote bookmark 'default/master' to 98d6f1036c3b
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg log -G -T '{rev} "{desc}" {remotebookmarks}'