merge with stable through 3.7 branch mercurial-3.6
authorPierre-Yves David <pierre-yves.david@ens-lyon.org>
Thu, 05 May 2016 23:51:35 +0200
branchmercurial-3.6
changeset 1697 093c445fd86a
parent 1604 61dd08f4dc62 (current diff)
parent 1696 898dfca94433 (diff)
child 1698 dd6f090b7342
child 1706 f89c429345e9
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.
tests/test-amend.t
tests/test-evolve-topic.t
tests/test-evolve.t
tests/test-unstable.t
--- 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
   |