2017-06-16 23:59:12 +03:00
|
|
|
# split.py - split a changeset into smaller parts
|
|
|
|
#
|
|
|
|
# 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
|
|
|
|
|
2019-01-30 03:25:33 +03:00
|
|
|
from edenscm.mercurial import (
|
2017-06-16 23:59:12 +03:00
|
|
|
bookmarks,
|
|
|
|
cmdutil,
|
|
|
|
commands,
|
|
|
|
error,
|
|
|
|
hg,
|
2018-10-01 16:15:01 +03:00
|
|
|
hintutil,
|
2017-06-16 23:59:12 +03:00
|
|
|
lock as lockmod,
|
2018-12-13 21:41:53 +03:00
|
|
|
mutation,
|
2017-06-16 23:59:12 +03:00
|
|
|
obsolete,
|
|
|
|
registrar,
|
|
|
|
scmutil,
|
|
|
|
)
|
2019-01-30 03:25:33 +03:00
|
|
|
from edenscm.mercurial.i18n import _
|
2017-06-16 23:59:12 +03:00
|
|
|
|
|
|
|
from . import common
|
2018-10-01 16:15:01 +03:00
|
|
|
from ..extlib.phabricator import diffprops
|
2017-06-16 23:59:12 +03:00
|
|
|
|
2018-06-05 06:59:57 +03:00
|
|
|
|
2017-06-16 23:59:12 +03:00
|
|
|
cmdtable = {}
|
|
|
|
command = registrar.command(cmdtable)
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
|
|
|
@command(
|
|
|
|
"^split",
|
|
|
|
[
|
|
|
|
("r", "rev", [], _("revision to split")),
|
|
|
|
("", "no-rebase", False, _("don't rebase descendants after split")),
|
|
|
|
]
|
|
|
|
+ commands.commitopts
|
|
|
|
+ commands.commitopts2,
|
2018-10-19 16:49:03 +03:00
|
|
|
_("[OPTION]... [[-r] REV]"),
|
2018-05-30 12:16:33 +03:00
|
|
|
)
|
2017-06-16 23:59:12 +03:00
|
|
|
def split(ui, repo, *revs, **opts):
|
|
|
|
"""split a changeset into smaller changesets
|
|
|
|
|
2018-06-05 06:59:57 +03:00
|
|
|
Prompt for hunks to be selected until exhausted. Each selection of hunks
|
|
|
|
will form a separate changeset, in order from parent to child: the first
|
|
|
|
selection will form the first changeset, the second selection will form
|
|
|
|
the second changeset, and so on.
|
2017-06-16 23:59:12 +03:00
|
|
|
|
2018-06-05 06:59:57 +03:00
|
|
|
Operates on the current revision by default. Use --rev to split a given
|
|
|
|
changeset instead.
|
2017-06-16 23:59:12 +03:00
|
|
|
"""
|
|
|
|
tr = wlock = lock = None
|
|
|
|
newcommits = []
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
revarg = (list(revs) + opts.get("rev")) or ["."]
|
2017-06-16 23:59:12 +03:00
|
|
|
if len(revarg) != 1:
|
|
|
|
msg = _("more than one revset is given")
|
|
|
|
hnt = _("use either `hg split <rs>` or `hg split --rev <rs>`, not both")
|
|
|
|
raise error.Abort(msg, hint=hnt)
|
|
|
|
|
|
|
|
rev = scmutil.revsingle(repo, revarg[0])
|
2018-05-30 12:16:33 +03:00
|
|
|
if opts.get("no_rebase"):
|
2017-06-16 23:59:12 +03:00
|
|
|
torebase = ()
|
|
|
|
else:
|
2018-05-30 12:16:33 +03:00
|
|
|
torebase = repo.revs("descendants(%d) - (%d)", rev, rev)
|
2017-06-16 23:59:12 +03:00
|
|
|
try:
|
|
|
|
wlock = repo.wlock()
|
|
|
|
lock = repo.lock()
|
|
|
|
cmdutil.bailifchanged(repo)
|
2017-08-23 18:47:31 +03:00
|
|
|
if torebase:
|
|
|
|
cmdutil.checkunfinished(repo)
|
2018-05-30 12:16:33 +03:00
|
|
|
tr = repo.transaction("split")
|
2017-06-16 23:59:12 +03:00
|
|
|
ctx = repo[rev]
|
|
|
|
r = ctx.rev()
|
2018-05-30 12:16:33 +03:00
|
|
|
disallowunstable = not obsolete.isenabled(repo, obsolete.allowunstableopt)
|
2017-06-16 23:59:12 +03:00
|
|
|
if disallowunstable:
|
|
|
|
# XXX We should check head revs
|
|
|
|
if repo.revs("(%d::) - %d", rev, rev):
|
|
|
|
raise error.Abort(_("cannot split commit: %s not a head") % ctx)
|
|
|
|
|
|
|
|
if len(ctx.parents()) > 1:
|
|
|
|
raise error.Abort(_("cannot split merge commits"))
|
|
|
|
prev = ctx.p1()
|
|
|
|
bmupdate = common.bookmarksupdater(repo, ctx.node(), tr)
|
|
|
|
bookactive = repo._activebookmark
|
|
|
|
if bookactive is not None:
|
|
|
|
repo.ui.status(_("(leaving bookmark %s)\n") % repo._activebookmark)
|
|
|
|
bookmarks.deactivate(repo)
|
|
|
|
hg.update(repo, prev)
|
|
|
|
|
|
|
|
commands.revert(ui, repo, rev=r, all=True)
|
|
|
|
|
|
|
|
def haschanges():
|
|
|
|
modified, added, removed, deleted = repo.status()[:4]
|
|
|
|
return modified or added or removed or deleted
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2018-12-13 21:41:53 +03:00
|
|
|
# We need to detect the case where the user selects all remaining
|
|
|
|
# changes, as that will end the split. That's the commit we want to
|
|
|
|
# mark as the result of the split. To do this, wrap the recordfilter
|
|
|
|
# function and compare the output to see if it contains all the
|
|
|
|
# originalchunks.
|
|
|
|
shouldrecordmutation = [False]
|
|
|
|
|
|
|
|
def commitextra(extra):
|
|
|
|
if shouldrecordmutation[0]:
|
|
|
|
mutation.record(
|
|
|
|
repo,
|
|
|
|
extra,
|
|
|
|
[ctx.node()],
|
|
|
|
"split",
|
|
|
|
splitting=[c.node() for c in newcommits],
|
|
|
|
)
|
|
|
|
|
|
|
|
def recordfilter(ui, originalchunks, operation=None):
|
|
|
|
chunks, newopts = cmdutil.recordfilter(ui, originalchunks, operation)
|
|
|
|
if cmdutil.comparechunks(chunks, originalchunks):
|
|
|
|
shouldrecordmutation[0] = True
|
|
|
|
return chunks, newopts
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
msg = (
|
|
|
|
"HG: This is the original pre-split commit message. "
|
|
|
|
"Edit it as appropriate.\n\n"
|
|
|
|
)
|
2017-06-16 23:59:12 +03:00
|
|
|
msg += ctx.description()
|
2018-05-30 12:16:33 +03:00
|
|
|
opts["message"] = msg
|
|
|
|
opts["edit"] = True
|
2018-12-13 21:41:53 +03:00
|
|
|
opts["_commitextrafunc"] = commitextra
|
2017-06-16 23:59:12 +03:00
|
|
|
while haschanges():
|
|
|
|
pats = ()
|
2018-05-30 12:16:33 +03:00
|
|
|
cmdutil.dorecord(
|
2018-12-13 21:41:53 +03:00
|
|
|
ui, repo, commands.commit, "commit", False, recordfilter, *pats, **opts
|
2018-05-30 12:16:33 +03:00
|
|
|
)
|
2017-06-16 23:59:12 +03:00
|
|
|
# TODO: Does no seem like the best way to do this
|
|
|
|
# We should make dorecord return the newly created commit
|
2018-05-30 12:16:33 +03:00
|
|
|
newcommits.append(repo["."])
|
2017-06-16 23:59:12 +03:00
|
|
|
if haschanges():
|
2018-05-30 12:16:33 +03:00
|
|
|
if ui.prompt("Done splitting? [yN]", default="n") == "y":
|
2018-12-13 21:41:53 +03:00
|
|
|
shouldrecordmutation[0] = True
|
2017-06-16 23:59:12 +03:00
|
|
|
commands.commit(ui, repo, **opts)
|
2018-05-30 12:16:33 +03:00
|
|
|
newcommits.append(repo["."])
|
2017-06-16 23:59:12 +03:00
|
|
|
break
|
|
|
|
else:
|
|
|
|
ui.status(_("no more change to split\n"))
|
|
|
|
|
|
|
|
if newcommits:
|
2018-10-01 16:15:01 +03:00
|
|
|
phabdiffs = {}
|
|
|
|
for c in newcommits:
|
|
|
|
phabdiff = diffprops.parserevfromcommitmsg(repo[c].description())
|
|
|
|
if phabdiff:
|
|
|
|
phabdiffs.setdefault(phabdiff, []).append(c)
|
|
|
|
if any(len(commits) > 1 for commits in phabdiffs.values()):
|
|
|
|
hintutil.trigger(
|
|
|
|
"split-phabricator", ui.config("split", "phabricatoradvice")
|
|
|
|
)
|
|
|
|
|
2017-06-16 23:59:12 +03:00
|
|
|
tip = repo[newcommits[-1]]
|
|
|
|
bmupdate(tip.node())
|
|
|
|
if bookactive is not None:
|
|
|
|
bookmarks.activate(repo, bookactive)
|
2018-05-30 12:16:33 +03:00
|
|
|
obsolete.createmarkers(repo, [(repo[r], newcommits)], operation="split")
|
2017-06-16 23:59:12 +03:00
|
|
|
|
|
|
|
if torebase:
|
2018-10-03 23:11:06 +03:00
|
|
|
common.restackonce(ui, repo, tip.rev())
|
2017-06-16 23:59:12 +03:00
|
|
|
tr.close()
|
|
|
|
finally:
|
|
|
|
lockmod.release(tr, lock, wlock)
|