mirror of
https://github.com/facebook/sapling.git
synced 2024-10-12 17:58:27 +03:00
9dc21f8d0b
Summary: D13853115 adds `edenscm/` to `sys.path` and code still uses `import mercurial`. That has nasty problems if both `import mercurial` and `import edenscm.mercurial` are used, because Python would think `mercurial.foo` and `edenscm.mercurial.foo` are different modules so code like `try: ... except mercurial.error.Foo: ...`, or `isinstance(x, mercurial.foo.Bar)` would fail to handle the `edenscm.mercurial` version. There are also some module-level states (ex. `extensions._extensions`) that would cause trouble if they have multiple versions in a single process. Change imports to use the `edenscm` so ideally the `mercurial` is no longer imported at all. Add checks in extensions.py to catch unexpected extensions importing modules from the old (wrong) locations when running tests. Reviewed By: phillco Differential Revision: D13868981 fbshipit-source-id: f4e2513766957fd81d85407994f7521a08e4de48
178 lines
5.7 KiB
Python
178 lines
5.7 KiB
Python
# fold.py - fold multiple revisions to a single one
|
|
#
|
|
# Copyright 2011 Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
|
|
# Logilab SA <contact@logilab.fr>
|
|
# Pierre-Yves David <pierre-yves.david@ens-lyon.org>
|
|
# Patrick Mezard <patrick@mezard.eu>
|
|
# Copyright 2017 Facebook, Inc.
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2 or any later version.
|
|
|
|
from __future__ import absolute_import
|
|
|
|
from edenscm.mercurial import commands, error, hg, node, phases, registrar, scmutil
|
|
from edenscm.mercurial.i18n import _
|
|
|
|
from . import common
|
|
|
|
|
|
cmdtable = {}
|
|
command = registrar.command(cmdtable)
|
|
hex = node.hex
|
|
|
|
|
|
@command(
|
|
"^fold|squash",
|
|
[
|
|
("r", "rev", [], _("revision to fold")),
|
|
("", "exact", None, _("only fold specified revisions")),
|
|
(
|
|
"",
|
|
"from",
|
|
None,
|
|
_("fold linearly from current revision to specified revision"),
|
|
),
|
|
("", "no-rebase", False, _("don't rebase descendants after split")),
|
|
]
|
|
+ (commands.commitopts + commands.commitopts2 + commands.formatteropts),
|
|
_("hg fold [OPTION]... (--from [-r] REV | --exact [-r] REV...)"),
|
|
)
|
|
def fold(ui, repo, *revs, **opts):
|
|
"""combine multiple commits into a single commit
|
|
|
|
With --from, folds all the revisions linearly between the current revision
|
|
and the specified revision.
|
|
|
|
With --exact, folds only the specified revisions while ignoring the revision
|
|
currently checked out. The given revisions must form a linear unbroken
|
|
chain.
|
|
|
|
.. container:: verbose
|
|
|
|
Some examples:
|
|
|
|
- Fold from the current revision to its parent::
|
|
|
|
hg fold --from .^
|
|
|
|
- Fold all draft revisions into the current revision::
|
|
|
|
hg fold --from 'draft()'
|
|
|
|
See :hg:`help phases` for more about draft revisions and
|
|
:hg:`help revsets` for more about the `draft()` keyword
|
|
|
|
- Fold revisions between 3 and 6 into the current revision::
|
|
|
|
hg fold --from 3::6
|
|
|
|
- Fold revisions 3 and 4:
|
|
|
|
hg fold "3 + 4" --exact
|
|
|
|
- Only fold revisions linearly between foo and @::
|
|
|
|
hg fold foo::@ --exact
|
|
"""
|
|
revs = list(revs)
|
|
revs.extend(opts["rev"])
|
|
if not revs:
|
|
raise error.Abort(_("no revisions specified"))
|
|
|
|
revs = scmutil.revrange(repo, revs)
|
|
|
|
if opts.get("no_rebase"):
|
|
torebase = ()
|
|
else:
|
|
torebase = repo.revs("descendants(%ld) - (%ld)", revs, revs)
|
|
|
|
if opts["from"] and opts["exact"]:
|
|
raise error.Abort(_("cannot use both --from and --exact"))
|
|
elif opts["from"]:
|
|
# Try to extend given revision starting from the working directory
|
|
extrevs = repo.revs("(%ld::.) or (.::%ld)", revs, revs)
|
|
discardedrevs = [r for r in revs if r not in extrevs]
|
|
if discardedrevs:
|
|
msg = _("cannot fold non-linear revisions")
|
|
hint = _("given revisions are unrelated to parent of working" " directory")
|
|
raise error.Abort(msg, hint=hint)
|
|
revs = extrevs
|
|
elif opts["exact"]:
|
|
# Nothing to do; "revs" is already set correctly
|
|
pass
|
|
else:
|
|
raise error.Abort(_("must specify either --from or --exact"))
|
|
|
|
if not revs:
|
|
raise error.Abort(
|
|
_("specified revisions evaluate to an empty set"),
|
|
hint=_("use different revision arguments"),
|
|
)
|
|
elif len(revs) == 1:
|
|
ui.write_err(_("single revision specified, nothing to fold\n"))
|
|
return 1
|
|
|
|
with repo.wlock(), repo.lock(), ui.formatter("fold", opts) as fm:
|
|
fm.startitem()
|
|
root, head = _foldcheck(repo, revs)
|
|
|
|
with repo.transaction("fold") as tr:
|
|
commitopts = opts.copy()
|
|
allctx = [repo[r] for r in revs]
|
|
targetphase = max(c.phase() for c in allctx)
|
|
|
|
if commitopts.get("message") or commitopts.get("logfile"):
|
|
commitopts["edit"] = False
|
|
else:
|
|
msgs = ["HG: This is a fold of %d changesets." % len(allctx)]
|
|
msgs += [
|
|
"HG: Commit message of changeset %s.\n\n%s\n"
|
|
% (c.rev(), c.description())
|
|
for c in allctx
|
|
]
|
|
commitopts["message"] = "\n".join(msgs)
|
|
commitopts["edit"] = True
|
|
|
|
newid, unusedvariable = common.rewrite(
|
|
repo,
|
|
root,
|
|
allctx,
|
|
head,
|
|
[root.p1().node(), root.p2().node()],
|
|
commitopts=commitopts,
|
|
mutop="fold",
|
|
)
|
|
phases.retractboundary(repo, tr, targetphase, [newid])
|
|
|
|
replacements = {ctx.node(): (newid,) for ctx in allctx}
|
|
nodechanges = {
|
|
fm.hexfunc(ctx.node()): [fm.hexfunc(newid)] for ctx in allctx
|
|
}
|
|
fm.data(nodechanges=fm.formatdict(nodechanges))
|
|
scmutil.cleanupnodes(repo, replacements, "fold")
|
|
fm.condwrite(not ui.quiet, "count", "%i changesets folded\n", len(revs))
|
|
if repo["."].rev() in revs:
|
|
hg.update(repo, newid)
|
|
|
|
if torebase:
|
|
common.restackonce(ui, repo, repo[newid].rev())
|
|
|
|
|
|
def _foldcheck(repo, revs):
|
|
roots = repo.revs("roots(%ld)", revs)
|
|
if len(roots) > 1:
|
|
raise error.Abort(
|
|
_("cannot fold non-linear revisions " "(multiple roots given)")
|
|
)
|
|
root = repo[roots.first()]
|
|
if root.phase() <= phases.public:
|
|
raise error.Abort(_("cannot fold public revisions"))
|
|
heads = repo.revs("heads(%ld)", revs)
|
|
if len(heads) > 1:
|
|
raise error.Abort(
|
|
_("cannot fold non-linear revisions " "(multiple heads given)")
|
|
)
|
|
head = repo[heads.first()]
|
|
return root, head
|