2019-11-01 23:24:22 +03:00
|
|
|
# Copyright (c) Facebook, Inc. and its affiliates.
|
2019-02-19 12:54:57 +03:00
|
|
|
#
|
|
|
|
# This software may be used and distributed according to the terms of the
|
2019-11-01 23:24:22 +03:00
|
|
|
# GNU General Public License version 2.
|
|
|
|
|
|
|
|
# sendunbundlereplay.py - send unbundlereplay wireproto command
|
2019-02-19 12:54:57 +03:00
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2019-05-24 13:37:45 +03:00
|
|
|
import contextlib
|
2019-04-11 04:02:26 +03:00
|
|
|
import datetime
|
2019-04-29 19:44:40 +03:00
|
|
|
import os
|
|
|
|
import sys
|
2019-04-11 04:02:26 +03:00
|
|
|
|
2019-02-26 00:04:21 +03:00
|
|
|
from edenscm.mercurial import error, hg, replay, util
|
2019-02-19 12:54:57 +03:00
|
|
|
from edenscm.mercurial.commands import command
|
|
|
|
from edenscm.mercurial.i18n import _
|
|
|
|
|
|
|
|
|
2019-04-29 19:44:40 +03:00
|
|
|
def getcommitdates(ui, fname=None):
|
|
|
|
if fname:
|
|
|
|
with open(fname, "r") as tf:
|
|
|
|
timestamps = tf.readlines()
|
|
|
|
else:
|
|
|
|
timestamps = ui.fin
|
2020-01-29 05:22:50 +03:00
|
|
|
return dict([s.split("=") for s in timestamps])
|
2019-04-29 19:44:40 +03:00
|
|
|
|
|
|
|
|
|
|
|
def getstream(fname):
|
|
|
|
with open(fname, "rb") as f:
|
|
|
|
return util.chunkbuffer([f.read()])
|
|
|
|
|
|
|
|
|
2019-05-25 20:54:40 +03:00
|
|
|
@util.timed(annotation="creating a peer took")
|
2019-04-29 19:44:40 +03:00
|
|
|
def getremote(ui, path):
|
2019-05-25 20:54:40 +03:00
|
|
|
return hg.peer(ui, {}, path)
|
2019-04-29 19:44:40 +03:00
|
|
|
|
|
|
|
|
2019-05-25 20:54:40 +03:00
|
|
|
@util.timed(annotation="single wireproto command took")
|
2019-04-29 19:44:40 +03:00
|
|
|
def runreplay(ui, remote, stream, commitdates, rebasedhead, ontobook):
|
|
|
|
returncode = 0
|
|
|
|
try:
|
|
|
|
reply = remote.unbundlereplay(
|
|
|
|
stream,
|
|
|
|
["force"],
|
|
|
|
remote.url(),
|
|
|
|
replay.ReplayData(commitdates, rebasedhead, ontobook),
|
|
|
|
ui.configbool("sendunbundlereplay", "respondlightly", True),
|
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
returncode = 255
|
|
|
|
finally:
|
|
|
|
if returncode != 0:
|
|
|
|
return returncode
|
|
|
|
|
|
|
|
for part in reply.iterparts():
|
|
|
|
part.read()
|
|
|
|
if part.type.startswith("error:"):
|
|
|
|
returncode = 1
|
|
|
|
ui.warn(_("replay failed: %s\n") % part.type)
|
|
|
|
if "message" in part.params:
|
|
|
|
ui.warn(_("part message: %s\n") % (part.params["message"]))
|
|
|
|
return returncode
|
|
|
|
|
|
|
|
|
|
|
|
def writereport(reportsfile, msg):
|
|
|
|
reportsfile.write(msg)
|
|
|
|
reportsfile.flush()
|
|
|
|
os.fsync(reportsfile.fileno())
|
|
|
|
|
|
|
|
|
2019-05-24 13:37:45 +03:00
|
|
|
@contextlib.contextmanager
|
|
|
|
def capturelogs(ui, remote, logfile):
|
|
|
|
if logfile is None:
|
|
|
|
yield
|
|
|
|
else:
|
|
|
|
uis = [remote.ui, ui]
|
|
|
|
for u in uis:
|
|
|
|
u.pushbuffer(error=True, subproc=True)
|
|
|
|
|
|
|
|
try:
|
|
|
|
yield
|
|
|
|
finally:
|
|
|
|
output = "".join([u.popbuffer() for u in uis])
|
|
|
|
ui.write_err(output)
|
|
|
|
with open(logfile, "w") as f:
|
|
|
|
f.write(output)
|
|
|
|
|
|
|
|
|
2019-04-29 19:44:40 +03:00
|
|
|
@command(
|
commands: define prefixes as aliases
Summary:
At a recent team meeting we've decided to remove the command prefix matching
behavior, as it can be really annoying for the Rust parser (since it needs to
know all the names, but it wants to avoid spinning up Python). It's even more
annoying for subcommand support. FWIW git does not have prefix matching.
This diff adds various aliases to "roughly" keep the command prefix matching
behavior.
The list of aliases are obtained by this script in `hg dbsh`:
def unique(prefix, names):
m = __import__('edenscm.mercurial').mercurial
try:
return m.cmdutil.findcmd(prefix, m.commands.table, False)[0][0] in names
except:
return False
nameslist=sorted([i.replace('^','') for i in m.commands.table])
aliases = {}
for names in nameslist:
names = names.split('|')
for name in names:
if name.startswith('debug'):
continue
for prefix in [name[:i] for i in xrange(1, len(name))]:
if unique(prefix, names):
aliases.setdefault(name, []).append(prefix)
Debug commands, and commands that are rarely used are not changed, including:
'backfillmanifestrevlog': ['backfillm', 'backfillma', 'backfillman', 'backfillmani', 'backfillmanif', 'backfillmanife', 'backfillmanifes', 'backfillmanifest', 'backfillmanifestr', 'backfillmanifestre', 'backfillmanifestrev', 'backfillmanifestrevl', 'backfillmanifestrevlo'],
'backfilltree': ['backfillt', 'backfilltr', 'backfilltre']}
'blackbox': ['blac', 'black', 'blackb', 'blackbo'],
'cachemanifest': ['cac', 'cach', 'cache', 'cachem', 'cachema', 'cacheman', 'cachemani', 'cachemanif', 'cachemanife', 'cachemanifes'],
'chistedit': ['chi', 'chis', 'chist', 'chiste', 'chisted', 'chistedi'],
'clone': ['clon'],
'cloud': ['clou'],
'convert': ['conv', 'conve', 'conver'],
'copy': ['cop'],
'fastannotate': ['fa', 'fas', 'fast', 'fasta', 'fastan', 'fastann', 'fastanno', 'fastannot', 'fastannota', 'fastannotat'],
'fold': ['fol'],
'githelp': ['gi', 'git', 'gith', 'githe', 'githel'],
'histgrep': ['histg', 'histgr', 'histgre'],
'incoming': ['in', 'inc', 'inco', 'incom', 'incomi', 'incomin'],
'isbackedup': ['is', 'isb', 'isba', 'isbac', 'isback', 'isbacke', 'isbacked', 'isbackedu'],
'manifest': ['ma', 'man', 'mani', 'manif', 'manife', 'manifes'],
'outgoing': ['o', 'ou', 'out', 'outg', 'outgo', 'outgoi', 'outgoin'],
'prefetch': ['pref', 'prefe', 'prefet', 'prefetc'],
'prune': ['pru', 'prun'],
'pushbackup': ['pushb', 'pushba', 'pushbac', 'pushback', 'pushbacku'],
'rage': ['ra', 'rag'],
'record': ['recor'],
'recover': ['recov', 'recove'],
'redo': ['red'],
'repack': ['rep', 'repa', 'repac'],
'reset': ['rese'],
'rollback': ['rol', 'roll', 'rollb', 'rollba', 'rollbac'],
'root': ['roo'],
'serve': ['se', 'ser', 'serv'],
'share': ['sha', 'shar'],
'sparse': ['spa', 'spar', 'spars'],
'svn': ['sv'],
'undo': ['und'],
'unshare': ['unsha', 'unshar'],
'verifyremotefilelog': ['verifyr', 'verifyre', 'verifyrem', 'verifyremo', 'verifyremot', 'verifyremote', 'verifyremotef', 'verifyremotefi', 'verifyremotefil', 'verifyremotefile', 'verifyremotefilel', 'verifyremotefilelo'],
Reviewed By: sfilipco
Differential Revision: D17644676
fbshipit-source-id: f60f5e6810279b52f9a4a1e048eeb529a96bd735
2019-10-08 19:44:04 +03:00
|
|
|
"sendunbundlereplaybatch",
|
2019-04-29 19:44:40 +03:00
|
|
|
[
|
|
|
|
("", "path", "", _("hg server remotepath (ssh)"), ""),
|
|
|
|
("", "reports", "", _("a file for unbundereplay progress reports"), ""),
|
|
|
|
],
|
|
|
|
_("[OPTION]..."),
|
|
|
|
norepo=True,
|
|
|
|
)
|
|
|
|
def sendunbundlereplaybatch(ui, **opts):
|
|
|
|
"""Send a batch of unbundlereplay wireproto commands to a given server
|
|
|
|
|
|
|
|
This exists to amortize the costs of `hg.peer` creation over multiple
|
|
|
|
`unbundlereplay` calls.
|
|
|
|
|
|
|
|
Reads `(bundlefile, timestampsfile, ontobook, rebasedhead)` from
|
|
|
|
stdin. See docs of `sendunbundlereplay` for more details.
|
|
|
|
|
|
|
|
Takes the `reports` argument on the command line. After each unbundlereplay
|
|
|
|
command is successfully executed, will write and flush a single line
|
|
|
|
into this file, thus reporting progress. File is truncated at the beginning
|
|
|
|
of this function.
|
|
|
|
|
|
|
|
``sendunbundlereplay.respondlightly`` config option instructs the server
|
|
|
|
to avoid sending large bundle2 parts back.
|
|
|
|
"""
|
|
|
|
if not opts.get("reports"):
|
|
|
|
raise error.Abort("--reports argument is required")
|
|
|
|
path = opts["path"]
|
|
|
|
returncode = 0
|
|
|
|
remote = getremote(ui, path)
|
|
|
|
ui.debug("using %s as a reports file\n" % opts["reports"])
|
|
|
|
with open(opts["reports"], "wb", 0) as reportsfile:
|
|
|
|
counter = 0
|
|
|
|
while True:
|
|
|
|
line = sys.stdin.readline()
|
|
|
|
if line == "":
|
|
|
|
break
|
|
|
|
|
2019-05-24 13:37:45 +03:00
|
|
|
# The newest sync job sends 5 parameters, but older versions send 4.
|
|
|
|
# We default the last parameter to None for compatibility.
|
|
|
|
parts = line.split()
|
|
|
|
if len(parts) == 4:
|
|
|
|
parts.append(None)
|
|
|
|
(bfname, tsfname, ontobook, rebasedhead, logfile) = parts
|
|
|
|
|
2019-04-29 19:44:40 +03:00
|
|
|
rebasedhead = None if rebasedhead == "DELETED" else rebasedhead
|
|
|
|
commitdates = getcommitdates(ui, tsfname)
|
|
|
|
stream = getstream(bfname)
|
2019-05-24 13:37:45 +03:00
|
|
|
|
|
|
|
with capturelogs(ui, remote, logfile):
|
|
|
|
returncode = runreplay(
|
|
|
|
ui, remote, stream, commitdates, rebasedhead, ontobook
|
|
|
|
)
|
|
|
|
|
2019-04-29 19:44:40 +03:00
|
|
|
if returncode != 0:
|
|
|
|
# the word "failed" is an identifier of failure, do not change
|
|
|
|
failure = "unbundle replay batch item #%i failed\n" % counter
|
|
|
|
ui.warn(failure)
|
|
|
|
writereport(reportsfile, failure)
|
|
|
|
break
|
|
|
|
success = "unbundle replay batch item #%i successfully sent\n" % counter
|
|
|
|
ui.warn(success)
|
|
|
|
writereport(reportsfile, success)
|
|
|
|
counter += 1
|
|
|
|
|
|
|
|
return returncode
|
|
|
|
|
|
|
|
|
2019-02-19 12:54:57 +03:00
|
|
|
@command(
|
commands: define prefixes as aliases
Summary:
At a recent team meeting we've decided to remove the command prefix matching
behavior, as it can be really annoying for the Rust parser (since it needs to
know all the names, but it wants to avoid spinning up Python). It's even more
annoying for subcommand support. FWIW git does not have prefix matching.
This diff adds various aliases to "roughly" keep the command prefix matching
behavior.
The list of aliases are obtained by this script in `hg dbsh`:
def unique(prefix, names):
m = __import__('edenscm.mercurial').mercurial
try:
return m.cmdutil.findcmd(prefix, m.commands.table, False)[0][0] in names
except:
return False
nameslist=sorted([i.replace('^','') for i in m.commands.table])
aliases = {}
for names in nameslist:
names = names.split('|')
for name in names:
if name.startswith('debug'):
continue
for prefix in [name[:i] for i in xrange(1, len(name))]:
if unique(prefix, names):
aliases.setdefault(name, []).append(prefix)
Debug commands, and commands that are rarely used are not changed, including:
'backfillmanifestrevlog': ['backfillm', 'backfillma', 'backfillman', 'backfillmani', 'backfillmanif', 'backfillmanife', 'backfillmanifes', 'backfillmanifest', 'backfillmanifestr', 'backfillmanifestre', 'backfillmanifestrev', 'backfillmanifestrevl', 'backfillmanifestrevlo'],
'backfilltree': ['backfillt', 'backfilltr', 'backfilltre']}
'blackbox': ['blac', 'black', 'blackb', 'blackbo'],
'cachemanifest': ['cac', 'cach', 'cache', 'cachem', 'cachema', 'cacheman', 'cachemani', 'cachemanif', 'cachemanife', 'cachemanifes'],
'chistedit': ['chi', 'chis', 'chist', 'chiste', 'chisted', 'chistedi'],
'clone': ['clon'],
'cloud': ['clou'],
'convert': ['conv', 'conve', 'conver'],
'copy': ['cop'],
'fastannotate': ['fa', 'fas', 'fast', 'fasta', 'fastan', 'fastann', 'fastanno', 'fastannot', 'fastannota', 'fastannotat'],
'fold': ['fol'],
'githelp': ['gi', 'git', 'gith', 'githe', 'githel'],
'histgrep': ['histg', 'histgr', 'histgre'],
'incoming': ['in', 'inc', 'inco', 'incom', 'incomi', 'incomin'],
'isbackedup': ['is', 'isb', 'isba', 'isbac', 'isback', 'isbacke', 'isbacked', 'isbackedu'],
'manifest': ['ma', 'man', 'mani', 'manif', 'manife', 'manifes'],
'outgoing': ['o', 'ou', 'out', 'outg', 'outgo', 'outgoi', 'outgoin'],
'prefetch': ['pref', 'prefe', 'prefet', 'prefetc'],
'prune': ['pru', 'prun'],
'pushbackup': ['pushb', 'pushba', 'pushbac', 'pushback', 'pushbacku'],
'rage': ['ra', 'rag'],
'record': ['recor'],
'recover': ['recov', 'recove'],
'redo': ['red'],
'repack': ['rep', 'repa', 'repac'],
'reset': ['rese'],
'rollback': ['rol', 'roll', 'rollb', 'rollba', 'rollbac'],
'root': ['roo'],
'serve': ['se', 'ser', 'serv'],
'share': ['sha', 'shar'],
'sparse': ['spa', 'spar', 'spars'],
'svn': ['sv'],
'undo': ['und'],
'unshare': ['unsha', 'unshar'],
'verifyremotefilelog': ['verifyr', 'verifyre', 'verifyrem', 'verifyremo', 'verifyremot', 'verifyremote', 'verifyremotef', 'verifyremotefi', 'verifyremotefil', 'verifyremotefile', 'verifyremotefilel', 'verifyremotefilelo'],
Reviewed By: sfilipco
Differential Revision: D17644676
fbshipit-source-id: f60f5e6810279b52f9a4a1e048eeb529a96bd735
2019-10-08 19:44:04 +03:00
|
|
|
"sendunbundlereplay",
|
2019-02-19 12:54:57 +03:00
|
|
|
[
|
|
|
|
("", "file", "", _("file to read bundle from"), ""),
|
|
|
|
("", "path", "", _("hg server remotepath (ssh)"), ""),
|
|
|
|
("r", "rebasedhead", "", _("expected rebased head hash"), ""),
|
2019-02-26 00:04:21 +03:00
|
|
|
(
|
|
|
|
"",
|
|
|
|
"deleted",
|
|
|
|
False,
|
|
|
|
_("bookmark was deleted, can't be used with `--rebasedhead`"),
|
|
|
|
),
|
2019-02-19 12:54:57 +03:00
|
|
|
("b", "ontobook", "", _("expected onto bookmark for pushrebase"), ""),
|
|
|
|
],
|
|
|
|
_("[OPTION]..."),
|
|
|
|
norepo=True,
|
|
|
|
)
|
|
|
|
def sendunbundlereplay(ui, **opts):
|
|
|
|
"""Send unbundlereplay wireproto command to a given server
|
|
|
|
|
|
|
|
Takes `rebasedhook` and `ontobook` arguments on the commmand
|
|
|
|
line, and commit dates in stdin. The commit date format is:
|
|
|
|
<commithash>=<hg-parseable-date>
|
2019-02-20 19:32:05 +03:00
|
|
|
|
|
|
|
``sendunbundlereplay.respondlightly`` config option instructs the server
|
|
|
|
to avoid sending large bundle2 parts back.
|
2019-02-19 12:54:57 +03:00
|
|
|
"""
|
|
|
|
fname = opts["file"]
|
|
|
|
path = opts["path"]
|
|
|
|
rebasedhead = opts["rebasedhead"]
|
2019-02-26 00:04:21 +03:00
|
|
|
deleted = opts["deleted"]
|
2019-02-19 12:54:57 +03:00
|
|
|
ontobook = opts["ontobook"]
|
2019-02-26 00:04:21 +03:00
|
|
|
if rebasedhead and deleted:
|
|
|
|
raise error.Abort("can't use `--rebasedhead` and `--deleted`")
|
|
|
|
|
|
|
|
if not (rebasedhead or deleted):
|
|
|
|
raise error.Abort("either `--rebasedhead` or `--deleted` should be used")
|
|
|
|
|
2019-04-29 19:44:40 +03:00
|
|
|
commitdates = getcommitdates(ui)
|
|
|
|
stream = getstream(fname)
|
|
|
|
remote = getremote(ui, path)
|
|
|
|
return runreplay(ui, remote, stream, commitdates, rebasedhead, ontobook)
|