diff hgext/evolve.py @ 1683:4fd0db2f6d84

commands: introduce a new command to edit commit metadata This patch introduces metaedit, a command to metadata of a set of revisions without updating the working copy. This is particularly relevant for repositories where changing the parent of the working copy is time-consuming. We could add more stack manipulation operations to metaedit in the future.
author Siddharth Agarwal <sid0@fb.com>
date Mon, 25 Apr 2016 16:24:42 -0700
parents 40d7b0c4abb1
children 474db2d60202
line wrap: on
line diff
--- a/hgext/evolve.py	Mon Apr 25 16:24:42 2016 -0700
+++ b/hgext/evolve.py	Mon Apr 25 16:24:42 2016 -0700
@@ -3158,6 +3158,101 @@
     finally:
         lockmod.release(lock, wlock)
 
+@command('^metaedit',
+         [('r', 'rev', [], _("revision to edit")),
+         ] + commitopts + commitopts2,
+         _('hg metaedit [OPTION]... [-r] [REV]'))
+def metaedit(ui, repo, *revs, **opts):
+    """edit commit information
+
+    Edits the commit information for the specified revision. By default, edits
+    commit information for the working directory parent.
+
+    .. 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>'
+
+    """
+    revs = list(revs)
+    revs.extend(opts['rev'])
+    if not revs:
+        revs = ['.']
+
+    wlock = lock = None
+    try:
+        wlock = repo.wlock()
+        lock = repo.lock()
+
+        revs = scmutil.revrange(repo, revs)
+        if len(revs) > 1:
+            # TODO: handle multiple revisions. This is somewhat tricky because
+            # if we want to edit a series of commits:
+            #
+            #   a ---- b ---- c
+            #
+            # we need to rewrite a first, then directly rewrite b on top of the
+            # new a, then rewrite c on top of the new b. So we need to handle
+            # revisions in topological order.
+            raise error.Abort(_('editing multiple revisions is not '
+                                'currently supported'))
+
+        newunstable = _disallowednewunstable(repo, revs)
+        if newunstable:
+            raise error.Abort(
+                _('cannot edit commit information in the middle of a stack'),
+                hint=_('%s will be affected') % repo[newunstable.first()])
+        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]
+            targetphase = max(c.phase() for c in allctx)
+
+            if commitopts.get('message') or commitopts.get('logfile'):
+                commitopts['edit'] = False
+            else:
+                msgs = [head.description()]
+                commitopts['message'] =  "\n".join(msgs)
+                commitopts['edit'] = True
+
+            # 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 = 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])
+                obsolete.createmarkers(repo, [(ctx, (repo[newid],))
+                                              for ctx in allctx])
+            else:
+                ui.status(_("nothing changed\n"))
+            tr.close()
+        finally:
+            tr.release()
+
+        if newp1 is not None:
+            hg.update(repo, newp1)
+    finally:
+        lockmod.release(lock, wlock)
+
 def _foldcheck(repo, revs):
     roots = repo.revs('roots(%ld)', revs)
     if len(roots) > 1: