hgext/push-to-try/__init__.py (88 lines of code) (raw):
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import json
from mercurial import (
commands,
context,
registrar,
)
from mercurial.i18n import _
OUR_DIR = os.path.normpath(os.path.dirname(__file__)).encode("ascii")
with open(os.path.join(OUR_DIR, b"..", b"bootstrap.py")) as f:
exec(f.read())
from mozhg.rewrite import preservefilectx
cmdtable = {}
command = registrar.command(cmdtable)
configtable = {}
configitem = registrar.configitem(configtable)
configitem(b"push-to-try", b"nodate", default=False)
testedwith = b"4.6 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5"
minimumhgversion = b"4.6"
@command(
b"push-to-try",
[
(b"m", b"message", b"", b"commit message to use", b"MESSAGE"),
(b"s", b"server", b"", b"push destination", b"URL"),
],
b"-m MESSAGE -s URL",
)
def push_to_try(ui, repo, server, message=None):
nodate = ui.configbool(b"push-to-try", b"nodate")
if not server:
if b"try" in ui.paths:
server = b"try"
else:
server = b"ssh://hg.mozilla.org/try"
if not message:
ui.status(b"STOP! A commit message is required.\n")
return
cctx = context.workingctx(repo)
if b"try_task_config.json" not in cctx and b"try:" not in message:
ui.status(
b"STOP! Either try_task_config.json must be added or the commit "
b"message must contain try syntax.\n"
)
return
if b"try_task_config.json" in cctx:
data = repo.wvfs.tryread(b"try_task_config.json")
try:
# data could be an empty string if tryread failed, which will
# produce a ValueError here.
data = json.loads(data)
except ValueError as e:
ui.status(b"Error reading try_task_config.json: could not decode as JSON\n")
return
# Invent a temporary commit with our message.
ui.status(b"Creating temporary commit for remote...\n")
status = repo.status()
if status.modified + status.added + status.removed:
# TODO: Achieve this by re-using the status call above to avoid the
# cost of running it twice.
commands.status(ui, repo)
preserve_ctx = preservefilectx(cctx)
def mk_memfilectx(repo, memctx, path):
if path not in status.removed:
return preserve_ctx(repo, memctx, path)
return None
mctx = context.memctx(
repo,
repo.dirstate.parents(),
message,
cctx.files(),
mk_memfilectx,
date=b"0 0" if nodate else None,
)
# These messages are expected when we abort our transaction, but aren't
# helpful to a user and may be misleading so we surpress them here.
filtered_phrases = {_(b"transaction abort!\n"), _(b"rollback completed\n")}
def filtered_warn(*msgs, **opts):
if msgs:
filtered = [m for m in msgs if m not in filtered_phrases]
if filtered:
ui.warn(*filtered, **opts)
lock = tr = None
try:
lock = repo.lock()
tr = repo.transaction(b"push-to-try", report=filtered_warn)
m = mctx.commit()
# Push to try.
commands.push(ui, repo, server, force=True, rev=[repo[m].rev()])
ui.status(b"push complete\n")
# And rollback to the previous state.
tr.abort()
finally:
if tr:
tr.release()
if lock:
lock.release()
ui.status(b"temporary commit removed, repository restored\n")