changeset 1697:093c445fd86a mercurial-3.6

merge with stable through 3.7 branch dropped the topic test and updated output with some minor difference Hg: Enter commit message. Lines beginning with 'HG:' are removed.
author Pierre-Yves David <pierre-yves.david@ens-lyon.org>
date Thu, 05 May 2016 23:51:35 +0200
parents 61dd08f4dc62 (current diff) 898dfca94433 (diff)
children dd6f090b7342 f89c429345e9
files tests/test-amend.t tests/test-evolve-topic.t tests/test-evolve.t tests/test-unstable.t
diffstat 28 files changed, 686 insertions(+), 129 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Wed Feb 10 23:44:00 2016 +0000
+++ b/.hgignore	Thu May 05 23:51:35 2016 +0200
@@ -1,7 +1,5 @@
 syntax: re
 /figures/[^/]+\.png$
-^docs/build/
-^docs/html/
 ^html/
 \.pyc$
 ~$
@@ -14,3 +12,4 @@
 ^MANIFEST$
 ^docs/tutorials/.*\.rst$
 \.ico$
+tests/\.testtimes
--- a/.hgtags	Wed Feb 10 23:44:00 2016 +0000
+++ b/.hgtags	Thu May 05 23:51:35 2016 +0200
@@ -38,3 +38,4 @@
 00026533ff9f52733a45df008e3d56a5d3a8e76a 5.2.0
 44a9dcb3fefcf8281ebe4e359e7dbb637512cf7f 5.2.0
 c15d6168412f175568dac89e6ee1cd8434fef906 5.2.1
+bd59cc2ee2039c370a0343f683488cde2a106565 5.3.0
--- a/README	Wed Feb 10 23:44:00 2016 +0000
+++ b/README	Thu May 05 23:51:35 2016 +0200
@@ -16,20 +16,20 @@
 You can enable it by adding the line below to the ``extensions``
 section of your hgrc::
 
-    evolve = PATH/TO/mutable-history/hgext/evolve.py
+    evolve = PATH/TO/evolve-main/hgext/evolve.py
 
 We recommend reading the documentation first. An online version is
 available here:
 
-    http://evolution.experimentalworks.net/doc/
+    https://www.mercurial-scm.org/doc/evolution/
 
 Or see the ``doc/`` directory for a local copy.
 
 Contribute
 ==========
 
-Bugs are to be reported on the mercurial's bug tracker: http://bz.mercurial-scm.com/
-Use the the "evolution" component.
+Bugs are to be reported on the mercurial's bug tracker (component: evolution):
+https://bz.mercurial-scm.org/buglist.cgi?component=evolution&query_format=advanced&resolution=---
 
 Please use the patchbomb extension to send email to mercurial devel. Please
 make sure to use the evolve-ext flag when doing so. You can use a command like
@@ -56,19 +56,33 @@
 Changelog
 =========
 
-5.3.0 --
+5.4.0 -- 
+
+- Some collaboration with the topic experimental extensions,
+  - hg evolve --all with consider all troubles in your current topic,
+  - preserve 'topic' during evolve,
+  - 'next' and 'prev' restrict themself to the current topic by default,
+- remove the dangerous 'kill' alias for 'prune' (because 'hg kill -1' without
+the leading 'hg' will give you an hardtime)
+- during 'hg evolve' skip unsupported merge instead of aborting
+- various documentation fix and update
+- hg summary now suggest 'hg evolve --continue when appropriate`
+- compatibility with Mercurial 3.8 'hgext' namespace package.
+- small improvement to the `hg split` instruction
+- add a 'metaedit' command to rewrite changeset meta data.
+
+5.3.0 -- 2016-02-11
 
 - split: add a new command to split changesets,
 - tests: drop our copy of 'run-tests.py' use core one instead,
 - bookmark: do all bookmark movement within a transaction.
 - evolve: compatibility with Mercurial 3.7
-- evolve: support merge with a single obsolete parent.
+- evolve: support merge with a single obsolete parent (hg-3.7+ only)
 - evolve: prevent added file to be marked as unknown if evolve fails (issue4966)
 - evolve: stop relying on graftstate file for save evolve state
           (for `hg evolve --continue`)
-
-5.2.2 --
-
+- evolve: fix divergence resolution when it result in an empty commit
+          (issue4950) (hg-3.5+ only)
 - no longer lock the repository for `hg parents` (issue4895)
 - updated help for the `evolve` command
 
@@ -95,7 +109,7 @@
           in now in `--all --any`.
 - evolve: add a 'experimental.evolutioncommands' for fine grained commands
           enabling
-- next/prev: requires `--merge` to move with uncommited changes
+- next/prev: requires `--merge` to move with uncommitted changes
 - next: significantly reword error messages
 - next: add a --evolve flag to evolve aspiring children when on a head
 
--- a/contrib/nopushpublish.py	Wed Feb 10 23:44:00 2016 +0000
+++ b/contrib/nopushpublish.py	Thu May 05 23:51:35 2016 +0200
@@ -27,7 +27,7 @@
 
     ret = orig(repo, remote, outgoing, *args)
     if npublish:
-        raise util.Abort("Publishing push forbiden",
+        raise util.Abort("Publishing push forbidden",
                          hint="Use `hg phase -p <rev>` to manually publish them")
 
     return ret
--- a/debian/control	Wed Feb 10 23:44:00 2016 +0000
+++ b/debian/control	Thu May 05 23:51:35 2016 +0200
@@ -15,7 +15,7 @@
  librsvg2-bin,
  wget,
 Python-Version: >= 2.6
-Homepage: https://bitbucket.org/marmoute/mutable-history
+Homepage: https://www.mercurial-scm.org/doc/evolution/
 
 Package: mercurial-evolve
 Architecture: all
--- a/debian/copyright	Wed Feb 10 23:44:00 2016 +0000
+++ b/debian/copyright	Thu May 05 23:51:35 2016 +0200
@@ -1,5 +1,5 @@
 This software was downloaded from
-https://bitbucket.org/marmoute/mutable-history
+http://hg.netv6.net/evolve-main/
 
 Copyright 2011 Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
                Logilab SA        <contact@logilab.fr>
--- a/docs/evolve-faq.rst	Wed Feb 10 23:44:00 2016 +0000
+++ b/docs/evolve-faq.rst	Thu May 05 23:51:35 2016 +0200
@@ -110,7 +110,7 @@
   $ hg record
   # commit the second part
   $ hg commit
-  # informs mercurial of what appened
+  # informs mercurial of what happened
   # current changeset (.) and previous one (.^) replace A (42)
   $ hg prune --new . --new .^ 42
 
--- a/docs/evolve-good-practice.rst	Wed Feb 10 23:44:00 2016 +0000
+++ b/docs/evolve-good-practice.rst	Thu May 05 23:51:35 2016 +0200
@@ -27,7 +27,7 @@
 There is no descent conflict detection and handling right now.
 Rewriting other people's changesets guarantees that you will get
 conflicts. Communicate with your fellow developers before trying to
-touch other people's work (which is a good pratice in any case).
+touch other people's work (which is a good practice in any case).
 
 Using multiple branches will help you to achieve this goal.
 
--- a/docs/from-mq.rst	Wed Feb 10 23:44:00 2016 +0000
+++ b/docs/from-mq.rst	Thu May 05 23:51:35 2016 +0200
@@ -85,7 +85,7 @@
 ..   $ hg record -m 'feature A'
 ..   # oups, I forgot some stuff
 ..   $ hg record babar.py
-..   $ hg amend -c .^ # .^ refer to "working directoy parent, here 'feature A'
+..   $ hg amend -c .^ # .^ refer to "working directory parent, here 'feature A'
 
 .. note: refresh is an alias for amend
 
--- a/docs/index.rst	Wed Feb 10 23:44:00 2016 +0000
+++ b/docs/index.rst	Thu May 05 23:51:35 2016 +0200
@@ -113,7 +113,7 @@
   #. Clone the ``evolve`` repository::
 
        cd ~/src
-       hg clone https://bitbucket.org/marmoute/mutable-history
+       hg clone http://hg.netv6.net/evolve-main
 
   #. Configure the extension, either locally ::
 
@@ -125,7 +125,7 @@
 
      Then add ::
 
-       evolve=~/src/mutable-history/hgext/evolve.py
+       evolve=~/src/evolve-main/hgext/evolve.py
 
      in the ``[extensions]`` section (adding the section if necessary). Use
      the directory that you actually cloned to, of course.
--- a/docs/obs-terms.rst	Wed Feb 10 23:44:00 2016 +0000
+++ b/docs/obs-terms.rst	Thu May 05 23:51:35 2016 +0200
@@ -20,7 +20,7 @@
 - multiple *successors*: the *precursor* were splits in multiple
   changesets.
 
-.. The *precursors* and *successors* terms can be used on changeset directy:
+.. The *precursors* and *successors* terms can be used on changeset directly:
 
 .. :precursors: of a changeset `A` are changesets used as *precursors* by
 ..              obsolete marker using changeset `A` as *successors*
@@ -84,8 +84,8 @@
 |                     |                          | *obsolete* with at least    |
 |                     |                          | one non-obsolete descendant |
 |                     |                          |                             |
-|                     |                          | Thoses descendants prevent  |
-|                     |                          | properties of extincts      |
+|                     |                          | Those descendants prevent   |
+|                     |                          | properties of extinct       |
 |                     |                          | changesets to apply. But    |
 |                     |                          | they will refuse to be      |
 |                     |                          | pushed without --force.     |
@@ -169,7 +169,7 @@
 | Rewriting operation refuse to work on immutable changeset.                   |
 |                                                                              |
 | Obsolete markers that refer an immutable changeset as precursors have        |
-| no effect on the precussors but may have effect on the successors.           |
+| no effect on the precursors but may have effect on the successors.           |
 |                                                                              |
 | When a *mutable* changeset becomes *immutable* (changing its phase from draft|
 | to public) it is just *immutable* and loose any property of it's former      |
--- a/docs/sharing.rst	Wed Feb 10 23:44:00 2016 +0000
+++ b/docs/sharing.rst	Thu May 05 23:51:35 2016 +0200
@@ -102,7 +102,7 @@
   publish = false
 
   [extensions]
-  evolve = /path/to/mutable-history/hgext/evolve.py
+  evolve = /path/to/evolve-main/hgext/evolve.py
 
 Then edit the configuration for ``dev-repo``::
 
@@ -111,7 +111,7 @@
 and add ::
 
   [extensions]
-  evolve = /path/to/mutable-history/hgext/evolve.py
+  evolve = /path/to/evolve-main/hgext/evolve.py
 
 Keep in mind that in real life, these repositories would probably be
 on separate computers, so you'd have to login to each one to configure
@@ -331,7 +331,7 @@
 and add ::
 
   [extensions]
-  evolve = /path/to/mutable-history/hgext/evolve.py
+  evolve = /path/to/evolve-main/hgext/evolve.py
 
 Then edit Bob's repository configuration::
 
@@ -545,7 +545,7 @@
 
   [extensions]
   rebase =
-  evolve = /path/to/mutable-history/hgext/evolve.py
+  evolve = /path/to/evolve-main/hgext/evolve.py
 
   [phases]
   publish = false
--- a/hgext/__init__.py	Wed Feb 10 23:44:00 2016 +0000
+++ b/hgext/__init__.py	Thu May 05 23:51:35 2016 +0200
@@ -1,1 +1,4 @@
-# Copyright 2011 Logilab SA <contact@logilab.fr>
+from __future__ import absolute_import
+import pkgutil
+__path__ = pkgutil.extend_path(__path__, __name__)
+
--- a/hgext/evolve.py	Wed Feb 10 23:44:00 2016 +0000
+++ b/hgext/evolve.py	Thu May 05 23:51:35 2016 +0200
@@ -9,18 +9,18 @@
 '''extends Mercurial feature related to Changeset Evolution
 
 This extension provides several commands to mutate history and deal with
-issues it may raise.
+resulting issues.
 
 It also:
 
-    - enables the "Changeset Obsolescence" feature of mercurial,
+    - enables the "Changeset Obsolescence" feature of Mercurial,
     - alters core commands and extensions that rewrite history to use
       this feature,
     - improves some aspect of the early implementation in Mercurial core
 '''
 
-__version__ = '5.2.1'
-testedwith = '3.4.3 3.5.2 3.6'
+__version__ = '5.3.0'
+testedwith = '3.4.3 3.5.2 3.6.2 3.7'
 buglink = 'http://bz.selenic.com/'
 
 
@@ -61,8 +61,12 @@
 
 import sys, os
 import random
-from StringIO import StringIO
-import struct
+try:
+    import StringIO as io
+    StringIO = io.StringIO
+except ImportError:
+    import io
+    StringIO = io.StringIO
 import re
 import collections
 import socket
@@ -231,7 +235,7 @@
             c(ui)
 
     def final_reposetup(self, ui, repo):
-        """Method to be used as a the extension reposetup
+        """Method to be used as the extension reposetup
 
         The following operations belong here:
 
@@ -324,7 +328,7 @@
         will be applied in the extension commandtable. This argument must be a
         string that will be searched using `extension.find` if not found and
         Abort error is raised. If the wrapping applies to an extension, it is
-        installed during `extsetup`
+        installed during `extsetup`.
 
         example::
 
@@ -398,7 +402,7 @@
     evolveopts = ui.configlist('experimental', 'evolution')
     if not evolveopts:
         evolveopts = ['all']
-        ui.setconfig('experimental', 'evolution', evolveopts)
+        ui.setconfig('experimental', 'evolution', evolveopts, 'evolve')
 
 @eh.uisetup
 def _configurecmdoptions(ui):
@@ -459,7 +463,7 @@
 # - Function to create markers
 # - useful alias pstatus and pdiff (should probably go in evolve)
 # - "troubles" method on changectx
-# - function to travel throught the obsolescence graph
+# - function to travel through the obsolescence graph
 # - function to find useful changeset to stabilize
 
 
@@ -468,22 +472,26 @@
 @eh.uisetup
 def _installalias(ui):
     if ui.config('alias', 'pstatus', None) is None:
-        ui.setconfig('alias', 'pstatus', 'status --rev .^')
+        ui.setconfig('alias', 'pstatus', 'status --rev .^', 'evolve')
     if ui.config('alias', 'pdiff', None) is None:
-        ui.setconfig('alias', 'pdiff', 'diff --rev .^')
+        ui.setconfig('alias', 'pdiff', 'diff --rev .^', 'evolve')
     if ui.config('alias', 'olog', None) is None:
-        ui.setconfig('alias', 'olog', "log -r 'precursors(.)' --hidden")
+        ui.setconfig('alias', 'olog', "log -r 'precursors(.)' --hidden",
+                     'evolve')
     if ui.config('alias', 'odiff', None) is None:
         ui.setconfig('alias', 'odiff',
-            "diff --hidden --rev 'limit(precursors(.),1)' --rev .")
+            "diff --hidden --rev 'limit(precursors(.),1)' --rev .",
+            'evolve')
     if ui.config('alias', 'grab', None) is None:
         if os.name == 'nt':
             ui.setconfig('alias', 'grab',
                 "! " + util.hgexecutable() + " rebase --dest . --rev $@ && "
-                 + util.hgexecutable() + " up tip")
+                 + util.hgexecutable() + " up tip",
+                         'evolve')
         else:
             ui.setconfig('alias', 'grab',
-                "! $HG rebase --dest . --rev $@ && $HG up tip")
+                "! $HG rebase --dest . --rev $@ && $HG up tip",
+                         'evolve')
 
 
 ### Troubled revset symbol
@@ -778,6 +786,10 @@
     else:
         # In 3.6.2, summary in core gained this feature, no need to display it
         pass
+    state = _evolvestateread(repo)
+    if state is not None:
+        # i18n: column positioning for "hg summary"
+        ui.write(_('evolve: (evolve --continue)\n'))
 
 @eh.extsetup
 def obssummarysetup(ui):
@@ -828,7 +840,7 @@
         wlock = repo.wlock()
         lock = repo.lock()
         tr = repo.transaction('rewrite')
-        if len(old.parents()) > 1: #XXX remove this unecessary limitation.
+        if len(old.parents()) > 1: #XXX remove this unnecessary limitation.
             raise error.Abort(_('cannot amend merge changesets'))
         base = old.p1()
         updatebookmarks = _bookmarksupdater(repo, old.node(), tr)
@@ -1079,7 +1091,7 @@
     except error.UnknownCommand:
         # Commands may be disabled
         return
-    for alias, e in cmdtable.iteritems():
+    for alias, e in cmdtable.items():
         if e is entry:
             break
 
@@ -1130,7 +1142,7 @@
                     ctx = unfi[rev]
                     parents = tuple(p.node() for p in ctx.parents())
                     before = len(store._all)
-                    store.create(tr, mark[0], mark[1], mark[2], marks[3],
+                    store.create(tr, mark[0], mark[1], mark[2], mark[3],
                                  parents=parents)
                     if len(store._all) - before:
                         ui.write(_('created new markers for %i\n') % rev)
@@ -1455,9 +1467,14 @@
         revs = repo.revs(targetcat+'()')
         if revopt:
             revs = scmutil.revrange(repo, revopt) & revs
-        elif not anyopt and targetcat == 'unstable':
-            revs = set(_aspiringdescendant(repo,
-                                           repo.revs('(.::) - obsolete()::')))
+        elif not anyopt:
+            topic = getattr(repo, 'currenttopic', '')
+            if topic:
+                revs = repo.revs('topic(%s)', topic) & revs
+            elif targetcat == 'unstable':
+                revs = _aspiringdescendant(repo,
+                                           repo.revs('(.::) - obsolete()::'))
+                revs = set(revs)
         if targetcat == 'divergent':
             # Pick one divergent per group of divergents
             revs = _dedupedivergents(repo, revs)
@@ -1510,6 +1527,115 @@
     ordering.extend(sorted(dependencies))
     return ordering
 
+def divergentsets(repo, ctx):
+    """Compute sets of commits divergent with a given one"""
+    cache = {}
+    succsets = {}
+    base = {}
+    for n in obsolete.allprecursors(repo.obsstore, [ctx.node()]):
+        if n == ctx.node():
+            # a node can't be a base for divergence with itself
+            continue
+        nsuccsets = obsolete.successorssets(repo, n, cache)
+        for nsuccset in nsuccsets:
+            if ctx.node() in nsuccset:
+                # we are only interested in *other* successor sets
+                continue
+            if tuple(nsuccset) in base:
+                # we already know the latest base for this divergency
+                continue
+            base[tuple(nsuccset)] = n
+    divergence = []
+    for divset, b in base.iteritems():
+        divergence.append({
+            'divergentnodes': divset,
+            'commonprecursor': b
+        })
+
+    return divergence
+
+def _preparelistctxs(items, condition):
+    return [item.hex() for item in items if condition(item)]
+
+def _formatctx(fm, ctx):
+    fm.data(node=ctx.hex())
+    fm.data(desc=ctx.description())
+    fm.data(date=ctx.date())
+    fm.data(user=ctx.user())
+
+def listtroubles(ui, repo, troublecategories, **opts):
+    """Print all the troubles for the repo (or given revset)"""
+    troublecategories = troublecategories or ['divergent', 'unstable', 'bumped']
+    showunstable = 'unstable' in troublecategories
+    showbumped = 'bumped' in troublecategories
+    showdivergent = 'divergent' in troublecategories
+
+    revs = repo.revs('+'.join("%s()" % t for t in troublecategories))
+    if opts.get('rev'):
+        revs = revs & repo.revs(opts.get('rev'))
+
+    fm = ui.formatter('evolvelist', opts)
+    for rev in revs:
+        ctx = repo[rev]
+        unpars = _preparelistctxs(ctx.parents(), lambda p: p.unstable())
+        obspars = _preparelistctxs(ctx.parents(), lambda p: p.obsolete())
+        imprecs = _preparelistctxs(repo.set("allprecursors(%n)", ctx.node()),
+                                   lambda p: not p.mutable())
+        dsets = divergentsets(repo, ctx)
+
+        fm.startitem()
+        # plain formatter section
+        hashlen, desclen = 12, 60
+        desc = ctx.description()
+        if desc:
+            desc = desc.splitlines()[0]
+        desc = (desc[:desclen] + '...') if len(desc) > desclen else desc
+        fm.plain('%s: ' % ctx.hex()[:hashlen])
+        fm.plain('%s\n' % desc)
+        fm.data(node=ctx.hex(), rev=ctx.rev(), desc=desc, phase=ctx.phasestr())
+
+        for unpar in unpars if showunstable else []:
+            fm.plain('  unstable: %s (unstable parent)\n' % unpar[:hashlen])
+        for obspar in obspars if showunstable else []:
+            fm.plain('  unstable: %s (obsolete parent)\n' % obspar[:hashlen])
+        for imprec in imprecs if showbumped else []:
+            fm.plain('  bumped: %s (immutable precursor)\n' % imprec[:hashlen])
+
+        if dsets and showdivergent:
+            for dset in dsets:
+                fm.plain('  divergent: ')
+                first = True
+                for n in dset['divergentnodes']:
+                    t = "%s (%s)" if first else " %s (%s)"
+                    first = False
+                    fm.plain(t % (node.hex(n)[:hashlen], repo[n].phasestr()))
+                comprec = node.hex(dset['commonprecursor'])[:hashlen]
+                fm.plain(" (precursor %s)\n" % comprec)
+        fm.plain("\n")
+
+        # templater-friendly section
+        _formatctx(fm, ctx)
+        troubles = []
+        for unpar in unpars:
+            troubles.append({'troubletype': 'unstable', 'sourcenode': unpar,
+                             'sourcetype': 'unstableparent'})
+        for obspar in obspars:
+            troubles.append({'troubletype': 'unstable', 'sourcenode': obspar,
+                             'sourcetype': 'obsoleteparent'})
+        for imprec in imprecs:
+            troubles.append({'troubletype': 'bumped', 'sourcenode': imprec,
+                             'sourcetype': 'immutableprecursor'})
+        for dset in dsets:
+            divnodes = [{'node': node.hex(n),
+                         'phase': repo[n].phasestr(),
+                        } for n in dset['divergentnodes']]
+            troubles.append({'troubletype': 'divergent',
+                             'commonprecursor': node.hex(dset['commonprecursor']),
+                             'divergentnodes': divnodes})
+        fm.data(troubles=troubles)
+
+    fm.end()
+
 @command('^evolve|stabilize|solve',
     [('n', 'dry-run', False,
         _('do not perform actions, just print what would be done')),
@@ -1525,6 +1651,7 @@
     ('a', 'all', False, _('evolve all troubled changesets related to the '
                           'current  working directory and its descendants')),
     ('c', 'continue', False, _('continue an interrupted evolution')),
+    ('l', 'list', False, 'provide details on troubled changesets in the repo'),
     ] + mergetoolopts,
     _('[OPTIONS]...'))
 def evolve(ui, repo, **opts):
@@ -1592,9 +1719,13 @@
     (the default). You can combine ``--bumped`` or ``--divergent`` with
     ``--rev``, ``--all``, or ``--any``.
 
+    You can also use the evolve command to list the troubles affecting your
+    repository by using the --list flag. You can choose to display only some
+    categories of troubles with the --unstable, --divergent or --bumped flags.
     """
 
     # Options
+    listopt = opts['list']
     contopt = opts['continue']
     anyopt = opts['any']
     allopt = opts['all']
@@ -1604,6 +1735,10 @@
     revopt = opts['rev']
     troublecategories = ['bumped', 'divergent', 'unstable']
     specifiedcategories = [t for t in troublecategories if opts[t]]
+    if listopt:
+        listtroubles(ui, repo, specifiedcategories, **opts)
+        return
+
     targetcat = 'unstable'
     if 1 < len(specifiedcategories):
         msg = _('cannot specify more than one trouble category to solve (yet)')
@@ -1645,7 +1780,7 @@
 
     def progresscb():
         if revopt or allopt:
-            ui.progress(_('evolve'), seen, unit='changesets', total=count)
+            ui.progress(_('evolve'), seen, unit=_('changesets'), total=count)
 
     # Continuation handling
     if contopt:
@@ -1753,10 +1888,12 @@
         if not pctx.obsolete():
             pctx = orig.p2()  # second parent is obsolete ?
         elif orig.p2().obsolete():
-            raise error.Abort(_("no support for evolving merge changesets "
-                                "with two obsolete parents yet"),
-                              hint=_("Redo the merge and use `hg prune <old> "
-                                   "--succ <new>` to obsolete the old one"))
+            hint = _("Redo the merge (%s) and use `hg prune <old> "
+                     "--succ <new>` to obsolete the old one") % orig.hex()[:12]
+            ui.warn(_("warning: no support for evolving merge changesets "
+                      "with two obsolete parents yet\n") +
+                    _("(%s)\n") % hint)
+            return False
 
     if not pctx.obsolete():
         ui.warn(_("cannot solve instability of %s, skipping\n") % orig)
@@ -1998,7 +2135,14 @@
         hg.update(repo, divergent.rev())
     repo.ui.note(_('merging divergent changeset\n'))
     if progresscb: progresscb()
-    if 'partial' in merge.update.__doc__:
+    try:
+        stats = merge.update(repo,
+                             other.node(),
+                             branchmerge=True,
+                             force=False,
+                             ancestor=base.node(),
+                             mergeancestor=True)
+    except TypeError:
         # Mercurial  < 43c00ca887d1 (3.7)
         stats = merge.update(repo,
                              other.node(),
@@ -2007,13 +2151,6 @@
                              partial=None,
                              ancestor=base.node(),
                              mergeancestor=True)
-    else:
-        stats = merge.update(repo,
-                             other.node(),
-                             branchmerge=True,
-                             force=False,
-                             ancestor=base.node(),
-                             mergeancestor=True)
 
     hg._showstats(repo, stats)
     if stats[3]:
@@ -2027,14 +2164,14 @@
 /!\ * hg up to the parent of the amended changeset (which are named W and Z)
 /!\ * hg revert --all -r X
 /!\ * hg ci -m "same message as the amended changeset" => new cset Y
-/!\ * hg kill -n Y W Z
+/!\ * hg prune -n Y W Z
 """)
     if progresscb: progresscb()
     emtpycommitallowed = repo.ui.backupconfig('ui', 'allowemptycommit')
     tr = repo.currenttransaction()
     assert tr is not None
     try:
-        repo.ui.setconfig('ui', 'allowemptycommit', True)
+        repo.ui.setconfig('ui', 'allowemptycommit', True, 'evolve')
         repo.dirstate.beginparentchange()
         repo.dirstate.setparents(divergent.node(), node.nullid)
         repo.dirstate.endparentchange()
@@ -2075,6 +2212,7 @@
          [('B', 'move-bookmark', False,
              _('move active bookmark after update')),
           ('', 'merge', False, _('bring uncommitted change along')),
+          ('', 'no-topic', False, _('ignore topic and move topologically')),
           ('n', 'dry-run', False,
              _('do not perform actions, just print what would be done'))],
          '[OPTION]...')
@@ -2095,8 +2233,14 @@
             raise
 
     parents = wparents[0].parents()
+    topic = getattr(repo, 'currenttopic', '')
+    if topic and not opts.get("no_topic", False):
+        parents = [ctx for ctx in parents if ctx.topic() == topic]
     displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
-    if len(parents) == 1:
+    if not parents:
+        ui.warn(_('no parent in topic "%s"\n') % topic)
+        ui.warn(_('(do you want --no-topic)\n'))
+    elif len(parents) == 1:
         p = parents[0]
         bm = bmactive(repo)
         shouldmove = opts.get('move_bookmark') and bm is not None
@@ -2133,6 +2277,7 @@
              _('move active bookmark after update')),
           ('', 'merge', False, _('bring uncommitted change along')),
           ('', 'evolve', False, _('evolve the next changeset if necessary')),
+          ('', 'no-topic', False, _('ignore topic and move topologically')),
           ('n', 'dry-run', False,
               _('do not perform actions, just print what would be done'))],
               '[OPTION]...')
@@ -2156,6 +2301,12 @@
             raise
 
     children = [ctx for ctx in wparents[0].children() if not ctx.obsolete()]
+    topic = getattr(repo, 'currenttopic', '')
+    filtered = []
+    if topic and not opts.get("no_topic", False):
+        filtered = [ctx for ctx in children if ctx.topic() != topic]
+        # XXX N-square membership on children
+        children = [ctx for ctx in children if ctx not in filtered]
     displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
     if len(children) == 1:
         c = children[0]
@@ -2191,8 +2342,17 @@
         result = 1
     else:
         aspchildren = _aspiringchildren(repo, [repo['.'].rev()])
+        if topic:
+            filtered.extend(repo[c] for c in children
+                            if repo[c].topic() != topic)
+            # XXX N-square membership on children
+            aspchildren = [ctx for ctx in aspchildren if ctx not in filtered]
         if not opts['evolve'] or not aspchildren:
-            ui.warn(_('no children\n'))
+            if filtered:
+                ui.warn(_('no children on topic "%s"\n') % topic)
+                ui.warn(_('do you want --no-topic\n'))
+            else:
+                ui.warn(_('no children\n'))
             if aspchildren:
                 msg = _('(%i unstable changesets to be evolved here, '
                         'do you want --evolve?)\n')
@@ -2272,7 +2432,7 @@
     return metadata
 
 
-@command('^prune|obsolete|kill',
+@command('^prune|obsolete',
     [('n', 'new', [], _("successor changeset (DEPRECATED)")),
      ('s', 'succ', [], _("successor changeset")),
      ('r', 'rev', [], _("revisions to prune")),
@@ -2349,9 +2509,8 @@
         if not precs:
             raise error.Abort('nothing to prune')
 
-        if not obsolete.isenabled(repo, obsolete.allowunstableopt):
-            if repo.revs("(%ld::) - %ld", revs, revs):
-                raise error.Abort(_("cannot prune in the middle of a stack"))
+        if _disallowednewunstable(repo, revs):
+            raise error.Abort(_("cannot prune in the middle of a stack"))
 
         # defines successors changesets
         sucs = scmutil.revrange(repo, succs)
@@ -2359,7 +2518,9 @@
         sucs = tuple(repo[n] for n in sucs)
         if not biject and len(sucs) > 1 and len(precs) > 1:
             msg = "Can't use multiple successors for multiple precursors"
-            raise error.Abort(msg)
+            hint = _("use --biject to mark a series as a replacement"
+                     " for another")
+            raise error.Abort(msg, hint=hint)
         elif biject and len(sucs) != len(precs):
             msg = "Can't use %d successors for %d precursors" \
                 % (len(sucs), len(precs))
@@ -2685,7 +2846,7 @@
         if obsoleted:
             obsoleted = repo.set('%lr', obsoleted)
         result = orig(ui, repo, *arg, **kwargs)
-        if not result: # commit successed
+        if not result: # commit succeeded
             new = repo['-1']
             oldbookmarks = []
             markers = []
@@ -2709,7 +2870,7 @@
         lockmod.release(tr, lock, wlock)
 
 @command('^split',
-    [('r', 'rev', [], _("revision to fold")),
+    [('r', 'rev', [], _("revision to split")),
     ] + commitopts + commitopts2,
     _('hg split [OPTION]... [-r] REV'))
 def cmdsplit(ui, repo, *revs, **opts):
@@ -2758,7 +2919,8 @@
         def haschanges():
             modified, added, removed, deleted = repo.status()[:4]
             return modified or added or removed or deleted
-        msg = 'HG: Please, edit the original changeset description.\n\n'
+        msg = ("HG: This is the original pre-split commit message. "
+               "Edit it as appropriate.\n\n")
         msg += ctx.description()
         opts['message'] = msg
         opts['edit'] = True
@@ -2961,27 +3123,13 @@
         ui.write_err(_('single revision specified, nothing to fold\n'))
         return 1
 
-    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()]
-    disallowunstable = not obsolete.isenabled(repo, obsolete.allowunstableopt)
-    if disallowunstable:
-        if repo.revs("(%ld::) - %ld", revs, revs):
-            raise error.Abort(_("cannot fold chain not ending with a head "\
-                               "or with branching"))
     wlock = lock = None
     try:
         wlock = repo.wlock()
         lock = repo.lock()
+
+        root, head = _foldcheck(repo, revs)
+
         tr = repo.transaction('touch')
         try:
             commitopts = opts.copy()
@@ -3013,7 +3161,147 @@
     finally:
         lockmod.release(lock, wlock)
 
-
+@command('^metaedit',
+         [('r', 'rev', [], _("revision to edit")),
+         ('', 'fold', None, _("also fold specified revisions into one")),
+         ] + commitopts + 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 not opts['fold'] and 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 without --fold is '
+                                'not currently supported'))
+
+        if opts['fold']:
+            root, head = _foldcheck(repo, revs)
+        else:
+            if repo.revs("%ld and public()", revs):
+                raise error.Abort(_('cannot edit commit information for public '
+                                    'revisions'))
+            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()])
+            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:
+                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
+
+            # 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 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 _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()]
+    if _disallowednewunstable(repo, revs):
+        raise error.Abort(_("cannot fold chain not ending with a head "\
+                            "or with branching"))
+    return root, head
+
+def _disallowednewunstable(repo, revs):
+    allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
+    if allowunstable:
+        return revset.baseset()
+    return repo.revs("(%ld::) - %ld", revs, revs)
 
 @eh.wrapcommand('graft')
 def graftwrapper(orig, ui, repo, *revs, **kwargs):
@@ -3044,7 +3332,7 @@
 
 @eh.extsetup
 def oldevolveextsetup(ui):
-    for cmd in ['kill', 'uncommit', 'touch', 'fold']:
+    for cmd in ['prune', 'uncommit', 'touch', 'fold']:
         try:
             entry = extensions.wrapcommand(cmdtable, cmd,
                                            warnobserrors)
@@ -3246,7 +3534,7 @@
         undecided.difference_update(common)
 
 
-    ui.progress(_("comparing with other"), None, total=totalnb)
+    ui.progress(_("comparing with other"), None)
     result = dag.headsetofconnecteds(common)
     ui.debug("%d total queries\n" % roundtrips)
 
@@ -3265,7 +3553,7 @@
         return len(self.getvalue())
 
     def read(self, size=None):
-        obsexcprg(self.ui, self.tell(), unit="bytes", total=self.length)
+        obsexcprg(self.ui, self.tell(), unit=_("bytes"), total=self.length)
         return StringIO.read(self, size)
 
     def __iter__(self):
@@ -3317,11 +3605,11 @@
                                % (len(markers), len(remotedata), totalbytes),
                       True)
             for key, data in remotedata:
-                obsexcprg(repo.ui, sentbytes, item=key, unit="bytes",
+                obsexcprg(repo.ui, sentbytes, item=key, unit=_("bytes"),
                           total=totalbytes)
                 rslts.append(remote.pushkey('obsolete', key, '', data))
                 sentbytes += len(data)
-                obsexcprg(repo.ui, sentbytes, item=key, unit="bytes",
+                obsexcprg(repo.ui, sentbytes, item=key, unit=_("bytes"),
                           total=totalbytes)
             obsexcprg(repo.ui, None)
             if [r for r in rslts if not r]:
@@ -3530,12 +3818,12 @@
     current = 0
     data = StringIO()
     ui = self.ui
-    obsexcprg(ui, current, unit="bytes", total=length)
+    obsexcprg(ui, current, unit=_("bytes"), total=length)
     while current < length:
         readsize = min(length - current, chunk)
         data.write(f.read(readsize))
         current += readsize
-        obsexcprg(ui, current, unit="bytes", total=length)
+        obsexcprg(ui, current, unit=_("bytes"), total=length)
     obsexcprg(ui, None)
     data.seek(0)
     return data
@@ -3718,7 +4006,7 @@
     backup = repo.ui.backupconfig('phases', 'new-commit')
     try:
         targetphase = max(orig.phase(), phases.draft)
-        repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
+        repo.ui.setconfig('phases', 'new-commit', targetphase, 'evolve')
         # Commit might fail if unresolved files exist
         nodenew = repo.commit(text=commitmsg, user=orig.user(),
                               date=orig.date(), extra=extra)
@@ -3780,7 +4068,7 @@
 def _evolvestateread(repo):
     try:
         f = repo.vfs('evolvestate')
-    except IOError, err:
+    except IOError as err:
         if err.errno != errno.ENOENT:
             raise
         return None
@@ -3834,6 +4122,14 @@
     bmdeactivate(repo)
     if keepbranch:
        repo.dirstate.setbranch(orig.branch())
+    if util.safehasattr(repo, 'currenttopic'):
+        # uurrgs
+        # there no other topic setter yet
+        if not orig.topic() and repo.vfs.exists('topic'):
+                repo.vfs.unlink('topic')
+        else:
+            with repo.vfs.open('topic', 'w') as f:
+                f.write(orig.topic())
 
     try:
        r = merge.graft(repo, orig, pctx, ['local', 'graft'], True)
--- a/hgext/obsolete.py	Wed Feb 10 23:44:00 2016 +0000
+++ b/hgext/obsolete.py	Thu May 05 23:51:35 2016 +0200
@@ -3,12 +3,12 @@
 #
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
-"""Deprecated extension that formely introduces "Changeset Obsolescence".
+"""Deprecated extension that formerly introduced "Changeset Obsolescence".
 
-This concept is now partially in Mercurial core (starting with mercurial 2.3).
-The remaining logic have been grouped with the evolve extension.
+This concept is now partially in Mercurial core (starting with Mercurial 2.3).
+The remaining logic has been grouped with the evolve extension.
 
-Some code cemains in this extensions to detect and convert prehistoric format
+Some code remains in this extensions to detect and convert prehistoric format
 of obsolete marker than early user may have create. Keep it enabled if you
 were such user.
 """
@@ -57,10 +57,10 @@
             raise error.Abort('old format of obsolete marker detected!\n'
                               'run `hg debugconvertobsolete` once.')
 
-def _obsdeserialise(flike):
-    """read a file like object serialised with _obsserialise
+def _obsdeserialize(flike):
+    """read a file like object serialized with _obsserialize
 
-    this desierialize into a {subject -> objects} mapping
+    this deserialize into a {subject -> objects} mapping
 
     this was the very first format ever."""
     rels = {}
--- a/setup.py	Wed Feb 10 23:44:00 2016 +0000
+++ b/setup.py	Thu May 05 23:51:35 2016 +0200
@@ -29,7 +29,7 @@
     author='Pierre-Yves David',
     maintainer='Pierre-Yves David',
     maintainer_email='pierre-yves.david@ens-lyon.org',
-    url='https://bitbucket.org/marmoute/mutable-history',
+    url='https://www.mercurial-scm.org/doc/evolution/',
     description='Flexible evolution of Mercurial history.',
     long_description=open('README').read(),
     keywords='hg mercurial',
--- a/tests/test-corrupt.t	Wed Feb 10 23:44:00 2016 +0000
+++ b/tests/test-corrupt.t	Thu May 05 23:51:35 2016 +0200
@@ -101,7 +101,7 @@
      summary:     add A
   
 
-  $ hg kill --fold -n -1 -- -2 -3
+  $ hg prune --fold -n -1 -- -2 -3
   2 changesets pruned
   $ hg push ../other
   pushing to ../other
--- a/tests/test-divergent.t	Wed Feb 10 23:44:00 2016 +0000
+++ b/tests/test-divergent.t	Thu May 05 23:51:35 2016 +0200
@@ -107,5 +107,53 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   working directory is now at 6602ff5a79dc
- 
-  $ cd ..  
+
+Test None docstring issue of evolve divergent, which caused hg crush
+
+  $ hg init test2
+  $ cd test2
+  $ mkcommits _a _b
+  $ hg up "desc(_a)"
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit bdivergent1
+  created new head
+  $ hg up "desc(_a)"
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit bdivergent2
+  created new head
+  $ hg prune -s "desc(bdivergent1)" "desc(_b)"
+  1 changesets pruned
+  $ hg prune -s "desc(bdivergent2)" "desc(_b)" --hidden
+  1 changesets pruned
+  2 new divergent changesets
+  $ hg log -G
+  @  3:e708fd28d5cf@default(draft) add bdivergent2 [divergent]
+  |
+  | o  2:c2f698071cba@default(draft) add bdivergent1 [divergent]
+  |/
+  o  0:135f39f4bd78@default(draft) add _a []
+  
+  $ cat >$TESTTMP/test_extension.py  << EOF
+  > from mercurial import merge
+  > origupdate = merge.update
+  > def newupdate(*args, **kwargs):
+  >   return origupdate(*args, **kwargs)
+  > merge.update = newupdate
+  > EOF
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > testextension=$TESTTMP/test_extension.py
+  > EOF
+  $ hg evolve --all
+  nothing to evolve on current working copy parent
+  (do you want to use --divergent)
+  [2]
+  $ hg evolve --divergent
+  merge:[3] add bdivergent2
+  with: [2] add bdivergent1
+  base: [1] add _b
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at aa26817f6fbe
+
+
+  $ cd ..
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-list.t	Thu May 05 23:51:35 2016 +0200
@@ -0,0 +1,80 @@
+Set up some configs
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > rebase=
+  > EOF
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+
+Test the instability listing
+  $ hg init r2
+  $ cd r2
+  $ echo a > a && hg ci -Am a
+  adding a
+  $ echo b > b && hg ci -Am b
+  adding b
+  $ echo c > c && hg ci -Am c
+  adding c
+  $ hg up 0
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ echo a >> a && hg ci --amend -m a
+  2 new unstable changesets
+  $ hg evolve --list
+  d2ae7f538514: b
+    unstable: cb9a9f314b8b (obsolete parent)
+  
+  177f92b77385: c
+    unstable: d2ae7f538514 (unstable parent)
+  
+  $ cd ..
+
+Test the bumpedness listing
+  $ hg init r3
+  $ cd r3
+  $ echo a > a && hg ci -Am a
+  adding a
+  $ echo b > b && hg ci --amend -m ab
+  $ hg phase --public --rev 0 --hidden
+  1 new bumped changesets
+  $ hg evolve --list
+  88cc282e27fc: ab
+    bumped: cb9a9f314b8b (immutable precursor)
+  
+  $ cd ..
+
+Test the divergence listing
+  $ hg init r1
+  $ cd r1
+  $ echo a > a && hg ci -Am a
+  adding a
+  $ hg up 0
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo b > b && hg ci -Am b
+  adding b
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo c > c && hg ci -Am c
+  adding c
+  created new head
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo d > d && hg ci -Am d
+  adding d
+  created new head
+  $ hg rebase -s 1 -d 2
+  rebasing 1:d2ae7f538514 "b"
+  $ hg rebase -s 1 -d 3 --hidden --config experimental.allowdivergence=True
+  rebasing 1:d2ae7f538514 "b"
+  2 new divergent changesets
+  $ hg evolve --list
+  c882616e9d84: b
+    divergent: a922b3733e98 (draft) (precursor d2ae7f538514)
+  
+  a922b3733e98: b
+    divergent: c882616e9d84 (draft) (precursor d2ae7f538514)
+  
+  $ hg phase -p a922b3733e98
+  $ hg evolve --list
+  c882616e9d84: b
+    divergent: a922b3733e98 (public) (precursor d2ae7f538514)
+  
+  $ cd ..
--- a/tests/test-evolve.t	Wed Feb 10 23:44:00 2016 +0000
+++ b/tests/test-evolve.t	Thu May 05 23:51:35 2016 +0200
@@ -2,6 +2,7 @@
   > [defaults]
   > amend=-d "0 0"
   > fold=-d "0 0"
+  > metaedit=-d "0 0"
   > [web]
   > push_ssl = false
   > allow_push = *
@@ -112,7 +113,7 @@
 
   $ hg log -r 1 --template '{rev} {phase} {obsolete}\n'
   1 public stable
-  $ hg kill 1
+  $ hg prune 1
   abort: cannot prune immutable changeset: 7c3bad9141dc
   (see "hg help phases" for details)
   [255]
@@ -123,7 +124,7 @@
 
   $ hg id -n
   5
-  $ hg kill .
+  $ hg prune .
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   working directory now at fbb94e3a0ecf
   1 changesets pruned
@@ -136,7 +137,7 @@
 
 test multiple kill
 
-  $ hg kill 4 -r 3
+  $ hg prune 4 -r 3
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   working directory now at 7c3bad9141dc
   2 changesets pruned
@@ -151,7 +152,7 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo 4 > g
   $ hg add g
-  $ hg kill .
+  $ hg prune .
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   working directory now at 7c3bad9141dc
   1 changesets pruned
@@ -789,7 +790,7 @@
   adding b
   $ hg mv a c
   $ hg ci -m c
-  $ hg kill .^
+  $ hg prune .^
   1 changesets pruned
   1 new unstable changesets
   $ hg stab --any
@@ -1448,3 +1449,118 @@
 
   $ hg status newlyadded
   A newlyadded
+
+hg metaedit
+-----------
+
+  $ hg update --clean .
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ rm newlyadded
+  $ hg metaedit -r 0
+  abort: cannot edit commit information for public revisions
+  [255]
+  $ hg metaedit --fold
+  abort: revisions must be specified with --fold
+  [255]
+  $ hg metaedit -r 0 --fold
+  abort: cannot fold public revisions
+  [255]
+  $ hg metaedit '36 + 42' --fold
+  abort: cannot fold non-linear revisions (multiple roots given)
+  [255]
+  $ hg metaedit '36::39 + 41' --fold
+  abort: cannot fold non-linear revisions (multiple heads given)
+  [255]
+check that metaedit respects allowunstable
+  $ hg metaedit '.^' --config 'experimental.evolution=createmarkers, allnewcommands'
+  abort: cannot edit commit information in the middle of a stack
+  (c904da5245b0 will be affected)
+  [255]
+  $ hg metaedit '18::20' --fold --config 'experimental.evolution=createmarkers, allnewcommands'
+  abort: cannot fold chain not ending with a head or with branching
+  [255]
+  $ hg metaedit --user foobar
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log --template '{rev}: {author}\n' -r '42:' --hidden
+  42: test
+  43: foobar
+  $ hg log --template '{rev}: {author}\n' -r .
+  43: foobar
+
+TODO: support this
+  $ hg metaedit '.^::.'
+  abort: editing multiple revisions without --fold is not currently supported
+  [255]
+
+  $ HGEDITOR=cat hg metaedit '.^::.' --fold
+  HG: This is a fold of 2 changesets.
+  HG: Commit message of changeset 41.
+  
+  amended
+  
+  HG: Commit message of changeset 43.
+  
+  will be evolved safely
+  
+  
+  
+  HG: Enter commit message.  Lines beginning with 'HG:' are removed.
+  HG: Leave message empty to abort commit.
+  HG: --
+  HG: user: test
+  HG: branch 'default'
+  HG: changed a
+  HG: changed newfile
+  2 changesets folded
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ glog -r .
+  @  44:41bf1183869c@default(draft) amended
+  |
+
+no new commit is created here because the date is the same
+  $ HGEDITOR=cat hg metaedit
+  amended
+  
+  
+  will be evolved safely
+  
+  
+  HG: Enter commit message.  Lines beginning with 'HG:' are removed.
+  HG: Leave message empty to abort commit.
+  HG: --
+  HG: user: test
+  HG: branch 'default'
+  HG: changed a
+  HG: changed newfile
+  nothing changed
+
+  $ glog -r '.^::.'
+  @  44:41bf1183869c@default(draft) amended
+  |
+  o  36:43c3f5ef149f@default(draft) add uu
+  |
+
+TODO: don't create a new commit in this case
+  $ hg metaedit --config defaults.metaedit=
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -r '.^::.' --template '{rev}: {desc|firstline}\n'
+  36: add uu
+  45: amended
+
+  $ hg up .^
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg metaedit --user foobar2 45
+  $ hg log --template '{rev}: {author}\n' -r '42:' --hidden
+  42: test
+  43: foobar
+  44: test
+  45: test
+  46: foobar2
+  $ hg diff -r 45 -r 46 --hidden
+
+'fold' one commit
+  $ hg metaedit 39 --fold --user foobar3
+  1 changesets folded
+  $ hg log -r 47 --template '{rev}: {author}\n'
+  47: foobar3
--- a/tests/test-obsolete-push.t	Wed Feb 10 23:44:00 2016 +0000
+++ b/tests/test-obsolete-push.t	Thu May 05 23:51:35 2016 +0200
@@ -25,7 +25,7 @@
   $ echo c > c
   $ hg ci -qAm C c
   $ hg phase --secret --force .
-  $ hg kill 0 1
+  $ hg prune 0 1
   2 changesets pruned
   1 new unstable changesets
   $ glog --hidden
--- a/tests/test-obsolete.t	Wed Feb 10 23:44:00 2016 +0000
+++ b/tests/test-obsolete.t	Thu May 05 23:51:35 2016 +0200
@@ -508,7 +508,7 @@
 should not rebase extinct changesets
 
 #excluded 'whole rebase set is extinct and ignored.' message not in core
-  $ hg rebase -b '3' -d 4 --traceback
+  $ hg rebase -b '3' -d 4 --traceback --config experimental.rebaseskipobsolete=0
   rebasing 3:0d3f46688ccc "add obsol_c"
   rebasing 8:159dfc9fa5d3 "add obsol_d''" (tip)
   2 new divergent changesets
--- a/tests/test-prune.t	Wed Feb 10 23:44:00 2016 +0000
+++ b/tests/test-prune.t	Thu May 05 23:51:35 2016 +0200
@@ -194,6 +194,7 @@
 
   $ hg prune 'desc("add cc")' 'desc("add bb")' -s 'desc("add nD")' -s 'desc("add nC")'
   abort: Can't use multiple successors for multiple precursors
+  (use --biject to mark a series as a replacement for another)
   [255]
   $ hg debugobsolete
   9d206ffc875e1bc304590549be293be36821e66c 0 {47d2a3944de8b013de3be9578e8e344ea2e6c097} (Sat Dec 15 00:00:00 1979 +0000) {'user': 'blah'}
--- a/tests/test-split.t	Wed Feb 10 23:44:00 2016 +0000
+++ b/tests/test-split.t	Thu May 05 23:51:35 2016 +0200
@@ -123,7 +123,7 @@
      summary:     add _a
   
 
-Cannot split a commit with uncommited changes
+Cannot split a commit with uncommitted changes
   $ hg up "desc(_c)"
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ echo "_cd" > _c
--- a/tests/test-stabilize-order.t	Wed Feb 10 23:44:00 2016 +0000
+++ b/tests/test-stabilize-order.t	Thu May 05 23:51:35 2016 +0200
@@ -132,7 +132,7 @@
   no troubled changesets
   [1]
 
-Test behaviour with --any
+Test behavior with --any
 
   $ hg up 8
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
--- a/tests/test-stabilize-result.t	Wed Feb 10 23:44:00 2016 +0000
+++ b/tests/test-stabilize-result.t	Thu May 05 23:51:35 2016 +0200
@@ -366,6 +366,6 @@
   /!\ * hg up to the parent of the amended changeset (which are named W and Z)
   /!\ * hg revert --all -r X
   /!\ * hg ci -m "same message as the amended changeset" => new cset Y
-  /!\ * hg kill -n Y W Z
+  /!\ * hg prune -n Y W Z
   )
   [255]
--- a/tests/test-tutorial.t	Wed Feb 10 23:44:00 2016 +0000
+++ b/tests/test-tutorial.t	Thu May 05 23:51:35 2016 +0200
@@ -74,7 +74,7 @@
 Fixing mistake with `hg amend`
 --------------------------------
 
-We are versionning a shopping list
+We are versioning a shopping list
 
   $ cd local
   $ cat  >> shopping << EOF
@@ -207,7 +207,7 @@
 Getting rid of branchy history
 ----------------------------------
 
-While I was working on my list. someone made a change remotly.
+While I was working on my list. someone made a change remotely.
 
   $ cd ../remote
   $ hg up -q
--- a/tests/test-unstable.t	Wed Feb 10 23:44:00 2016 +0000
+++ b/tests/test-unstable.t	Thu May 05 23:51:35 2016 +0200
@@ -156,9 +156,8 @@
   
 
   $ hg evo --all --any --unstable
-  abort: no support for evolving merge changesets with two obsolete parents yet
-  (Redo the merge and use `hg prune <old> --succ <new>` to obsolete the old one)
-  [255]
+  warning: no support for evolving merge changesets with two obsolete parents yet
+  (Redo the merge (6b4280e33286) and use `hg prune <old> --succ <new>` to obsolete the old one)
   $ hg log -G
   @  5:2db39fda7e2f@default(draft) cprime
   |