sapling/hgext/fbamend/metaedit.py
Kostia Balytskyi e75b9fc1b1 fb-hgext: move most of hgext3rd and related tests to core
Summary:
This commit moves most of the stuff in hgext3rd and related tests to
hg-crew/hgext and hg-crew/test respectively.

The things that are not moved are the ones which require some more complex
imports.


Depends on D6675309

Test Plan: - tests are failing at this commit, fixes are in the following commits

Reviewers: #sourcecontrol

Differential Revision: https://phabricator.intern.facebook.com/D6675329
2018-01-09 03:03:59 -08:00

189 lines
6.7 KiB
Python

# metaedit.py - edit changeset metadata
#
# 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 mercurial import (
commands,
error,
hg,
lock as lockmod,
phases,
registrar,
scmutil,
)
from mercurial.i18n import _
from . import (
common,
fold,
)
cmdtable = {}
command = registrar.command(cmdtable)
@command('^metaedit',
[('r', 'rev', [], _("revision to split")),
('', 'fold', False, _("fold specified revisions into one")),
] + commands.commitopts + commands.commitopts2,
_('hg metaedit [OPTION]... [-r] [REV]'))
def metaedit(ui, repo, *revs, **opts):
"""edit commit information
Edits the commit information for the specified revisions. By default, edits
commit information for the working directory parent.
With --fold, also folds multiple revisions into one if necessary. In this
case, the given revisions must form a linear unbroken chain.
.. container:: verbose
Some examples:
- Edit the commit message for the working directory parent::
hg metaedit
- Change the username for the working directory parent::
hg metaedit --user 'New User <new-email@example.com>'
- Combine all draft revisions that are ancestors of foo but not of @ into
one::
hg metaedit --fold 'draft() and only(foo,@)'
See :hg:`help phases` for more about draft revisions, and
:hg:`help revsets` for more about the `draft()` and `only()` keywords.
"""
revs = list(revs)
revs.extend(opts['rev'])
if not revs:
if opts['fold']:
raise error.Abort(_('revisions must be specified with --fold'))
revs = ['.']
wlock = lock = None
try:
wlock = repo.wlock()
lock = repo.lock()
revs = scmutil.revrange(repo, revs)
if opts['fold']:
root, head = fold._foldcheck(repo, revs)
else:
if repo.revs("%ld and public()", revs):
raise error.Abort(_('cannot edit commit information for public '
'revisions'))
root = head = repo[revs.first()]
wctx = repo[None]
p1 = wctx.p1()
tr = repo.transaction('metaedit')
newp1 = None
try:
commitopts = opts.copy()
allctx = [repo[r] for r in revs]
if commitopts.get('message') or commitopts.get('logfile'):
commitopts['edit'] = False
else:
if opts['fold']:
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]
else:
msgs = [head.description()]
commitopts['message'] = "\n".join(msgs)
commitopts['edit'] = True
if root == head:
# fast path: use metarewrite
replacemap = {}
# we need topological order
allctx = sorted(allctx, key=lambda c: c.rev())
# all descendats that can be safely rewritten
newunstable = common.newunstable(repo, revs)
newunstablectx = sorted([repo[r] for r in newunstable],
key=lambda c: c.rev())
def _rewritesingle(c, _commitopts):
if _commitopts.get('edit', False):
_commitopts['message'] = \
"HG: Commit message of changeset %s\n%s" %\
(str(c), c.description())
bases = [
replacemap.get(c.p1().node(), c.p1().node()),
replacemap.get(c.p2().node(), c.p2().node()),
]
newid, created = common.metarewrite(repo, c, bases,
commitopts=_commitopts)
if created:
replacemap[c.node()] = newid
for c in allctx:
_rewritesingle(c, commitopts)
if _histediting(repo):
ui.note(_('during histedit, the descendants of '
'the edited commit weren\'t auto-rebased\n'))
else:
for c in newunstablectx:
_rewritesingle(c,
{'date': commitopts.get('date') or None})
if p1.node() in replacemap:
repo.setparents(replacemap[p1.node()])
if len(replacemap) > 0:
mapping = dict(map(lambda oldnew: (oldnew[0], [oldnew[1]]),
replacemap.iteritems()))
scmutil.cleanupnodes(repo, mapping, 'metaedit')
# TODO: set poroper phase boundaries (affects secret
# phase only)
else:
ui.status(_("nothing changed\n"))
return 1
else:
# slow path: create a new commit
targetphase = max(c.phase() for c in allctx)
# TODO: if the author and message are the same, don't create a
# new hash. Right now we create a new hash because the date can
# be different.
newid, created = common.rewrite(
repo, root, allctx, head,
[root.p1().node(), root.p2().node()],
commitopts=commitopts)
if created:
if p1.rev() in revs:
newp1 = newid
phases.retractboundary(repo, tr, targetphase, [newid])
mapping = dict(
[(repo[rev].node(), [newid]) for rev in revs])
scmutil.cleanupnodes(repo, mapping, 'metaedit')
else:
ui.status(_("nothing changed\n"))
return 1
tr.close()
finally:
tr.release()
if opts['fold']:
ui.status(_('%i changesets folded\n') % len(revs))
if newp1 is not None:
hg.update(repo, newp1)
finally:
lockmod.release(lock, wlock)
def _histediting(repo):
return repo.vfs.exists('histedit-state')