changeset 27408:2916ebaef312

merge with stable
author Matt Mackall <mpm@selenic.com>
date Thu, 17 Dec 2015 17:27:32 -0600
parents bf4d5d8dc2aa (diff) 00aa37c65e0a (current diff)
children 32bea9daa877
files mercurial/ui.py
diffstat 254 files changed, 9092 insertions(+), 3037 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Mon Nov 30 13:47:29 2015 -0600
+++ b/Makefile	Thu Dec 17 17:27:32 2015 -0600
@@ -161,12 +161,11 @@
 	rm -rf dist/mercurial-*.mpkg
 
 deb:
-	mkdir -p packages/debian-unknown
-	contrib/builddeb --release unknown
+	contrib/builddeb
 
 docker-debian-jessie:
 	mkdir -p packages/debian-jessie
-	contrib/dockerdeb jessie
+	contrib/dockerdeb debian jessie
 
 fedora20:
 	mkdir -p packages/fedora20
--- a/contrib/Makefile.python	Mon Nov 30 13:47:29 2015 -0600
+++ b/contrib/Makefile.python	Thu Dec 17 17:27:32 2015 -0600
@@ -47,8 +47,8 @@
 	[ -f $(PYTHON_SRCFILE) ] || wget http://www.python.org/ftp/python/$(PYTHONVER)/$(PYTHON_SRCFILE) || curl -OL http://www.python.org/ftp/python/$(PYTHONVER)/$(PYTHON_SRCFILE) || [ -f $(PYTHON_SRCFILE) ]
 	rm -rf $(PYTHON_SRCDIR)
 	tar xf $(PYTHON_SRCFILE)
-	# Ubuntu disables SSLv2 the hard way, disable it on old Pythons too
-	-sed -i 's,self.*SSLv2_method(),0;//\0,g' $(PYTHON_SRCDIR)/Modules/_ssl.c
+	# Debian/Ubuntu disables SSLv2,3 the hard way, disable it on old Pythons too
+	-sed -i 's,self.*SSLv[23]_method(),0;//\0,g' $(PYTHON_SRCDIR)/Modules/_ssl.c
 	# Find multiarch system libraries on Ubuntu and disable fortify error when setting argv
 	LDFLAGS="-L/usr/lib/`dpkg-architecture -qDEB_HOST_MULTIARCH`"; \
 	BASECFLAGS=-U_FORTIFY_SOURCE; \
--- a/contrib/bash_completion	Mon Nov 30 13:47:29 2015 -0600
+++ b/contrib/bash_completion	Thu Dec 17 17:27:32 2015 -0600
@@ -629,7 +629,7 @@
 
 _hg_cmd_shelve()
 {
-    if [[ "$prev" = @(-d|--delete|-l|--list) ]]; then
+    if [[ "$prev" = @(-d|--delete|-l|--list|-p|--patch|--stat) ]]; then
         _hg_shelves
     else
         _hg_status "mard"
--- a/contrib/builddeb	Mon Nov 30 13:47:29 2015 -0600
+++ b/contrib/builddeb	Thu Dec 17 17:27:32 2015 -0600
@@ -8,12 +8,18 @@
 
 BUILD=1
 CLEANUP=1
-DEBVERSION=jessie
+DISTID=`(lsb_release -is 2> /dev/null | tr '[:upper:]' '[:lower:]') || echo debian`
+CODENAME=`lsb_release -cs 2> /dev/null || echo unknown`
 while [ "$1" ]; do
     case "$1" in
-    --release )
+    --distid )
+        shift
+        DISTID="$1"
         shift
-        DEBVERSION="$1"
+        ;;
+    --codename )
+        shift
+        CODENAME="$1"
         shift
         ;;
     --cleanup )
@@ -24,11 +30,6 @@
         shift
         CLEANUP=
         ;;
-    --debbuilddir )
-        shift
-        DEBBUILDDIR="$1"
-        shift
-        ;;
     * )
         echo "Invalid parameter $1!" 1>&2
         exit 1
@@ -82,7 +83,8 @@
 fi
 if [ "$CLEANUP" ] ; then
     echo
-    OUTPUTDIR=${OUTPUTDIR:=packages/debian-$DEBVERSION}
+    OUTPUTDIR=${OUTPUTDIR:=packages/$DISTID-$CODENAME}
+    mkdir -p "$OUTPUTDIR"
     find ../mercurial*.deb ../mercurial_*.build ../mercurial_*.changes \
           -type f -newer $control -print0 | \
       xargs -Inarf -0 mv narf "$OUTPUTDIR"
--- a/contrib/check-commit	Mon Nov 30 13:47:29 2015 -0600
+++ b/contrib/check-commit	Thu Dec 17 17:27:32 2015 -0600
@@ -27,7 +27,7 @@
     (r"^# .*\n[A-Z][a-z]\S+", "don't capitalize summary lines"),
     (r"^# .*\n[^\n]*: *[A-Z][a-z]\S+", "don't capitalize summary lines"),
     (r"^# .*\n.*\.\s+$", "don't add trailing period on summary line"),
-    (r"^# .*\n.{78,}", "summary line too long (limit is 78)"),
+    (r"^# .*\n[^#].{77,}", "summary line too long (limit is 78)"),
     (r"^\+\n \n", "adds double empty line"),
     (r"^ \n\+\n", "adds double empty line"),
     (r"^\+[ \t]+def [a-z]+_[a-z]", "adds a function with foo_bar naming"),
--- a/contrib/check-config.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/contrib/check-config.py	Thu Dec 17 17:27:32 2015 -0600
@@ -13,14 +13,16 @@
 foundopts = {}
 documented = {}
 
-configre = (r"""ui\.config(|int|bool|list)\(['"](\S+)['"], ?"""
-            r"""['"](\S+)['"](,\s(?:default=)?(\S+?))?\)""")
+configre = (r"""ui\.config(|int|bool|list)\(['"](\S+)['"],\s*"""
+            r"""['"](\S+)['"](,\s+(?:default=)?(\S+?))?\)""")
+configpartialre = (r"""ui\.config""")
 
 def main(args):
     for f in args:
         sect = ''
         prevname = ''
         confsect = ''
+        carryover = ''
         for l in open(f):
 
             # check topic-like bits
@@ -40,29 +42,35 @@
             if m:
                 confsect = m.group(1)
                 continue
-            m = re.match(r'^\s+(?:#\s*)?([a-z._]+) = ', l)
+            m = re.match(r'^\s+(?:#\s*)?(\S+) = ', l)
             if m:
                 name = confsect + '.' + m.group(1)
                 documented[name] = 1
 
             # like the bugzilla extension
-            m = re.match(r'^\s*([a-z]+\.[a-z]+)$', l)
+            m = re.match(r'^\s*(\S+\.\S+)$', l)
+            if m:
+                documented[m.group(1)] = 1
+
+            # like convert
+            m = re.match(r'^\s*:(\S+\.\S+):\s+', l)
             if m:
                 documented[m.group(1)] = 1
 
             # quoted in help or docstrings
-            m = re.match(r'.*?``([-a-z_]+\.[-a-z_]+)``', l)
+            m = re.match(r'.*?``(\S+\.\S+)``', l)
             if m:
                 documented[m.group(1)] = 1
 
             # look for ignore markers
             m = re.search(r'# (?:internal|experimental|deprecated|developer)'
-                          ' config: (\S+.\S+)$', l)
+                          ' config: (\S+\.\S+)$', l)
             if m:
                 documented[m.group(1)] = 1
 
             # look for code-like bits
-            m = re.search(configre, l)
+            line = carryover + l
+            m = re.search(configre, line, re.MULTILINE)
             if m:
                 ctype = m.group(1)
                 if not ctype:
@@ -78,6 +86,13 @@
                     print "conflict on %s: %r != %r" % (name, (ctype, default),
                                                         foundopts[name])
                 foundopts[name] = (ctype, default)
+                carryover = ''
+            else:
+                m = re.search(configpartialre, line)
+                if m:
+                    carryover = line
+                else:
+                    carryover = ''
 
     for name in sorted(foundopts):
         if name not in documented:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/check-py3-compat.py	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+#
+# check-py3-compat - check Python 3 compatibility of Mercurial files
+#
+# Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from __future__ import absolute_import, print_function
+
+import ast
+import sys
+
+def check_compat(f):
+    """Check Python 3 compatibility for a file."""
+    with open(f, 'rb') as fh:
+        content = fh.read()
+
+    # Ignore empty files.
+    if not content.strip():
+        return
+
+    root = ast.parse(content)
+    futures = set()
+    haveprint = False
+    for node in ast.walk(root):
+        if isinstance(node, ast.ImportFrom):
+            if node.module == '__future__':
+                futures |= set(n.name for n in node.names)
+        elif isinstance(node, ast.Print):
+            haveprint = True
+
+    if 'absolute_import' not in futures:
+        print('%s not using absolute_import' % f)
+    if haveprint and 'print_function' not in futures:
+        print('%s requires print_function' % f)
+
+if __name__ == '__main__':
+    for f in sys.argv[1:]:
+        check_compat(f)
+
+    sys.exit(0)
--- a/contrib/dockerdeb	Mon Nov 30 13:47:29 2015 -0600
+++ b/contrib/dockerdeb	Thu Dec 17 17:27:32 2015 -0600
@@ -8,8 +8,9 @@
 
 checkdocker
 
-DEBPLATFORM="$1"
-PLATFORM="debian-$1"
+DISTID="$1"
+CODENAME="$2"
+PLATFORM="$1-$2"
 shift # extra params are passed to build process
 
 OUTPUTDIR=${OUTPUTDIR:=$ROOTDIR/packages/$PLATFORM}
@@ -26,8 +27,8 @@
             sh -c "cd /mnt/$dn && make clean && make local"
 fi
 $DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \
-  sh -c "cd /mnt/$dn && DEB_BUILD_OPTIONS='${DEB_BUILD_OPTIONS:=}' contrib/builddeb --build --release $DEBPLATFORM"
-contrib/builddeb --cleanup --release $DEBPLATFORM
+  sh -c "cd /mnt/$dn && DEB_BUILD_OPTIONS='${DEB_BUILD_OPTIONS:=}' contrib/builddeb --build --distid $DISTID --codename $CODENAME"
+contrib/builddeb --cleanup --distid $DISTID --codename $CODENAME
 if [ $(uname) = "Darwin" ] ; then
     $DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \
             sh -c "cd /mnt/$dn && make clean"
--- a/contrib/import-checker.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/contrib/import-checker.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,4 +1,7 @@
+#!/usr/bin/env python
+
 import ast
+import collections
 import os
 import sys
 
@@ -11,6 +14,8 @@
 # Whitelist of modules that symbols can be directly imported from.
 allowsymbolimports = (
     '__future__',
+    'mercurial.hgweb.common',
+    'mercurial.hgweb.request',
     'mercurial.i18n',
     'mercurial.node',
 )
@@ -35,6 +40,17 @@
 
     return False
 
+def walklocal(root):
+    """Recursively yield all descendant nodes but not in a different scope"""
+    todo = collections.deque(ast.iter_child_nodes(root))
+    yield root, False
+    while todo:
+        node = todo.popleft()
+        newscope = isinstance(node, ast.FunctionDef)
+        if not newscope:
+            todo.extend(ast.iter_child_nodes(node))
+        yield node, newscope
+
 def dotted_name_of_path(path, trimpure=False):
     """Given a relative path to a source file, return its dotted module name.
 
@@ -237,7 +253,7 @@
     >>> sorted(imported_modules(
     ...        'import foo1; from bar import bar1',
     ...        modulename, localmods))
-    ['foo.bar.__init__', 'foo.bar.bar1', 'foo.foo1']
+    ['foo.bar.bar1', 'foo.foo1']
     >>> sorted(imported_modules(
     ...        'from bar.bar1 import name1, name2, name3',
     ...        modulename, localmods))
@@ -284,21 +300,28 @@
                 continue
 
             absname, dottedpath, hassubmod = found
-            yield dottedpath
             if not hassubmod:
+                # "dottedpath" is not a package; must be imported
+                yield dottedpath
                 # examination of "node.names" should be redundant
                 # e.g.: from mercurial.node import nullid, nullrev
                 continue
 
+            modnotfound = False
             prefix = absname + '.'
             for n in node.names:
                 found = fromlocal(prefix + n.name)
                 if not found:
                     # this should be a function or a property of "node.module"
+                    modnotfound = True
                     continue
                 yield found[1]
+            if modnotfound:
+                # "dottedpath" is a package, but imported because of non-module
+                # lookup
+                yield dottedpath
 
-def verify_import_convention(module, source):
+def verify_import_convention(module, source, localmods):
     """Verify imports match our established coding convention.
 
     We have 2 conventions: legacy and modern. The modern convention is in
@@ -311,11 +334,11 @@
     absolute = usingabsolute(root)
 
     if absolute:
-        return verify_modern_convention(module, root)
+        return verify_modern_convention(module, root, localmods)
     else:
         return verify_stdlib_on_own_line(root)
 
-def verify_modern_convention(module, root):
+def verify_modern_convention(module, root, localmods, root_col_offset=0):
     """Verify a file conforms to the modern import convention rules.
 
     The rules of the modern convention are:
@@ -342,6 +365,7 @@
       and readability problems. See `requirealias`.
     """
     topmodule = module.split('.')[0]
+    fromlocal = fromlocalfunc(module, localmods)
 
     # Whether a local/non-stdlib import has been performed.
     seenlocal = False
@@ -352,29 +376,36 @@
     # Relative import levels encountered so far.
     seenlevels = set()
 
-    for node in ast.walk(root):
-        if isinstance(node, ast.Import):
+    for node, newscope in walklocal(root):
+        def msg(fmt, *args):
+            return (fmt % args, node.lineno)
+        if newscope:
+            # Check for local imports in function
+            for r in verify_modern_convention(module, node, localmods,
+                                              node.col_offset + 4):
+                yield r
+        elif isinstance(node, ast.Import):
             # Disallow "import foo, bar" and require separate imports
             # for each module.
             if len(node.names) > 1:
-                yield 'multiple imported names: %s' % ', '.join(
-                    n.name for n in node.names)
+                yield msg('multiple imported names: %s',
+                          ', '.join(n.name for n in node.names))
 
             name = node.names[0].name
             asname = node.names[0].asname
 
             # Ignore sorting rules on imports inside blocks.
-            if node.col_offset == 0:
+            if node.col_offset == root_col_offset:
                 if lastname and name < lastname:
-                    yield 'imports not lexically sorted: %s < %s' % (
-                           name, lastname)
+                    yield msg('imports not lexically sorted: %s < %s',
+                              name, lastname)
 
                 lastname = name
 
             # stdlib imports should be before local imports.
             stdlib = name in stdlib_modules
-            if stdlib and seenlocal and node.col_offset == 0:
-                yield 'stdlib import follows local import: %s' % name
+            if stdlib and seenlocal and node.col_offset == root_col_offset:
+                yield msg('stdlib import follows local import: %s', name)
 
             if not stdlib:
                 seenlocal = True
@@ -382,11 +413,11 @@
             # Import of sibling modules should use relative imports.
             topname = name.split('.')[0]
             if topname == topmodule:
-                yield 'import should be relative: %s' % name
+                yield msg('import should be relative: %s', name)
 
             if name in requirealias and asname != requirealias[name]:
-                yield '%s module must be "as" aliased to %s' % (
-                       name, requirealias[name])
+                yield msg('%s module must be "as" aliased to %s',
+                          name, requirealias[name])
 
         elif isinstance(node, ast.ImportFrom):
             # Resolve the full imported module name.
@@ -400,39 +431,49 @@
 
                 topname = fullname.split('.')[0]
                 if topname == topmodule:
-                    yield 'import should be relative: %s' % fullname
+                    yield msg('import should be relative: %s', fullname)
 
             # __future__ is special since it needs to come first and use
             # symbol import.
             if fullname != '__future__':
                 if not fullname or fullname in stdlib_modules:
-                    yield 'relative import of stdlib module'
+                    yield msg('relative import of stdlib module')
                 else:
                     seenlocal = True
 
             # Direct symbol import is only allowed from certain modules and
             # must occur before non-symbol imports.
-            if node.module and node.col_offset == 0:
-                if fullname not in allowsymbolimports:
-                    yield 'direct symbol import from %s' % fullname
+            if node.module and node.col_offset == root_col_offset:
+                found = fromlocal(node.module, node.level)
+                if found and found[2]:  # node.module is a package
+                    prefix = found[0] + '.'
+                    symbols = [n.name for n in node.names
+                               if not fromlocal(prefix + n.name)]
+                else:
+                    symbols = [n.name for n in node.names]
 
-                if seennonsymbolrelative:
-                    yield ('symbol import follows non-symbol import: %s' %
-                           fullname)
+                if symbols and fullname not in allowsymbolimports:
+                    yield msg('direct symbol import %s from %s',
+                              ', '.join(symbols), fullname)
+
+                if symbols and seennonsymbolrelative:
+                    yield msg('symbol import follows non-symbol import: %s',
+                              fullname)
 
             if not node.module:
                 assert node.level
                 seennonsymbolrelative = True
 
                 # Only allow 1 group per level.
-                if node.level in seenlevels and node.col_offset == 0:
-                    yield 'multiple "from %s import" statements' % (
-                           '.' * node.level)
+                if (node.level in seenlevels
+                    and node.col_offset == root_col_offset):
+                    yield msg('multiple "from %s import" statements',
+                              '.' * node.level)
 
                 # Higher-level groups come before lower-level groups.
                 if any(node.level > l for l in seenlevels):
-                    yield 'higher-level import should come first: %s' % (
-                           fullname)
+                    yield msg('higher-level import should come first: %s',
+                              fullname)
 
                 seenlevels.add(node.level)
 
@@ -442,14 +483,14 @@
 
             for n in node.names:
                 if lastentryname and n.name < lastentryname:
-                    yield 'imports from %s not lexically sorted: %s < %s' % (
-                           fullname, n.name, lastentryname)
+                    yield msg('imports from %s not lexically sorted: %s < %s',
+                              fullname, n.name, lastentryname)
 
                 lastentryname = n.name
 
                 if n.name in requirealias and n.asname != requirealias[n.name]:
-                    yield '%s from %s must be "as" aliased to %s' % (
-                          n.name, fullname, requirealias[n.name])
+                    yield msg('%s from %s must be "as" aliased to %s',
+                              n.name, fullname, requirealias[n.name])
 
 def verify_stdlib_on_own_line(root):
     """Given some python source, verify that stdlib imports are done
@@ -460,7 +501,7 @@
     http://bugs.python.org/issue19510.
 
     >>> list(verify_stdlib_on_own_line(ast.parse('import sys, foo')))
-    ['mixed imports\\n   stdlib:    sys\\n   relative:  foo']
+    [('mixed imports\\n   stdlib:    sys\\n   relative:  foo', 1)]
     >>> list(verify_stdlib_on_own_line(ast.parse('import sys, os')))
     []
     >>> list(verify_stdlib_on_own_line(ast.parse('import foo, bar')))
@@ -474,7 +515,7 @@
             if from_stdlib[True] and from_stdlib[False]:
                 yield ('mixed imports\n   stdlib:    %s\n   relative:  %s' %
                        (', '.join(sorted(from_stdlib[True])),
-                        ', '.join(sorted(from_stdlib[False]))))
+                        ', '.join(sorted(from_stdlib[False]))), node.lineno)
 
 class CircularImport(Exception):
     pass
@@ -546,9 +587,9 @@
         src = f.read()
         used_imports[modname] = sorted(
             imported_modules(src, modname, localmods, ignore_nested=True))
-        for error in verify_import_convention(modname, src):
+        for error, lineno in verify_import_convention(modname, src, localmods):
             any_errors = True
-            print source_path, error
+            print '%s:%d: %s' % (source_path, lineno, error)
         f.close()
     cycles = find_cycles(used_imports)
     if cycles:
--- a/contrib/perf.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/contrib/perf.py	Thu Dec 17 17:27:32 2015 -0600
@@ -2,8 +2,9 @@
 '''helper extension to measure performance'''
 
 from mercurial import cmdutil, scmutil, util, commands, obsolete
-from mercurial import repoview, branchmap, merge, copies
+from mercurial import repoview, branchmap, merge, copies, error
 import time, os, sys
+import random
 import functools
 
 formatteropts = commands.formatteropts
@@ -11,11 +12,16 @@
 cmdtable = {}
 command = cmdutil.command(cmdtable)
 
+def getlen(ui):
+    if ui.configbool("perf", "stub"):
+        return lambda x: 1
+    return len
+
 def gettimer(ui, opts=None):
     """return a timer function and formatter: (timer, formatter)
 
-    This functions exist to gather the creation of formatter in a single
-    place instead of duplicating it in all performance command."""
+    This function exists to gather the creation of formatter in a single
+    place instead of duplicating it in all performance commands."""
 
     # enforce an idle period before execution to counteract power management
     # experimental config: perf.presleep
@@ -28,8 +34,15 @@
     ui.fout = ui.ferr
     # get a formatter
     fm = ui.formatter('perf', opts)
+    # stub function, runs code only once instead of in a loop
+    # experimental config: perf.stub
+    if ui.configbool("perf", "stub"):
+        return functools.partial(stub_timer, fm), fm
     return functools.partial(_timer, fm), fm
 
+def stub_timer(fm, func, title=None):
+    func()
+
 def _timer(fm, func, title=None):
     results = []
     begin = time.time()
@@ -91,7 +104,7 @@
     #m = match.always(repo.root, repo.getcwd())
     #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
     #                                                False))))
-    timer, fm = gettimer(ui, **opts)
+    timer, fm = gettimer(ui, opts)
     timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
     fm.end()
 
@@ -193,7 +206,7 @@
     fm.end()
 
 @command('perfdirstatefoldmap', formatteropts)
-def perffilefoldmap(ui, repo, **opts):
+def perfdirstatefoldmap(ui, repo, **opts):
     timer, fm = gettimer(ui, opts)
     dirstate = repo.dirstate
     'a' in dirstate
@@ -239,8 +252,8 @@
     def d():
         # acceptremote is True because we don't want prompts in the middle of
         # our benchmark
-        merge.calculateupdates(repo, wctx, rctx, ancestor, False, False, False,
-                               acceptremote=True)
+        merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
+                               acceptremote=True, followcopies=True)
     timer(d)
     fm.end()
 
@@ -293,14 +306,24 @@
     timer, fm = gettimer(ui, opts)
     cmd = sys.argv[0]
     def d():
-        os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
+        if os.name != 'nt':
+            os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
+        else:
+            os.environ['HGRCPATH'] = ''
+            os.system("%s version -q > NUL" % cmd)
     timer(d)
     fm.end()
 
 @command('perfparents', formatteropts)
 def perfparents(ui, repo, **opts):
     timer, fm = gettimer(ui, opts)
-    nl = [repo.changelog.node(i) for i in xrange(1000)]
+    # control the number of commits perfparents iterates over
+    # experimental config: perf.parentscount
+    count = ui.configint("perf", "parentscount", 1000)
+    if len(repo.changelog) < count:
+        raise error.Abort("repo needs %d commits for this test" % count)
+    repo = repo.unfiltered()
+    nl = [repo.changelog.node(i) for i in xrange(count)]
     def d():
         for n in nl:
             repo.changelog.parents(n)
@@ -308,7 +331,7 @@
     fm.end()
 
 @command('perfctxfiles', formatteropts)
-def perfparents(ui, repo, x, **opts):
+def perfctxfiles(ui, repo, x, **opts):
     x = int(x)
     timer, fm = gettimer(ui, opts)
     def d():
@@ -317,7 +340,7 @@
     fm.end()
 
 @command('perfrawfiles', formatteropts)
-def perfparents(ui, repo, x, **opts):
+def perfrawfiles(ui, repo, x, **opts):
     x = int(x)
     timer, fm = gettimer(ui, opts)
     cl = repo.changelog
@@ -329,10 +352,6 @@
 @command('perflookup', formatteropts)
 def perflookup(ui, repo, rev, **opts):
     timer, fm = gettimer(ui, opts)
-
-@command('perflookup', formatteropts)
-def perflookup(ui, repo, rev, **opts):
-    timer, fm = gettimer(ui, opts)
     timer(lambda: len(repo.lookup(rev)))
     fm.end()
 
@@ -358,10 +377,12 @@
 
 @command('perflog',
          [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
-def perflog(ui, repo, **opts):
+def perflog(ui, repo, rev=None, **opts):
+    if rev is None:
+        rev=[]
     timer, fm = gettimer(ui, opts)
     ui.pushbuffer()
-    timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
+    timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
                                copies=opts.get('rename')))
     ui.popbuffer()
     fm.end()
@@ -381,10 +402,12 @@
     fm.end()
 
 @command('perftemplating', formatteropts)
-def perftemplating(ui, repo, **opts):
+def perftemplating(ui, repo, rev=None, **opts):
+    if rev is None:
+        rev=[]
     timer, fm = gettimer(ui, opts)
     ui.pushbuffer()
-    timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
+    timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
                                template='{date|shortdate} [{rev}:{node|short}]'
                                ' {author|person}: {desc|firstline}\n'))
     ui.popbuffer()
@@ -410,10 +433,13 @@
     timer, fm = gettimer(ui, opts)
     s = repo.store
     s.fncache._load()
+    lock = repo.lock()
+    tr = repo.transaction('perffncachewrite')
     def d():
         s.fncache._dirty = True
-        s.fncache.write()
+        s.fncache.write(tr)
     timer(d)
+    lock.release()
     fm.end()
 
 @command('perffncacheencode', formatteropts)
@@ -454,18 +480,20 @@
     timer, fm = gettimer(ui, opts)
     from mercurial import revlog
     dist = opts['dist']
+    _len = getlen(ui)
     def d():
         r = revlog.revlog(lambda fn: open(fn, 'rb'), file_)
-        for x in xrange(0, len(r), dist):
+        for x in xrange(0, _len(r), dist):
             r.revision(r.node(x))
 
     timer(d)
     fm.end()
 
 @command('perfrevset',
-         [('C', 'clear', False, 'clear volatile cache between each call.')]
+         [('C', 'clear', False, 'clear volatile cache between each call.'),
+          ('', 'contexts', False, 'obtain changectx for each revision')]
          + formatteropts, "REVSET")
-def perfrevset(ui, repo, expr, clear=False, **opts):
+def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
     """benchmark the execution time of a revset
 
     Use the --clean option if need to evaluate the impact of build volatile
@@ -475,7 +503,10 @@
     def d():
         if clear:
             repo.invalidatevolatilesets()
-        for r in repo.revs(expr): pass
+        if contexts:
+            for ctx in repo.set(expr): pass
+        else:
+            for r in repo.revs(expr): pass
     timer(d)
     fm.end()
 
@@ -576,3 +607,79 @@
     timer, fm = gettimer(ui)
     timer(lambda: len(obsolete.obsstore(repo.svfs)))
     fm.end()
+
+@command('perflrucachedict', formatteropts +
+    [('', 'size', 4, 'size of cache'),
+     ('', 'gets', 10000, 'number of key lookups'),
+     ('', 'sets', 10000, 'number of key sets'),
+     ('', 'mixed', 10000, 'number of mixed mode operations'),
+     ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
+    norepo=True)
+def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
+                 mixedgetfreq=50, **opts):
+    def doinit():
+        for i in xrange(10000):
+            util.lrucachedict(size)
+
+    values = []
+    for i in xrange(size):
+        values.append(random.randint(0, sys.maxint))
+
+    # Get mode fills the cache and tests raw lookup performance with no
+    # eviction.
+    getseq = []
+    for i in xrange(gets):
+        getseq.append(random.choice(values))
+
+    def dogets():
+        d = util.lrucachedict(size)
+        for v in values:
+            d[v] = v
+        for key in getseq:
+            value = d[key]
+            value # silence pyflakes warning
+
+    # Set mode tests insertion speed with cache eviction.
+    setseq = []
+    for i in xrange(sets):
+        setseq.append(random.randint(0, sys.maxint))
+
+    def dosets():
+        d = util.lrucachedict(size)
+        for v in setseq:
+            d[v] = v
+
+    # Mixed mode randomly performs gets and sets with eviction.
+    mixedops = []
+    for i in xrange(mixed):
+        r = random.randint(0, 100)
+        if r < mixedgetfreq:
+            op = 0
+        else:
+            op = 1
+
+        mixedops.append((op, random.randint(0, size * 2)))
+
+    def domixed():
+        d = util.lrucachedict(size)
+
+        for op, v in mixedops:
+            if op == 0:
+                try:
+                    d[v]
+                except KeyError:
+                    pass
+            else:
+                d[v] = v
+
+    benches = [
+        (doinit, 'init'),
+        (dogets, 'gets'),
+        (dosets, 'sets'),
+        (domixed, 'mixed')
+    ]
+
+    for fn, title in benches:
+        timer, fm = gettimer(ui, opts)
+        timer(fn, title=title)
+        fm.end()
--- a/contrib/revsetbenchmarks.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/contrib/revsetbenchmarks.py	Thu Dec 17 17:27:32 2015 -0600
@@ -53,10 +53,13 @@
     fullcmd += cmd
     return check_output(fullcmd, stderr=STDOUT)
 
-def perf(revset, target=None):
+def perf(revset, target=None, contexts=False):
     """run benchmark for this very revset"""
     try:
-        output = hg(['perfrevset', revset], repo=target)
+        args = ['perfrevset', revset]
+        if contexts:
+            args.append('--contexts')
+        output = hg(args, repo=target)
         return parseoutput(output)
     except CalledProcessError as exc:
         print >> sys.stderr, 'abort: cannot run revset benchmark: %s' % exc.cmd
@@ -238,6 +241,9 @@
                   default=','.join(DEFAULTVARIANTS),
                   help="comma separated list of variant to test "
                        "(eg: plain,min,sorted) (plain = no modification)")
+parser.add_option('', '--contexts',
+                  action='store_true',
+                  help='obtain changectx from results instead of integer revs')
 
 (options, args) = parser.parse_args()
 
@@ -283,7 +289,7 @@
         varres = {}
         for var in variants:
             varrset = applyvariants(rset, var)
-            data = perf(varrset, target=options.repo)
+            data = perf(varrset, target=options.repo, contexts=options.contexts)
             varres[var] = data
         res.append(varres)
         printresult(variants, idx, varres, len(revsets),
--- a/contrib/wix/templates.wxs	Mon Nov 30 13:47:29 2015 -0600
+++ b/contrib/wix/templates.wxs	Thu Dec 17 17:27:32 2015 -0600
@@ -34,6 +34,7 @@
           <File Name="map-cmdline.default" />
           <File Name="map-cmdline.bisect" />
           <File Name="map-cmdline.xml" />
+          <File Name="map-cmdline.status" />
           <File Name="map-cmdline.phases" />
         </Component>
 
--- a/doc/check-seclevel.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/doc/check-seclevel.py	Thu Dec 17 17:27:32 2015 -0600
@@ -6,9 +6,8 @@
 import optparse
 
 # import from the live mercurial repo
+os.environ['HGMODULEPOLICY'] = 'py'
 sys.path.insert(0, "..")
-# fall back to pure modules if required C extensions are not available
-sys.path.append(os.path.join('..', 'mercurial', 'pure'))
 from mercurial import demandimport; demandimport.enable()
 from mercurial.commands import table
 from mercurial.help import helptable
--- a/doc/gendoc.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/doc/gendoc.py	Thu Dec 17 17:27:32 2015 -0600
@@ -4,10 +4,12 @@
 """
 
 import os, sys, textwrap
+
+# This script is executed during installs and may not have C extensions
+# available. Relax C module requirements.
+os.environ['HGMODULEPOLICY'] = 'allow'
 # import from the live mercurial repo
 sys.path.insert(0, "..")
-# fall back to pure modules if required C extensions are not available
-sys.path.append(os.path.join('..', 'mercurial', 'pure'))
 from mercurial import demandimport; demandimport.enable()
 from mercurial import minirst
 from mercurial.commands import table, globalopts
--- a/hgext/censor.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/censor.py	Thu Dec 17 17:27:32 2015 -0600
@@ -28,6 +28,7 @@
 from mercurial.node import short
 from mercurial import cmdutil, error, filelog, revlog, scmutil, util
 from mercurial.i18n import _
+from mercurial import lock as lockmod
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
@@ -42,6 +43,15 @@
      ('t', 'tombstone', '', _('replacement tombstone data'), _('TEXT'))],
     _('-r REV [-t TEXT] [FILE]'))
 def censor(ui, repo, path, rev='', tombstone='', **opts):
+    wlock = lock = None
+    try:
+        wlock = repo.wlock()
+        lock = repo.lock()
+        return _docensor(ui, repo, path, rev, tombstone, **opts)
+    finally:
+        lockmod.release(lock, wlock)
+
+def _docensor(ui, repo, path, rev='', tombstone='', **opts):
     if not path:
         raise error.Abort(_('must specify file path to censor'))
     if not rev:
--- a/hgext/clonebundles.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/clonebundles.py	Thu Dec 17 17:27:32 2015 -0600
@@ -47,7 +47,7 @@
 * Generating bundle files of repository content (typically periodically,
   such as once per day).
 * A file server that clients have network access to and that Python knows
-  how to talk to through its normal URL handling facility (typically a
+  how to talk to through its normal URL handling facility (typically an
   HTTP server).
 * A process for keeping the bundles manifest in sync with available bundle
   files.
--- a/hgext/color.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/color.py	Thu Dec 17 17:27:32 2015 -0600
@@ -419,16 +419,6 @@
             _styles[status] = ' '.join(good)
 
 class colorui(uimod.ui):
-    def popbuffer(self, labeled=False):
-        if self._colormode is None:
-            return super(colorui, self).popbuffer(labeled)
-
-        self._bufferstates.pop()
-        if labeled:
-            return ''.join(self.label(a, label) for a, label
-                           in self._buffers.pop())
-        return ''.join(a for a, label in self._buffers.pop())
-
     _colormode = 'ansi'
     def write(self, *args, **opts):
         if self._colormode is None:
@@ -436,13 +426,16 @@
 
         label = opts.get('label', '')
         if self._buffers:
-            self._buffers[-1].extend([(str(a), label) for a in args])
+            if self._bufferapplylabels:
+                self._buffers[-1].extend(self.label(a, label) for a in args)
+            else:
+                self._buffers[-1].extend(args)
         elif self._colormode == 'win32':
             for a in args:
                 win32print(a, super(colorui, self).write, **opts)
         else:
             return super(colorui, self).write(
-                *[self.label(str(a), label) for a in args], **opts)
+                *[self.label(a, label) for a in args], **opts)
 
     def write_err(self, *args, **opts):
         if self._colormode is None:
@@ -456,7 +449,7 @@
                 win32print(a, super(colorui, self).write_err, **opts)
         else:
             return super(colorui, self).write_err(
-                *[self.label(str(a), label) for a in args], **opts)
+                *[self.label(a, label) for a in args], **opts)
 
     def showlabel(self, msg, label):
         if label and msg:
--- a/hgext/convert/hg.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/convert/hg.py	Thu Dec 17 17:27:32 2015 -0600
@@ -23,6 +23,7 @@
 from mercurial.node import bin, hex, nullid
 from mercurial import hg, util, context, bookmarks, error, scmutil, exchange
 from mercurial import phases
+from mercurial import lock as lockmod
 from mercurial import merge as mergemod
 
 from common import NoRepo, commit, converter_source, converter_sink, mapfile
@@ -191,7 +192,6 @@
             self.repo, p1ctx, p2ctx, anc,
             True,  # branchmerge
             True,  # force
-            False, # partial
             False, # acceptremote
             False, # followcopies
         )
@@ -410,12 +410,19 @@
     def putbookmarks(self, updatedbookmark):
         if not len(updatedbookmark):
             return
-
-        self.ui.status(_("updating bookmarks\n"))
-        destmarks = self.repo._bookmarks
-        for bookmark in updatedbookmark:
-            destmarks[bookmark] = bin(updatedbookmark[bookmark])
-        destmarks.write()
+        wlock = lock = tr = None
+        try:
+            wlock = self.repo.wlock()
+            lock = self.repo.lock()
+            tr = self.repo.transaction('bookmark')
+            self.ui.status(_("updating bookmarks\n"))
+            destmarks = self.repo._bookmarks
+            for bookmark in updatedbookmark:
+                destmarks[bookmark] = bin(updatedbookmark[bookmark])
+            destmarks.recordchange(tr)
+            tr.close()
+        finally:
+            lockmod.release(lock, wlock, tr)
 
     def hascommitfrommap(self, rev):
         # the exact semantics of clonebranches is unclear so we can't say no
--- a/hgext/convert/subversion.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/convert/subversion.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1041,7 +1041,7 @@
             relpaths.append(p.strip('/'))
         args = [self.baseurl, relpaths, start, end, limit,
                 discover_changed_paths, strict_node_history]
-        # undocumented feature: debugsvnlog can be disabled
+        # developer config: convert.svn.debugsvnlog
         if not self.ui.configbool('convert', 'svn.debugsvnlog', True):
             return directlogstream(*args)
         arg = encodeargs(args)
--- a/hgext/gpg.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/gpg.py	Thu Dec 17 17:27:32 2015 -0600
@@ -9,6 +9,7 @@
 from mercurial import util, commands, match, cmdutil, error
 from mercurial import node as hgnode
 from mercurial.i18n import _
+from mercurial import lock as lockmod
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
@@ -168,7 +169,7 @@
             ui.write("%-30s %s\n" % (keystr(ui, k), r))
 
 @command("sigcheck", [], _('hg sigcheck REV'))
-def check(ui, repo, rev):
+def sigcheck(ui, repo, rev):
     """verify all the signatures there may be for a particular revision"""
     mygpg = newgpg(ui)
     rev = repo.lookup(rev)
@@ -222,7 +223,14 @@
 
     See :hg:`help dates` for a list of formats valid for -d/--date.
     """
+    wlock = None
+    try:
+        wlock = repo.wlock()
+        return _dosign(ui, repo, *revs, **opts)
+    finally:
+        lockmod.release(wlock)
 
+def _dosign(ui, repo, *revs, **opts):
     mygpg = newgpg(ui, **opts)
     sigver = "0"
     sigmessage = ""
--- a/hgext/graphlog.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/graphlog.py	Thu Dec 17 17:27:32 2015 -0600
@@ -49,7 +49,7 @@
     ] + commands.logopts + commands.walkopts,
     _('[OPTION]... [FILE]'),
     inferrepo=True)
-def graphlog(ui, repo, *pats, **opts):
+def glog(ui, repo, *pats, **opts):
     """show revision history alongside an ASCII revision graph
 
     Print a revision history alongside a revision graph drawn with
--- a/hgext/histedit.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/histedit.py	Thu Dec 17 17:27:32 2015 -0600
@@ -149,6 +149,13 @@
 
   [histedit]
   linelen = 120      # truncate rule lines at 120 characters
+
+``hg histedit`` attempts to automatically choose an appropriate base
+revision to use. To change which base revision is used, define a
+revset in your configuration file::
+
+  [histedit]
+  defaultrev = only(.) & draft()
 """
 
 try:
@@ -166,6 +173,7 @@
 from mercurial import error
 from mercurial import copies
 from mercurial import context
+from mercurial import destutil
 from mercurial import exchange
 from mercurial import extensions
 from mercurial import hg
@@ -181,6 +189,18 @@
 cmdtable = {}
 command = cmdutil.command(cmdtable)
 
+class _constraints(object):
+    # aborts if there are multiple rules for one node
+    noduplicates = 'noduplicates'
+    # abort if the node does belong to edited stack
+    forceother = 'forceother'
+    # abort if the node doesn't belong to edited stack
+    noother = 'noother'
+
+    @classmethod
+    def known(cls):
+        return set([v for k, v in cls.__dict__.items() if k[0] != '_'])
+
 # Note for extension authors: ONLY specify testedwith = 'internal' for
 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
 # be specifying the version(s) of Mercurial they are tested with, or
@@ -203,10 +223,10 @@
 """)
 
 class histeditstate(object):
-    def __init__(self, repo, parentctxnode=None, rules=None, keep=None,
+    def __init__(self, repo, parentctxnode=None, actions=None, keep=None,
             topmost=None, replacements=None, lock=None, wlock=None):
         self.repo = repo
-        self.rules = rules
+        self.actions = actions
         self.keep = keep
         self.topmost = topmost
         self.parentctxnode = parentctxnode
@@ -236,7 +256,9 @@
             parentctxnode, rules, keep, topmost, replacements, backupfile = data
 
         self.parentctxnode = parentctxnode
-        self.rules = rules
+        rules = "\n".join(["%s %s" % (verb, rest) for [verb, rest] in rules])
+        actions = parserules(rules, self)
+        self.actions = actions
         self.keep = keep
         self.topmost = topmost
         self.replacements = replacements
@@ -248,10 +270,9 @@
         fp.write('%s\n' % node.hex(self.parentctxnode))
         fp.write('%s\n' % node.hex(self.topmost))
         fp.write('%s\n' % self.keep)
-        fp.write('%d\n' % len(self.rules))
-        for rule in self.rules:
-            fp.write('%s\n' % rule[0]) # action
-            fp.write('%s\n' % rule[1]) # remainder
+        fp.write('%d\n' % len(self.actions))
+        for action in self.actions:
+            fp.write('%s\n' % action.tostate())
         fp.write('%d\n' % len(self.replacements))
         for replacement in self.replacements:
             fp.write('%s%s\n' % (node.hex(replacement[0]), ''.join(node.hex(r)
@@ -316,6 +337,7 @@
     def inprogress(self):
         return self.repo.vfs.exists('histedit-state')
 
+
 class histeditaction(object):
     def __init__(self, state, node):
         self.state = state
@@ -326,13 +348,54 @@
     def fromrule(cls, state, rule):
         """Parses the given rule, returning an instance of the histeditaction.
         """
-        repo = state.repo
         rulehash = rule.strip().split(' ', 1)[0]
+        return cls(state, node.bin(rulehash))
+
+    def verify(self):
+        """ Verifies semantic correctness of the rule"""
+        repo = self.repo
+        ha = node.hex(self.node)
         try:
-            node = repo[rulehash].node()
+            self.node = repo[ha].node()
         except error.RepoError:
-            raise error.Abort(_('unknown changeset %s listed') % rulehash[:12])
-        return cls(state, node)
+            raise error.Abort(_('unknown changeset %s listed')
+                              % ha[:12])
+
+    def torule(self):
+        """build a histedit rule line for an action
+
+        by default lines are in the form:
+        <hash> <rev> <summary>
+        """
+        ctx = self.repo[self.node]
+        summary = ''
+        if ctx.description():
+            summary = ctx.description().splitlines()[0]
+        line = '%s %s %d %s' % (self.verb, ctx, ctx.rev(), summary)
+        # trim to 75 columns by default so it's not stupidly wide in my editor
+        # (the 5 more are left for verb)
+        maxlen = self.repo.ui.configint('histedit', 'linelen', default=80)
+        maxlen = max(maxlen, 22) # avoid truncating hash
+        return util.ellipsis(line, maxlen)
+
+    def tostate(self):
+        """Print an action in format used by histedit state files
+           (the first line is a verb, the remainder is the second)
+        """
+        return "%s\n%s" % (self.verb, node.hex(self.node))
+
+    def constraints(self):
+        """Return a set of constrains that this action should be verified for
+        """
+        return set([_constraints.noduplicates, _constraints.noother])
+
+    def nodetoverify(self):
+        """Returns a node associated with the action that will be used for
+        verification purposes.
+
+        If the action doesn't correspond to node it should return None
+        """
+        return self.node
 
     def run(self):
         """Runs the action. The default behavior is simply apply the action's
@@ -346,7 +409,7 @@
         parentctx, but does not commit them."""
         repo = self.repo
         rulectx = repo[self.node]
-        hg.update(repo, self.state.parentctxnode)
+        hg.update(repo, self.state.parentctxnode, quietempty=True)
         stats = applychanges(repo.ui, repo, rulectx, {})
         if stats and stats[3] > 0:
             raise error.InterventionRequired(_('Fix up the change and run '
@@ -502,6 +565,29 @@
                          editor=editor)
     return repo.commitctx(new)
 
+def _isdirtywc(repo):
+    return repo[None].dirty(missing=True)
+
+def abortdirty():
+    raise error.Abort(_('working copy has pending changes'),
+        hint=_('amend, commit, or revert them and run histedit '
+            '--continue, or abort with histedit --abort'))
+
+
+actiontable = {}
+actionlist = []
+
+def addhisteditaction(verbs):
+    def wrap(cls):
+        cls.verb = verbs[0]
+        for verb in verbs:
+            actiontable[verb] = cls
+        actionlist.append(cls)
+        return cls
+    return wrap
+
+
+@addhisteditaction(['pick', 'p'])
 class pick(histeditaction):
     def run(self):
         rulectx = self.repo[self.node]
@@ -511,11 +597,12 @@
 
         return super(pick, self).run()
 
+@addhisteditaction(['edit', 'e'])
 class edit(histeditaction):
     def run(self):
         repo = self.repo
         rulectx = repo[self.node]
-        hg.update(repo, self.state.parentctxnode)
+        hg.update(repo, self.state.parentctxnode, quietempty=True)
         applychanges(repo.ui, repo, rulectx, {})
         raise error.InterventionRequired(
             _('Make changes as needed, you may commit or record as needed '
@@ -525,6 +612,7 @@
     def commiteditor(self):
         return cmdutil.getcommiteditor(edit=True, editform='histedit.edit')
 
+@addhisteditaction(['fold', 'f'])
 class fold(histeditaction):
     def continuedirty(self):
         repo = self.repo
@@ -618,6 +706,24 @@
             replacements.append((ich, (n,)))
         return repo[n], replacements
 
+class base(histeditaction):
+    def constraints(self):
+        return set([_constraints.forceother])
+
+    def run(self):
+        if self.repo['.'].node() != self.node:
+            mergemod.update(self.repo, self.node, False, True)
+            #                                     branchmerge, force)
+        return self.continueclean()
+
+    def continuedirty(self):
+        abortdirty()
+
+    def continueclean(self):
+        basectx = self.repo['.']
+        return basectx, []
+
+@addhisteditaction(['_multifold'])
 class _multifold(fold):
     """fold subclass used for when multiple folds happen in a row
 
@@ -630,6 +736,7 @@
     def skipprompt(self):
         return True
 
+@addhisteditaction(["roll", "r"])
 class rollup(fold):
     def mergedescs(self):
         return False
@@ -637,11 +744,13 @@
     def skipprompt(self):
         return True
 
+@addhisteditaction(["drop", "d"])
 class drop(histeditaction):
     def run(self):
         parentctx = self.repo[self.state.parentctxnode]
         return parentctx, [(self.node, tuple())]
 
+@addhisteditaction(["mess", "m"])
 class message(histeditaction):
     def commiteditor(self):
         return cmdutil.getcommiteditor(edit=True, editform='histedit.mess')
@@ -672,20 +781,6 @@
         raise error.Abort(msg, hint=hint)
     return repo.lookup(roots[0])
 
-actiontable = {'p': pick,
-               'pick': pick,
-               'e': edit,
-               'edit': edit,
-               'f': fold,
-               'fold': fold,
-               '_multifold': _multifold,
-               'r': rollup,
-               'roll': rollup,
-               'd': drop,
-               'drop': drop,
-               'm': message,
-               'mess': message,
-               }
 
 @command('histedit',
     [('', 'commands', '',
@@ -699,13 +794,19 @@
      ('f', 'force', False,
       _('force outgoing even for unrelated repositories')),
      ('r', 'rev', [], _('first revision to be edited'), _('REV'))],
-     _("ANCESTOR | --outgoing [URL]"))
+     _("[ANCESTOR] | --outgoing [URL]"))
 def histedit(ui, repo, *freeargs, **opts):
     """interactively edit changeset history
 
-    This command edits changesets between ANCESTOR and the parent of
+    This command edits changesets between an ANCESTOR and the parent of
     the working directory.
 
+    The value from the "histedit.defaultrev" config option is used as a
+    revset to select the base revision when ANCESTOR is not specified.
+    The first revision returned by the revset is used. By default, this
+    selects the editable history that is unique to the ancestry of the
+    working directory.
+
     With --outgoing, this edits changesets not found in the
     destination repository. If URL of the destination is omitted, the
     'default-push' (or 'default') path will be used.
@@ -719,6 +820,56 @@
     such ambiguous situation. See :hg:`help revsets` for detail about
     selecting revisions.
 
+    .. container:: verbose
+
+       Examples:
+
+         - A number of changes have been made.
+           Revision 3 is no longer needed.
+
+           Start history editing from revision 3::
+
+             hg histedit -r 3
+
+           An editor opens, containing the list of revisions,
+           with specific actions specified::
+
+             pick 5339bf82f0ca 3 Zworgle the foobar
+             pick 8ef592ce7cc4 4 Bedazzle the zerlog
+             pick 0a9639fcda9d 5 Morgify the cromulancy
+
+           Additional information about the possible actions
+           to take appears below the list of revisions.
+
+           To remove revision 3 from the history,
+           its action (at the beginning of the relevant line)
+           is changed to 'drop'::
+
+             drop 5339bf82f0ca 3 Zworgle the foobar
+             pick 8ef592ce7cc4 4 Bedazzle the zerlog
+             pick 0a9639fcda9d 5 Morgify the cromulancy
+
+         - A number of changes have been made.
+           Revision 2 and 4 need to be swapped.
+
+           Start history editing from revision 2::
+
+             hg histedit -r 2
+
+           An editor opens, containing the list of revisions,
+           with specific actions specified::
+
+             pick 252a1af424ad 2 Blorb a morgwazzle
+             pick 5339bf82f0ca 3 Zworgle the foobar
+             pick 8ef592ce7cc4 4 Bedazzle the zerlog
+
+           To swap revision 2 and 4, its lines are swapped
+           in the editor::
+
+             pick 8ef592ce7cc4 4 Bedazzle the zerlog
+             pick 5339bf82f0ca 3 Zworgle the foobar
+             pick 252a1af424ad 2 Blorb a morgwazzle
+
     Returns 0 on success, 1 if user intervention is required (not only
     for intentional "edit" command, but also for resolving unexpected
     conflicts).
@@ -728,11 +879,16 @@
         state.wlock = repo.wlock()
         state.lock = repo.lock()
         _histedit(ui, repo, state, *freeargs, **opts)
+    except error.Abort:
+        if repo.vfs.exists('histedit-last-edit.txt'):
+            ui.warn(_('warning: histedit rules saved '
+                      'to: .hg/histedit-last-edit.txt\n'))
+        raise
     finally:
         release(state.lock, state.wlock)
 
 def _histedit(ui, repo, state, *freeargs, **opts):
-    # TODO only abort if we try and histedit mq patches, not just
+    # TODO only abort if we try to histedit mq patches, not just
     # blanket if mq patches are applied somewhere
     mq = getattr(repo, 'mq', None)
     if mq and mq.applied:
@@ -775,10 +931,10 @@
         else:
             revs.extend(freeargs)
             if len(revs) == 0:
-                # experimental config: histedit.defaultrev
-                histeditdefault = ui.config('histedit', 'defaultrev')
-                if histeditdefault:
-                    revs.append(histeditdefault)
+                defaultrev = destutil.desthistedit(ui, repo)
+                if defaultrev is not None:
+                    revs.append(defaultrev)
+
             if len(revs) != 1:
                 raise error.Abort(
                     _('histedit requires exactly one ancestor revision'))
@@ -797,7 +953,7 @@
         if not rules:
             comment = editcomment % (node.short(state.parentctxnode),
                                      node.short(state.topmost))
-            rules = ruleeditor(repo, ui, state.rules, comment)
+            rules = ruleeditor(repo, ui, state.actions, comment)
         else:
             if rules == '-':
                 f = sys.stdin
@@ -805,10 +961,11 @@
                 f = open(rules)
             rules = f.read()
             f.close()
-        rules = [l for l in (r.strip() for r in rules.splitlines())
-                 if l and not l.startswith('#')]
-        rules = verifyrules(rules, repo, [repo[c] for [_a, c] in state.rules])
-        state.rules = rules
+        actions = parserules(rules, state)
+        ctxs = [repo[act.nodetoverify()] \
+                for act in state.actions if act.nodetoverify()]
+        verifyactions(actions, state, ctxs)
+        state.actions = actions
         state.write()
         return
     elif goal == 'abort':
@@ -840,7 +997,7 @@
             # check whether we should update away
             if repo.unfiltered().revs('parents() and (%n  or %ln::)',
                                     state.parentctxnode, leafs | tmpnodes):
-                hg.clean(repo, state.topmost)
+                hg.clean(repo, state.topmost, show_stats=True, quietempty=True)
             cleanupnode(ui, repo, 'created', tmpnodes)
             cleanupnode(ui, repo, 'temp', leafs)
         except Exception:
@@ -856,6 +1013,8 @@
         cmdutil.checkunfinished(repo)
         cmdutil.bailifchanged(repo)
 
+        if repo.vfs.exists('histedit-last-edit.txt'):
+            repo.vfs.unlink('histedit-last-edit.txt')
         topmost, empty = repo.dirstate.parents()
         if outg:
             if freeargs:
@@ -878,7 +1037,8 @@
         ctxs = [repo[r] for r in revs]
         if not rules:
             comment = editcomment % (node.short(root), node.short(topmost))
-            rules = ruleeditor(repo, ui, [['pick', c] for c in ctxs], comment)
+            actions = [pick(state, r) for r in revs]
+            rules = ruleeditor(repo, ui, actions, comment)
         else:
             if rules == '-':
                 f = sys.stdin
@@ -886,14 +1046,13 @@
                 f = open(rules)
             rules = f.read()
             f.close()
-        rules = [l for l in (r.strip() for r in rules.splitlines())
-                 if l and not l.startswith('#')]
-        rules = verifyrules(rules, repo, ctxs)
+        actions = parserules(rules, state)
+        verifyactions(actions, state, ctxs)
 
         parentctxnode = repo[root].parents()[0].node()
 
         state.parentctxnode = parentctxnode
-        state.rules = rules
+        state.actions = actions
         state.topmost = topmost
         state.replacements = replacements
 
@@ -906,23 +1065,23 @@
 
     # preprocess rules so that we can hide inner folds from the user
     # and only show one editor
-    rules = state.rules[:]
-    for idx, ((action, ha), (nextact, unused)) in enumerate(
-            zip(rules, rules[1:] + [(None, None)])):
-        if action == 'fold' and nextact == 'fold':
-            state.rules[idx] = '_multifold', ha
+    actions = state.actions[:]
+    for idx, (action, nextact) in enumerate(
+            zip(actions, actions[1:] + [None])):
+        if action.verb == 'fold' and nextact and nextact.verb == 'fold':
+            state.actions[idx].__class__ = _multifold
 
-    while state.rules:
+    while state.actions:
         state.write()
-        action, ha = state.rules.pop(0)
-        ui.debug('histedit: processing %s %s\n' % (action, ha[:12]))
-        actobj = actiontable[action].fromrule(state, ha)
+        actobj = state.actions.pop(0)
+        ui.debug('histedit: processing %s %s\n' % (actobj.verb,\
+                                                   actobj.torule()))
         parentctx, replacement_ = actobj.run()
         state.parentctxnode = parentctx.node()
         state.replacements.extend(replacement_)
     state.write()
 
-    hg.update(repo, state.parentctxnode)
+    hg.update(repo, state.parentctxnode, quietempty=True)
 
     mapping, tmpnodes, created, ntm = processreplacement(state)
     if mapping:
@@ -966,17 +1125,13 @@
 
 def bootstrapcontinue(ui, state, opts):
     repo = state.repo
-    if state.rules:
-        action, currentnode = state.rules.pop(0)
-
-        actobj = actiontable[action].fromrule(state, currentnode)
+    if state.actions:
+        actobj = state.actions.pop(0)
 
-        s = repo.status()
-        if s.modified or s.added or s.removed or s.deleted:
+        if _isdirtywc(repo):
             actobj.continuedirty()
-            s = repo.status()
-            if s.modified or s.added or s.removed or s.deleted:
-                raise error.Abort(_("working copy still dirty"))
+            if _isdirtywc(repo):
+                abortdirty()
 
         parentctx, replacements = actobj.continueclean()
 
@@ -1002,32 +1157,15 @@
                              hint=_('see "hg help phases" for details'))
     return [c.node() for c in ctxs]
 
-def makedesc(repo, action, rev):
-    """build a initial action line for a ctx
-
-    line are in the form:
-
-      <action> <hash> <rev> <summary>
-    """
-    ctx = repo[rev]
-    summary = ''
-    if ctx.description():
-        summary = ctx.description().splitlines()[0]
-    line = '%s %s %d %s' % (action, ctx, ctx.rev(), summary)
-    # trim to 80 columns so it's not stupidly wide in my editor
-    maxlen = repo.ui.configint('histedit', 'linelen', default=80)
-    maxlen = max(maxlen, 22) # avoid truncating hash
-    return util.ellipsis(line, maxlen)
-
-def ruleeditor(repo, ui, rules, editcomment=""):
+def ruleeditor(repo, ui, actions, editcomment=""):
     """open an editor to edit rules
 
     rules are in the format [ [act, ctx], ...] like in state.rules
     """
-    rules = '\n'.join([makedesc(repo, act, rev) for [act, rev] in rules])
+    rules = '\n'.join([act.torule() for act in actions])
     rules += '\n\n'
     rules += editcomment
-    rules = ui.edit(rules, ui.username())
+    rules = ui.edit(rules, ui.username(), {'prefix': 'histedit'})
 
     # Save edit rules in .hg/histedit-last-edit.txt in case
     # the user needs to ask for help after something
@@ -1038,40 +1176,59 @@
 
     return rules
 
-def verifyrules(rules, repo, ctxs):
-    """Verify that there exists exactly one edit rule per given changeset.
+def parserules(rules, state):
+    """Read the histedit rules string and return list of action objects """
+    rules = [l for l in (r.strip() for r in rules.splitlines())
+                if l and not l.startswith('#')]
+    actions = []
+    for r in rules:
+        if ' ' not in r:
+            raise error.Abort(_('malformed line "%s"') % r)
+        verb, rest = r.split(' ', 1)
+
+        if verb not in actiontable:
+            raise error.Abort(_('unknown action "%s"') % verb)
+
+        action = actiontable[verb].fromrule(state, rest)
+        actions.append(action)
+    return actions
+
+def verifyactions(actions, state, ctxs):
+    """Verify that there exists exactly one action per given changeset and
+    other constraints.
 
     Will abort if there are to many or too few rules, a malformed rule,
     or a rule on a changeset outside of the user-given range.
     """
-    parsed = []
     expected = set(c.hex() for c in ctxs)
     seen = set()
-    for r in rules:
-        if ' ' not in r:
-            raise error.Abort(_('malformed line "%s"') % r)
-        action, rest = r.split(' ', 1)
-        ha = rest.strip().split(' ', 1)[0]
-        try:
-            ha = repo[ha].hex()
-        except error.RepoError:
-            raise error.Abort(_('unknown changeset %s listed') % ha[:12])
-        if ha not in expected:
-            raise error.Abort(
-                _('may not use changesets other than the ones listed'))
-        if ha in seen:
-            raise error.Abort(_('duplicated command for changeset %s') %
-                    ha[:12])
-        seen.add(ha)
-        if action not in actiontable or action.startswith('_'):
-            raise error.Abort(_('unknown action "%s"') % action)
-        parsed.append([action, ha])
+    for action in actions:
+        action.verify()
+        constraints = action.constraints()
+        for constraint in constraints:
+            if constraint not in _constraints.known():
+                raise error.Abort(_('unknown constraint "%s"') % constraint)
+
+        nodetoverify = action.nodetoverify()
+        if nodetoverify is not None:
+            ha = node.hex(nodetoverify)
+            if _constraints.noother in constraints and ha not in expected:
+                raise error.Abort(
+                    _('may not use "%s" with changesets '
+                      'other than the ones listed') % action.verb)
+            if _constraints.forceother in constraints and ha in expected:
+                raise error.Abort(
+                    _('may not use "%s" with changesets '
+                      'within the edited list') % action.verb)
+            if _constraints.noduplicates in constraints and ha in seen:
+                raise error.Abort(_('duplicated command for changeset %s') %
+                        ha[:12])
+            seen.add(ha)
     missing = sorted(expected - seen)  # sort to stabilize output
     if missing:
         raise error.Abort(_('missing rules for changeset %s') %
                 missing[0][:12],
-                hint=_('do you want to use the drop action?'))
-    return parsed
+                hint=_('use "drop %s" to discard the change') % missing[0][:12])
 
 def newnodestoabort(state):
     """process the list of replacements to return
@@ -1179,13 +1336,20 @@
             # nothing to move
         moves.append((bk, new[-1]))
     if moves:
-        marks = repo._bookmarks
-        for mark, new in moves:
-            old = marks[mark]
-            ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
-                    % (mark, node.short(old), node.short(new)))
-            marks[mark] = new
-        marks.write()
+        lock = tr = None
+        try:
+            lock = repo.lock()
+            tr = repo.transaction('histedit')
+            marks = repo._bookmarks
+            for mark, new in moves:
+                old = marks[mark]
+                ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
+                        % (mark, node.short(old), node.short(new)))
+                marks[mark] = new
+            marks.recordchange(tr)
+            tr.close()
+        finally:
+            release(tr, lock)
 
 def cleanupnode(ui, repo, name, nodes):
     """strip a group of nodes from the repository
@@ -1219,8 +1383,8 @@
     if os.path.exists(os.path.join(repo.path, 'histedit-state')):
         state = histeditstate(repo)
         state.read()
-        histedit_nodes = set([repo[rulehash].node() for (action, rulehash)
-                             in state.rules if rulehash in repo])
+        histedit_nodes = set([action.nodetoverify() for action
+                             in state.actions if action.nodetoverify()])
         strip_nodes = set([repo[n].node() for n in nodelist])
         common_nodes = histedit_nodes & strip_nodes
         if common_nodes:
@@ -1235,14 +1399,16 @@
         return
     state = histeditstate(repo)
     state.read()
-    if state.rules:
+    if state.actions:
         # i18n: column positioning for "hg summary"
         ui.write(_('hist:   %s (histedit --continue)\n') %
                  (ui.label(_('%d remaining'), 'histedit.remaining') %
-                  len(state.rules)))
+                  len(state.actions)))
 
 def extsetup(ui):
     cmdutil.summaryhooks.add('histedit', summaryhook)
     cmdutil.unfinishedstates.append(
         ['histedit-state', False, True, _('histedit in progress'),
          _("use 'hg histedit --continue' or 'hg histedit --abort'")])
+    if ui.configbool("experimental", "histeditng"):
+        globals()['base'] = addhisteditaction(['base', 'b'])(base)
--- a/hgext/largefiles/lfcommands.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/largefiles/lfcommands.py	Thu Dec 17 17:27:32 2015 -0600
@@ -444,12 +444,14 @@
         updated, removed = 0, 0
         for lfile in lfiles:
             abslfile = repo.wjoin(lfile)
+            abslfileorig = cmdutil.origpath(ui, repo, abslfile)
             absstandin = repo.wjoin(lfutil.standin(lfile))
+            absstandinorig = cmdutil.origpath(ui, repo, absstandin)
             if os.path.exists(absstandin):
-                if (os.path.exists(absstandin + '.orig') and
+                if (os.path.exists(absstandinorig) and
                     os.path.exists(abslfile)):
-                    shutil.copyfile(abslfile, abslfile + '.orig')
-                    util.unlinkpath(absstandin + '.orig')
+                    shutil.copyfile(abslfile, abslfileorig)
+                    util.unlinkpath(absstandinorig)
                 expecthash = lfutil.readstandin(repo, lfile)
                 if expecthash != '':
                     if lfile not in repo[None]: # not switched to normal file
--- a/hgext/largefiles/overrides.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/largefiles/overrides.py	Thu Dec 17 17:27:32 2015 -0600
@@ -458,11 +458,11 @@
 # writing the files into the working copy and lfcommands.updatelfiles
 # will update the largefiles.
 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
-                             partial, acceptremote, followcopies):
+                             acceptremote, followcopies, matcher=None):
     overwrite = force and not branchmerge
     actions, diverge, renamedelete = origfn(
-        repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
-        followcopies)
+        repo, p1, p2, pas, branchmerge, force, acceptremote,
+        followcopies, matcher=matcher)
 
     if overwrite:
         return actions, diverge, renamedelete
@@ -481,6 +481,9 @@
         (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
         (sm, sargs, smsg) = actions.get(standin, (None, None, None))
         if sm in ('g', 'dc') and lm != 'r':
+            if sm == 'dc':
+                f1, f2, fa, move, anc = sargs
+                sargs = (p2[f2].flags(),)
             # Case 1: normal file in the working copy, largefile in
             # the second parent
             usermsg = _('remote turned local normal file %s into a largefile\n'
@@ -496,6 +499,9 @@
                 else:
                     actions[standin] = ('r', None, 'replaced by non-standin')
         elif lm in ('g', 'dc') and sm != 'r':
+            if lm == 'dc':
+                f1, f2, fa, move, anc = largs
+                largs = (p2[f2].flags(),)
             # Case 2: largefile in the working copy, normal file in
             # the second parent
             usermsg = _('remote turned local largefile %s into a normal file\n'
@@ -538,7 +544,7 @@
 # largefiles. This will handle identical edits without prompting the user.
 def overridefilemerge(origfn, premerge, repo, mynode, orig, fcd, fco, fca,
                       labels=None):
-    if not lfutil.isstandin(orig):
+    if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
         return origfn(premerge, repo, mynode, orig, fcd, fco, fca,
                       labels=labels)
 
@@ -555,7 +561,7 @@
                (lfutil.splitstandin(orig), ahash, dhash, ohash),
              0) == 1)):
         repo.wwrite(fcd.path(), fco.data(), fco.flags())
-    return True, 0
+    return True, 0, False
 
 def copiespathcopies(orig, ctx1, ctx2, match=None):
     copies = orig(ctx1, ctx2, match=match)
@@ -1358,8 +1364,11 @@
         err = 0
     return err
 
-def mergeupdate(orig, repo, node, branchmerge, force, partial,
+def mergeupdate(orig, repo, node, branchmerge, force,
                 *args, **kwargs):
+    matcher = kwargs.get('matcher', None)
+    # note if this is a partial update
+    partial = matcher and not matcher.always()
     wlock = repo.wlock()
     try:
         # branch |       |         |
@@ -1399,7 +1408,7 @@
 
         oldstandins = lfutil.getstandinsstate(repo)
 
-        result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
+        result = orig(repo, node, branchmerge, force, *args, **kwargs)
 
         newstandins = lfutil.getstandinsstate(repo)
         filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
--- a/hgext/mq.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/mq.py	Thu Dec 17 17:27:32 2015 -0600
@@ -68,6 +68,7 @@
 from mercurial import commands, cmdutil, hg, scmutil, util, revset
 from mercurial import extensions, error, phases
 from mercurial import patch as patchmod
+from mercurial import lock as lockmod
 from mercurial import localrepo
 from mercurial import subrepo
 import os, re, errno, shutil
@@ -697,11 +698,13 @@
             absf = repo.wjoin(f)
             if os.path.lexists(absf):
                 self.ui.note(_('saving current version of %s as %s\n') %
-                             (f, f + '.orig'))
+                             (f, cmdutil.origpath(self.ui, repo, f)))
+
+                absorig = cmdutil.origpath(self.ui, repo, absf)
                 if copy:
-                    util.copyfile(absf, absf + '.orig')
+                    util.copyfile(absf, absorig)
                 else:
-                    util.rename(absf, absf + '.orig')
+                    util.rename(absf, absorig)
 
     def printdiff(self, repo, diffopts, node1, node2=None, files=None,
                   fp=None, changes=None, opts={}):
@@ -1788,27 +1791,34 @@
 
                 # Ensure we create a new changeset in the same phase than
                 # the old one.
-                n = newcommit(repo, oldphase, message, user, ph.date,
+                lock = tr = None
+                try:
+                    lock = repo.lock()
+                    tr = repo.transaction('mq')
+                    n = newcommit(repo, oldphase, message, user, ph.date,
                               match=match, force=True, editor=editor)
-                # only write patch after a successful commit
-                c = [list(x) for x in refreshchanges]
-                if inclsubs:
-                    self.putsubstate2changes(substatestate, c)
-                chunks = patchmod.diff(repo, patchparent,
-                                       changes=c, opts=diffopts)
-                comments = str(ph)
-                if comments:
-                    patchf.write(comments)
-                for chunk in chunks:
-                    patchf.write(chunk)
-                patchf.close()
-
-                marks = repo._bookmarks
-                for bm in bmlist:
-                    marks[bm] = n
-                marks.write()
-
-                self.applied.append(statusentry(n, patchfn))
+                    # only write patch after a successful commit
+                    c = [list(x) for x in refreshchanges]
+                    if inclsubs:
+                        self.putsubstate2changes(substatestate, c)
+                    chunks = patchmod.diff(repo, patchparent,
+                                           changes=c, opts=diffopts)
+                    comments = str(ph)
+                    if comments:
+                        patchf.write(comments)
+                    for chunk in chunks:
+                        patchf.write(chunk)
+                    patchf.close()
+
+                    marks = repo._bookmarks
+                    for bm in bmlist:
+                        marks[bm] = n
+                    marks.recordchange(tr)
+                    tr.close()
+
+                    self.applied.append(statusentry(n, patchfn))
+                finally:
+                    lockmod.release(lock, tr)
             except: # re-raises
                 ctx = repo[cparents[0]]
                 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
--- a/hgext/pager.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/pager.py	Thu Dec 17 17:27:32 2015 -0600
@@ -49,9 +49,13 @@
 To ignore global commands like :hg:`version` or :hg:`help`, you have
 to specify them in your user configuration file.
 
-The --pager=... option can also be used to control when the pager is
-used. Use a boolean value like yes, no, on, off, or use auto for
-normal behavior.
+To control whether the pager is used at all for an individual command,
+you can use --pager=<value>::
+
+  - use as needed: `auto`.
+  - require the pager: `yes` or `on`.
+  - suppress the pager: `no` or `off` (any unrecognized value
+  will also work).
 
 '''
 
--- a/hgext/patchbomb.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/patchbomb.py	Thu Dec 17 17:27:32 2015 -0600
@@ -58,7 +58,7 @@
 '''
 
 import os, errno, socket, tempfile, cStringIO
-import email
+import email as emailmod
 
 from mercurial import cmdutil, commands, hg, mail, patch, util, error
 from mercurial import scmutil
@@ -155,7 +155,7 @@
         body += '\n'.join(patchlines)
 
     if addattachment:
-        msg = email.MIMEMultipart.MIMEMultipart()
+        msg = emailmod.MIMEMultipart.MIMEMultipart()
         if body:
             msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
         p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch',
@@ -272,15 +272,15 @@
             or prompt(ui, 'Subject:', 'A bundle for your repository'))
 
     body = _getdescription(repo, '', sender, **opts)
-    msg = email.MIMEMultipart.MIMEMultipart()
+    msg = emailmod.MIMEMultipart.MIMEMultipart()
     if body:
         msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
-    datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
+    datapart = emailmod.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
     datapart.set_payload(bundle)
     bundlename = '%s.hg' % opts.get('bundlename', 'bundle')
     datapart.add_header('Content-Disposition', 'attachment',
                         filename=bundlename)
-    email.Encoders.encode_base64(datapart)
+    emailmod.Encoders.encode_base64(datapart)
     msg.attach(datapart)
     msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
     return [(msg, subj, None)]
@@ -403,7 +403,7 @@
     ('', 'intro', None, _('send an introduction email for a single patch')),
     ] + emailopts + commands.remoteopts,
     _('hg email [OPTION]... [DEST]...'))
-def patchbomb(ui, repo, *revs, **opts):
+def email(ui, repo, *revs, **opts):
     '''send changesets by email
 
     By default, diffs are sent in the format generated by
@@ -641,7 +641,7 @@
         if not parent.endswith('>'):
             parent += '>'
 
-    sender_addr = email.Utils.parseaddr(sender)[1]
+    sender_addr = emailmod.Utils.parseaddr(sender)[1]
     sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
     sendmail = None
     firstpatch = None
@@ -660,7 +660,7 @@
             parent = m['Message-Id']
 
         m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version()
-        m['Date'] = email.Utils.formatdate(start_time[0], localtime=True)
+        m['Date'] = emailmod.Utils.formatdate(start_time[0], localtime=True)
 
         start_time = (start_time[0] + 1, start_time[1])
         m['From'] = sender
@@ -678,7 +678,7 @@
                 fp = util.popen(os.environ['PAGER'], 'w')
             else:
                 fp = ui
-            generator = email.Generator.Generator(fp, mangle_from_=False)
+            generator = emailmod.Generator.Generator(fp, mangle_from_=False)
             try:
                 generator.flatten(m, 0)
                 fp.write('\n')
@@ -702,7 +702,7 @@
                 # Exim does not remove the Bcc field
                 del m['Bcc']
             fp = cStringIO.StringIO()
-            generator = email.Generator.Generator(fp, mangle_from_=False)
+            generator = emailmod.Generator.Generator(fp, mangle_from_=False)
             generator.flatten(m, 0)
             sendmail(sender_addr, to + bcc + cc, fp.getvalue())
 
--- a/hgext/progress.py	Mon Nov 30 13:47:29 2015 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-# progress.py show progress bars for some actions
-#
-# Copyright (C) 2010 Augie Fackler <durin42@gmail.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2 or any later version.
-
-"""show progress bars for some actions (DEPRECATED)
-
-This extension has been merged into core, you can remove it from your config.
-See hg help config.progress for configuration options.
-"""
-# Note for extension authors: ONLY specify testedwith = 'internal' for
-# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
-# be specifying the version(s) of Mercurial they are tested with, or
-# leave the attribute unspecified.
-testedwith = 'internal'
--- a/hgext/rebase.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/rebase.py	Thu Dec 17 17:27:32 2015 -0600
@@ -30,8 +30,11 @@
 revtodo = -1
 nullmerge = -2
 revignored = -3
-# To do with obsolescence
+# successor in rebase destination
 revprecursor = -4
+# plain prune (no successor)
+revpruned = -5
+revskipped = (revignored, revprecursor, revpruned)
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
@@ -44,14 +47,6 @@
 def _nothingtorebase():
     return 1
 
-def _savegraft(ctx, extra):
-    s = ctx.extra().get('source', None)
-    if s is not None:
-        extra['source'] = s
-
-def _savebranch(ctx, extra):
-    extra['branch'] = ctx.branch()
-
 def _makeextrafn(copiers):
     """make an extrafn out of the given copy-functions.
 
@@ -220,8 +215,9 @@
         abortf = opts.get('abort')
         collapsef = opts.get('collapse', False)
         collapsemsg = cmdutil.logmessage(ui, opts)
+        date = opts.get('date', None)
         e = opts.get('extrafn') # internal, used by e.g. hgsubversion
-        extrafns = [_savegraft]
+        extrafns = []
         if e:
             extrafns = [e]
         keepf = opts.get('keep', False)
@@ -390,18 +386,13 @@
             if dest.closesbranch() and not keepbranchesf:
                 ui.status(_('reopening closed branch head %s\n') % dest)
 
-        if keepbranchesf:
-            # insert _savebranch at the start of extrafns so if
-            # there's a user-provided extrafn it can clobber branch if
-            # desired
-            extrafns.insert(0, _savebranch)
-            if collapsef:
-                branches = set()
-                for rev in state:
-                    branches.add(repo[rev].branch())
-                    if len(branches) > 1:
-                        raise error.Abort(_('cannot collapse multiple named '
-                            'branches'))
+        if keepbranchesf and collapsef:
+            branches = set()
+            for rev in state:
+                branches.add(repo[rev].branch())
+                if len(branches) > 1:
+                    raise error.Abort(_('cannot collapse multiple named '
+                                        'branches'))
 
         # Rebase
         if not targetancestors:
@@ -434,7 +425,7 @@
                                              targetancestors)
                 storestatus(repo, originalwd, target, state, collapsef, keepf,
                             keepbranchesf, external, activebookmark)
-                if len(repo.parents()) == 2:
+                if len(repo[None].parents()) == 2:
                     repo.ui.debug('resuming interrupted rebase\n')
                 else:
                     try:
@@ -454,7 +445,8 @@
                     editor = cmdutil.getcommiteditor(editform=editform, **opts)
                     newnode = concludenode(repo, rev, p1, p2, extrafn=extrafn,
                                            editor=editor,
-                                           keepbranches=keepbranchesf)
+                                           keepbranches=keepbranchesf,
+                                           date=date)
                 else:
                     # Skip commit if we are collapsing
                     repo.dirstate.beginparentchange()
@@ -482,6 +474,9 @@
                              targetctx.description().split('\n', 1)[0])
                 msg = _('note: not rebasing %s, already in destination as %s\n')
                 ui.status(msg % (desc, desctarget))
+            elif state[rev] == revpruned:
+                msg = _('note: not rebasing %s, it has no successor\n')
+                ui.status(msg % desc)
             else:
                 ui.status(_('already rebased %s as %s\n') %
                           (desc, repo[state[rev]]))
@@ -505,7 +500,8 @@
             editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
             newnode = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
                                    extrafn=extrafn, editor=editor,
-                                   keepbranches=keepbranchesf)
+                                   keepbranches=keepbranchesf,
+                                   date=date)
             if newnode is None:
                 newrev = target
             else:
@@ -543,13 +539,19 @@
                 collapsedas = newnode
             clearrebased(ui, repo, state, skipped, collapsedas)
 
-        if currentbookmarks:
-            updatebookmarks(repo, targetnode, nstate, currentbookmarks)
-            if activebookmark not in repo._bookmarks:
-                # active bookmark was divergent one and has been deleted
-                activebookmark = None
+        tr = None
+        try:
+            tr = repo.transaction('bookmark')
+            if currentbookmarks:
+                updatebookmarks(repo, targetnode, nstate, currentbookmarks, tr)
+                if activebookmark not in repo._bookmarks:
+                    # active bookmark was divergent one and has been deleted
+                    activebookmark = None
+            tr.close()
+        finally:
+            release(tr)
+        clearstatus(repo)
 
-        clearstatus(repo)
         ui.note(_("rebase completed\n"))
         util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
         if skipped:
@@ -586,7 +588,7 @@
                       ', '.join(str(p) for p in sorted(parents))))
 
 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
-                 keepbranches=False):
+                 keepbranches=False, date=None):
     '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
     but also store useful information in extra.
     Return node of committed revision.'''
@@ -597,7 +599,10 @@
         if commitmsg is None:
             commitmsg = ctx.description()
         keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
-        extra = {'rebase_source': ctx.hex()}
+        extra = ctx.extra().copy()
+        if not keepbranches:
+            del extra['branch']
+        extra['rebase_source'] = ctx.hex()
         if extrafn:
             extrafn(ctx, extra)
 
@@ -608,8 +613,10 @@
             if keepbranch:
                 repo.ui.setconfig('ui', 'allowemptycommit', True)
             # Commit might fail if unresolved files exist
+            if date is None:
+                date = ctx.date()
             newnode = repo.commit(text=commitmsg, user=ctx.user(),
-                                  date=ctx.date(), extra=extra, editor=editor)
+                                  date=date, extra=extra, editor=editor)
         finally:
             repo.ui.restoreconfig(backup)
 
@@ -625,7 +632,7 @@
     # Update to target and merge it with local
     if repo['.'].rev() != p1:
         repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
-        merge.update(repo, p1, False, True, False)
+        merge.update(repo, p1, False, True)
     else:
         repo.ui.debug(" already in target\n")
     repo.dirstate.write(repo.currenttransaction())
@@ -634,7 +641,7 @@
         repo.ui.debug("   detach base %d:%s\n" % (base, repo[base]))
     # When collapsing in-place, the parent is the common ancestor, we
     # have to allow merging with it.
-    stats = merge.update(repo, rev, True, True, False, base, collapse,
+    stats = merge.update(repo, rev, True, True, base, collapse,
                         labels=['dest', 'source'])
     if collapse:
         copies.duplicatecopies(repo, rev, target)
@@ -668,7 +675,7 @@
     elif p1n in state:
         if state[p1n] == nullmerge:
             p1 = target
-        elif state[p1n] in (revignored, revprecursor):
+        elif state[p1n] in revskipped:
             p1 = nearestrebased(repo, p1n, state)
             if p1 is None:
                 p1 = target
@@ -684,7 +691,7 @@
         if p2n in state:
             if p1 == target: # p1n in targetancestors or external
                 p1 = state[p2n]
-            elif state[p2n] in (revignored, revprecursor):
+            elif state[p2n] in revskipped:
                 p2 = nearestrebased(repo, p2n, state)
                 if p2 is None:
                     # no ancestors rebased yet, detach
@@ -802,7 +809,7 @@
         mq.seriesdirty = True
         mq.savedirty()
 
-def updatebookmarks(repo, targetnode, nstate, originalbookmarks):
+def updatebookmarks(repo, targetnode, nstate, originalbookmarks, tr):
     'Move bookmarks to their correct changesets, and delete divergent ones'
     marks = repo._bookmarks
     for k, v in originalbookmarks.iteritems():
@@ -810,8 +817,7 @@
             # update the bookmarks for revs that have moved
             marks[k] = nstate[v]
             bookmarks.deletedivergent(repo, [targetnode], k)
-
-    marks.write()
+    marks.recordchange(tr)
 
 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
                 external, activebookmark):
@@ -874,7 +880,7 @@
             else:
                 oldrev, newrev = l.split(':')
                 if newrev in (str(nullmerge), str(revignored),
-                              str(revprecursor)):
+                              str(revprecursor), str(revpruned)):
                     state[repo[oldrev].rev()] = int(newrev)
                 elif newrev == nullid:
                     state[repo[oldrev].rev()] = revtodo
@@ -908,7 +914,7 @@
 def needupdate(repo, state):
     '''check whether we should `update --clean` away from a merge, or if
     somehow the working dir got forcibly updated, e.g. by older hg'''
-    parents = [p.rev() for p in repo.parents()]
+    parents = [p.rev() for p in repo[None].parents()]
 
     # Are we in a merge state at all?
     if len(parents) < 2:
@@ -952,7 +958,7 @@
         if cleanup:
             # Update away from the rebase if necessary
             if needupdate(repo, state):
-                merge.update(repo, originalwd, False, True, False)
+                merge.update(repo, originalwd, False, True)
 
             # Strip from the first rebased revision
             rebased = filter(lambda x: x >= 0 and x != target, state.values())
@@ -1058,7 +1064,10 @@
         for ignored in set(rebasedomain) - set(rebaseset):
             state[ignored] = revignored
     for r in obsoletenotrebased:
-        state[r] = revprecursor
+        if obsoletenotrebased[r] is None:
+            state[r] = revpruned
+        else:
+            state[r] = revprecursor
     return repo['.'].rev(), dest.rev(), state
 
 def clearrebased(ui, repo, state, skipped, collapsedas=None):
@@ -1172,7 +1181,9 @@
 
 def _computeobsoletenotrebased(repo, rebasesetrevs, dest):
     """return a mapping obsolete => successor for all obsolete nodes to be
-    rebased that have a successors in the destination"""
+    rebased that have a successors in the destination
+
+    obsolete => None entries in the mapping indicate nodes with no succesor"""
     obsoletenotrebased = {}
 
     # Build a mapping successor => obsolete nodes for the obsolete
@@ -1198,6 +1209,11 @@
         for s in allsuccessors:
             if s in ancs:
                 obsoletenotrebased[allsuccessors[s]] = s
+            elif (s == allsuccessors[s] and
+                  allsuccessors.values().count(s) == 1):
+                # plain prune
+                obsoletenotrebased[s] = None
+
     return obsoletenotrebased
 
 def summaryhook(ui, repo):
--- a/hgext/share.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/share.py	Thu Dec 17 17:27:32 2015 -0600
@@ -73,7 +73,8 @@
        the broken clone to reset it to a changeset that still exists.
     """
 
-    return hg.share(ui, source, dest, not noupdate, bookmarks)
+    return hg.share(ui, source, dest=dest, update=not noupdate,
+                    bookmarks=bookmarks)
 
 @command('unshare', [], '')
 def unshare(ui, repo):
@@ -121,7 +122,7 @@
     return orig(ui, source, *args, **opts)
 
 def extsetup(ui):
-    extensions.wrapfunction(bookmarks.bmstore, 'getbkfile', getbkfile)
+    extensions.wrapfunction(bookmarks, '_getbkfile', getbkfile)
     extensions.wrapfunction(bookmarks.bmstore, 'recordchange', recordchange)
     extensions.wrapfunction(bookmarks.bmstore, '_writerepo', writerepo)
     extensions.wrapcommand(commands.table, 'clone', clone)
@@ -149,12 +150,12 @@
     srcurl, branches = parseurl(source)
     return repository(repo.ui, srcurl)
 
-def getbkfile(orig, self, repo):
+def getbkfile(orig, repo):
     if _hassharedbookmarks(repo):
         srcrepo = _getsrcrepo(repo)
         if srcrepo is not None:
             repo = srcrepo
-    return orig(self, repo)
+    return orig(repo)
 
 def recordchange(orig, self, tr):
     # Continue with write to local bookmarks file as usual
--- a/hgext/shelve.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/shelve.py	Thu Dec 17 17:27:32 2015 -0600
@@ -224,7 +224,14 @@
 
 def createcmd(ui, repo, pats, opts):
     """subcommand that creates a new shelve"""
+    wlock = repo.wlock()
+    try:
+        cmdutil.checkunfinished(repo)
+        return _docreatecmd(ui, repo, pats, opts)
+    finally:
+        lockmod.release(wlock)
 
+def _docreatecmd(ui, repo, pats, opts):
     def mutableancestors(ctx):
         """return all mutable ancestors for ctx (included)
 
@@ -276,7 +283,7 @@
                 repo.mq.checkapplied = saved
 
     if parent.node() != nullid:
-        desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
+        desc = "changes to: %s" % parent.description().split('\n', 1)[0]
     else:
         desc = '(changes in empty repository)'
 
@@ -285,9 +292,8 @@
 
     name = opts['name']
 
-    wlock = lock = tr = None
+    lock = tr = None
     try:
-        wlock = repo.wlock()
         lock = repo.lock()
 
         # use an uncommitted transaction to generate the bundle to avoid
@@ -346,7 +352,7 @@
 
         _aborttransaction(repo)
     finally:
-        lockmod.release(tr, lock, wlock)
+        lockmod.release(tr, lock)
 
 def cleanupcmd(ui, repo):
     """subcommand that deletes all shelves"""
@@ -467,7 +473,6 @@
 
 def unshelveabort(ui, repo, state, opts):
     """subcommand that abort an in-progress unshelve"""
-    wlock = repo.wlock()
     lock = None
     try:
         checkparents(repo, state)
@@ -491,7 +496,7 @@
     finally:
         shelvedstate.clear(repo)
         ui.warn(_("unshelve of '%s' aborted\n") % state.name)
-        lockmod.release(lock, wlock)
+        lockmod.release(lock)
 
 def mergefiles(ui, repo, wctx, shelvectx):
     """updates to wctx and merges the changes from shelvectx into the
@@ -507,7 +512,7 @@
         # revert will overwrite unknown files, so move them out of the way
         for file in repo.status(unknown=True).unknown:
             if file in files:
-                util.rename(file, file + ".orig")
+                util.rename(file, cmdutil.origpath(ui, repo, file))
         ui.pushbuffer(True)
         cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
                        *pathtofiles(repo, files),
@@ -527,11 +532,10 @@
     """subcommand to continue an in-progress unshelve"""
     # We're finishing off a merge. First parent is our original
     # parent, second is the temporary "fake" commit we're unshelving.
-    wlock = repo.wlock()
     lock = None
     try:
         checkparents(repo, state)
-        ms = merge.mergestate(repo)
+        ms = merge.mergestate.read(repo)
         if [f for f in ms if ms[f] == 'u']:
             raise error.Abort(
                 _("unresolved conflicts, can't continue"),
@@ -565,15 +569,16 @@
         unshelvecleanup(ui, repo, state.name, opts)
         ui.status(_("unshelve of '%s' complete\n") % state.name)
     finally:
-        lockmod.release(lock, wlock)
+        lockmod.release(lock)
 
 @command('unshelve',
          [('a', 'abort', None,
            _('abort an incomplete unshelve operation')),
           ('c', 'continue', None,
            _('continue an incomplete unshelve operation')),
-          ('', 'keep', None,
+          ('k', 'keep', None,
            _('keep shelve after unshelving')),
+          ('t', 'tool', '', _('specify merge tool')),
           ('', 'date', '',
            _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
          _('hg unshelve [SHELVED]'))
@@ -609,6 +614,13 @@
        than ``maxbackups`` backups are kept, if same timestamp
        prevents from deciding exact order of them, for safety.
     """
+    wlock = repo.wlock()
+    try:
+        return _dounshelve(ui, repo, *shelved, **opts)
+    finally:
+        lockmod.release(wlock)
+
+def _dounshelve(ui, repo, *shelved, **opts):
     abortf = opts['abort']
     continuef = opts['continue']
     if not abortf and not continuef:
@@ -620,6 +632,8 @@
         if shelved:
             raise error.Abort(_('cannot combine abort/continue with '
                                'naming a shelved change'))
+        if abortf and opts.get('tool', False):
+            ui.warn(_('tool option will be ignored\n'))
 
         try:
             state = shelvedstate.load(repo)
@@ -647,9 +661,10 @@
         raise error.Abort(_("shelved change '%s' not found") % basename)
 
     oldquiet = ui.quiet
-    wlock = lock = tr = None
+    lock = tr = None
+    forcemerge = ui.backupconfig('ui', 'forcemerge')
     try:
-        wlock = repo.wlock()
+        ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'unshelve')
         lock = repo.lock()
 
         tr = repo.transaction('unshelve', report=lambda x: None)
@@ -675,7 +690,7 @@
 
                 backup = repo.ui.backupconfig('phases', 'new-commit')
                 try:
-                    repo.ui. setconfig('phases', 'new-commit', phases.secret)
+                    repo.ui.setconfig('phases', 'new-commit', phases.secret)
                     return repo.commit(message, 'shelve@localhost',
                                        opts.get('date'), match)
                 finally:
@@ -706,6 +721,7 @@
                     'rev' : [shelvectx.rev()],
                     'dest' : str(tmpwctx.rev()),
                     'keep' : True,
+                    'tool' : opts.get('tool', ''),
                 })
             except error.InterventionRequired:
                 tr.close()
@@ -743,7 +759,8 @@
         ui.quiet = oldquiet
         if tr:
             tr.release()
-        lockmod.release(lock, wlock)
+        lockmod.release(lock)
+        ui.restoreconfig(forcemerge)
 
 @command('shelve',
          [('A', 'addremove', None,
@@ -796,8 +813,6 @@
     To delete specific shelved changes, use ``--delete``. To delete
     all shelved changes, use ``--cleanup``.
     '''
-    cmdutil.checkunfinished(repo)
-
     allowables = [
         ('addremove', set(['create'])), # 'create' is pseudo action
         ('cleanup', set(['cleanup'])),
--- a/hgext/strip.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/strip.py	Thu Dec 17 17:27:32 2015 -0600
@@ -7,7 +7,7 @@
 from mercurial.node import nullid
 from mercurial.lock import release
 from mercurial import cmdutil, hg, scmutil, util, error
-from mercurial import repair, bookmarks, merge
+from mercurial import repair, bookmarks as bookmarksmod , merge
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
@@ -44,7 +44,7 @@
             raise error.Abort(_("local changed subrepos found" + excsuffix))
     return s
 
-def strip(ui, repo, revs, update=True, backup=True, force=None, bookmark=None):
+def strip(ui, repo, revs, update=True, backup=True, force=None, bookmarks=None):
     wlock = lock = None
     try:
         wlock = repo.wlock()
@@ -62,13 +62,21 @@
 
         repair.strip(ui, repo, revs, backup)
 
-        marks = repo._bookmarks
-        if bookmark:
-            if bookmark == repo._activebookmark:
-                bookmarks.deactivate(repo)
-            del marks[bookmark]
-            marks.write()
-            ui.write(_("bookmark '%s' deleted\n") % bookmark)
+        repomarks = repo._bookmarks
+        if bookmarks:
+            tr = None
+            try:
+                tr = repo.transaction('strip')
+                if repo._activebookmark in bookmarks:
+                    bookmarksmod.deactivate(repo)
+                for bookmark in bookmarks:
+                    del repomarks[bookmark]
+                repomarks.recordchange(tr)
+                tr.close()
+                for bookmark in sorted(bookmarks):
+                    ui.write(_("bookmark '%s' deleted\n") % bookmark)
+            finally:
+                release(tr)
     finally:
         release(lock, wlock)
 
@@ -85,7 +93,7 @@
           ('n', '', None, _('ignored  (DEPRECATED)')),
           ('k', 'keep', None, _("do not modify working directory during "
                                 "strip")),
-          ('B', 'bookmark', '', _("remove revs only reachable from given"
+          ('B', 'bookmark', [], _("remove revs only reachable from given"
                                   " bookmark"))],
           _('hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...'))
 def stripcmd(ui, repo, *revs, **opts):
@@ -127,27 +135,36 @@
 
     wlock = repo.wlock()
     try:
-        if opts.get('bookmark'):
-            mark = opts.get('bookmark')
-            marks = repo._bookmarks
-            if mark not in marks:
-                raise error.Abort(_("bookmark '%s' not found") % mark)
+        bookmarks = set(opts.get('bookmark'))
+        if bookmarks:
+            repomarks = repo._bookmarks
+            if not bookmarks.issubset(repomarks):
+                raise error.Abort(_("bookmark '%s' not found") %
+                    ','.join(sorted(bookmarks - set(repomarks.keys()))))
 
             # If the requested bookmark is not the only one pointing to a
             # a revision we have to only delete the bookmark and not strip
             # anything. revsets cannot detect that case.
-            uniquebm = True
-            for m, n in marks.iteritems():
-                if m != mark and n == repo[mark].node():
-                    uniquebm = False
-                    break
-            if uniquebm:
-                rsrevs = repair.stripbmrevset(repo, mark)
-                revs.update(set(rsrevs))
+            nodetobookmarks = {}
+            for mark, node in repomarks.iteritems():
+                nodetobookmarks.setdefault(node, []).append(mark)
+            for marks in nodetobookmarks.values():
+                if bookmarks.issuperset(marks):
+                    rsrevs = repair.stripbmrevset(repo, marks[0])
+                    revs.update(set(rsrevs))
             if not revs:
-                del marks[mark]
-                marks.write()
-                ui.write(_("bookmark '%s' deleted\n") % mark)
+                lock = tr = None
+                try:
+                    lock = repo.lock()
+                    tr = repo.transaction('bookmark')
+                    for bookmark in bookmarks:
+                        del repomarks[bookmark]
+                    repomarks.recordchange(tr)
+                    tr.close()
+                    for bookmark in sorted(bookmarks):
+                        ui.write(_("bookmark '%s' deleted\n") % bookmark)
+                finally:
+                    release(lock, tr)
 
         if not revs:
             raise error.Abort(_('empty revision set'))
@@ -208,14 +225,13 @@
             repo.dirstate.write(repo.currenttransaction())
 
             # clear resolve state
-            ms = merge.mergestate(repo)
-            ms.reset(repo['.'].node())
+            merge.mergestate.clean(repo, repo['.'].node())
 
             update = False
 
 
         strip(ui, repo, revs, backup=backup, update=update,
-              force=opts.get('force'), bookmark=opts.get('bookmark'))
+              force=opts.get('force'), bookmarks=bookmarks)
     finally:
         wlock.release()
 
--- a/hgext/transplant.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/hgext/transplant.py	Thu Dec 17 17:27:32 2015 -0600
@@ -20,6 +20,7 @@
 from mercurial import bundlerepo, hg, merge, match
 from mercurial import patch, revlog, scmutil, util, error, cmdutil
 from mercurial import revset, templatekw, exchange
+from mercurial import lock as lockmod
 
 class TransplantError(error.Abort):
     pass
@@ -127,9 +128,8 @@
         diffopts = patch.difffeatureopts(self.ui, opts)
         diffopts.git = True
 
-        lock = wlock = tr = None
+        lock = tr = None
         try:
-            wlock = repo.wlock()
             lock = repo.lock()
             tr = repo.transaction('transplant')
             for rev in revs:
@@ -152,7 +152,7 @@
                     if pulls:
                         if source != repo:
                             exchange.pull(repo, source.peer(), heads=pulls)
-                        merge.update(repo, pulls[-1], False, False, None)
+                        merge.update(repo, pulls[-1], False, False)
                         p1, p2 = repo.dirstate.parents()
                         pulls = []
 
@@ -216,7 +216,7 @@
             tr.close()
             if pulls:
                 exchange.pull(repo, source.peer(), heads=pulls)
-                merge.update(repo, pulls[-1], False, False, None)
+                merge.update(repo, pulls[-1], False, False)
         finally:
             self.saveseries(revmap, merges)
             self.transplants.write()
@@ -224,7 +224,6 @@
                 tr.release()
             if lock:
                 lock.release()
-            wlock.release()
 
     def filter(self, filter, node, changelog, patchfile):
         '''arbitrarily rewrite changeset before applying it'''
@@ -345,7 +344,6 @@
                 merge = True
 
         extra = {'transplant_source': node}
-        wlock = repo.wlock()
         try:
             p1, p2 = repo.dirstate.parents()
             if p1 != parent:
@@ -367,7 +365,9 @@
 
             return n, node
         finally:
-            wlock.release()
+            # TODO: get rid of this meaningless try/finally enclosing.
+            # this is kept only to reduce changes in a patch.
+            pass
 
     def readseries(self):
         nodes = []
@@ -572,6 +572,14 @@
     and then resume where you left off by calling :hg:`transplant
     --continue/-c`.
     '''
+    wlock = None
+    try:
+        wlock = repo.wlock()
+        return _dotransplant(ui, repo, *revs, **opts)
+    finally:
+        lockmod.release(wlock)
+
+def _dotransplant(ui, repo, *revs, **opts):
     def incwalk(repo, csets, match=util.always):
         for node in csets:
             if match(node):
@@ -599,7 +607,7 @@
             return
         if not (opts.get('source') or revs or
                 opts.get('merge') or opts.get('branch')):
-            raise error.Abort(_('no source URL, branch revision or revision '
+            raise error.Abort(_('no source URL, branch revision, or revision '
                                'list provided'))
         if opts.get('all'):
             if not opts.get('branch'):
--- a/mercurial/__init__.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/__init__.py	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,144 @@
+# __init__.py - Startup and module loading logic for Mercurial.
+#
+# Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from __future__ import absolute_import
+
+import imp
+import os
+import sys
+import zipimport
+
+__all__ = []
+
+# Rules for how modules can be loaded. Values are:
+#
+#    c - require C extensions
+#    allow - allow pure Python implementation when C loading fails
+#    py - only load pure Python modules
+modulepolicy = '@MODULELOADPOLICY@'
+
+# By default, require the C extensions for performance reasons.
+if modulepolicy == '@' 'MODULELOADPOLICY' '@':
+    modulepolicy = 'c'
+
+# PyPy doesn't load C extensions.
+#
+# The canonical way to do this is to test platform.python_implementation().
+# But we don't import platform and don't bloat for it here.
+if '__pypy__' in sys.builtin_module_names:
+    modulepolicy = 'py'
+
+# Environment variable can always force settings.
+modulepolicy = os.environ.get('HGMODULEPOLICY', modulepolicy)
+
+# Modules that have both Python and C implementations. See also the
+# set of .py files under mercurial/pure/.
+_dualmodules = set([
+    'mercurial.base85',
+    'mercurial.bdiff',
+    'mercurial.diffhelpers',
+    'mercurial.mpatch',
+    'mercurial.osutil',
+    'mercurial.parsers',
+])
+
+class hgimporter(object):
+    """Object that conforms to import hook interface defined in PEP-302."""
+    def find_module(self, name, path=None):
+        # We only care about modules that have both C and pure implementations.
+        if name in _dualmodules:
+            return self
+        return None
+
+    def load_module(self, name):
+        mod = sys.modules.get(name, None)
+        if mod:
+            return mod
+
+        mercurial = sys.modules['mercurial']
+
+        # The zip importer behaves sufficiently differently from the default
+        # importer to warrant its own code path.
+        loader = getattr(mercurial, '__loader__', None)
+        if isinstance(loader, zipimport.zipimporter):
+            def ziploader(*paths):
+                """Obtain a zipimporter for a directory under the main zip."""
+                path = os.path.join(loader.archive, *paths)
+                zl = sys.path_importer_cache.get(path)
+                if not zl:
+                    zl = zipimport.zipimporter(path)
+                return zl
+
+            try:
+                if modulepolicy == 'py':
+                    raise ImportError()
+
+                zl = ziploader('mercurial')
+                mod = zl.load_module(name)
+                # Unlike imp, ziploader doesn't expose module metadata that
+                # indicates the type of module. So just assume what we found
+                # is OK (even though it could be a pure Python module).
+            except ImportError:
+                if modulepolicy == 'c':
+                    raise
+                zl = ziploader('mercurial', 'pure')
+                mod = zl.load_module(name)
+
+            sys.modules[name] = mod
+            return mod
+
+        # Unlike the default importer which searches special locations and
+        # sys.path, we only look in the directory where "mercurial" was
+        # imported from.
+
+        # imp.find_module doesn't support submodules (modules with ".").
+        # Instead you have to pass the parent package's __path__ attribute
+        # as the path argument.
+        stem = name.split('.')[-1]
+
+        try:
+            if modulepolicy == 'py':
+                raise ImportError()
+
+            modinfo = imp.find_module(stem, mercurial.__path__)
+
+            # The Mercurial installer used to copy files from
+            # mercurial/pure/*.py to mercurial/*.py. Therefore, it's possible
+            # for some installations to have .py files under mercurial/*.
+            # Loading Python modules when we expected C versions could result
+            # in a) poor performance b) loading a version from a previous
+            # Mercurial version, potentially leading to incompatibility. Either
+            # scenario is bad. So we verify that modules loaded from
+            # mercurial/* are C extensions. If the current policy allows the
+            # loading of .py modules, the module will be re-imported from
+            # mercurial/pure/* below.
+            if modinfo[2][2] != imp.C_EXTENSION:
+                raise ImportError('.py version of %s found where C '
+                                  'version should exist' % name)
+
+        except ImportError:
+            if modulepolicy == 'c':
+                raise
+
+            # Could not load the C extension and pure Python is allowed. So
+            # try to load them.
+            from . import pure
+            modinfo = imp.find_module(stem, pure.__path__)
+            if not modinfo:
+                raise ImportError('could not find mercurial module %s' %
+                                  name)
+
+        mod = imp.load_module(name, *modinfo)
+        sys.modules[name] = mod
+        return mod
+
+# We automagically register our custom importer as a side-effect of loading.
+# This is necessary to ensure that any entry points are able to import
+# mercurial.* modules without having to perform this registration themselves.
+if not any(isinstance(x, hgimporter) for x in sys.meta_path):
+    # meta_path is used before any implicit finders and before sys.path.
+    sys.meta_path.insert(0, hgimporter())
--- a/mercurial/base85.c	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/base85.c	Thu Dec 17 17:27:32 2015 -0600
@@ -18,8 +18,7 @@
 	"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
 static char b85dec[256];
 
-static void
-b85prep(void)
+static void b85prep(void)
 {
 	unsigned i;
 
@@ -28,8 +27,7 @@
 		b85dec[(int)(b85chars[i])] = i + 1;
 }
 
-static PyObject *
-b85encode(PyObject *self, PyObject *args)
+static PyObject *b85encode(PyObject *self, PyObject *args)
 {
 	const unsigned char *text;
 	PyObject *out;
@@ -76,8 +74,7 @@
 	return out;
 }
 
-static PyObject *
-b85decode(PyObject *self, PyObject *args)
+static PyObject *b85decode(PyObject *self, PyObject *args)
 {
 	PyObject *out;
 	const char *text;
--- a/mercurial/bookmarks.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/bookmarks.py	Thu Dec 17 17:27:32 2015 -0600
@@ -22,6 +22,25 @@
     util,
 )
 
+def _getbkfile(repo):
+    """Hook so that extensions that mess with the store can hook bm storage.
+
+    For core, this just handles wether we should see pending
+    bookmarks or the committed ones. Other extensions (like share)
+    may need to tweak this behavior further.
+    """
+    bkfile = None
+    if 'HG_PENDING' in os.environ:
+        try:
+            bkfile = repo.vfs('bookmarks.pending')
+        except IOError as inst:
+            if inst.errno != errno.ENOENT:
+                raise
+    if bkfile is None:
+        bkfile = repo.vfs('bookmarks')
+    return bkfile
+
+
 class bmstore(dict):
     """Storage for bookmarks.
 
@@ -41,7 +60,7 @@
         dict.__init__(self)
         self._repo = repo
         try:
-            bkfile = self.getbkfile(repo)
+            bkfile = _getbkfile(repo)
             for line in bkfile:
                 line = line.strip()
                 if not line:
@@ -59,18 +78,15 @@
         except IOError as inst:
             if inst.errno != errno.ENOENT:
                 raise
+        self._clean = True
 
-    def getbkfile(self, repo):
-        bkfile = None
-        if 'HG_PENDING' in os.environ:
-            try:
-                bkfile = repo.vfs('bookmarks.pending')
-            except IOError as inst:
-                if inst.errno != errno.ENOENT:
-                    raise
-        if bkfile is None:
-            bkfile = repo.vfs('bookmarks')
-        return bkfile
+    def __setitem__(self, *args, **kwargs):
+        self._clean = False
+        return dict.__setitem__(self, *args, **kwargs)
+
+    def __delitem__(self, key):
+        self._clean = False
+        return dict.__delitem__(self, key)
 
     def recordchange(self, tr):
         """record that bookmarks have been changed in a transaction
@@ -89,6 +105,10 @@
         We also store a backup of the previous state in undo.bookmarks that
         can be copied back on rollback.
         '''
+        msg = 'bm.write() is deprecated, use bm.recordchange(transaction)'
+        self._repo.ui.deprecwarn(msg, '3.7')
+        if self._clean:
+            return
         repo = self._repo
         if (repo.ui.configbool('devel', 'all-warnings')
                 or repo.ui.configbool('devel', 'check-locks')):
@@ -114,9 +134,14 @@
         wlock = repo.wlock()
         try:
 
-            file = repo.vfs('bookmarks', 'w', atomictemp=True)
-            self._write(file)
-            file.close()
+            file_ = repo.vfs('bookmarks', 'w', atomictemp=True)
+            try:
+                self._write(file_)
+            except: # re-raises
+                file_.discard()
+                raise
+            finally:
+                file_.close()
 
         finally:
             wlock.release()
@@ -124,6 +149,7 @@
     def _write(self, fp):
         for name, node in self.iteritems():
             fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
+        self._clean = True
 
 def readactive(repo):
     """
@@ -249,7 +275,14 @@
         update = True
 
     if update:
-        marks.write()
+        lock = tr = None
+        try:
+            lock = repo.lock()
+            tr = repo.transaction('bookmark')
+            marks.recordchange(tr)
+            tr.close()
+        finally:
+            lockmod.release(tr, lock)
     return update
 
 def listbookmarks(repo):
--- a/mercurial/changegroup.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/changegroup.py	Thu Dec 17 17:27:32 2015 -0600
@@ -312,6 +312,7 @@
         - number of heads stays the same: 1
         """
         repo = repo.unfiltered()
+        wasempty = (len(repo.changelog) == 0)
         def csmap(x):
             repo.ui.debug("add changeset %s\n" % short(x))
             return len(cl)
@@ -386,7 +387,7 @@
             self.callback = None
             pr = prog(_('files'), efiles)
             newrevs, newfiles = _addchangegroupfiles(
-                repo, self, revmap, trp, pr, needfiles)
+                repo, self, revmap, trp, pr, needfiles, wasempty)
             revisions += newrevs
             files += newfiles
 
@@ -612,7 +613,8 @@
         clrevorder = {}
         mfs = {} # needed manifests
         fnodes = {} # needed file nodes
-        changedfiles = set()
+        # maps manifest node id -> set(changed files)
+        mfchangedfiles = {}
 
         # Callback for the changelog, used to collect changed files and manifest
         # nodes.
@@ -620,9 +622,12 @@
         def lookupcl(x):
             c = cl.read(x)
             clrevorder[x] = len(clrevorder)
-            changedfiles.update(c[3])
+            n = c[0]
             # record the first changeset introducing this manifest version
-            mfs.setdefault(c[0], x)
+            mfs.setdefault(n, x)
+            # Record a complete list of potentially-changed files in
+            # this manifest.
+            mfchangedfiles.setdefault(n, set()).update(c[3])
             return x
 
         self._verbosenote(_('uncompressed size of bundle content:\n'))
@@ -651,19 +656,36 @@
         # Callback for the manifest, used to collect linkrevs for filelog
         # revisions.
         # Returns the linkrev node (collected in lookupcl).
-        def lookupmflinknode(x):
-            clnode = mfs[x]
-            if not fastpathlinkrev:
+        if fastpathlinkrev:
+            lookupmflinknode = mfs.__getitem__
+        else:
+            def lookupmflinknode(x):
+                """Callback for looking up the linknode for manifests.
+
+                Returns the linkrev node for the specified manifest.
+
+                SIDE EFFECT:
+
+                  fclnodes gets populated with the list of relevant
+                  file nodes.
+
+                Note that this means you can't trust fclnodes until
+                after manifests have been sent to the client.
+                """
+                clnode = mfs[x]
                 mdata = ml.readfast(x)
-                for f, n in mdata.iteritems():
-                    if f in changedfiles:
-                        # record the first changeset introducing this filelog
-                        # version
-                        fclnodes = fnodes.setdefault(f, {})
-                        fclnode = fclnodes.setdefault(n, clnode)
-                        if clrevorder[clnode] < clrevorder[fclnode]:
-                            fclnodes[n] = clnode
-            return clnode
+                for f in mfchangedfiles[x]:
+                    try:
+                        n = mdata[f]
+                    except KeyError:
+                        continue
+                    # record the first changeset introducing this filelog
+                    # version
+                    fclnodes = fnodes.setdefault(f, {})
+                    fclnode = fclnodes.setdefault(n, clnode)
+                    if clrevorder[clnode] < clrevorder[fclnode]:
+                        fclnodes[n] = clnode
+                return clnode
 
         mfnodes = self.prune(ml, mfs, commonrevs)
         for x in self._packmanifests(mfnodes, lookupmflinknode):
@@ -672,17 +694,20 @@
         mfs.clear()
         clrevs = set(cl.rev(x) for x in clnodes)
 
-        def linknodes(filerevlog, fname):
-            if fastpathlinkrev:
+        if not fastpathlinkrev:
+            def linknodes(unused, fname):
+                return fnodes.get(fname, {})
+        else:
+            cln = cl.node
+            def linknodes(filerevlog, fname):
                 llr = filerevlog.linkrev
-                def genfilenodes():
-                    for r in filerevlog:
-                        linkrev = llr(r)
-                        if linkrev in clrevs:
-                            yield filerevlog.node(r), cl.node(linkrev)
-                return dict(genfilenodes())
-            return fnodes.get(fname, {})
+                fln = filerevlog.node
+                revs = ((r, llr(r)) for r in filerevlog)
+                return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs)
 
+        changedfiles = set()
+        for x in mfchangedfiles.itervalues():
+            changedfiles.update(x)
         for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
                                         source):
             yield chunk
@@ -903,7 +928,7 @@
     # to avoid a race we use changegroupsubset() (issue1320)
     return changegroupsubset(repo, basenodes, repo.heads(), source)
 
-def _addchangegroupfiles(repo, source, revmap, trp, pr, needfiles):
+def _addchangegroupfiles(repo, source, revmap, trp, pr, needfiles, wasempty):
     revisions = 0
     files = 0
     while True:
--- a/mercurial/cmdutil.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/cmdutil.py	Thu Dec 17 17:27:32 2015 -0600
@@ -70,11 +70,11 @@
     testfile = ui.config('experimental', 'crecordtest', None)
     oldwrite = setupwrapcolorwrite(ui)
     try:
-        newchunks = filterchunks(ui, originalhunks, usecurses, testfile,
-                                 operation)
+        newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
+                                          testfile, operation)
     finally:
         ui.write = oldwrite
-    return newchunks
+    return newchunks, newopts
 
 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
             filterfn, *pats, **opts):
@@ -121,9 +121,10 @@
 
         # 1. filter patch, so we have intending-to apply subset of it
         try:
-            chunks = filterfn(ui, originalchunks)
+            chunks, newopts = filterfn(ui, originalchunks)
         except patch.PatchError as err:
             raise error.Abort(_('error parsing patch: %s') % err)
+        opts.update(newopts)
 
         # We need to keep a backup of files that have been newly added and
         # modified during the recording process because there is a previous
@@ -181,9 +182,9 @@
             # 3a. apply filtered patch to clean repo  (clean)
             if backups:
                 # Equivalent to hg.revert
-                choices = lambda key: key in backups
+                m = scmutil.matchfiles(repo, backups.keys())
                 mergemod.update(repo, repo.dirstate.p1(),
-                        False, True, choices)
+                        False, True, matcher=m)
 
             # 3b. (apply)
             if dopatch:
@@ -1182,9 +1183,9 @@
 
     def show(self, ctx, copies=None, matchfn=None, **props):
         if self.buffered:
-            self.ui.pushbuffer()
+            self.ui.pushbuffer(labeled=True)
             self._show(ctx, copies, matchfn, props)
-            self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
+            self.hunk[ctx.rev()] = self.ui.popbuffer()
         else:
             self._show(ctx, copies, matchfn, props)
 
@@ -1297,16 +1298,17 @@
                               label='log.summary')
         self.ui.write("\n")
 
-        self.showpatch(changenode, matchfn)
-
-    def showpatch(self, node, matchfn):
+        self.showpatch(ctx, matchfn)
+
+    def showpatch(self, ctx, matchfn):
         if not matchfn:
             matchfn = self.matchfn
         if matchfn:
             stat = self.diffopts.get('stat')
             diff = self.diffopts.get('patch')
             diffopts = patch.diffallopts(self.ui, self.diffopts)
-            prev = self.repo.changelog.parents(node)[0]
+            node = ctx.node()
+            prev = ctx.p1()
             if stat:
                 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
                                match=matchfn, stat=True)
@@ -1488,7 +1490,7 @@
             # write changeset metadata, then patch if requested
             key = self._parts['changeset']
             self.ui.write(templater.stringify(self.t(key, **props)))
-            self.showpatch(ctx.node(), matchfn)
+            self.showpatch(ctx, matchfn)
 
             if self._parts['footer']:
                 if not self.footer:
@@ -2153,17 +2155,31 @@
 
     return revs, expr, filematcher
 
-def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
+def _graphnodeformatter(ui, displayer):
+    spec = ui.config('ui', 'graphnodetemplate')
+    if not spec:
+        return templatekw.showgraphnode  # fast path for "{graphnode}"
+
+    templ = formatter.gettemplater(ui, 'graphnode', spec)
+    cache = {}
+    if isinstance(displayer, changeset_templater):
+        cache = displayer.cache  # reuse cache of slow templates
+    props = templatekw.keywords.copy()
+    props['templ'] = templ
+    props['cache'] = cache
+    def formatnode(repo, ctx):
+        props['ctx'] = ctx
+        props['repo'] = repo
+        props['revcache'] = {}
+        return templater.stringify(templ('graphnode', **props))
+    return formatnode
+
+def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
                  filematcher=None):
+    formatnode = _graphnodeformatter(ui, displayer)
     seen, state = [], graphmod.asciistate()
     for rev, type, ctx, parents in dag:
-        char = 'o'
-        if ctx.node() in showparents:
-            char = '@'
-        elif ctx.obsolete():
-            char = 'x'
-        elif ctx.closesbranch():
-            char = '_'
+        char = formatnode(repo, ctx)
         copies = None
         if getrenamed and ctx.rev():
             copies = []
@@ -2196,9 +2212,8 @@
             endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
         getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
     displayer = show_changeset(ui, repo, opts, buffered=True)
-    showparents = [ctx.node() for ctx in repo[None].parents()]
-    displaygraph(ui, revdag, displayer, showparents,
-                 graphmod.asciiedges, getrenamed, filematcher)
+    displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
+                 filematcher)
 
 def checkunsupportedgraphflags(pats, opts):
     for op in ["newest_first"]:
@@ -2614,6 +2629,11 @@
                 message = old.description()
 
             pureextra = extra.copy()
+            if 'amend_source' in pureextra:
+                del pureextra['amend_source']
+            pureoldextra = old.extra()
+            if 'amend_source' in pureoldextra:
+                del pureoldextra['amend_source']
             extra['amend_source'] = old.hex()
 
             new = context.memctx(repo,
@@ -2631,7 +2651,7 @@
                 and newdesc == old.description()
                 and user == old.user()
                 and date == old.date()
-                and pureextra == old.extra()):
+                and pureextra == pureoldextra):
                 # nothing changed. continuing here would create a new node
                 # anyway because of the amend_source noise.
                 #
@@ -3078,7 +3098,7 @@
                     xlist.append(abs)
                     if dobackup and (backup <= dobackup
                                      or wctx[abs].cmp(ctx[abs])):
-                            bakname = "%s.orig" % rel
+                            bakname = origpath(ui, repo, rel)
                             ui.note(_('saving current version of %s as %s\n') %
                                     (rel, bakname))
                             if not opts.get('dry_run'):
@@ -3110,6 +3130,26 @@
     finally:
         wlock.release()
 
+def origpath(ui, repo, filepath):
+    '''customize where .orig files are created
+
+    Fetch user defined path from config file: [ui] origbackuppath = <path>
+    Fall back to default (filepath) if not specified
+    '''
+    origbackuppath = ui.config('ui', 'origbackuppath', None)
+    if origbackuppath is None:
+        return filepath + ".orig"
+
+    filepathfromroot = os.path.relpath(filepath, start=repo.root)
+    fullorigpath = repo.wjoin(origbackuppath, filepathfromroot)
+
+    origbackupdir = repo.vfs.dirname(fullorigpath)
+    if not repo.vfs.exists(origbackupdir):
+        ui.note(_('creating directory: %s\n') % origbackupdir)
+        util.makedirs(origbackupdir)
+
+    return fullorigpath + ".orig"
+
 def _revertprefetch(repo, ctx, *files):
     """Let extension changing the storage layer prefetch content"""
     pass
@@ -3171,7 +3211,7 @@
 
         try:
 
-            chunks = recordfilter(repo.ui, originalchunks)
+            chunks, opts = recordfilter(repo.ui, originalchunks)
             if reversehunks:
                 chunks = patch.reversehunks(chunks)
 
--- a/mercurial/commands.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/commands.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from node import hex, bin, nullid, nullrev, short
+from node import hex, bin, nullhex, nullid, nullrev, short
 from lock import release
 from i18n import _
 import os, re, difflib, time, tempfile, errno, shlex
@@ -15,7 +15,6 @@
 import archival, changegroup, cmdutil, hbisect
 import sshserver, hgweb
 import extensions
-from hgweb import server as hgweb_server
 import merge as mergemod
 import minirst, revset, fileset
 import dagparser, context, simplemerge, graphmod, copies
@@ -24,6 +23,7 @@
 import phases, obsolete, exchange, bundle2, repair, lock as lockmod
 import ui as uimod
 import streamclone
+import commandserver
 
 table = {}
 
@@ -172,6 +172,12 @@
      _('recurse into subrepositories'))
 ]
 
+debugrevlogopts = [
+    ('c', 'changelog', False, _('open changelog')),
+    ('m', 'manifest', False, _('open manifest')),
+    ('', 'dir', False, _('open directory manifest')),
+]
+
 # Commands start here, listed alphabetically
 
 @command('^add',
@@ -191,17 +197,31 @@
 
     .. container:: verbose
 
-       An example showing how new (unknown) files are added
-       automatically by :hg:`add`::
-
-         $ ls
-         foo.c
-         $ hg status
-         ? foo.c
-         $ hg add
-         adding foo.c
-         $ hg status
-         A foo.c
+       Examples:
+
+         - New (unknown) files are added
+           automatically by :hg:`add`::
+
+             $ ls
+             foo.c
+             $ hg status
+             ? foo.c
+             $ hg add
+             adding foo.c
+             $ hg status
+             A foo.c
+
+         - Specific files to be added can be specified::
+
+             $ ls
+             bar.c  foo.c
+             $ hg status
+             ? bar.c
+             ? foo.c
+             $ hg add bar.c
+             $ hg status
+             A bar.c
+             ? foo.c
 
     Returns 0 if all files are successfully added.
     """
@@ -234,6 +254,46 @@
     not specified, -s/--similarity defaults to 100 and only renames of
     identical files are detected.
 
+    .. container:: verbose
+
+       Examples:
+
+         - A number of files (bar.c and foo.c) are new,
+           while foobar.c has been removed (without using :hg:`remove`)
+           from the repository::
+
+             $ ls
+             bar.c foo.c
+             $ hg status
+             ! foobar.c
+             ? bar.c
+             ? foo.c
+             $ hg addremove
+             adding bar.c
+             adding foo.c
+             removing foobar.c
+             $ hg status
+             A bar.c
+             A foo.c
+             R foobar.c
+
+         - A file foobar.c was moved to foo.c without using :hg:`rename`.
+           Afterwards, it was edited slightly::
+
+             $ ls
+             foo.c
+             $ hg status
+             ! foobar.c
+             ? foo.c
+             $ hg addremove --similarity 90
+             removing foobar.c
+             adding foo.c
+             recording removal of foobar.c as rename to foo.c (94% similar)
+             $ hg status -C
+             A foo.c
+               foobar.c
+             R foobar.c
+
     Returns 0 if all files are successfully added.
     """
     try:
@@ -486,6 +546,23 @@
 
     .. container:: verbose
 
+      Examples:
+
+      - Reverse the effect of the parent of the working directory.
+        This backout will be committed immediately::
+
+          hg backout -r .
+
+      - Reverse the effect of previous bad revision 23::
+
+          hg backout -r 23
+          hg commit -m "Backout revision 23"
+
+      - Reverse the effect of previous bad revision 23 and
+        commit the backout immediately::
+
+          hg backout -r 23 --commit
+
       By default, the pending changeset will have one parent,
       maintaining a linear history. With --merge, the pending
       changeset will instead have two parents: the old parent of the
@@ -504,6 +581,15 @@
     Returns 0 on success, 1 if nothing to backout or there are unresolved
     files.
     '''
+    wlock = lock = None
+    try:
+        wlock = repo.wlock()
+        lock = repo.lock()
+        return _dobackout(ui, repo, node, rev, commit, **opts)
+    finally:
+        release(lock, wlock)
+
+def _dobackout(ui, repo, node=None, rev=None, commit=False, **opts):
     if rev and node:
         raise error.Abort(_("please specify just one revision"))
 
@@ -542,7 +628,6 @@
         parent = p1
 
     # the backout should appear on the same branch
-    wlock = repo.wlock()
     try:
         branch = repo.dirstate.branch()
         bheads = repo.branchheads(branch)
@@ -552,8 +637,7 @@
             try:
                 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
                              'backout')
-                stats = mergemod.update(repo, parent, True, True, False,
-                                        node, False)
+                stats = mergemod.update(repo, parent, True, True, node, False)
                 repo.setparents(op1, op2)
                 dsguard.close()
                 hg._showstats(repo, stats)
@@ -605,7 +689,9 @@
             finally:
                 ui.setconfig('ui', 'forcemerge', '', '')
     finally:
-        wlock.release()
+        # TODO: get rid of this meaningless try/finally enclosing.
+        # this is kept only to reduce changes in a patch.
+        pass
     return 0
 
 @command('bisect',
@@ -1120,7 +1206,7 @@
             ui.status(_('reset working directory to branch %s\n') % label)
         elif label:
             if not opts.get('force') and label in repo.branchmap():
-                if label not in [p.branch() for p in repo.parents()]:
+                if label not in [p.branch() for p in repo[None].parents()]:
                     raise error.Abort(_('a branch of the same name already'
                                        ' exists'),
                                      # i18n: "it" refers to an existing branch
@@ -1337,7 +1423,8 @@
 @command('^clone',
     [('U', 'noupdate', None, _('the clone will include an empty working '
                                'directory (only a repository)')),
-    ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
+    ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
+        _('REV')),
     ('r', 'rev', [], _('include the specified changeset'), _('REV')),
     ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
     ('', 'pull', None, _('use pull protocol to copy metadata')),
@@ -1505,7 +1592,32 @@
     See :hg:`help dates` for a list of formats valid for -d/--date.
 
     Returns 0 on success, 1 if nothing changed.
+
+    .. container:: verbose
+
+      Examples:
+
+      - commit all files ending in .py::
+
+          hg commit --include "set:**.py"
+
+      - commit all non-binary files::
+
+          hg commit --exclude "set:binary()"
+
+      - amend the current commit and set the date to now::
+
+          hg commit --amend --date now
     """
+    wlock = lock = None
+    try:
+        wlock = repo.wlock()
+        lock = repo.lock()
+        return _docommit(ui, repo, *pats, **opts)
+    finally:
+        release(lock, wlock)
+
+def _docommit(ui, repo, *pats, **opts):
     if opts.get('interactive'):
         opts.pop('interactive')
         cmdutil.dorecord(ui, repo, commit, None, False,
@@ -1530,8 +1642,8 @@
         if not bheads:
             raise error.Abort(_('can only close branch heads'))
         elif opts.get('amend'):
-            if repo.parents()[0].p1().branch() != branch and \
-                    repo.parents()[0].p2().branch() != branch:
+            if repo[None].parents()[0].p1().branch() != branch and \
+                    repo[None].parents()[0].p2().branch() != branch:
                 raise error.Abort(_('can only close branch heads'))
 
     if opts.get('amend'):
@@ -1547,6 +1659,9 @@
         if not allowunstable and old.children():
             raise error.Abort(_('cannot amend changeset with children'))
 
+        newextra = extra.copy()
+        newextra['branch'] = branch
+        extra = newextra
         # commitfunc is used only for temporary amend commit by cmdutil.amend
         def commitfunc(ui, repo, message, match, opts):
             return repo.commit(message,
@@ -2111,11 +2226,7 @@
         ui.write(line)
         ui.write("\n")
 
-@command('debugdata',
-    [('c', 'changelog', False, _('open changelog')),
-     ('m', 'manifest', False, _('open manifest')),
-     ('', 'dir', False, _('open directory manifest'))],
-    _('-c|-m|FILE REV'))
+@command('debugdata', debugrevlogopts, _('-c|-m|FILE REV'))
 def debugdata(ui, repo, file_, rev=None, **opts):
     """dump the contents of a data file revision"""
     if opts.get('changelog') or opts.get('manifest'):
@@ -2321,11 +2432,8 @@
     else:
         raise error.Abort(_("no ignore patterns found"))
 
-@command('debugindex',
-    [('c', 'changelog', False, _('open changelog')),
-     ('m', 'manifest', False, _('open manifest')),
-     ('', 'dir', False, _('open directory manifest')),
-     ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
+@command('debugindex', debugrevlogopts +
+    [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
     _('[-f FORMAT] -c|-m|FILE'),
     optionalrepo=True)
 def debugindex(ui, repo, file_=None, **opts):
@@ -2380,16 +2488,11 @@
                     i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
                     base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
 
-@command('debugindexdot', [], _('FILE'), optionalrepo=True)
-def debugindexdot(ui, repo, file_):
+@command('debugindexdot', debugrevlogopts,
+    _('-c|-m|FILE'), optionalrepo=True)
+def debugindexdot(ui, repo, file_=None, **opts):
     """dump an index DAG as a graphviz dot file"""
-    r = None
-    if repo:
-        filelog = repo.file(file_)
-        if len(filelog):
-            r = filelog
-    if not r:
-        r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
+    r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
     ui.write(("digraph G {\n"))
     for i in r:
         node = r.node(i)
@@ -2399,6 +2502,117 @@
             ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
     ui.write("}\n")
 
+@command('debugdeltachain',
+    debugrevlogopts + formatteropts,
+    _('-c|-m|FILE'),
+    optionalrepo=True)
+def debugdeltachain(ui, repo, file_=None, **opts):
+    """dump information about delta chains in a revlog
+
+    Output can be templatized. Available template keywords are:
+
+       rev          revision number
+       chainid      delta chain identifier (numbered by unique base)
+       chainlen     delta chain length to this revision
+       prevrev      previous revision in delta chain
+       deltatype    role of delta / how it was computed
+       compsize     compressed size of revision
+       uncompsize   uncompressed size of revision
+       chainsize    total size of compressed revisions in chain
+       chainratio   total chain size divided by uncompressed revision size
+                    (new delta chains typically start at ratio 2.00)
+       lindist      linear distance from base revision in delta chain to end
+                    of this revision
+       extradist    total size of revisions not part of this delta chain from
+                    base of delta chain to end of this revision; a measurement
+                    of how much extra data we need to read/seek across to read
+                    the delta chain for this revision
+       extraratio   extradist divided by chainsize; another representation of
+                    how much unrelated data is needed to load this delta chain
+    """
+    r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
+    index = r.index
+    generaldelta = r.version & revlog.REVLOGGENERALDELTA
+
+    def revinfo(rev):
+        iterrev = rev
+        e = index[iterrev]
+        chain = []
+        compsize = e[1]
+        uncompsize = e[2]
+        chainsize = 0
+
+        if generaldelta:
+            if e[3] == e[5]:
+                deltatype = 'p1'
+            elif e[3] == e[6]:
+                deltatype = 'p2'
+            elif e[3] == rev - 1:
+                deltatype = 'prev'
+            elif e[3] == rev:
+                deltatype = 'base'
+            else:
+                deltatype = 'other'
+        else:
+            if e[3] == rev:
+                deltatype = 'base'
+            else:
+                deltatype = 'prev'
+
+        while iterrev != e[3]:
+            chain.append(iterrev)
+            chainsize += e[1]
+            if generaldelta:
+                iterrev = e[3]
+            else:
+                iterrev -= 1
+            e = index[iterrev]
+        else:
+            chainsize += e[1]
+            chain.append(iterrev)
+
+        chain.reverse()
+        return compsize, uncompsize, deltatype, chain, chainsize
+
+    fm = ui.formatter('debugdeltachain', opts)
+
+    fm.plain('    rev  chain# chainlen     prev   delta       '
+             'size    rawsize  chainsize     ratio   lindist extradist '
+             'extraratio\n')
+
+    chainbases = {}
+    for rev in r:
+        comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
+        chainbase = chain[0]
+        chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
+        basestart = r.start(chainbase)
+        revstart = r.start(rev)
+        lineardist = revstart + comp - basestart
+        extradist = lineardist - chainsize
+        try:
+            prevrev = chain[-2]
+        except IndexError:
+            prevrev = -1
+
+        chainratio = float(chainsize) / float(uncomp)
+        extraratio = float(extradist) / float(chainsize)
+
+        fm.startitem()
+        fm.write('rev chainid chainlen prevrev deltatype compsize '
+                 'uncompsize chainsize chainratio lindist extradist '
+                 'extraratio',
+                 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f\n',
+                 rev, chainid, len(chain), prevrev, deltatype, comp,
+                 uncomp, chainsize, chainratio, lineardist, extradist,
+                 extraratio,
+                 rev=rev, chainid=chainid, chainlen=len(chain),
+                 prevrev=prevrev, deltatype=deltatype, compsize=comp,
+                 uncompsize=uncomp, chainsize=chainsize,
+                 chainratio=chainratio, lindist=lineardist,
+                 extradist=extradist, extraratio=extraratio)
+
+    fm.end()
+
 @command('debuginstall', [], '', norepo=True)
 def debuginstall(ui):
     '''test Mercurial installation
@@ -2522,6 +2736,12 @@
 
     Use --verbose to print out information about whether v1 or v2 merge state
     was chosen."""
+    def _hashornull(h):
+        if h == nullhex:
+            return 'null'
+        else:
+            return h
+
     def printrecords(version):
         ui.write(('* version %s records\n') % version)
         if version == 1:
@@ -2539,7 +2759,7 @@
                 driver, mdstate = record.split('\0', 1)
                 ui.write(('merge driver: %s (state "%s")\n')
                          % (driver, mdstate))
-            elif rtype in 'FD':
+            elif rtype in 'FDC':
                 r = record.split('\0')
                 f, state, hash, lfile, afile, anode, ofile = r[0:7]
                 if version == 1:
@@ -2547,15 +2767,20 @@
                     flags = r[7]
                 else:
                     onode, flags = r[7:9]
-                ui.write(('file: %s (state "%s", hash %s)\n')
-                         % (f, state, hash))
+                ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
+                         % (f, rtype, state, _hashornull(hash)))
                 ui.write(('  local path: %s (flags "%s")\n') % (lfile, flags))
-                ui.write(('  ancestor path: %s (node %s)\n') % (afile, anode))
-                ui.write(('  other path: %s (node %s)\n') % (ofile, onode))
+                ui.write(('  ancestor path: %s (node %s)\n')
+                         % (afile, _hashornull(anode)))
+                ui.write(('  other path: %s (node %s)\n')
+                         % (ofile, _hashornull(onode)))
             else:
                 ui.write(('unrecognized entry: %s\t%s\n')
                          % (rtype, record.replace('\0', '\t')))
 
+    # Avoid mergestate.read() since it may raise an exception for unsupported
+    # merge state records. We shouldn't be doing this, but this is OK since this
+    # command is pretty low-level.
     ms = mergemod.mergestate(repo)
 
     # sort so that reasonable information is on top
@@ -2881,21 +3106,17 @@
     wlock = repo.wlock()
     try:
         dirstate = repo.dirstate
-
+        changedfiles = None
         # See command doc for what minimal does.
         if opts.get('minimal'):
+            manifestfiles = set(ctx.manifest().keys())
             dirstatefiles = set(dirstate)
-            ctxfiles = set(ctx.manifest().keys())
-            for file in (dirstatefiles | ctxfiles):
-                indirstate = file in dirstatefiles
-                inctx = file in ctxfiles
-
-                if indirstate and not inctx and dirstate[file] != 'a':
-                    dirstate.drop(file)
-                elif inctx and not indirstate:
-                    dirstate.normallookup(file)
-        else:
-            dirstate.rebuild(ctx.node(), ctx.manifest())
+            manifestonly = manifestfiles - dirstatefiles
+            dsonly = dirstatefiles - manifestfiles
+            dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
+            changedfiles =  manifestonly | dsnotadded
+
+        dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
     finally:
         wlock.release()
 
@@ -2921,11 +3142,8 @@
         else:
             ui.write(_("%s not renamed\n") % rel)
 
-@command('debugrevlog',
-    [('c', 'changelog', False, _('open changelog')),
-     ('m', 'manifest', False, _('open manifest')),
-     ('', 'dir', False, _('open directory manifest')),
-     ('d', 'dump', False, _('dump index data'))],
+@command('debugrevlog', debugrevlogopts +
+    [('d', 'dump', False, _('dump index data'))],
     _('-c|-m|FILE'),
     optionalrepo=True)
 def debugrevlog(ui, repo, file_=None, **opts):
@@ -3162,8 +3380,12 @@
     [('', 'nodates', None, _('do not display the saved mtime')),
     ('', 'datesort', None, _('sort by saved mtime'))],
     _('[OPTION]...'))
-def debugstate(ui, repo, nodates=None, datesort=None):
+def debugstate(ui, repo, **opts):
     """show the contents of the current dirstate"""
+
+    nodates = opts.get('nodates')
+    datesort = opts.get('datesort')
+
     timestr = ""
     if datesort:
         keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
@@ -3628,7 +3850,14 @@
 
     Returns 0 on successful completion.
     '''
-
+    wlock = None
+    try:
+        wlock = repo.wlock()
+        return _dograft(ui, repo, *revs, **opts)
+    finally:
+        release(wlock)
+
+def _dograft(ui, repo, *revs, **opts):
     revs = list(revs)
     revs.extend(opts['rev'])
 
@@ -3734,7 +3963,6 @@
         if not revs:
             return -1
 
-    wlock = repo.wlock()
     try:
         for pos, ctx in enumerate(repo.set("%ld", revs)):
             desc = '%d:%s "%s"' % (ctx.rev(), ctx,
@@ -3746,10 +3974,10 @@
             if opts.get('dry_run'):
                 continue
 
-            source = ctx.extra().get('source')
-            extra = {}
+            extra = ctx.extra().copy()
+            del extra['branch']
+            source = extra.get('source')
             if source:
-                extra['source'] = source
                 extra['intermediate-source'] = ctx.hex()
             else:
                 extra['source'] = ctx.hex()
@@ -3779,9 +4007,17 @@
                     # write out state for --continue
                     nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
                     repo.vfs.write('graftstate', ''.join(nodelines))
+                    extra = ''
+                    if opts.get('user'):
+                        extra += ' --user %s' % opts['user']
+                    if opts.get('date'):
+                        extra += ' --date %s' % opts['date']
+                    if opts.get('log'):
+                        extra += ' --log'
+                    hint=_('use hg resolve and hg graft --continue%s') % extra
                     raise error.Abort(
                         _("unresolved conflicts, can't continue"),
-                        hint=_('use hg resolve and hg graft --continue'))
+                        hint=hint)
             else:
                 cont = False
 
@@ -3793,7 +4029,9 @@
                     _('note: graft of %d:%s created no changes to commit\n') %
                     (ctx.rev(), ctx))
     finally:
-        wlock.release()
+        # TODO: get rid of this meaningless try/finally enclosing.
+        # this is kept only to reduce changes in a patch.
+        pass
 
     # remove state when we complete successfully
     if not opts.get('dry_run'):
@@ -4129,11 +4367,16 @@
         keep.append(sys.platform.lower())
 
     section = None
+    subtopic = None
     if name and '.' in name:
         name, section = name.split('.', 1)
         section = section.lower()
-
-    text = help.help_(ui, name, **opts)
+        if '.' in section:
+            subtopic, section = section.split('.', 1)
+        else:
+            subtopic = section
+
+    text = help.help_(ui, name, subtopic=subtopic, **opts)
 
     formatted, pruned = minirst.format(text, textwidth, keep=keep,
                                        section=section)
@@ -4195,6 +4438,9 @@
 
           hg id -r tip http://selenic.com/hg/
 
+    See :hg:`log` for generating more information about specific revisions,
+    including full hash identifiers.
+
     Returns 0 if successful.
     """
 
@@ -4328,14 +4574,23 @@
     Import a list of patches and commit them individually (unless
     --no-commit is specified).
 
-    Because import first applies changes to the working directory,
-    import will abort if there are outstanding changes.
+    To read a patch from standard input, use "-" as the patch name. If
+    a URL is specified, the patch will be downloaded from there.
+
+    Import first applies changes to the working directory (unless
+    --bypass is specified), import will abort if there are outstanding
+    changes.
+
+    Use --bypass to apply and commit patches directly to the
+    repository, without affecting the working directory. Without
+    --exact, patches will be applied on top of the working directory
+    parent revision.
 
     You can import a patch straight from a mail message. Even patches
     as attachments work (to use the body part, it must have type
     text/plain or text/x-patch). From and Subject headers of email
     message are used as default committer and commit message. All
-    text/plain body parts before first diff are added to commit
+    text/plain body parts before first diff are added to the commit
     message.
 
     If the imported patch was generated by :hg:`export`, user and
@@ -4349,14 +4604,6 @@
     the patch. This may happen due to character set problems or other
     deficiencies in the text patch format.
 
-    Use --bypass to apply and commit patches directly to the
-    repository, not touching the working directory. Without --exact,
-    patches will be applied on top of the working directory parent
-    revision.
-
-    With -s/--similarity, hg will attempt to discover renames and
-    copies in the patch in the same way as :hg:`addremove`.
-
     Use --partial to ensure a changeset will be created from the patch
     even if some hunks fail to apply. Hunks that fail to apply will be
     written to a <target-file>.rej file. Conflicts can then be resolved
@@ -4367,14 +4614,15 @@
     cleanly, :hg:`import --partial` will create an empty changeset,
     importing only the patch metadata.
 
+    With -s/--similarity, hg will attempt to discover renames and
+    copies in the patch in the same way as :hg:`addremove`.
+
     It is possible to use external patch programs to perform the patch
     by setting the ``ui.patch`` configuration option. For the default
     internal tool, the fuzz can also be configured via ``patch.fuzz``.
     See :hg:`help config` for more information about configuration
     files and how to use these options.
 
-    To read a patch from standard input, use "-" as the patch name. If
-    a URL is specified, the patch will be downloaded from it.
     See :hg:`help dates` for a list of formats valid for -d/--date.
 
     .. container:: verbose
@@ -4419,6 +4667,7 @@
     if date:
         opts['date'] = util.parsedate(date)
 
+    exact = opts.get('exact')
     update = not opts.get('bypass')
     if not update and opts.get('no_commit'):
         raise error.Abort(_('cannot use --no-commit with --bypass'))
@@ -4430,15 +4679,11 @@
         raise error.Abort(_('similarity must be between 0 and 100'))
     if sim and not update:
         raise error.Abort(_('cannot use --similarity with --bypass'))
-    if opts.get('exact') and opts.get('edit'):
-        raise error.Abort(_('cannot use --exact with --edit'))
-    if opts.get('exact') and opts.get('prefix'):
-        raise error.Abort(_('cannot use --exact with --prefix'))
-
-    if update:
-        cmdutil.checkunfinished(repo)
-    if (opts.get('exact') or not opts.get('force')) and update:
-        cmdutil.bailifchanged(repo)
+    if exact:
+        if opts.get('edit'):
+            raise error.Abort(_('cannot use --exact with --edit'))
+        if opts.get('prefix'):
+            raise error.Abort(_('cannot use --exact with --prefix'))
 
     base = opts["base"]
     wlock = dsguard = lock = tr = None
@@ -4449,12 +4694,18 @@
     try:
         try:
             wlock = repo.wlock()
+
+            if update:
+                cmdutil.checkunfinished(repo)
+                if (exact or not opts.get('force')):
+                    cmdutil.bailifchanged(repo)
+
             if not opts.get('no_commit'):
                 lock = repo.lock()
                 tr = repo.transaction('import')
             else:
                 dsguard = cmdutil.dirstateguard(repo, 'import')
-            parents = repo.parents()
+            parents = repo[None].parents()
             for patchurl in patches:
                 if patchurl == '-':
                     ui.status(_('applying patch from stdin\n'))
@@ -4473,8 +4724,8 @@
                     if msg:
                         haspatch = True
                         ui.note(msg + '\n')
-                    if update or opts.get('exact'):
-                        parents = repo.parents()
+                    if update or exact:
+                        parents = repo[None].parents()
                     else:
                         parents = [repo[node]]
                     if rej:
@@ -4573,8 +4824,7 @@
         cmdutil.checkunsupportedgraphflags([], opts)
         def display(other, chlist, displayer):
             revdag = cmdutil.graphrevs(other, chlist, opts)
-            showparents = [ctx.node() for ctx in repo[None].parents()]
-            cmdutil.displaygraph(ui, revdag, displayer, showparents,
+            cmdutil.displaygraph(ui, repo, revdag, displayer,
                                  graphmod.asciiedges)
 
         hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
@@ -4755,6 +5005,10 @@
 
           hg log -k bug --template "{rev}\\n"
 
+      - the full hash identifier of the working directory parent::
+
+          hg log -r . --template "{node}\\n"
+
       - list available log templates::
 
           hg log -T list
@@ -5007,9 +5261,7 @@
 
         revdag = cmdutil.graphrevs(repo, o, opts)
         displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
-        showparents = [ctx.node() for ctx in repo[None].parents()]
-        cmdutil.displaygraph(ui, revdag, displayer, showparents,
-                             graphmod.asciiedges)
+        cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
         cmdutil.outgoinghooks(ui, repo, other, opts, o)
         return 0
 
@@ -5043,6 +5295,13 @@
     last changed (before the working directory revision or the
     argument to --rev if given) is printed.
 
+    This command is equivalent to::
+
+        hg log -r "parents()" or
+        hg log -r "parents(REV)" or
+        hg log -r "max(file(FILE))" or
+        hg log -r "max(::REV and file(FILE))"
+
     See :hg:`summary` and :hg:`help revsets` for related information.
 
     Returns 0 on success.
@@ -5122,6 +5381,8 @@
             else:
                 ui.write("%s = %s\n" % (name,
                                         util.hidepassword(path.rawloc)))
+                for subopt, value in sorted(path.suboptions.items()):
+                    ui.write('%s:%s = %s\n' % (name, subopt, value))
 
 @command('phase',
     [('p', 'public', False, _('set changeset phase to public')),
@@ -5227,10 +5488,9 @@
                 checkout, movemarkfrom, brev = updata
             ret = hg.update(repo, checkout)
         except error.UpdateAbort as inst:
-            ui.warn(_("not updating: %s\n") % str(inst))
-            if inst.hint:
-                ui.warn(_("(%s)\n") % inst.hint)
-            return 0
+            msg = _("not updating: %s") % str(inst)
+            hint = inst.hint
+            raise error.UpdateAbort(msg, hint=hint)
         if not ret and not checkout:
             if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
                 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
@@ -5398,7 +5658,8 @@
     if not path:
         raise error.Abort(_('default repository not configured!'),
                          hint=_('see the "path" section in "hg help config"'))
-    dest, branches = path.pushloc, (path.branch, opts.get('branch') or [])
+    dest = path.pushloc or path.loc
+    branches = (path.branch, opts.get('branch') or [])
     ui.status(_('pushing to %s\n') % util.hidepassword(dest))
     revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
     other = hg.peer(repo, opts, dest)
@@ -5590,7 +5851,7 @@
 
     if show:
         fm = ui.formatter('resolve', opts)
-        ms = mergemod.mergestate(repo)
+        ms = mergemod.mergestate.read(repo)
         m = scmutil.match(repo[None], pats, opts)
         for f in ms:
             if not m(f):
@@ -5605,7 +5866,7 @@
 
     wlock = repo.wlock()
     try:
-        ms = mergemod.mergestate(repo)
+        ms = mergemod.mergestate.read(repo)
 
         if not (ms.active() or repo.dirstate.p2() != nullid):
             raise error.Abort(
@@ -5655,7 +5916,11 @@
             else:
                 # backup pre-resolve (merge uses .orig for its own purposes)
                 a = repo.wjoin(f)
-                util.copyfile(a, a + ".resolve")
+                try:
+                    util.copyfile(a, a + ".resolve")
+                except (IOError, OSError) as inst:
+                    if inst.errno != errno.ENOENT:
+                        raise
 
                 try:
                     # preresolve file
@@ -5673,7 +5938,12 @@
                 # replace filemerge's .orig file with our resolve file, but only
                 # for merges that are complete
                 if complete:
-                    util.rename(a + ".resolve", a + ".orig")
+                    try:
+                        util.rename(a + ".resolve",
+                                    cmdutil.origpath(ui, repo, a))
+                    except OSError as inst:
+                        if inst.errno != errno.ENOENT:
+                            raise
 
         for f in tocomplete:
             try:
@@ -5689,9 +5959,14 @@
 
             # replace filemerge's .orig file with our resolve file
             a = repo.wjoin(f)
-            util.rename(a + ".resolve", a + ".orig")
+            try:
+                util.rename(a + ".resolve", cmdutil.origpath(ui, repo, a))
+            except OSError as inst:
+                if inst.errno != errno.ENOENT:
+                    raise
 
         ms.commit()
+        ms.recordactions()
 
         if not didwork and pats:
             ui.warn(_("arguments do not match paths that need resolving\n"))
@@ -5919,81 +6194,10 @@
         s.serve_forever()
 
     if opts["cmdserver"]:
-        import commandserver
         service = commandserver.createservice(ui, repo, opts)
-        return cmdutil.service(opts, initfn=service.init, runfn=service.run)
-
-    # this way we can check if something was given in the command-line
-    if opts.get('port'):
-        opts['port'] = util.getport(opts.get('port'))
-
-    if repo:
-        baseui = repo.baseui
     else:
-        baseui = ui
-    optlist = ("name templates style address port prefix ipv6"
-               " accesslog errorlog certificate encoding")
-    for o in optlist.split():
-        val = opts.get(o, '')
-        if val in (None, ''): # should check against default options instead
-            continue
-        baseui.setconfig("web", o, val, 'serve')
-        if repo and repo.ui != baseui:
-            repo.ui.setconfig("web", o, val, 'serve')
-
-    o = opts.get('web_conf') or opts.get('webdir_conf')
-    if not o:
-        if not repo:
-            raise error.RepoError(_("there is no Mercurial repository"
-                                    " here (.hg not found)"))
-        o = repo
-
-    app = hgweb.hgweb(o, baseui=baseui)
-    service = httpservice(ui, app, opts)
-    cmdutil.service(opts, initfn=service.init, runfn=service.run)
-
-class httpservice(object):
-    def __init__(self, ui, app, opts):
-        self.ui = ui
-        self.app = app
-        self.opts = opts
-
-    def init(self):
-        util.setsignalhandler()
-        self.httpd = hgweb_server.create_server(self.ui, self.app)
-
-        if self.opts['port'] and not self.ui.verbose:
-            return
-
-        if self.httpd.prefix:
-            prefix = self.httpd.prefix.strip('/') + '/'
-        else:
-            prefix = ''
-
-        port = ':%d' % self.httpd.port
-        if port == ':80':
-            port = ''
-
-        bindaddr = self.httpd.addr
-        if bindaddr == '0.0.0.0':
-            bindaddr = '*'
-        elif ':' in bindaddr: # IPv6
-            bindaddr = '[%s]' % bindaddr
-
-        fqaddr = self.httpd.fqaddr
-        if ':' in fqaddr:
-            fqaddr = '[%s]' % fqaddr
-        if self.opts['port']:
-            write = self.ui.status
-        else:
-            write = self.ui.write
-        write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
-              (fqaddr, port, prefix, bindaddr, self.httpd.port))
-        self.ui.flush()  # avoid buffering of status message
-
-    def run(self):
-        self.httpd.serve_forever()
-
+        service = hgweb.createservice(ui, repo, opts)
+    return cmdutil.service(opts, initfn=service.init, runfn=service.run)
 
 @command('^status|st',
     [('A', 'all', None, _('show status of all files')),
@@ -6205,8 +6409,15 @@
         if d in status.added:
             status.added.remove(d)
 
-    ms = mergemod.mergestate(repo)
-    unresolved = [f for f in ms if ms[f] == 'u']
+    try:
+        ms = mergemod.mergestate.read(repo)
+    except error.UnsupportedMergeRecords as e:
+        s = ' '.join(e.recordtypes)
+        ui.warn(
+            _('warning: merge state has unsupported record types: %s\n') % s)
+        unresolved = 0
+    else:
+        unresolved = [f for f in ms if ms[f] == 'u']
 
     subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
 
@@ -6227,6 +6438,8 @@
     t = ', '.join(t)
     cleanworkdir = False
 
+    if repo.vfs.exists('graftstate'):
+        t += _(' (graft in progress)')
     if repo.vfs.exists('updatestate'):
         t += _(' (interrupted update)')
     elif len(parents) > 1:
@@ -6284,6 +6497,18 @@
     if draft or secret:
         ui.status(_('phases: %s\n') % ', '.join(t))
 
+    if obsolete.isenabled(repo, obsolete.createmarkersopt):
+        for trouble in ("unstable", "divergent", "bumped"):
+            numtrouble = len(repo.revs(trouble + "()"))
+            # We write all the possibilities to ease translation
+            troublemsg = {
+               "unstable": _("unstable: %d changeset"),
+               "divergent": _("divergent: %d changeset"),
+               "bumped": _("bumped: %d changeset"),
+            }
+            if numtrouble > 0:
+                ui.status(troublemsg[trouble] % numtrouble + "\n")
+
     cmdutil.summaryhooks(ui, repo)
 
     if opts.get('remote'):
@@ -6582,10 +6807,10 @@
                     tr.close()
                 except error.BundleUnknownFeatureError as exc:
                     raise error.Abort(_('%s: unknown bundle feature, %s')
-                                     % (fname, exc),
-                                     hint=_("see https://mercurial-scm.org/"
-                                            "wiki/BundleFeature for more "
-                                            "information"))
+                                      % (fname, exc),
+                                      hint=_("see https://mercurial-scm.org/"
+                                             "wiki/BundleFeature for more "
+                                             "information"))
                 finally:
                     if tr:
                         tr.release()
--- a/mercurial/commandserver.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/commandserver.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,10 +5,21 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
+from __future__ import absolute_import
+
+import SocketServer
+import errno
+import os
 import struct
-import sys, os, errno, traceback, SocketServer
-import dispatch, encoding, util, error
+import sys
+import traceback
+
+from .i18n import _
+from . import (
+    encoding,
+    error,
+    util,
+)
 
 logfile = None
 
@@ -174,6 +185,7 @@
     def runcommand(self):
         """ reads a list of \0 terminated arguments, executes
         and writes the return code to the result channel """
+        from . import dispatch  # avoid cycle
 
         length = struct.unpack('>I', self._read(4))[0]
         if not length:
--- a/mercurial/context.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/context.py	Thu Dec 17 17:27:32 2015 -0600
@@ -221,9 +221,10 @@
         return self._parents[0]
 
     def p2(self):
-        if len(self._parents) == 2:
-            return self._parents[1]
-        return changectx(self._repo, -1)
+        parents = self._parents
+        if len(parents) == 2:
+            return parents[1]
+        return changectx(self._repo, nullrev)
 
     def _fileinfo(self, path):
         if '_manifest' in self.__dict__:
@@ -270,7 +271,7 @@
         r = self._repo
         return matchmod.match(r.root, r.getcwd(), pats,
                               include, exclude, default,
-                              auditor=r.auditor, ctx=self,
+                              auditor=r.nofsauditor, ctx=self,
                               listsubrepos=listsubrepos, badfn=badfn)
 
     def diff(self, ctx2=None, match=None, **opts):
@@ -335,17 +336,19 @@
 
         if listsubrepos:
             for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
-                rev2 = ctx2.subrev(subpath)
                 try:
-                    submatch = matchmod.narrowmatcher(subpath, match)
-                    s = sub.status(rev2, match=submatch, ignored=listignored,
-                                   clean=listclean, unknown=listunknown,
-                                   listsubrepos=True)
-                    for rfiles, sfiles in zip(r, s):
-                        rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
-                except error.LookupError:
-                    self._repo.ui.status(_("skipping missing "
-                                           "subrepository: %s\n") % subpath)
+                    rev2 = ctx2.subrev(subpath)
+                except KeyError:
+                    # A subrepo that existed in node1 was deleted between
+                    # node1 and node2 (inclusive). Thus, ctx2's substate
+                    # won't contain that subpath. The best we can do ignore it.
+                    rev2 = None
+                submatch = matchmod.narrowmatcher(subpath, match)
+                s = sub.status(rev2, match=submatch, ignored=listignored,
+                               clean=listclean, unknown=listunknown,
+                               listsubrepos=True)
+                for rfiles, sfiles in zip(r, s):
+                    rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
 
         for l in r:
             l.sort()
@@ -511,10 +514,11 @@
 
     @propertycache
     def _parents(self):
-        p = self._repo.changelog.parentrevs(self._rev)
-        if p[1] == nullrev:
-            p = p[:-1]
-        return [changectx(self._repo, x) for x in p]
+        repo = self._repo
+        p1, p2 = repo.changelog.parentrevs(self._rev)
+        if p2 == nullrev:
+            return [changectx(repo, p1)]
+        return [changectx(repo, p1), changectx(repo, p2)]
 
     def changeset(self):
         return self._changeset
@@ -747,11 +751,22 @@
     def islink(self):
         return 'l' in self.flags()
 
+    def isabsent(self):
+        """whether this filectx represents a file not in self._changectx
+
+        This is mainly for merge code to detect change/delete conflicts. This is
+        expected to be True for all subclasses of basectx."""
+        return False
+
+    _customcmp = False
     def cmp(self, fctx):
         """compare with other file context
 
         returns True if different than fctx.
         """
+        if fctx._customcmp:
+            return fctx.cmp(self)
+
         if (fctx._filerev is None
             and (self._repo._encodefilterpats
                  # if file data starts with '\1\n', empty metadata block is
@@ -1140,17 +1155,17 @@
         # filesystem doesn't support them
 
         copiesget = self._repo.dirstate.copies().get
-
-        if len(self._parents) < 2:
+        parents = self.parents()
+        if len(parents) < 2:
             # when we have one parent, it's easy: copy from parent
-            man = self._parents[0].manifest()
+            man = parents[0].manifest()
             def func(f):
                 f = copiesget(f, f)
                 return man.flags(f)
         else:
             # merges are tricky: we try to reconstruct the unstored
             # result from the merge (issue1802)
-            p1, p2 = self._parents
+            p1, p2 = parents
             pa = p1.ancestor(p2)
             m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
 
@@ -1180,10 +1195,11 @@
         an extra 'a'. This is used by manifests merge to see that files
         are different and by update logic to avoid deleting newly added files.
         """
+        parents = self.parents()
 
-        man1 = self._parents[0].manifest()
+        man1 = parents[0].manifest()
         man = man1.copy()
-        if len(self._parents) > 1:
+        if len(parents) > 1:
             man2 = self.p2().manifest()
             def getman(f):
                 if f in man1:
@@ -1694,7 +1710,7 @@
     def date(self):
         t, tz = self._changectx.date()
         try:
-            return (util.statmtimesec(self._repo.wvfs.lstat(self._path)), tz)
+            return (self._repo.wvfs.lstat(self._path).st_mtime, tz)
         except OSError as err:
             if err.errno != errno.ENOENT:
                 raise
--- a/mercurial/crecord.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/crecord.py	Thu Dec 17 17:27:32 2015 -0600
@@ -24,6 +24,7 @@
     encoding,
     error,
     patch as patchmod,
+    util,
 )
 
 # This is required for ncurses to display non-ASCII characters in default user
@@ -450,11 +451,11 @@
 
     # if there are no changed files
     if len(headers) == 0:
-        return []
+        return [], {}
     uiheaders = [uiheader(h) for h in headers]
     # let user choose headers/hunks/lines, and mark their applied flags
     # accordingly
-    chunkselector(ui, uiheaders)
+    ret = chunkselector(ui, uiheaders)
     appliedhunklist = []
     for hdr in uiheaders:
         if (hdr.applied and
@@ -472,7 +473,7 @@
                 else:
                     fixoffset += hnk.removed - hnk.added
 
-    return appliedhunklist
+    return (appliedhunklist, ret)
 
 def gethw():
     """
@@ -501,6 +502,7 @@
         raise error.Abort(chunkselector.initerr)
     # ncurses does not restore signal handler for SIGTSTP
     signal.signal(signal.SIGTSTP, f)
+    return chunkselector.opts
 
 def testdecorator(testfn, f):
     def u(*args, **kwargs):
@@ -521,6 +523,7 @@
         while True:
             if chunkselector.handlekeypressed(testcommands.pop(0), test=True):
                 break
+    return chunkselector.opts
 
 class curseschunkselector(object):
     def __init__(self, headerlist, ui):
@@ -528,6 +531,7 @@
         self.headerlist = patch(headerlist)
 
         self.ui = ui
+        self.opts = {}
 
         self.errorstr = None
         # list of all chunks
@@ -1007,7 +1011,7 @@
                         pairname="legend")
             printstring(self.statuswin,
                         " (f)old/unfold; (c)onfirm applied; (q)uit; (?) help "
-                        "| [X]=hunk applied **=folded",
+                        "| [X]=hunk applied **=folded, toggle [a]mend mode",
                         pairname="legend")
         except curses.error:
             pass
@@ -1363,7 +1367,7 @@
                       F : fold / unfold parent item and all of its ancestors
                       m : edit / resume editing the commit message
                       e : edit the currently selected hunk
-                      a : toggle amend mode (hg rev >= 2.2)
+                      a : toggle amend mode (hg rev >= 2.2), only with commit -i
                       c : confirm selected changes
                       r : review/edit and confirm selected changes
                       q : quit without confirming (no changes will be made)
@@ -1430,6 +1434,35 @@
         else:
             return False
 
+    def toggleamend(self, opts, test):
+        """Toggle the amend flag.
+
+        When the amend flag is set, a commit will modify the most recently
+        committed changeset, instead of creating a new changeset.  Otherwise, a
+        new changeset will be created (the normal commit behavior).
+
+        """
+        try:
+            ver = float(util.version()[:3])
+        except ValueError:
+            ver = 1
+        if ver < 2.19:
+            msg = ("The amend option is unavailable with hg versions < 2.2\n\n"
+                   "Press any key to continue.")
+        elif opts.get('amend') is None:
+            opts['amend'] = True
+            msg = ("Amend option is turned on -- commiting the currently "
+                   "selected changes will not create a new changeset, but "
+                   "instead update the most recently committed changeset.\n\n"
+                   "Press any key to continue.")
+        elif opts.get('amend') is True:
+            opts['amend'] = None
+            msg = ("Amend option is turned off -- commiting the currently "
+                   "selected changes will create a new changeset.\n\n"
+                   "Press any key to continue.")
+        if not test:
+            self.confirmationwindow(msg)
+
     def recenterdisplayedarea(self):
         """
         once we scrolled with pg up pg down we can be pointing outside of the
@@ -1567,6 +1600,8 @@
             self.leftarrowshiftevent()
         elif keypressed in ["q"]:
             raise error.Abort(_('user quit'))
+        elif keypressed in ['a']:
+            self.toggleamend(self.opts, test)
         elif keypressed in ["c"]:
             if self.confirmcommit():
                 return True
--- a/mercurial/demandimport.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/demandimport.py	Thu Dec 17 17:27:32 2015 -0600
@@ -245,6 +245,10 @@
 
 def enable():
     "enable global demand-loading of modules"
+    # PyPy doesn't work with demand import.
+    if '__pypy__' in sys.builtin_module_names:
+        return
+
     if os.environ.get('HGDEMANDIMPORT') != 'disable':
         builtins.__import__ = _demandimport
 
--- a/mercurial/destutil.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/destutil.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,6 +5,8 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 from .i18n import _
 from . import (
     bookmarks,
@@ -198,3 +200,18 @@
     else:
         node = _destmergebranch(repo)
     return repo[node].rev()
+
+histeditdefaultrevset = 'reverse(only(.) and not public() and not ::merge())'
+
+def desthistedit(ui, repo):
+    """Default base revision to edit for `hg histedit`."""
+    default = ui.config('histedit', 'defaultrev', histeditdefaultrevset)
+    if default:
+        revs = repo.revs(default)
+        if revs:
+            # The revset supplied by the user may not be in ascending order nor
+            # take the first revision. So do this manually.
+            revs.sort()
+            return revs.first()
+
+    return None
--- a/mercurial/dirstate.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/dirstate.py	Thu Dec 17 17:27:32 2015 -0600
@@ -31,7 +31,7 @@
     '''Get "now" timestamp on filesystem'''
     tmpfd, tmpname = vfs.mkstemp()
     try:
-        return util.statmtimesec(os.fstat(tmpfd))
+        return os.fstat(tmpfd).st_mtime
     finally:
         os.close(tmpfd)
         vfs.unlink(tmpname)
@@ -471,7 +471,7 @@
     def normal(self, f):
         '''Mark a file normal and clean.'''
         s = os.lstat(self._join(f))
-        mtime = util.statmtimesec(s)
+        mtime = s.st_mtime
         self._addpath(f, 'n', s.st_mode,
                       s.st_size & _rangemask, mtime & _rangemask)
         if f in self._copymap:
@@ -639,17 +639,22 @@
 
     def rebuild(self, parent, allfiles, changedfiles=None):
         if changedfiles is None:
+            # Rebuild entire dirstate
             changedfiles = allfiles
-        oldmap = self._map
-        self.clear()
-        for f in allfiles:
-            if f not in changedfiles:
-                self._map[f] = oldmap[f]
+            lastnormaltime = self._lastnormaltime
+            self.clear()
+            self._lastnormaltime = lastnormaltime
+
+        for f in changedfiles:
+            mode = 0o666
+            if f in allfiles and 'x' in allfiles.flags(f):
+                mode = 0o777
+
+            if f in allfiles:
+                self._map[f] = dirstatetuple('n', mode, -1, 0)
             else:
-                if 'x' in allfiles.flags(f):
-                    self._map[f] = dirstatetuple('n', 0o777, -1, 0)
-                else:
-                    self._map[f] = dirstatetuple('n', 0o666, -1, 0)
+                self._map.pop(f, None)
+
         self._pl = (parent, nullid)
         self._dirty = True
 
@@ -657,13 +662,6 @@
         if not self._dirty:
             return
 
-        # enough 'delaywrite' prevents 'pack_dirstate' from dropping
-        # timestamp of each entries in dirstate, because of 'now > mtime'
-        delaywrite = self._ui.configint('debug', 'dirstate.delaywrite', 0)
-        if delaywrite > 0:
-            import time # to avoid useless import
-            time.sleep(delaywrite)
-
         filename = self._filename
         if tr is False: # not explicitly specified
             if (self._ui.configbool('devel', 'all-warnings')
@@ -704,7 +702,24 @@
     def _writedirstate(self, st):
         # use the modification time of the newly created temporary file as the
         # filesystem's notion of 'now'
-        now = util.statmtimesec(util.fstat(st)) & _rangemask
+        now = util.fstat(st).st_mtime & _rangemask
+
+        # enough 'delaywrite' prevents 'pack_dirstate' from dropping
+        # timestamp of each entries in dirstate, because of 'now > mtime'
+        delaywrite = self._ui.configint('debug', 'dirstate.delaywrite', 0)
+        if delaywrite > 0:
+            # do we have any files to delay for?
+            for f, e in self._map.iteritems():
+                if e[0] == 'n' and e[3] == now:
+                    import time # to avoid useless import
+                    # rather than sleep n seconds, sleep until the next
+                    # multiple of n seconds
+                    clock = time.time()
+                    start = int(clock) - (int(clock) % delaywrite)
+                    end = start + delaywrite
+                    time.sleep(end - clock)
+                    break
+
         st.write(parsers.pack_dirstate(self._map, self._copymap, self._pl, now))
         st.close()
         self._lastnormaltime = 0
@@ -1008,14 +1023,8 @@
                 # We may not have walked the full directory tree above,
                 # so stat and check everything we missed.
                 nf = iter(visit).next
-                pos = 0
-                while pos < len(visit):
-                    # visit in mid-sized batches so that we don't
-                    # block signals indefinitely
-                    xr = xrange(pos, min(len(visit), pos + 1000))
-                    for st in util.statfiles([join(visit[n]) for n in xr]):
-                        results[nf()] = st
-                    pos += 1000
+                for st in util.statfiles([join(i) for i in visit]):
+                    results[nf()] = st
         return results
 
     def status(self, match, subrepos, ignored, clean, unknown):
@@ -1084,16 +1093,15 @@
             if not st and state in "nma":
                 dadd(fn)
             elif state == 'n':
-                mtime = util.statmtimesec(st)
                 if (size >= 0 and
                     ((size != st.st_size and size != st.st_size & _rangemask)
                      or ((mode ^ st.st_mode) & 0o100 and checkexec))
                     or size == -2 # other parent
                     or fn in copymap):
                     madd(fn)
-                elif time != mtime and time != mtime & _rangemask:
+                elif time != st.st_mtime and time != st.st_mtime & _rangemask:
                     ladd(fn)
-                elif mtime == lastnormaltime:
+                elif st.st_mtime == lastnormaltime:
                     # fn may have just been marked as normal and it may have
                     # changed in the same second without changing its size.
                     # This can happen if we quickly do multiple commits.
--- a/mercurial/discovery.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/discovery.py	Thu Dec 17 17:27:32 2015 -0600
@@ -238,12 +238,42 @@
         unsynced = set()
     return {None: (oldheads, newheads, unsynced)}
 
-def checkheads(repo, remote, outgoing, remoteheads, newbranch=False, inc=False,
-               newbookmarks=[]):
+def _nowarnheads(pushop):
+    # Compute newly pushed bookmarks. We don't warn about bookmarked heads.
+
+    # internal config: bookmarks.pushing
+    newbookmarks = pushop.ui.configlist('bookmarks', 'pushing')
+
+    repo = pushop.repo.unfiltered()
+    remote = pushop.remote
+    localbookmarks = repo._bookmarks
+    remotebookmarks = remote.listkeys('bookmarks')
+    bookmarkedheads = set()
+    for bm in localbookmarks:
+        rnode = remotebookmarks.get(bm)
+        if rnode and rnode in repo:
+            lctx, rctx = repo[bm], repo[rnode]
+            if bookmarks.validdest(repo, rctx, lctx):
+                bookmarkedheads.add(lctx.node())
+        else:
+            if bm in newbookmarks and bm not in remotebookmarks:
+                bookmarkedheads.add(repo[bm].node())
+
+    return bookmarkedheads
+
+def checkheads(pushop):
     """Check that a push won't add any outgoing head
 
     raise Abort error and display ui message as needed.
     """
+
+    repo = pushop.repo.unfiltered()
+    remote = pushop.remote
+    outgoing = pushop.outgoing
+    remoteheads = pushop.remoteheads
+    newbranch = pushop.newbranch
+    inc = bool(pushop.incoming)
+
     # Check for each named branch if we're creating new remote heads.
     # To be a remote head after push, node must be either:
     # - unknown locally
@@ -268,19 +298,8 @@
                          hint=_("use 'hg push --new-branch' to create"
                                 " new remote branches"))
 
-    # 2. Compute newly pushed bookmarks. We don't warn about bookmarked heads.
-    localbookmarks = repo._bookmarks
-    remotebookmarks = remote.listkeys('bookmarks')
-    bookmarkedheads = set()
-    for bm in localbookmarks:
-        rnode = remotebookmarks.get(bm)
-        if rnode and rnode in repo:
-            lctx, rctx = repo[bm], repo[rnode]
-            if bookmarks.validdest(repo, rctx, lctx):
-                bookmarkedheads.add(lctx.node())
-        else:
-            if bm in newbookmarks and bm not in remotebookmarks:
-                bookmarkedheads.add(repo[bm].node())
+    # 2. Find heads that we need not warn about
+    nowarnheads = _nowarnheads(pushop)
 
     # 3. Check for new heads.
     # If there are more heads after the push than before, a suitable
@@ -366,7 +385,7 @@
                              " pushing new heads")
         elif len(newhs) > len(oldhs):
             # remove bookmarked or existing remote heads from the new heads list
-            dhs = sorted(newhs - bookmarkedheads - oldhs)
+            dhs = sorted(newhs - nowarnheads - oldhs)
         if dhs:
             if errormsg is None:
                 if branch not in ('default', None):
--- a/mercurial/dispatch.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/dispatch.py	Thu Dec 17 17:27:32 2015 -0600
@@ -320,7 +320,6 @@
     except socket.error as inst:
         ui.warn(_("abort: %s\n") % inst.args[-1])
     except: # re-raises
-        myver = util.version()
         # For compatibility checking, we discard the portion of the hg
         # version after the + on the assumption that if a "normal
         # user" is running a build with a + in it the packager
@@ -328,8 +327,7 @@
         # 'make local' copy of hg (where the version number can be out
         # of date) will be clueful enough to notice the implausible
         # version number and try updating.
-        compare = myver.split('+')[0]
-        ct = tuplever(compare)
+        ct = util.versiontuple(n=2)
         worst = None, ct, ''
         if ui.config('ui', 'supportcontact', None) is None:
             for name, mod in extensions.extensions():
@@ -344,7 +342,7 @@
                 if testedwith == 'internal':
                     continue
 
-                tested = [tuplever(t) for t in testedwith.split()]
+                tested = [util.versiontuple(t, 2) for t in testedwith.split()]
                 if ct in tested:
                     continue
 
@@ -369,7 +367,8 @@
             warning = (_("** unknown exception encountered, "
                          "please report by visiting\n** ") + bugtracker + '\n')
         warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
-                    (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
+                    (_("** Mercurial Distributed SCM (version %s)\n") %
+                     util.version()) +
                     (_("** Extensions loaded: %s\n") %
                      ", ".join([x[0] for x in extensions.extensions()])))
         ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
@@ -378,15 +377,6 @@
 
     return -1
 
-def tuplever(v):
-    try:
-        # Assertion: tuplever is only used for extension compatibility
-        # checking. Otherwise, the discarding of extra version fields is
-        # incorrect.
-        return tuple([int(i) for i in v.split('.')[0:2]])
-    except ValueError:
-        return tuple()
-
 def aliasargs(fn, givenargs):
     args = getattr(fn, 'args', [])
     if args:
@@ -864,7 +854,7 @@
     if options['version']:
         return commands.version_(ui)
     if options['help']:
-        return commands.help_(ui, cmd, command=True)
+        return commands.help_(ui, cmd, command=cmd is not None)
     elif not cmd:
         return commands.help_(ui, 'shortlist')
 
--- a/mercurial/encoding.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/encoding.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,8 +5,15 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import error
-import unicodedata, locale, os
+from __future__ import absolute_import
+
+import locale
+import os
+import unicodedata
+
+from . import (
+    error,
+)
 
 # These unicode characters are ignored by HFS+ (Apple Technote 1150,
 # "Unicode Subtleties"), so we need to ignore them in some places for
@@ -194,7 +201,7 @@
     'ellipsis' is always placed at trimmed side.
 
     >>> ellipsis = '+++'
-    >>> from mercurial import encoding
+    >>> from . import encoding
     >>> encoding.encoding = 'utf-8'
     >>> t= '1234567890'
     >>> print trim(t, 12, ellipsis=ellipsis)
@@ -290,7 +297,7 @@
 def asciilower(s):
     # delay importing avoids cyclic dependency around "parsers" in
     # pure Python build (util => i18n => encoding => parsers => util)
-    import parsers
+    from . import parsers
     impl = getattr(parsers, 'asciilower', _asciilower)
     global asciilower
     asciilower = impl
@@ -306,7 +313,7 @@
 def asciiupper(s):
     # delay importing avoids cyclic dependency around "parsers" in
     # pure Python build (util => i18n => encoding => parsers => util)
-    import parsers
+    from . import parsers
     impl = getattr(parsers, 'asciiupper', _asciiupper)
     global asciiupper
     asciiupper = impl
@@ -400,7 +407,7 @@
 
     if not _jsonmap:
         for x in xrange(32):
-            _jsonmap[chr(x)] = "\u%04x" %x
+            _jsonmap[chr(x)] = "\\u%04x" % x
         for x in xrange(32, 256):
             c = chr(x)
             _jsonmap[c] = c
@@ -414,6 +421,25 @@
 
     return ''.join(_jsonmap[c] for c in toutf8b(s))
 
+_utf8len = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 4]
+
+def getutf8char(s, pos):
+    '''get the next full utf-8 character in the given string, starting at pos
+
+    Raises a UnicodeError if the given location does not start a valid
+    utf-8 character.
+    '''
+
+    # find how many bytes to attempt decoding from first nibble
+    l = _utf8len[ord(s[pos]) >> 4]
+    if not l: # ascii
+        return s[pos]
+
+    c = s[pos:pos + l]
+    # validate with attempted decode
+    c.decode("utf-8")
+    return c
+
 def toutf8b(s):
     '''convert a local, possibly-binary string into UTF-8b
 
@@ -444,24 +470,32 @@
     internal surrogate encoding as a UTF-8 string.)
     '''
 
-    if isinstance(s, localstr):
-        return s._utf8
+    if "\xed" not in s:
+        if isinstance(s, localstr):
+            return s._utf8
+        try:
+            s.decode('utf-8')
+            return s
+        except UnicodeDecodeError:
+            pass
 
-    try:
-        s.decode('utf-8')
-        return s
-    except UnicodeDecodeError:
-        # surrogate-encode any characters that don't round-trip
-        s2 = s.decode('utf-8', 'ignore').encode('utf-8')
-        r = ""
-        pos = 0
-        for c in s:
-            if s2[pos:pos + 1] == c:
-                r += c
+    r = ""
+    pos = 0
+    l = len(s)
+    while pos < l:
+        try:
+            c = getutf8char(s, pos)
+            if "\xed\xb0\x80" <= c <= "\xed\xb3\xbf":
+                # have to re-escape existing U+DCxx characters
+                c = unichr(0xdc00 + ord(s[pos])).encode('utf-8')
                 pos += 1
             else:
-                r += unichr(0xdc00 + ord(c)).encode('utf-8')
-        return r
+                pos += len(c)
+        except UnicodeDecodeError:
+            c = unichr(0xdc00 + ord(s[pos])).encode('utf-8')
+            pos += 1
+        r += c
+    return r
 
 def fromutf8b(s):
     '''Given a UTF-8b string, return a local, possibly-binary string.
@@ -470,11 +504,17 @@
     is a round-trip process for strings like filenames, but metadata
     that's was passed through tolocal will remain in UTF-8.
 
+    >>> roundtrip = lambda x: fromutf8b(toutf8b(x)) == x
     >>> m = "\\xc3\\xa9\\x99abcd"
-    >>> n = toutf8b(m)
-    >>> n
+    >>> toutf8b(m)
     '\\xc3\\xa9\\xed\\xb2\\x99abcd'
-    >>> fromutf8b(n) == m
+    >>> roundtrip(m)
+    True
+    >>> roundtrip("\\xc2\\xc2\\x80")
+    True
+    >>> roundtrip("\\xef\\xbf\\xbd")
+    True
+    >>> roundtrip("\\xef\\xef\\xbf\\xbd")
     True
     '''
 
@@ -485,7 +525,7 @@
     u = s.decode("utf-8")
     r = ""
     for c in u:
-        if ord(c) & 0xff00 == 0xdc00:
+        if ord(c) & 0xffff00 == 0xdc00:
             r += chr(ord(c) & 0xff)
         else:
             r += c.encode("utf-8")
--- a/mercurial/error.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/error.py	Thu Dec 17 17:27:32 2015 -0600
@@ -72,6 +72,12 @@
 class UpdateAbort(Abort):
     """Raised when an update is aborted for destination issue"""
 
+class ResponseExpected(Abort):
+    """Raised when an EOF is received for a prompt"""
+    def __init__(self):
+        from .i18n import _
+        Abort.__init__(self, _('response expected'))
+
 class OutOfBandError(Exception):
     """Exception raised when a remote repo reports failure"""
 
@@ -106,6 +112,16 @@
 class RequirementError(RepoError):
     """Exception raised if .hg/requires has an unknown entry."""
 
+class UnsupportedMergeRecords(Abort):
+    def __init__(self, recordtypes):
+        from .i18n import _
+        self.recordtypes = sorted(recordtypes)
+        s = ' '.join(self.recordtypes)
+        Abort.__init__(
+            self, _('unsupported merge state records: %s') % s,
+            hint=_('see https://mercurial-scm.org/wiki/MergeStateRecords for '
+                   'more information'))
+
 class LockError(IOError):
     def __init__(self, errno, strerror, filename, desc):
         IOError.__init__(self, errno, strerror, filename)
--- a/mercurial/exchange.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/exchange.py	Thu Dec 17 17:27:32 2015 -0600
@@ -571,13 +571,7 @@
                 elif ctx.troubled():
                     raise error.Abort(mst[ctx.troubles()[0]] % ctx)
 
-        # internal config: bookmarks.pushing
-        newbm = pushop.ui.configlist('bookmarks', 'pushing')
-        discovery.checkheads(unfi, pushop.remote, outgoing,
-                             pushop.remoteheads,
-                             pushop.newbranch,
-                             bool(pushop.incoming),
-                             newbm)
+        discovery.checkheads(pushop)
     return True
 
 # List of names of steps to perform for an outgoing bundle2, order matters.
@@ -1427,6 +1421,11 @@
         return func
     return dec
 
+def bundle2requested(bundlecaps):
+    if bundlecaps is not None:
+        return any(cap.startswith('HG2') for cap in bundlecaps)
+    return False
+
 def getbundle(repo, source, heads=None, common=None, bundlecaps=None,
               **kwargs):
     """return a full bundle (with potentially multiple kind of parts)
@@ -1442,10 +1441,8 @@
     The implementation is at a very early stage and will get massive rework
     when the API of bundle is refined.
     """
+    usebundle2 = bundle2requested(bundlecaps)
     # bundle10 case
-    usebundle2 = False
-    if bundlecaps is not None:
-        usebundle2 = any((cap.startswith('HG2') for cap in bundlecaps))
     if not usebundle2:
         if bundlecaps and not kwargs.get('cg', True):
             raise ValueError(_('request for bundle10 must include changegroup'))
--- a/mercurial/extensions.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/extensions.py	Thu Dec 17 17:27:32 2015 -0600
@@ -24,7 +24,8 @@
 _extensions = {}
 _aftercallbacks = {}
 _order = []
-_ignore = ['hbisect', 'bookmarks', 'parentrevspec', 'interhg', 'inotify']
+_builtin = set(['hbisect', 'bookmarks', 'parentrevspec', 'progress', 'interhg',
+                'inotify'])
 
 def extensions(ui=None):
     if ui:
@@ -75,7 +76,7 @@
         shortname = name[6:]
     else:
         shortname = name
-    if shortname in _ignore:
+    if shortname in _builtin:
         return None
     if shortname in _extensions:
         return _extensions[shortname]
@@ -100,6 +101,17 @@
             if ui.debugflag:
                 ui.traceback()
             mod = importh(name)
+
+    # Before we do anything with the extension, check against minimum stated
+    # compatibility. This gives extension authors a mechanism to have their
+    # extensions short circuit when loaded with a known incompatible version
+    # of Mercurial.
+    minver = getattr(mod, 'minimumhgversion', None)
+    if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2):
+        ui.warn(_('(third party extension %s requires version %s or newer '
+                  'of Mercurial; disabling)\n') % (shortname, minver))
+        return
+
     _extensions[shortname] = mod
     _order.append(shortname)
     for fn in _aftercallbacks.get(shortname, []):
--- a/mercurial/filemerge.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/filemerge.py	Thu Dec 17 17:27:32 2015 -0600
@@ -13,9 +13,10 @@
 import tempfile
 
 from .i18n import _
-from .node import short
+from .node import nullid, short
 
 from . import (
+    cmdutil,
     error,
     match,
     simplemerge,
@@ -43,6 +44,50 @@
 mergeonly = 'mergeonly'  # just the full merge, no premerge
 fullmerge = 'fullmerge'  # both premerge and merge
 
+class absentfilectx(object):
+    """Represents a file that's ostensibly in a context but is actually not
+    present in it.
+
+    This is here because it's very specific to the filemerge code for now --
+    other code is likely going to break with the values this returns."""
+    def __init__(self, ctx, f):
+        self._ctx = ctx
+        self._f = f
+
+    def path(self):
+        return self._f
+
+    def size(self):
+        return None
+
+    def data(self):
+        return None
+
+    def filenode(self):
+        return nullid
+
+    _customcmp = True
+    def cmp(self, fctx):
+        """compare with other file context
+
+        returns True if different from fctx.
+        """
+        return not (fctx.isabsent() and
+                    fctx.ctx() == self.ctx() and
+                    fctx.path() == self.path())
+
+    def flags(self):
+        return ''
+
+    def changectx(self):
+        return self._ctx
+
+    def isbinary(self):
+        return False
+
+    def isabsent(self):
+        return True
+
 def internaltool(name, mergetype, onfailure=None, precheck=None):
     '''return a decorator for populating internal merge tool table'''
     def decorator(func):
@@ -75,8 +120,11 @@
     exe = _toolstr(ui, tool, "executable", tool)
     return util.findexe(util.expandpath(exe))
 
-def _picktool(repo, ui, path, binary, symlink):
-    def check(tool, pat, symlink, binary):
+def _picktool(repo, ui, path, binary, symlink, changedelete):
+    def supportscd(tool):
+        return tool in internals and internals[tool].mergetype == nomerge
+
+    def check(tool, pat, symlink, binary, changedelete):
         tmsg = tool
         if pat:
             tmsg += " specified for " + pat
@@ -89,6 +137,10 @@
             ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
         elif binary and not _toolbool(ui, tool, "binary"):
             ui.warn(_("tool %s can't handle binary\n") % tmsg)
+        elif changedelete and not supportscd(tool):
+            # the nomerge tools are the only tools that support change/delete
+            # conflicts
+            pass
         elif not util.gui() and _toolbool(ui, tool, "gui"):
             ui.warn(_("tool %s requires a GUI\n") % tmsg)
         else:
@@ -100,21 +152,27 @@
     force = ui.config('ui', 'forcemerge')
     if force:
         toolpath = _findtool(ui, force)
-        if toolpath:
-            return (force, util.shellquote(toolpath))
+        if changedelete and not supportscd(toolpath):
+            return ":prompt", None
         else:
-            # mimic HGMERGE if given tool not found
-            return (force, force)
+            if toolpath:
+                return (force, util.shellquote(toolpath))
+            else:
+                # mimic HGMERGE if given tool not found
+                return (force, force)
 
     # HGMERGE takes next precedence
     hgmerge = os.environ.get("HGMERGE")
     if hgmerge:
-        return (hgmerge, hgmerge)
+        if changedelete and not supportscd(hgmerge):
+            return ":prompt", None
+        else:
+            return (hgmerge, hgmerge)
 
     # then patterns
     for pat, tool in ui.configitems("merge-patterns"):
         mf = match.match(repo.root, '', [pat])
-        if mf(path) and check(tool, pat, symlink, False):
+        if mf(path) and check(tool, pat, symlink, False, changedelete):
             toolpath = _findtool(ui, tool)
             return (tool, util.shellquote(toolpath))
 
@@ -131,17 +189,19 @@
     tools = sorted([(-p, t) for t, p in tools.items() if t not in disabled])
     uimerge = ui.config("ui", "merge")
     if uimerge:
-        if uimerge not in names:
+        # external tools defined in uimerge won't be able to handle
+        # change/delete conflicts
+        if uimerge not in names and not changedelete:
             return (uimerge, uimerge)
         tools.insert(0, (None, uimerge)) # highest priority
     tools.append((None, "hgmerge")) # the old default, if found
     for p, t in tools:
-        if check(t, None, symlink, binary):
+        if check(t, None, symlink, binary, changedelete):
             toolpath = _findtool(ui, t)
             return (t, util.shellquote(toolpath))
 
     # internal merge or prompt as last resort
-    if symlink or binary:
+    if symlink or binary or changedelete:
         return ":prompt", None
     return ":merge", None
 
@@ -175,23 +235,53 @@
     ui = repo.ui
     fd = fcd.path()
 
-    if ui.promptchoice(_(" no tool found to merge %s\n"
-                         "keep (l)ocal or take (o)ther?"
-                         "$$ &Local $$ &Other") % fd, 0):
-        return _iother(repo, mynode, orig, fcd, fco, fca, toolconf)
-    else:
-        return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf)
+    try:
+        if fco.isabsent():
+            index = ui.promptchoice(
+                _("local changed %s which remote deleted\n"
+                  "use (c)hanged version, (d)elete, or leave (u)nresolved?"
+                  "$$ &Changed $$ &Delete $$ &Unresolved") % fd, 0)
+            choice = ['local', 'other', 'unresolved'][index]
+        elif fcd.isabsent():
+            index = ui.promptchoice(
+                _("remote changed %s which local deleted\n"
+                  "use (c)hanged version, leave (d)eleted, or "
+                  "leave (u)nresolved?"
+                  "$$ &Changed $$ &Deleted $$ &Unresolved") % fd, 0)
+            choice = ['other', 'local', 'unresolved'][index]
+        else:
+            index = ui.promptchoice(
+                _("no tool found to merge %s\n"
+                  "keep (l)ocal, take (o)ther, or leave (u)nresolved?"
+                  "$$ &Local $$ &Other $$ &Unresolved") % fd, 2)
+            choice = ['local', 'other', 'unresolved'][index]
+
+        if choice == 'other':
+            return _iother(repo, mynode, orig, fcd, fco, fca, toolconf)
+        elif choice == 'local':
+            return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf)
+        elif choice == 'unresolved':
+            return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf)
+    except error.ResponseExpected:
+        ui.write("\n")
+        return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf)
 
 @internaltool('local', nomerge)
 def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf):
     """Uses the local version of files as the merged version."""
-    return 0
+    return 0, fcd.isabsent()
 
 @internaltool('other', nomerge)
 def _iother(repo, mynode, orig, fcd, fco, fca, toolconf):
     """Uses the other version of files as the merged version."""
-    repo.wwrite(fcd.path(), fco.data(), fco.flags())
-    return 0
+    if fco.isabsent():
+        # local changed, remote deleted -- 'deleted' picked
+        repo.wvfs.unlinkpath(fcd.path())
+        deleted = True
+    else:
+        repo.wwrite(fcd.path(), fco.data(), fco.flags())
+        deleted = False
+    return 0, deleted
 
 @internaltool('fail', nomerge)
 def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf):
@@ -199,11 +289,14 @@
     Rather than attempting to merge files that were modified on both
     branches, it marks them as unresolved. The resolve command must be
     used to resolve these conflicts."""
-    return 1
+    # for change/delete conflicts write out the changed version, then fail
+    if fcd.isabsent():
+        repo.wwrite(fcd.path(), fco.data(), fco.flags())
+    return 1, False
 
-def _premerge(repo, toolconf, files, labels=None):
+def _premerge(repo, fcd, fco, fca, toolconf, files, labels=None):
     tool, toolpath, binary, symlink = toolconf
-    if symlink:
+    if symlink or fcd.isabsent() or fco.isabsent():
         return 1
     a, b, c, back = files
 
@@ -236,12 +329,16 @@
             util.copyfile(back, a) # restore from backup and try again
     return 1 # continue merging
 
-def _symlinkcheck(repo, mynode, orig, fcd, fco, fca, toolconf):
+def _mergecheck(repo, mynode, orig, fcd, fco, fca, toolconf):
     tool, toolpath, binary, symlink = toolconf
     if symlink:
         repo.ui.warn(_('warning: internal %s cannot merge symlinks '
                        'for %s\n') % (tool, fcd.path()))
         return False
+    if fcd.isabsent() or fco.isabsent():
+        repo.ui.warn(_('warning: internal %s cannot merge change/delete '
+                       'conflict for %s\n') % (tool, fcd.path()))
+        return False
     return True
 
 def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode):
@@ -255,12 +352,12 @@
     ui = repo.ui
 
     r = simplemerge.simplemerge(ui, a, b, c, label=labels, mode=mode)
-    return True, r
+    return True, r, False
 
 @internaltool('union', fullmerge,
               _("warning: conflicts while merging %s! "
                 "(edit, then use 'hg resolve --mark')\n"),
-              precheck=_symlinkcheck)
+              precheck=_mergecheck)
 def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
     """
     Uses the internal non-interactive simple merge algorithm for merging
@@ -272,7 +369,7 @@
 @internaltool('merge', fullmerge,
               _("warning: conflicts while merging %s! "
                 "(edit, then use 'hg resolve --mark')\n"),
-              precheck=_symlinkcheck)
+              precheck=_mergecheck)
 def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
     """
     Uses the internal non-interactive simple merge algorithm for merging
@@ -285,7 +382,7 @@
 @internaltool('merge3', fullmerge,
               _("warning: conflicts while merging %s! "
                 "(edit, then use 'hg resolve --mark')\n"),
-              precheck=_symlinkcheck)
+              precheck=_mergecheck)
 def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
     """
     Uses the internal non-interactive simple merge algorithm for merging
@@ -305,30 +402,26 @@
     """
     assert localorother is not None
     tool, toolpath, binary, symlink = toolconf
-    if symlink:
-        repo.ui.warn(_('warning: :merge-%s cannot merge symlinks '
-                       'for %s\n') % (localorother, fcd.path()))
-        return False, 1
     a, b, c, back = files
     r = simplemerge.simplemerge(repo.ui, a, b, c, label=labels,
                                 localorother=localorother)
     return True, r
 
-@internaltool('merge-local', mergeonly)
+@internaltool('merge-local', mergeonly, precheck=_mergecheck)
 def _imergelocal(*args, **kwargs):
     """
     Like :merge, but resolve all conflicts non-interactively in favor
     of the local changes."""
     success, status = _imergeauto(localorother='local', *args, **kwargs)
-    return success, status
+    return success, status, False
 
-@internaltool('merge-other', mergeonly)
+@internaltool('merge-other', mergeonly, precheck=_mergecheck)
 def _imergeother(*args, **kwargs):
     """
     Like :merge, but resolve all conflicts non-interactively in favor
     of the other changes."""
     success, status = _imergeauto(localorother='other', *args, **kwargs)
-    return success, status
+    return success, status, False
 
 @internaltool('tagmerge', mergeonly,
               _("automatic tag merging of %s failed! "
@@ -338,7 +431,8 @@
     """
     Uses the internal tag merge algorithm (experimental).
     """
-    return tagmerge.merge(repo, fcd, fco, fca)
+    success, status = tagmerge.merge(repo, fcd, fco, fca)
+    return success, status, False
 
 @internaltool('dump', fullmerge)
 def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
@@ -356,10 +450,14 @@
     util.copyfile(a, a + ".local")
     repo.wwrite(fd + ".other", fco.data(), fco.flags())
     repo.wwrite(fd + ".base", fca.data(), fca.flags())
-    return False, 1
+    return False, 1, False
 
 def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
     tool, toolpath, binary, symlink = toolconf
+    if fcd.isabsent() or fco.isabsent():
+        repo.ui.warn(_('warning: %s cannot merge change/delete conflict '
+                       'for %s\n') % (tool, fcd.path()))
+        return False, 1, None
     a, b, c, back = files
     out = ""
     env = {'HG_FILE': fcd.path(),
@@ -383,7 +481,7 @@
     repo.ui.debug('launching merge tool: %s\n' % cmd)
     r = ui.system(cmd, cwd=repo.root, environ=env)
     repo.ui.debug('merge tool returned: %s\n' % r)
-    return True, r
+    return True, r, False
 
 def _formatconflictmarker(repo, ctx, template, label, pad):
     """Applies the given template to the ctx, prefixed by the label.
@@ -448,8 +546,8 @@
     fca = ancestor file context
     fcd = local file context for current/destination file
 
-    Returns whether the merge is complete, and the return value of the merge.
-    """
+    Returns whether the merge is complete, the return value of the merge, and
+    a boolean indicating whether the file was deleted from disk."""
 
     def temp(prefix, ctx):
         pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
@@ -461,18 +559,19 @@
         return name
 
     if not fco.cmp(fcd): # files identical?
-        return True, None
+        return True, None, False
 
     ui = repo.ui
     fd = fcd.path()
     binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
     symlink = 'l' in fcd.flags() + fco.flags()
-    tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
+    changedelete = fcd.isabsent() or fco.isabsent()
+    tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete)
     if tool in internals and tool.startswith('internal:'):
         # normalize to new-style names (':merge' etc)
         tool = tool[len('internal'):]
-    ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" %
-               (tool, fd, binary, symlink))
+    ui.debug("picked tool '%s' for %s (binary %s symlink %s changedelete %s)\n"
+             % (tool, fd, binary, symlink, changedelete))
 
     if tool in internals:
         func = internals[tool]
@@ -488,7 +587,8 @@
     toolconf = tool, toolpath, binary, symlink
 
     if mergetype == nomerge:
-        return True, func(repo, mynode, orig, fcd, fco, fca, toolconf)
+        r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf)
+        return True, r, deleted
 
     if premerge:
         if orig != fco.path():
@@ -502,14 +602,17 @@
                                  toolconf):
         if onfailure:
             ui.warn(onfailure % fd)
-        return True, 1
+        return True, 1, False
 
     a = repo.wjoin(fd)
     b = temp("base", fca)
     c = temp("other", fco)
-    back = a + ".orig"
-    if premerge:
-        util.copyfile(a, back)
+    if not fcd.isabsent():
+        back = cmdutil.origpath(ui, repo, a)
+        if premerge:
+            util.copyfile(a, back)
+    else:
+        back = None
     files = (a, b, c, back)
 
     r = 1
@@ -521,12 +624,13 @@
             labels = _formatlabels(repo, fcd, fco, fca, labels)
 
         if premerge and mergetype == fullmerge:
-            r = _premerge(repo, toolconf, files, labels=labels)
+            r = _premerge(repo, fcd, fco, fca, toolconf, files, labels=labels)
             # complete if premerge successful (r is 0)
-            return not r, r
+            return not r, r, False
 
-        needcheck, r = func(repo, mynode, orig, fcd, fco, fca, toolconf, files,
-                            labels=labels)
+        needcheck, r, deleted = func(repo, mynode, orig, fcd, fco, fca,
+                                     toolconf, files, labels=labels)
+
         if needcheck:
             r = _check(r, ui, tool, fcd, files)
 
@@ -534,9 +638,9 @@
             if onfailure:
                 ui.warn(onfailure % fd)
 
-        return True, r
+        return True, r, deleted
     finally:
-        if not r:
+        if not r and back is not None:
             util.unlink(back)
         util.unlink(b)
         util.unlink(c)
@@ -561,13 +665,13 @@
     if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
                                   'changed' in
                                   _toollist(ui, tool, "check")):
-        if filecmp.cmp(a, back):
+        if back is not None and filecmp.cmp(a, back):
             if ui.promptchoice(_(" output file %s appears unchanged\n"
                                  "was merge successful (yn)?"
                                  "$$ &Yes $$ &No") % fd, 1):
                 r = 1
 
-    if _toolbool(ui, tool, "fixeol"):
+    if back is not None and _toolbool(ui, tool, "fixeol"):
         _matcheol(a, back)
 
     return r
--- a/mercurial/fileset.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/fileset.py	Thu Dec 17 17:27:32 2015 -0600
@@ -159,13 +159,22 @@
 
 def deleted(mctx, x):
     """``deleted()``
-    File that is deleted according to :hg:`status`.
+    Alias for ``missing()``.
     """
     # i18n: "deleted" is a keyword
     getargs(x, 0, 0, _("deleted takes no arguments"))
     s = mctx.status().deleted
     return [f for f in mctx.subset if f in s]
 
+def missing(mctx, x):
+    """``missing()``
+    File that is missing according to :hg:`status`.
+    """
+    # i18n: "missing" is a keyword
+    getargs(x, 0, 0, _("missing takes no arguments"))
+    s = mctx.status().deleted
+    return [f for f in mctx.subset if f in s]
+
 def unknown(mctx, x):
     """``unknown()``
     File that is unknown according to :hg:`status`. These files will only be
@@ -249,7 +258,7 @@
     getargs(x, 0, 0, _("resolved takes no arguments"))
     if mctx.ctx.rev() is not None:
         return []
-    ms = merge.mergestate(mctx.ctx.repo())
+    ms = merge.mergestate.read(mctx.ctx.repo())
     return [f for f in mctx.subset if f in ms and ms[f] == 'r']
 
 def unresolved(mctx, x):
@@ -260,7 +269,7 @@
     getargs(x, 0, 0, _("unresolved takes no arguments"))
     if mctx.ctx.rev() is not None:
         return []
-    ms = merge.mergestate(mctx.ctx.repo())
+    ms = merge.mergestate.read(mctx.ctx.repo())
     return [f for f in mctx.subset if f in ms and ms[f] == 'u']
 
 def hgignore(mctx, x):
@@ -441,6 +450,7 @@
     'grep': grep,
     'ignored': ignored,
     'hgignore': hgignore,
+    'missing': missing,
     'modified': modified,
     'portable': portable,
     'removed': removed,
@@ -511,7 +521,7 @@
 
     # do we need status info?
     if (_intree(['modified', 'added', 'removed', 'deleted',
-                 'unknown', 'ignored', 'clean'], tree) or
+                 'missing', 'unknown', 'ignored', 'clean'], tree) or
         # Using matchctx.existing() on a workingctx requires us to check
         # for deleted files.
         (ctx.rev() is None and _intree(_existingcallers, tree))):
--- a/mercurial/help.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/help.py	Thu Dec 17 17:27:32 2015 -0600
@@ -27,11 +27,12 @@
     '''return a text listing of the given extensions'''
     rst = []
     if exts:
-        rst.append('\n%s\n\n' % header)
         for name, desc in sorted(exts.iteritems()):
             if not showdeprecated and any(w in desc for w in _exclkeywords):
                 continue
             rst.append('%s:%s: %s\n' % (' ' * indent, name, desc))
+    if rst:
+        rst.insert(0, '\n%s\n\n' % header)
     return rst
 
 def extshelp(ui):
@@ -83,6 +84,13 @@
     if notomitted:
         rst.append('\n\n.. container:: notomitted\n\n    %s\n\n' % notomitted)
 
+def filtercmd(ui, cmd, kw, doc):
+    if not ui.debugflag and cmd.startswith("debug") and kw != "debug":
+        return True
+    if not ui.verbose and doc and any(w in doc for w in _exclkeywords):
+        return True
+    return False
+
 def topicmatch(ui, kw):
     """Return help topics matching kw.
 
@@ -115,32 +123,38 @@
             doclines = docs.splitlines()
             if doclines:
                 summary = doclines[0]
-            cmdname = cmd.split('|')[0].lstrip('^')
+            cmdname = cmd.partition('|')[0].lstrip('^')
+            if filtercmd(ui, cmdname, kw, docs):
+                continue
             results['commands'].append((cmdname, summary))
     for name, docs in itertools.chain(
         extensions.enabled(False).iteritems(),
         extensions.disabled().iteritems()):
         # extensions.load ignores the UI argument
         mod = extensions.load(None, name, '')
-        name = name.split('.')[-1]
+        name = name.rpartition('.')[-1]
         if lowercontains(name) or lowercontains(docs):
             # extension docs are already translated
             results['extensions'].append((name, docs.splitlines()[0]))
         for cmd, entry in getattr(mod, 'cmdtable', {}).iteritems():
             if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])):
-                cmdname = cmd.split('|')[0].lstrip('^')
+                cmdname = cmd.partition('|')[0].lstrip('^')
                 if entry[0].__doc__:
                     cmddoc = gettext(entry[0].__doc__).splitlines()[0]
                 else:
                     cmddoc = _('(no help text available)')
+                if filtercmd(ui, cmdname, kw, cmddoc):
+                    continue
                 results['extensioncommands'].append((cmdname, cmddoc))
     return results
 
-def loaddoc(topic):
+def loaddoc(topic, subdir=None):
     """Return a delayed loader for help/topic.txt."""
 
     def loader(ui):
         docdir = os.path.join(util.datapath, 'help')
+        if subdir:
+            docdir = os.path.join(docdir, subdir)
         path = os.path.join(docdir, topic + ".txt")
         doc = gettext(util.readfile(path))
         for rewriter in helphooks.get(topic, []):
@@ -149,6 +163,21 @@
 
     return loader
 
+internalstable = sorted([
+    (['bundles'], _('container for exchange of repository data'),
+     loaddoc('bundles', subdir='internals')),
+    (['changegroups'], _('representation of revlog data'),
+     loaddoc('changegroups', subdir='internals')),
+])
+
+def internalshelp(ui):
+    """Generate the index for the "internals" topic."""
+    lines = []
+    for names, header, doc in internalstable:
+        lines.append(' :%s: %s\n' % (names[0], header))
+
+    return ''.join(lines)
+
 helptable = sorted([
     (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
     (["dates"], _("Date Formats"), loaddoc('dates')),
@@ -175,8 +204,15 @@
     (["phases"], _("Working with Phases"), loaddoc('phases')),
     (['scripting'], _('Using Mercurial from scripts and automation'),
      loaddoc('scripting')),
+    (['internals'], _("Technical implementation topics"),
+     internalshelp),
 ])
 
+# Maps topics with sub-topics to a list of their sub-topics.
+subtopics = {
+    'internals': internalstable,
+}
+
 # Map topics to lists of callable taking the current topic help and
 # returning the updated version
 helphooks = {}
@@ -226,7 +262,7 @@
 addtopicsymbols('hgweb', '.. webcommandsmarker', webcommands.commands,
                 dedent=True)
 
-def help_(ui, name, unknowncmd=False, full=True, **opts):
+def help_(ui, name, unknowncmd=False, full=True, subtopic=None, **opts):
     '''
     Generate the help for 'name' as unformatted restructured text. If
     'name' is None, describe the commands available.
@@ -234,7 +270,7 @@
 
     import commands # avoid cycle
 
-    def helpcmd(name):
+    def helpcmd(name, subtopic=None):
         try:
             aliases, entry = cmdutil.findcmd(name, commands.table,
                                              strict=unknowncmd)
@@ -318,7 +354,7 @@
         return rst
 
 
-    def helplist(select=None):
+    def helplist(select=None, **opts):
         # list of commands
         if name == "shortlist":
             header = _('basic commands:\n\n')
@@ -330,7 +366,7 @@
         h = {}
         cmds = {}
         for c, e in commands.table.iteritems():
-            f = c.split("|", 1)[0]
+            f = c.partition("|")[0]
             if select and not select(f):
                 continue
             if (not select and name != 'shortlist' and
@@ -339,10 +375,8 @@
             if name == "shortlist" and not f.startswith("^"):
                 continue
             f = f.lstrip("^")
-            if not ui.debugflag and f.startswith("debug") and name != "debug":
-                continue
             doc = e[0].__doc__
-            if not ui.verbose and doc and any(w in doc for w in _exclkeywords):
+            if filtercmd(ui, f, name, doc):
                 continue
             doc = gettext(doc)
             if not doc:
@@ -366,7 +400,9 @@
             else:
                 rst.append(' :%s: %s\n' % (f, h[f]))
 
-        if not name:
+        ex = opts.get
+        anyopts = (ex('keyword') or not (ex('command') or ex('extension')))
+        if not name and anyopts:
             exts = listexts(_('enabled extensions:'), extensions.enabled())
             if exts:
                 rst.append('\n')
@@ -403,12 +439,20 @@
                            % (name and " " + name or ""))
         return rst
 
-    def helptopic(name):
-        for names, header, doc in helptable:
-            if name in names:
-                break
-        else:
-            raise error.UnknownCommand(name)
+    def helptopic(name, subtopic=None):
+        # Look for sub-topic entry first.
+        header, doc = None, None
+        if subtopic and name in subtopics:
+            for names, header, doc in subtopics[name]:
+                if subtopic in names:
+                    break
+
+        if not header:
+            for names, header, doc in helptable:
+                if name in names:
+                    break
+            else:
+                raise error.UnknownCommand(name)
 
         rst = [minirst.section(header)]
 
@@ -431,7 +475,7 @@
             pass
         return rst
 
-    def helpext(name):
+    def helpext(name, subtopic=None):
         try:
             mod = extensions.find(name)
             doc = gettext(mod.__doc__) or _('no help text available')
@@ -445,7 +489,7 @@
             head, tail = doc, ""
         else:
             head, tail = doc.split('\n', 1)
-        rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
+        rst = [_('%s extension - %s\n\n') % (name.rpartition('.')[-1], head)]
         if tail:
             rst.extend(tail.splitlines(True))
             rst.append('\n')
@@ -460,20 +504,21 @@
                 ct = mod.cmdtable
             except AttributeError:
                 ct = {}
-            modcmds = set([c.split('|', 1)[0] for c in ct])
+            modcmds = set([c.partition('|')[0] for c in ct])
             rst.extend(helplist(modcmds.__contains__))
         else:
             rst.append(_('(use "hg help extensions" for information on enabling'
                        ' extensions)\n'))
         return rst
 
-    def helpextcmd(name):
+    def helpextcmd(name, subtopic=None):
         cmd, ext, mod = extensions.disabledcmd(ui, name,
                                                ui.configbool('ui', 'strict'))
         doc = gettext(mod.__doc__).splitlines()[0]
 
         rst = listexts(_("'%s' is provided by the following "
-                              "extension:") % cmd, {ext: doc}, indent=4)
+                              "extension:") % cmd, {ext: doc}, indent=4,
+                       showdeprecated=True)
         rst.append('\n')
         rst.append(_('(use "hg help extensions" for information on enabling '
                    'extensions)\n'))
@@ -482,8 +527,8 @@
 
     rst = []
     kw = opts.get('keyword')
-    if kw:
-        matches = topicmatch(ui, name)
+    if kw or name is None and any(opts[o] for o in opts):
+        matches = topicmatch(ui, name or '')
         helpareas = []
         if opts.get('extension'):
             helpareas += [('extensions', _('Extensions'))]
@@ -515,7 +560,7 @@
             queries = (helptopic, helpcmd, helpext, helpextcmd)
         for f in queries:
             try:
-                rst = f(name)
+                rst = f(name, subtopic)
                 break
             except error.UnknownCommand:
                 pass
@@ -530,6 +575,6 @@
         # program name
         if not ui.quiet:
             rst = [_("Mercurial Distributed SCM\n"), '\n']
-        rst.extend(helplist())
+        rst.extend(helplist(None, **opts))
 
     return ''.join(rst)
--- a/mercurial/help/config.txt	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/help/config.txt	Thu Dec 17 17:27:32 2015 -0600
@@ -666,6 +666,14 @@
 ``format``
 ----------
 
+``usegeneraldelta``
+    Enable or disable the "generaldelta" repository format which improves
+    repository compression by allowing "revlog" to store delta against arbitrary
+    revision instead of the previous stored one. This provides significant
+    improvement for repositories with branches. Enabled by default. Disabling
+    this option ensures that the on-disk format of newly created repository will
+    be compatible with Mercurial before version 1.9.
+
 ``usestore``
     Enable or disable the "store" repository format which improves
     compatibility with systems that fold case or otherwise mangle
@@ -1119,29 +1127,43 @@
 ``paths``
 ---------
 
-Assigns symbolic names to repositories. The left side is the
-symbolic name, and the right gives the directory or URL that is the
-location of the repository. Default paths can be declared by setting
-the following entries.
+Assigns symbolic names and behavior to repositories.
+
+Options are symbolic names defining the URL or directory that is the
+location of the repository. Example::
+
+    [paths]
+    my_server = https://example.com/my_repo
+    local_path = /home/me/repo
+
+These symbolic names can be used from the command line. To pull
+from ``my_server``: :hg:`pull my_server`. To push to ``local_path``:
+:hg:`push local_path`.
+
+Options containing colons (``:``) denote sub-options that can influence
+behavior for that specific path. Example::
+
+    [paths]
+    my_server = https://example.com/my_path
+    my_server:pushurl = ssh://example.com/my_path
+
+The following sub-options can be defined:
+
+``pushurl``
+   The URL to use for push operations. If not defined, the location
+   defined by the path's main entry is used.
+
+The following special named paths exist:
 
 ``default``
-    Directory or URL to use when pulling if no source is specified.
-    (default: repository from which the current repository was cloned)
+   The URL or directory to use when no source or remote is specified.
+
+   :hg:`clone` will automatically define this path to the location the
+   repository was cloned from.
 
 ``default-push``
-    Optional. Directory or URL to use when pushing if no destination
-    is specified.
-
-Custom paths can be defined by assigning the path to a name that later can be
-used from the command line. Example::
-
-    [paths]
-    my_path = http://example.com/path
-
-To push to the path defined in ``my_path`` run the command::
-
-    hg push my_path
-
+   (deprecated) The URL or directory for the default :hg:`push` location.
+   ``default:pushurl`` should be used instead.
 
 ``phases``
 ----------
@@ -1311,6 +1333,23 @@
     Instruct HTTP clients not to send request headers longer than this
     many bytes. (default: 1024)
 
+``bundle1``
+    Whether to allow clients to push and pull using the legacy bundle1
+    exchange format. (default: True)
+
+``bundle1.push``
+    Whether to allow clients to push using the legacy bundle1 exchange
+    format. (default: True)
+
+``bundle1.pull``
+    Whether to allow clients to pull using the legacy bundle1 exchange
+    format. (default: True)
+
+    Large repositories using the *generaldelta* storage format should
+    consider setting this option because converting *generaldelta*
+    repositories to the exchange format required by the bundle1 data
+    format can consume a lot of CPU.
+
 ``smtp``
 --------
 
@@ -1446,6 +1485,10 @@
     Encoding to try if it's not possible to decode the changelog using
     UTF-8. (default: ISO-8859-1)
 
+``graphnodetemplate``
+    The template used to print changeset nodes in an ASCII revision graph.
+    (default: ``{graphnode}``)
+
 ``ignore``
     A file to read per-user ignore patterns from. This file should be
     in the same format as a repository-wide .hgignore file. Filenames
@@ -1488,6 +1531,10 @@
     markers is different from the encoding of the merged files,
     serious problems may occur.
 
+``origbackuppath``
+    The path to a directory used to store generated .orig files. If the path is
+    not a directory, one will be created.
+
 ``patch``
     An optional external tool that ``hg import`` and some extensions
     will use for applying patches. By default Mercurial uses an
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/help/internals/bundles.txt	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,97 @@
+Bundles
+=======
+
+A bundle is a container for repository data.
+
+Bundles are used as standalone files as well as the interchange format
+over the wire protocol used when two Mercurial peers communicate with
+each other.
+
+Headers
+-------
+
+Bundles produced since Mercurial 0.7 (September 2005) have a 4 byte
+header identifying the major bundle type. The header always begins with
+``HG`` and the follow 2 bytes indicate the bundle type/version. Some
+bundle types have additional data after this 4 byte header.
+
+The following sections describe each bundle header/type.
+
+HG10
+----
+
+``HG10`` headers indicate a *changegroup bundle*. This is the original
+bundle format, so it is sometimes referred to as *bundle1*. It has been
+present since version 0.7 (released September 2005).
+
+This header is followed by 2 bytes indicating the compression algorithm
+used for data that follows. All subsequent data following this
+compression identifier is compressed according to the algorithm/method
+specified.
+
+Supported algorithms include the following.
+
+``BZ``
+   *bzip2* compression.
+
+   Bzip2 compressors emit a leading ``BZ`` header. Mercurial uses this
+   leading ``BZ`` as part of the bundle header. Therefore consumers
+   of bzip2 bundles need to *seed* the bzip2 decompressor with ``BZ`` or
+   seek the input stream back to the beginning of the algorithm component
+   of the bundle header so that decompressor input is valid. This behavior
+   is unique among supported compression algorithms.
+
+   Supported since version 0.7 (released December 2006).
+
+``GZ``
+  *zlib* compression.
+
+   Supported since version 0.9.2 (released December 2006).
+
+``UN``
+  *Uncompressed* or no compression. Unmodified changegroup data follows.
+
+  Supported since version 0.9.2 (released December 2006).
+
+3rd party extensions may implement their own compression. However, no
+authority reserves values for their compression algorithm identifiers.
+
+HG2X
+----
+
+``HG2X`` headers (where ``X`` is any value) denote a *bundle2* bundle.
+Bundle2 bundles are a container format for various kinds of repository
+data and capabilities, beyond changegroup data (which was the only data
+supported by ``HG10`` bundles.
+
+``HG20`` is currently the only defined bundle2 version.
+
+The ``HG20`` format is not yet documented here. See the inline comments
+in ``mercurial/exchange.py`` for now.
+
+Initial ``HG20`` support was added in Mercurial 3.0 (released May
+2014). However, bundle2 bundles were hidden behind an experimental flag
+until version 3.5 (released August 2015), when they were enabled in the
+wire protocol. Various commands (including ``hg bundle``) did not
+support generating bundle2 files until Mercurial 3.6 (released November
+2015).
+
+HGS1
+----
+
+*Experimental*
+
+A ``HGS1`` header indicates a *streaming clone bundle*. This is a bundle
+that contains raw revlog data from a repository store. (Typically revlog
+data is exchanged in the form of changegroups.)
+
+The purpose of *streaming clone bundles* are to *clone* repository data
+very efficiently.
+
+The ``HGS1`` header is always followed by 2 bytes indicating a
+compression algorithm of the data that follows. Only ``UN``
+(uncompressed data) is currently allowed.
+
+``HGS1UN`` support was added as an experimental feature in version 3.6
+(released November 2015) as part of the initial offering of the *clone
+bundles* feature.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/help/internals/changegroups.txt	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,142 @@
+Changegroups
+============
+
+Changegroups are representations of repository revlog data, specifically
+the changelog, manifest, and filelogs.
+
+There are 2 versions of changegroups: ``1`` and ``2``. From a
+high-level, they are almost exactly the same, with the only difference
+being a header on entries in the changeset segment.
+
+Changegroups consists of 3 logical segments::
+
+   +---------------------------------+
+   |           |          |          |
+   | changeset | manifest | filelogs |
+   |           |          |          |
+   +---------------------------------+
+
+The principle building block of each segment is a *chunk*. A *chunk*
+is a framed piece of data::
+
+   +---------------------------------------+
+   |           |                           |
+   |  length   |           data            |
+   | (32 bits) |       <length> bytes      |
+   |           |                           |
+   +---------------------------------------+
+
+Each chunk starts with a 32-bit big-endian signed integer indicating
+the length of the raw data that follows.
+
+There is a special case chunk that has 0 length (``0x00000000``). We
+call this an *empty chunk*.
+
+Delta Groups
+------------
+
+A *delta group* expresses the content of a revlog as a series of deltas,
+or patches against previous revisions.
+
+Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
+to signal the end of the delta group::
+
+  +------------------------------------------------------------------------+
+  |                |             |               |             |           |
+  | chunk0 length  | chunk0 data | chunk1 length | chunk1 data |    0x0    |
+  |   (32 bits)    |  (various)  |   (32 bits)   |  (various)  | (32 bits) |
+  |                |             |               |             |           |
+  +------------------------------------------------------------+-----------+
+
+Each *chunk*'s data consists of the following::
+
+  +-----------------------------------------+
+  |              |              |           |
+  | delta header | mdiff header |   delta   |
+  |  (various)   |  (12 bytes)  | (various) |
+  |              |              |           |
+  +-----------------------------------------+
+
+The *length* field is the byte length of the remaining 3 logical pieces
+of data. The *delta* is a diff from an existing entry in the changelog.
+
+The *delta header* is different between versions ``1`` and ``2`` of the
+changegroup format.
+
+Version 1::
+
+   +------------------------------------------------------+
+   |            |             |             |             |
+   |    node    |   p1 node   |   p2 node   |  link node  |
+   | (20 bytes) |  (20 bytes) |  (20 bytes) |  (20 bytes) |
+   |            |             |             |             |
+   +------------------------------------------------------+
+
+Version 2::
+
+   +------------------------------------------------------------------+
+   |            |             |             |            |            |
+   |    node    |   p1 node   |   p2 node   | base node  | link node  |
+   | (20 bytes) |  (20 bytes) |  (20 bytes) | (20 bytes) | (20 bytes) |
+   |            |             |             |            |            |
+   +------------------------------------------------------------------+
+
+The *mdiff header* consists of 3 32-bit big-endian signed integers
+describing offsets at which to apply the following delta content::
+
+   +-------------------------------------+
+   |           |            |            |
+   |  offset   | old length | new length |
+   | (32 bits) |  (32 bits) |  (32 bits) |
+   |           |            |            |
+   +-------------------------------------+
+
+In version 1, the delta is always applied against the previous node from
+the changegroup or the first parent if this is the first entry in the
+changegroup.
+
+In version 2, the delta base node is encoded in the entry in the
+changegroup. This allows the delta to be expressed against any parent,
+which can result in smaller deltas and more efficient encoding of data.
+
+Changeset Segment
+-----------------
+
+The *changeset segment* consists of a single *delta group* holding
+changelog data. It is followed by an *empty chunk* to denote the
+boundary to the *manifests segment*.
+
+Manifest Segment
+----------------
+
+The *manifest segment* consists of a single *delta group* holding
+manifest data. It is followed by an *empty chunk* to denote the boundary
+to the *filelogs segment*.
+
+Filelogs Segment
+----------------
+
+The *filelogs* segment consists of multiple sub-segments, each
+corresponding to an individual file whose data is being described::
+
+   +--------------------------------------+
+   |          |          |          |     |
+   | filelog0 | filelog1 | filelog2 | ... |
+   |          |          |          |     |
+   +--------------------------------------+
+
+The final filelog sub-segment is followed by an *empty chunk* to denote
+the end of the segment and the overall changegroup.
+
+Each filelog sub-segment consists of the following::
+
+   +------------------------------------------+
+   |               |            |             |
+   | filename size |  filename  | delta group |
+   |   (32 bits)   |  (various) |  (various)  |
+   |               |            |             |
+   +------------------------------------------+
+
+That is, a *chunk* consisting of the filename (not terminated or padded)
+followed by N chunks constituting the *delta group* for this file.
+
--- a/mercurial/hg.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/hg.py	Thu Dec 17 17:27:32 2015 -0600
@@ -235,13 +235,7 @@
     destvfs.write('sharedpath', sharedpath)
 
     r = repository(ui, destwvfs.base)
-
-    default = srcrepo.ui.config('paths', 'default')
-    if default:
-        fp = r.vfs("hgrc", "w", text=True)
-        fp.write("[paths]\n")
-        fp.write("default = %s\n" % default)
-        fp.close()
+    postshare(srcrepo, r, bookmarks=bookmarks)
 
     if update:
         r.ui.status(_("updating working directory\n"))
@@ -257,8 +251,24 @@
                 continue
         _update(r, uprev)
 
+def postshare(sourcerepo, destrepo, bookmarks=True):
+    """Called after a new shared repo is created.
+
+    The new repo only has a requirements file and pointer to the source.
+    This function configures additional shared data.
+
+    Extensions can wrap this function and write additional entries to
+    destrepo/.hg/shared to indicate additional pieces of data to be shared.
+    """
+    default = sourcerepo.ui.config('paths', 'default')
+    if default:
+        fp = destrepo.vfs("hgrc", "w", text=True)
+        fp.write("[paths]\n")
+        fp.write("default = %s\n" % default)
+        fp.close()
+
     if bookmarks:
-        fp = r.vfs('shared', 'w')
+        fp = destrepo.vfs('shared', 'w')
         fp.write('bookmarks\n')
         fp.close()
 
@@ -546,13 +556,22 @@
                                        "support clone by revision"))
                 revs = [srcpeer.lookup(r) for r in rev]
                 checkout = revs[0]
-            if destpeer.local():
+            local = destpeer.local()
+            if local:
                 if not stream:
                     if pull:
                         stream = False
                     else:
                         stream = None
-                destpeer.local().clone(srcpeer, heads=revs, stream=stream)
+                # internal config: ui.quietbookmarkmove
+                quiet = local.ui.backupconfig('ui', 'quietbookmarkmove')
+                try:
+                    local.ui.setconfig(
+                        'ui', 'quietbookmarkmove', True, 'clone')
+                    exchange.pull(local, srcpeer, revs,
+                                  streamclonerequested=stream)
+                finally:
+                    local.ui.restoreconfig(quiet)
             elif srcrepo:
                 exchange.push(srcrepo, destpeer, revs=revs,
                               bookmarks=srcrepo._bookmarks.keys())
@@ -618,7 +637,9 @@
             srcpeer.close()
     return srcpeer, destpeer
 
-def _showstats(repo, stats):
+def _showstats(repo, stats, quietempty=False):
+    if quietempty and not any(stats):
+        return
     repo.ui.status(_("%d files updated, %d files merged, "
                      "%d files removed, %d files unresolved\n") % stats)
 
@@ -628,13 +649,13 @@
     When overwrite is set, changes are clobbered, merged else
 
     returns stats (see pydoc mercurial.merge.applyupdates)"""
-    return mergemod.update(repo, node, False, overwrite, None,
+    return mergemod.update(repo, node, False, overwrite,
                            labels=['working copy', 'destination'])
 
-def update(repo, node):
+def update(repo, node, quietempty=False):
     """update the working directory to node, merging linear changes"""
     stats = updaterepo(repo, node, False)
-    _showstats(repo, stats)
+    _showstats(repo, stats, quietempty)
     if stats[3]:
         repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
     return stats[3] > 0
@@ -642,18 +663,18 @@
 # naming conflict in clone()
 _update = update
 
-def clean(repo, node, show_stats=True):
+def clean(repo, node, show_stats=True, quietempty=False):
     """forcibly switch the working directory to node, clobbering changes"""
     stats = updaterepo(repo, node, True)
     util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
     if show_stats:
-        _showstats(repo, stats)
+        _showstats(repo, stats, quietempty)
     return stats[3] > 0
 
 def merge(repo, node, force=None, remind=True):
     """Branch merge with node, resolving changes. Return true if any
     unresolved conflicts."""
-    stats = mergemod.update(repo, node, True, force, False)
+    stats = mergemod.update(repo, node, True, force)
     _showstats(repo, stats)
     if stats[3]:
         repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
--- a/mercurial/hgweb/__init__.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/hgweb/__init__.py	Thu Dec 17 17:27:32 2015 -0600
@@ -6,8 +6,22 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import os
-import hgweb_mod, hgwebdir_mod
+
+from ..i18n import _
+
+from .. import (
+    error,
+    util,
+)
+
+from . import (
+    hgweb_mod,
+    hgwebdir_mod,
+    server,
+)
 
 def hgweb(config, name=None, baseui=None):
     '''create an hgweb wsgi object
@@ -29,3 +43,83 @@
 def hgwebdir(config, baseui=None):
     return hgwebdir_mod.hgwebdir(config, baseui=baseui)
 
+class httpservice(object):
+    def __init__(self, ui, app, opts):
+        self.ui = ui
+        self.app = app
+        self.opts = opts
+
+    def init(self):
+        util.setsignalhandler()
+        self.httpd = server.create_server(self.ui, self.app)
+
+        if self.opts['port'] and not self.ui.verbose:
+            return
+
+        if self.httpd.prefix:
+            prefix = self.httpd.prefix.strip('/') + '/'
+        else:
+            prefix = ''
+
+        port = ':%d' % self.httpd.port
+        if port == ':80':
+            port = ''
+
+        bindaddr = self.httpd.addr
+        if bindaddr == '0.0.0.0':
+            bindaddr = '*'
+        elif ':' in bindaddr: # IPv6
+            bindaddr = '[%s]' % bindaddr
+
+        fqaddr = self.httpd.fqaddr
+        if ':' in fqaddr:
+            fqaddr = '[%s]' % fqaddr
+        if self.opts['port']:
+            write = self.ui.status
+        else:
+            write = self.ui.write
+        write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
+              (fqaddr, port, prefix, bindaddr, self.httpd.port))
+        self.ui.flush()  # avoid buffering of status message
+
+    def run(self):
+        self.httpd.serve_forever()
+
+def createservice(ui, repo, opts):
+    # this way we can check if something was given in the command-line
+    if opts.get('port'):
+        opts['port'] = util.getport(opts.get('port'))
+
+    alluis = set([ui])
+    if repo:
+        baseui = repo.baseui
+        alluis.update([repo.baseui, repo.ui])
+    else:
+        baseui = ui
+    webconf = opts.get('web_conf') or opts.get('webdir_conf')
+    if webconf:
+        # load server settings (e.g. web.port) to "copied" ui, which allows
+        # hgwebdir to reload webconf cleanly
+        servui = ui.copy()
+        servui.readconfig(webconf, sections=['web'])
+        alluis.add(servui)
+    else:
+        servui = ui
+
+    optlist = ("name templates style address port prefix ipv6"
+               " accesslog errorlog certificate encoding")
+    for o in optlist.split():
+        val = opts.get(o, '')
+        if val in (None, ''): # should check against default options instead
+            continue
+        for u in alluis:
+            u.setconfig("web", o, val, 'serve')
+
+    if webconf:
+        app = hgwebdir_mod.hgwebdir(webconf, baseui=baseui)
+    else:
+        if not repo:
+            raise error.RepoError(_("there is no Mercurial repository"
+                                    " here (.hg not found)"))
+        app = hgweb_mod.hgweb(repo, baseui=baseui)
+    return httpservice(servui, app, opts)
--- a/mercurial/hgweb/common.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/hgweb/common.py	Thu Dec 17 17:27:32 2015 -0600
@@ -6,7 +6,12 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import errno, mimetypes, os
+from __future__ import absolute_import
+
+import BaseHTTPServer
+import errno
+import mimetypes
+import os
 
 HTTP_OK = 200
 HTTP_NOT_MODIFIED = 304
@@ -102,8 +107,7 @@
         raise AttributeError
 
 def _statusmessage(code):
-    from BaseHTTPServer import BaseHTTPRequestHandler
-    responses = BaseHTTPRequestHandler.responses
+    responses = BaseHTTPServer.BaseHTTPRequestHandler.responses
     return responses.get(code, ('Error', 'Unknown error'))[0]
 
 def statusmessage(code, message=None):
--- a/mercurial/hgweb/hgweb_mod.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/hgweb/hgweb_mod.py	Thu Dec 17 17:27:32 2015 -0600
@@ -6,15 +6,41 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import contextlib
 import os
-from mercurial import ui, hg, hook, error, encoding, templater, util, repoview
-from mercurial.templatefilters import websub
-from common import ErrorResponse, permhooks, caching
-from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
-from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
-from request import wsgirequest
-import webcommands, protocol, webutil
+
+from .common import (
+    ErrorResponse,
+    HTTP_BAD_REQUEST,
+    HTTP_NOT_FOUND,
+    HTTP_NOT_MODIFIED,
+    HTTP_OK,
+    HTTP_SERVER_ERROR,
+    caching,
+    permhooks,
+)
+from .request import wsgirequest
+
+from .. import (
+    encoding,
+    error,
+    hg,
+    hook,
+    repoview,
+    templatefilters,
+    templater,
+    ui as uimod,
+    util,
+)
+
+from . import (
+    protocol,
+    webcommands,
+    webutil,
+    wsgicgi,
+)
 
 perms = {
     'changegroup': 'pull',
@@ -158,7 +184,7 @@
                              or req.url.strip('/') or self.repo.root)
 
         def websubfilter(text):
-            return websub(text, self.websubtable)
+            return templatefilters.websub(text, self.websubtable)
 
         # create the templater
 
@@ -195,7 +221,7 @@
             if baseui:
                 u = baseui.copy()
             else:
-                u = ui.ui()
+                u = uimod.ui()
             r = hg.repository(u, repo)
         else:
             # we trust caller to give us a private copy
@@ -260,7 +286,6 @@
         if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
             raise RuntimeError("This function is only intended to be "
                                "called while running as a CGI script.")
-        import mercurial.hgweb.wsgicgi as wsgicgi
         wsgicgi.launch(self)
 
     def __call__(self, env, respond):
@@ -304,8 +329,8 @@
                 parts = parts[len(repo_parts):]
             query = '/'.join(parts)
         else:
-            query = req.env['QUERY_STRING'].split('&', 1)[0]
-            query = query.split(';', 1)[0]
+            query = req.env['QUERY_STRING'].partition('&')[0]
+            query = query.partition(';')[0]
 
         # process this if it's a protocol request
         # protocol bits don't need to create any URLs
--- a/mercurial/hgweb/hgwebdir_mod.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/hgweb/hgwebdir_mod.py	Thu Dec 17 17:27:32 2015 -0600
@@ -6,15 +6,42 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import os, re, time
-from mercurial.i18n import _
-from mercurial import ui, hg, scmutil, util, templater
-from mercurial import error, encoding
-from common import ErrorResponse, get_mtime, staticfile, paritygen, ismember, \
-                   get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
-from hgweb_mod import hgweb, makebreadcrumb
-from request import wsgirequest
-import webutil
+from __future__ import absolute_import
+
+import os
+import re
+import time
+
+from ..i18n import _
+
+from .common import (
+    ErrorResponse,
+    HTTP_NOT_FOUND,
+    HTTP_OK,
+    HTTP_SERVER_ERROR,
+    get_contact,
+    get_mtime,
+    ismember,
+    paritygen,
+    staticfile,
+)
+from .request import wsgirequest
+
+from .. import (
+    encoding,
+    error,
+    hg,
+    scmutil,
+    templater,
+    ui as uimod,
+    util,
+)
+
+from . import (
+    hgweb_mod,
+    webutil,
+    wsgicgi,
+)
 
 def cleannames(items):
     return [(util.pconvert(name).strip('/'), path) for name, path in items]
@@ -108,7 +135,7 @@
         if self.baseui:
             u = self.baseui.copy()
         else:
-            u = ui.ui()
+            u = uimod.ui()
             u.setconfig('ui', 'report_untrusted', 'off', 'hgwebdir')
             u.setconfig('ui', 'nontty', 'true', 'hgwebdir')
             # displaying bundling progress bar while serving feels wrong and may
@@ -161,7 +188,6 @@
         if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
             raise RuntimeError("This function is only intended to be "
                                "called while running as a CGI script.")
-        import mercurial.hgweb.wsgicgi as wsgicgi
         wsgicgi.launch(self)
 
     def __call__(self, env, respond):
@@ -231,7 +257,7 @@
                     try:
                         # ensure caller gets private copy of ui
                         repo = hg.repository(self.ui.copy(), real)
-                        return hgweb(repo).run_wsgi(req)
+                        return hgweb_mod.hgweb(repo).run_wsgi(req)
                     except IOError as inst:
                         msg = inst.strerror
                         raise ErrorResponse(HTTP_SERVER_ERROR, msg)
@@ -426,7 +452,7 @@
         self.updatereqenv(req.env)
 
         return tmpl("index", entries=entries, subdir=subdir,
-                    pathdef=makebreadcrumb('/' + subdir, self.prefix),
+                    pathdef=hgweb_mod.makebreadcrumb('/' + subdir, self.prefix),
                     sortcolumn=sortcolumn, descending=descending,
                     **dict(sort))
 
--- a/mercurial/hgweb/protocol.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/hgweb/protocol.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,9 +5,21 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import cgi, cStringIO, zlib, urllib
-from mercurial import util, wireproto
-from common import HTTP_OK
+from __future__ import absolute_import
+
+import cStringIO
+import cgi
+import urllib
+import zlib
+
+from .common import (
+    HTTP_OK,
+)
+
+from .. import (
+    util,
+    wireproto,
+)
 
 HGTYPE = 'application/mercurial-0.1'
 HGERRTYPE = 'application/hg-error'
--- a/mercurial/hgweb/request.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/hgweb/request.py	Thu Dec 17 17:27:32 2015 -0600
@@ -6,9 +6,21 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import socket, cgi, errno
-from mercurial import util
-from common import ErrorResponse, statusmessage, HTTP_NOT_MODIFIED
+from __future__ import absolute_import
+
+import cgi
+import errno
+import socket
+
+from .common import (
+    ErrorResponse,
+    HTTP_NOT_MODIFIED,
+    statusmessage,
+)
+
+from .. import (
+    util,
+)
 
 shortcuts = {
     'cl': [('cmd', ['changelog']), ('rev', None)],
@@ -80,7 +92,7 @@
         if self._start_response is not None:
             self.headers.append(('Content-Type', type))
             if filename:
-                filename = (filename.split('/')[-1]
+                filename = (filename.rpartition('/')[-1]
                             .replace('\\', '\\\\').replace('"', '\\"'))
                 self.headers.append(('Content-Disposition',
                                      'inline; filename="%s"' % filename))
--- a/mercurial/hgweb/server.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/hgweb/server.py	Thu Dec 17 17:27:32 2015 -0600
@@ -6,10 +6,27 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
-from mercurial import util, error
-from mercurial.hgweb import common
-from mercurial.i18n import _
+from __future__ import absolute_import
+
+import BaseHTTPServer
+import SocketServer
+import errno
+import os
+import socket
+import sys
+import traceback
+import urllib
+
+from ..i18n import _
+
+from .. import (
+    error,
+    util,
+)
+
+from . import (
+    common,
+)
 
 def _splitURI(uri):
     """Return path and query that has been split from uri
@@ -197,47 +214,6 @@
             self.wfile.write('0\r\n\r\n')
             self.wfile.flush()
 
-class _httprequesthandleropenssl(_httprequesthandler):
-    """HTTPS handler based on pyOpenSSL"""
-
-    url_scheme = 'https'
-
-    @staticmethod
-    def preparehttpserver(httpserver, ssl_cert):
-        try:
-            import OpenSSL
-            OpenSSL.SSL.Context
-        except ImportError:
-            raise error.Abort(_("SSL support is unavailable"))
-        ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
-        ctx.use_privatekey_file(ssl_cert)
-        ctx.use_certificate_file(ssl_cert)
-        sock = socket.socket(httpserver.address_family, httpserver.socket_type)
-        httpserver.socket = OpenSSL.SSL.Connection(ctx, sock)
-        httpserver.server_bind()
-        httpserver.server_activate()
-
-    def setup(self):
-        self.connection = self.request
-        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
-        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
-
-    def do_write(self):
-        import OpenSSL
-        try:
-            _httprequesthandler.do_write(self)
-        except OpenSSL.SSL.SysCallError as inst:
-            if inst.args[0] != errno.EPIPE:
-                raise
-
-    def handle_one_request(self):
-        import OpenSSL
-        try:
-            _httprequesthandler.handle_one_request(self)
-        except (OpenSSL.SSL.SysCallError, OpenSSL.SSL.ZeroReturnError):
-            self.close_connection = True
-            pass
-
 class _httprequesthandlerssl(_httprequesthandler):
     """HTTPS handler based on Python's ssl module"""
 
@@ -260,8 +236,8 @@
         self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
 
 try:
-    from threading import activeCount
-    activeCount() # silence pyflakes
+    import threading
+    threading.activeCount() # silence pyflakes and bypass demandimport
     _mixin = SocketServer.ThreadingMixIn
 except ImportError:
     if util.safehasattr(os, "fork"):
@@ -311,10 +287,7 @@
 def create_server(ui, app):
 
     if ui.config('web', 'certificate'):
-        if sys.version_info >= (2, 6):
-            handler = _httprequesthandlerssl
-        else:
-            handler = _httprequesthandleropenssl
+        handler = _httprequesthandlerssl
     else:
         handler = _httprequesthandler
 
--- a/mercurial/hgweb/webcommands.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/hgweb/webcommands.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,18 +5,43 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import os, mimetypes, re, cgi, copy
-import webutil
-from mercurial import error, encoding, archival, templater, templatefilters
-from mercurial.node import short, hex
-from mercurial import util
-from common import paritygen, staticfile, get_contact, ErrorResponse
-from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
-from mercurial import graphmod, patch
-from mercurial import scmutil
-from mercurial.i18n import _
-from mercurial.error import ParseError, RepoLookupError, Abort
-from mercurial import revset
+from __future__ import absolute_import
+
+import cgi
+import copy
+import mimetypes
+import os
+import re
+
+from ..i18n import _
+from ..node import hex, short
+
+from .common import (
+    ErrorResponse,
+    HTTP_FORBIDDEN,
+    HTTP_NOT_FOUND,
+    HTTP_OK,
+    get_contact,
+    paritygen,
+    staticfile,
+)
+
+from .. import (
+    archival,
+    encoding,
+    error,
+    graphmod,
+    patch,
+    revset,
+    scmutil,
+    templatefilters,
+    templater,
+    util,
+)
+
+from . import (
+    webutil,
+)
 
 __all__ = []
 commands = {}
@@ -120,20 +145,10 @@
                 file=f,
                 path=webutil.up(f),
                 text=lines(),
-                rev=fctx.rev(),
                 symrev=webutil.symrevorshortnode(req, fctx),
-                node=fctx.hex(),
-                author=fctx.user(),
-                date=fctx.date(),
-                desc=fctx.description(),
-                extra=fctx.extra(),
-                branch=webutil.nodebranchnodefault(fctx),
-                parent=webutil.parents(fctx),
-                child=webutil.children(fctx),
                 rename=webutil.renamelink(fctx),
-                tags=webutil.nodetagsdict(web.repo, fctx.node()),
-                bookmarks=webutil.nodebookmarksdict(web.repo, fctx.node()),
-                permissions=fctx.manifest().flags(f))
+                permissions=fctx.manifest().flags(f),
+                **webutil.commonentry(web.repo, fctx))
 
 @webcommand('file')
 def file(web, req, tmpl):
@@ -225,7 +240,7 @@
         revdef = 'reverse(%s)' % query
         try:
             tree = revset.parse(revdef)
-        except ParseError:
+        except error.ParseError:
             # can't parse to a revset tree
             return MODE_KEYWORD, query
 
@@ -249,7 +264,8 @@
             # RepoLookupError: no such revision, e.g. in 'revision:'
             # Abort: bookmark/tag not exists
             # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
-        except (ParseError, RepoLookupError, Abort, LookupError):
+        except (error.ParseError, error.RepoLookupError, error.Abort,
+                LookupError):
             return MODE_KEYWORD, query
 
     def changelist(**map):
@@ -263,20 +279,9 @@
 
             yield tmpl('searchentry',
                        parity=parity.next(),
-                       author=ctx.user(),
-                       parent=webutil.parents(ctx),
-                       child=webutil.children(ctx),
                        changelogtag=showtags,
-                       desc=ctx.description(),
-                       extra=ctx.extra(),
-                       date=ctx.date(),
                        files=files,
-                       rev=ctx.rev(),
-                       node=hex(n),
-                       tags=webutil.nodetagsdict(web.repo, n),
-                       bookmarks=webutil.nodebookmarksdict(web.repo, n),
-                       inbranch=webutil.nodeinbranch(web.repo, ctx),
-                       branches=webutil.nodebranchdict(web.repo, ctx))
+                       **webutil.commonentry(web.repo, ctx))
 
             if count >= revcount:
                 break
@@ -546,20 +551,14 @@
                    "basename": d}
 
     return tmpl("manifest",
-                rev=ctx.rev(),
                 symrev=symrev,
-                node=hex(node),
                 path=abspath,
                 up=webutil.up(abspath),
                 upparity=parity.next(),
                 fentries=filelist,
                 dentries=dirlist,
                 archives=web.archivelist(hex(node)),
-                tags=webutil.nodetagsdict(web.repo, node),
-                bookmarks=webutil.nodebookmarksdict(web.repo, node),
-                branch=webutil.nodebranchnodefault(ctx),
-                inbranch=webutil.nodeinbranch(web.repo, ctx),
-                branches=webutil.nodebranchdict(web.repo, ctx))
+                **webutil.commonentry(web.repo, ctx))
 
 @webcommand('tags')
 def tags(web, req, tmpl):
@@ -693,22 +692,11 @@
             revs = web.repo.changelog.revs(start, end - 1)
         for i in revs:
             ctx = web.repo[i]
-            n = ctx.node()
-            hn = hex(n)
 
             l.append(tmpl(
-               'shortlogentry',
+                'shortlogentry',
                 parity=parity.next(),
-                author=ctx.user(),
-                desc=ctx.description(),
-                extra=ctx.extra(),
-                date=ctx.date(),
-                rev=i,
-                node=hn,
-                tags=webutil.nodetagsdict(web.repo, n),
-                bookmarks=webutil.nodebookmarksdict(web.repo, n),
-                inbranch=webutil.nodeinbranch(web.repo, ctx),
-                branches=webutil.nodebranchdict(web.repo, ctx)))
+                **webutil.commonentry(web.repo, ctx)))
 
         l.reverse()
         yield l
@@ -753,12 +741,8 @@
             raise
 
     if fctx is not None:
-        n = fctx.node()
         path = fctx.path()
         ctx = fctx.changectx()
-    else:
-        n = ctx.node()
-        # path already defined in except clause
 
     parity = paritygen(web.stripecount)
     style = web.config('web', 'style', 'paper')
@@ -766,7 +750,7 @@
         style = req.form['style'][0]
 
     diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style)
-    if fctx:
+    if fctx is not None:
         rename = webutil.renamelink(fctx)
         ctx = fctx
     else:
@@ -774,20 +758,10 @@
         ctx = ctx
     return tmpl("filediff",
                 file=path,
-                node=hex(n),
-                rev=ctx.rev(),
                 symrev=webutil.symrevorshortnode(req, ctx),
-                date=ctx.date(),
-                desc=ctx.description(),
-                extra=ctx.extra(),
-                author=ctx.user(),
                 rename=rename,
-                branch=webutil.nodebranchnodefault(ctx),
-                parent=webutil.parents(ctx),
-                child=webutil.children(ctx),
-                tags=webutil.nodetagsdict(web.repo, n),
-                bookmarks=webutil.nodebookmarksdict(web.repo, n),
-                diff=diffs)
+                diff=diffs,
+                **webutil.commonentry(web.repo, ctx))
 
 diff = webcommand('diff')(filediff)
 
@@ -812,7 +786,6 @@
     if 'file' not in req.form:
         raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
     path = webutil.cleanpath(web.repo, req.form['file'][0])
-    rename = path in ctx and webutil.renamelink(ctx[path]) or []
 
     parsecontext = lambda v: v == 'full' and -1 or int(v)
     if 'context' in req.form:
@@ -828,6 +801,7 @@
             return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
         return f.data().splitlines()
 
+    fctx = None
     parent = ctx.p1()
     leftrev = parent.rev()
     leftnode = parent.node()
@@ -843,30 +817,26 @@
             leftlines = filelines(pfctx)
     else:
         rightlines = ()
-        fctx = ctx.parents()[0][path]
-        leftlines = filelines(fctx)
+        pfctx = ctx.parents()[0][path]
+        leftlines = filelines(pfctx)
 
     comparison = webutil.compare(tmpl, context, leftlines, rightlines)
+    if fctx is not None:
+        rename = webutil.renamelink(fctx)
+        ctx = fctx
+    else:
+        rename = []
+        ctx = ctx
     return tmpl('filecomparison',
                 file=path,
-                node=hex(ctx.node()),
-                rev=ctx.rev(),
                 symrev=webutil.symrevorshortnode(req, ctx),
-                date=ctx.date(),
-                desc=ctx.description(),
-                extra=ctx.extra(),
-                author=ctx.user(),
                 rename=rename,
-                branch=webutil.nodebranchnodefault(ctx),
-                parent=webutil.parents(fctx),
-                child=webutil.children(fctx),
-                tags=webutil.nodetagsdict(web.repo, ctx.node()),
-                bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
                 leftrev=leftrev,
                 leftnode=hex(leftnode),
                 rightrev=rightrev,
                 rightnode=hex(rightnode),
-                comparison=comparison)
+                comparison=comparison,
+                **webutil.commonentry(web.repo, ctx))
 
 @webcommand('annotate')
 def annotate(web, req, tmpl):
@@ -918,20 +888,10 @@
                 file=f,
                 annotate=annotate,
                 path=webutil.up(f),
-                rev=fctx.rev(),
                 symrev=webutil.symrevorshortnode(req, fctx),
-                node=fctx.hex(),
-                author=fctx.user(),
-                date=fctx.date(),
-                desc=fctx.description(),
-                extra=fctx.extra(),
                 rename=webutil.renamelink(fctx),
-                branch=webutil.nodebranchnodefault(fctx),
-                parent=webutil.parents(fctx),
-                child=webutil.children(fctx),
-                tags=webutil.nodetagsdict(web.repo, fctx.node()),
-                bookmarks=webutil.nodebookmarksdict(web.repo, fctx.node()),
-                permissions=fctx.manifest().flags(f))
+                permissions=fctx.manifest().flags(f),
+                **webutil.commonentry(web.repo, fctx))
 
 @webcommand('filelog')
 def filelog(web, req, tmpl):
@@ -993,23 +953,12 @@
         for i in revs:
             iterfctx = fctx.filectx(i)
 
-            l.append({"parity": parity.next(),
-                      "filerev": i,
-                      "file": f,
-                      "node": iterfctx.hex(),
-                      "author": iterfctx.user(),
-                      "date": iterfctx.date(),
-                      "rename": webutil.renamelink(iterfctx),
-                      "parent": webutil.parents(iterfctx),
-                      "child": webutil.children(iterfctx),
-                      "desc": iterfctx.description(),
-                      "extra": iterfctx.extra(),
-                      "tags": webutil.nodetagsdict(repo, iterfctx.node()),
-                      "bookmarks": webutil.nodebookmarksdict(
-                          repo, iterfctx.node()),
-                      "branch": webutil.nodebranchnodefault(iterfctx),
-                      "inbranch": webutil.nodeinbranch(repo, iterfctx),
-                      "branches": webutil.nodebranchdict(repo, iterfctx)})
+            l.append(dict(
+                parity=parity.next(),
+                filerev=i,
+                file=f,
+                rename=webutil.renamelink(iterfctx),
+                **webutil.commonentry(repo, iterfctx)))
         for e in reversed(l):
             yield e
 
@@ -1018,11 +967,16 @@
 
     revnav = webutil.filerevnav(web.repo, fctx.path())
     nav = revnav.gen(end - 1, revcount, count)
-    return tmpl("filelog", file=f, node=fctx.hex(), nav=nav,
+    return tmpl("filelog",
+                file=f,
+                nav=nav,
                 symrev=webutil.symrevorshortnode(req, fctx),
                 entries=entries,
                 latestentry=latestentry,
-                revcount=revcount, morevars=morevars, lessvars=lessvars)
+                revcount=revcount,
+                morevars=morevars,
+                lessvars=lessvars,
+                **webutil.commonentry(web.repo, fctx))
 
 @webcommand('archive')
 def archive(web, req, tmpl):
@@ -1248,7 +1202,7 @@
 def _getdoc(e):
     doc = e[0].__doc__
     if doc:
-        doc = _(doc).split('\n')[0]
+        doc = _(doc).partition('\n')[0]
     else:
         doc = _('(no help text available)')
     return doc
@@ -1268,8 +1222,7 @@
     The ``help`` template will be rendered when requesting help for a topic.
     ``helptopics`` will be rendered for the index of help topics.
     """
-    from mercurial import commands # avoid cycle
-    from mercurial import help as helpmod # avoid cycle
+    from .. import commands, help as helpmod  # avoid cycle
 
     topicname = req.form.get('node', [None])[0]
     if not topicname:
@@ -1278,7 +1231,7 @@
                 yield {'topic': entries[0], 'summary': summary}
 
         early, other = [], []
-        primary = lambda s: s.split('|')[0]
+        primary = lambda s: s.partition('|')[0]
         for c, e in commands.table.iteritems():
             doc = _getdoc(e)
             if 'DEPRECATED' in doc or c.startswith('debug'):
--- a/mercurial/hgweb/webutil.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/hgweb/webutil.py	Thu Dec 17 17:27:32 2015 -0600
@@ -6,15 +6,32 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import os, copy
+from __future__ import absolute_import
+
+import copy
+import difflib
+import os
 import re
-from mercurial import match, patch, error, ui, util, pathutil, context
-from mercurial.i18n import _
-from mercurial.node import hex, nullid, short
-from mercurial.templatefilters import revescape
-from common import ErrorResponse, paritygen
-from common import HTTP_NOT_FOUND
-import difflib
+
+from ..i18n import _
+from ..node import hex, nullid, short
+
+from .common import (
+    ErrorResponse,
+    HTTP_NOT_FOUND,
+    paritygen,
+)
+
+from .. import (
+    context,
+    error,
+    match,
+    patch,
+    pathutil,
+    templatefilters,
+    ui as uimod,
+    util,
+)
 
 def up(p):
     if p[0] != "/":
@@ -124,20 +141,28 @@
     def hex(self, rev):
         return hex(self._changelog.node(self._revlog.linkrev(rev)))
 
+class _siblings(object):
+    def __init__(self, siblings=[], hiderev=None):
+        self.siblings = [s for s in siblings if s.node() != nullid]
+        if len(self.siblings) == 1 and self.siblings[0].rev() == hiderev:
+            self.siblings = []
 
-def _siblings(siblings=[], hiderev=None):
-    siblings = [s for s in siblings if s.node() != nullid]
-    if len(siblings) == 1 and siblings[0].rev() == hiderev:
-        return
-    for s in siblings:
-        d = {'node': s.hex(), 'rev': s.rev()}
-        d['user'] = s.user()
-        d['date'] = s.date()
-        d['description'] = s.description()
-        d['branch'] = s.branch()
-        if util.safehasattr(s, 'path'):
-            d['file'] = s.path()
-        yield d
+    def __iter__(self):
+        for s in self.siblings:
+            d = {
+                'node': s.hex(),
+                'rev': s.rev(),
+                'user': s.user(),
+                'date': s.date(),
+                'description': s.description(),
+                'branch': s.branch(),
+            }
+            if util.safehasattr(s, 'path'):
+                d['file'] = s.path()
+            yield d
+
+    def __len__(self):
+        return len(self.siblings)
 
 def parents(ctx, hide=None):
     if isinstance(ctx, context.basefilectx):
@@ -283,6 +308,25 @@
 
     return fctx
 
+def commonentry(repo, ctx):
+    node = ctx.node()
+    return {
+        'rev': ctx.rev(),
+        'node': hex(node),
+        'author': ctx.user(),
+        'desc': ctx.description(),
+        'date': ctx.date(),
+        'extra': ctx.extra(),
+        'phase': ctx.phasestr(),
+        'branch': nodebranchnodefault(ctx),
+        'inbranch': nodeinbranch(repo, ctx),
+        'branches': nodebranchdict(repo, ctx),
+        'tags': nodetagsdict(repo, node),
+        'bookmarks': nodebookmarksdict(repo, node),
+        'parent': lambda **x: parents(ctx),
+        'child': lambda **x: children(ctx),
+    }
+
 def changelistentry(web, ctx, tmpl):
     '''Obtain a dictionary to be used for entries in a changelist.
 
@@ -295,26 +339,18 @@
     showtags = showtag(repo, tmpl, 'changelogtag', n)
     files = listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
 
-    return {
-        "author": ctx.user(),
-        "parent": parents(ctx, rev - 1),
-        "child": children(ctx, rev + 1),
-        "changelogtag": showtags,
-        "desc": ctx.description(),
-        "extra": ctx.extra(),
-        "date": ctx.date(),
-        "files": files,
-        "rev": rev,
-        "node": hex(n),
-        "tags": nodetagsdict(repo, n),
-        "bookmarks": nodebookmarksdict(repo, n),
-        "inbranch": nodeinbranch(repo, ctx),
-        "branches": nodebranchdict(repo, ctx)
-    }
+    entry = commonentry(repo, ctx)
+    entry.update(
+        parent=lambda **x: parents(ctx, rev - 1),
+        child=lambda **x: children(ctx, rev + 1),
+        changelogtag=showtags,
+        files=files,
+    )
+    return entry
 
 def symrevorshortnode(req, ctx):
     if 'node' in req.form:
-        return revescape(req.form['node'][0])
+        return templatefilters.revescape(req.form['node'][0])
     else:
         return short(ctx.node())
 
@@ -351,29 +387,16 @@
 
     return dict(
         diff=diff,
-        rev=ctx.rev(),
-        node=ctx.hex(),
         symrev=symrevorshortnode(req, ctx),
-        parent=tuple(parents(ctx)),
-        child=children(ctx),
         basenode=basectx.hex(),
         changesettag=showtags,
         changesetbookmark=showbookmarks,
         changesetbranch=showbranch,
-        author=ctx.user(),
-        desc=ctx.description(),
-        extra=ctx.extra(),
-        date=ctx.date(),
-        phase=ctx.phasestr(),
         files=files,
         diffsummary=lambda **x: diffsummary(diffstatsgen),
         diffstat=diffstats,
         archives=web.archivelist(ctx.hex()),
-        tags=nodetagsdict(web.repo, ctx.node()),
-        bookmarks=nodebookmarksdict(web.repo, ctx.node()),
-        branch=showbranch,
-        inbranch=nodeinbranch(web.repo, ctx),
-        branches=nodebranchdict(web.repo, ctx))
+        **commonentry(web.repo, ctx))
 
 def listfilediffs(tmpl, files, node, max):
     for f in files[:max]:
@@ -537,7 +560,7 @@
             yield {'name': key, 'value': str(value), 'separator': separator}
             separator = '&'
 
-class wsgiui(ui.ui):
+class wsgiui(uimod.ui):
     # default termwidth breaks under mod_wsgi
     def termwidth(self):
         return 80
--- a/mercurial/hgweb/wsgicgi.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/hgweb/wsgicgi.py	Thu Dec 17 17:27:32 2015 -0600
@@ -8,9 +8,18 @@
 # This was originally copied from the public domain code at
 # http://www.python.org/dev/peps/pep-0333/#the-server-gateway-side
 
-import os, sys
-from mercurial import util
-from mercurial.hgweb import common
+from __future__ import absolute_import
+
+import os
+import sys
+
+from .. import (
+    util,
+)
+
+from . import (
+    common,
+)
 
 def launch(application):
     util.setbinary(sys.stdin)
--- a/mercurial/localrepo.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/localrepo.py	Thu Dec 17 17:27:32 2015 -0600
@@ -214,6 +214,8 @@
         self.path = self.wvfs.join(".hg")
         self.origroot = path
         self.auditor = pathutil.pathauditor(self.root, self._checknested)
+        self.nofsauditor = pathutil.pathauditor(self.root, self._checknested,
+                                                realfs=False)
         self.vfs = scmutil.vfs(self.path)
         self.opener = self.vfs
         self.baseui = baseui
@@ -258,8 +260,7 @@
                         '\0\0\0\2' # represents revlogv2
                         ' dummy changelog to prevent using the old repo layout'
                     )
-                # experimental config: format.generaldelta
-                if self.ui.configbool('format', 'generaldelta', False):
+                if scmutil.gdinitconfig(self.ui):
                     self.requirements.add("generaldelta")
                 if self.ui.configbool('experimental', 'treemanifest', False):
                     self.requirements.add("treemanifest")
@@ -359,6 +360,7 @@
         aggressivemergedeltas = self.ui.configbool('format',
             'aggressivemergedeltas', False)
         self.svfs.options['aggressivemergedeltas'] = aggressivemergedeltas
+        self.svfs.options['lazydeltabase'] = not scmutil.gddeltaconfig(self.ui)
 
     def _writerequirements(self):
         scmutil.writerequires(self.vfs, self.requirements)
@@ -517,15 +519,23 @@
         return iter(self.changelog)
 
     def revs(self, expr, *args):
-        '''Return a list of revisions matching the given revset'''
+        '''Find revisions matching a revset.
+
+        The revset is specified as a string ``expr`` that may contain
+        %-formatting to escape certain types. See ``revset.formatspec``.
+
+        Return a revset.abstractsmartset, which is a list-like interface
+        that contains integer revisions.
+        '''
         expr = revset.formatspec(expr, *args)
         m = revset.match(None, expr)
         return m(self)
 
     def set(self, expr, *args):
-        '''
-        Yield a context for each matching revision, after doing arg
-        replacement via revset.formatspec
+        '''Find revisions matching a revset and emit changectx instances.
+
+        This is a convenience wrapper around ``revs()`` that iterates the
+        result and is a generator of changectx instances.
         '''
         for r in self.revs(expr, *args):
             yield self[r]
@@ -751,6 +761,7 @@
         return self._tagscache.nodetagscache.get(node, [])
 
     def nodebookmarks(self, node):
+        """return the list of bookmarks pointing to the specified node"""
         marks = []
         for bookmark, n in self._bookmarks.iteritems():
             if n == node:
@@ -797,12 +808,13 @@
         return repo[key].branch()
 
     def known(self, nodes):
-        nm = self.changelog.nodemap
-        pc = self._phasecache
+        cl = self.changelog
+        nm = cl.nodemap
+        filtered = cl.filteredrevs
         result = []
         for n in nodes:
             r = nm.get(n)
-            resp = not (r is None or pc.phase(self, r) >= phases.secret)
+            resp = not (r is None or r in filtered)
             result.append(resp)
         return result
 
@@ -840,13 +852,15 @@
             f = f[1:]
         return filelog.filelog(self.svfs, f)
 
+    def parents(self, changeid=None):
+        '''get list of changectxs for parents of changeid'''
+        msg = 'repo.parents() is deprecated, use repo[%r].parents()' % changeid
+        self.ui.deprecwarn(msg, '3.7')
+        return self[changeid].parents()
+
     def changectx(self, changeid):
         return self[changeid]
 
-    def parents(self, changeid=None):
-        '''get list of changectxs for parents of changeid'''
-        return self[changeid].parents()
-
     def setparents(self, p1, p2=nullid):
         self.dirstate.beginparentchange()
         copies = self.dirstate.setparents(p1, p2)
@@ -1161,15 +1175,14 @@
                         % self.dirstate.branch())
 
             self.dirstate.invalidate()
-            parents = tuple([p.rev() for p in self.parents()])
+            parents = tuple([p.rev() for p in self[None].parents()])
             if len(parents) > 1:
                 ui.status(_('working directory now based on '
                             'revisions %d and %d\n') % parents)
             else:
                 ui.status(_('working directory now based on '
                             'revision %d\n') % parents)
-            ms = mergemod.mergestate(self)
-            ms.reset(self['.'].node())
+            mergemod.mergestate.clean(self, self['.'].node())
 
         # TODO: if we know which new heads may result from this rollback, pass
         # them to destroy(), which will prevent the branchhead cache from being
@@ -1456,8 +1469,11 @@
             match.explicitdir = vdirs.append
             match.bad = fail
 
-        wlock = self.wlock()
+        wlock = lock = tr = None
         try:
+            wlock = self.wlock()
+            lock = self.lock() # for recent changelog (see issue4368)
+
             wctx = self[None]
             merge = len(wctx.parents()) > 1
 
@@ -1556,7 +1572,7 @@
             if merge and cctx.deleted():
                 raise error.Abort(_("cannot commit merge with missing files"))
 
-            ms = mergemod.mergestate(self)
+            ms = mergemod.mergestate.read(self)
 
             if list(ms.unresolved()):
                 raise error.Abort(_('unresolved merge conflicts '
@@ -1589,19 +1605,21 @@
             try:
                 self.hook("precommit", throw=True, parent1=hookp1,
                           parent2=hookp2)
+                tr = self.transaction('commit')
                 ret = self.commitctx(cctx, True)
             except: # re-raises
                 if edited:
                     self.ui.write(
                         _('note: commit message saved in %s\n') % msgfn)
                 raise
-
             # update bookmarks, dirstate and mergestate
             bookmarks.update(self, [p1, p2], ret)
             cctx.markcommitted(ret)
             ms.reset()
+            tr.close()
+
         finally:
-            wlock.release()
+            lockmod.release(tr, lock, wlock)
 
         def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
             # hack for command that use a temporary commit (eg: histedit)
@@ -1838,22 +1856,6 @@
         """
         return util.hooks()
 
-    def clone(self, remote, heads=[], stream=None):
-        '''clone remote repository.
-
-        keyword arguments:
-        heads: list of revs to clone (forces use of pull)
-        stream: use streaming clone if possible'''
-        # internal config: ui.quietbookmarkmove
-        quiet = self.ui.backupconfig('ui', 'quietbookmarkmove')
-        try:
-            self.ui.setconfig('ui', 'quietbookmarkmove', True, 'clone')
-            pullop = exchange.pull(self, remote, heads,
-                                   streamclonerequested=stream)
-            return pullop.cgresult
-        finally:
-            self.ui.restoreconfig(quiet)
-
     def pushkey(self, namespace, key, old, new):
         try:
             tr = self.currenttransaction()
--- a/mercurial/lsprof.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/lsprof.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,5 +1,12 @@
+from __future__ import absolute_import
+
+import _lsprof
 import sys
-from _lsprof import Profiler, profiler_entry
+
+Profiler = _lsprof.Profiler
+
+# PyPy doesn't expose profiler_entry from the module.
+profiler_entry = getattr(_lsprof, 'profiler_entry', None)
 
 __all__ = ['profile', 'Stats']
 
@@ -22,8 +29,13 @@
 
     def sort(self, crit="inlinetime"):
         """XXX docstring"""
-        if crit not in profiler_entry.__dict__:
+        # profiler_entries isn't defined when running under PyPy.
+        if profiler_entry:
+            if crit not in profiler_entry.__dict__:
+                raise ValueError("Can't sort by %s" % crit)
+        elif self.data and not getattr(self.data[0], crit, None):
             raise ValueError("Can't sort by %s" % crit)
+
         self.data.sort(key=lambda x: getattr(x, crit), reverse=True)
         for e in self.data:
             if e.calls:
--- a/mercurial/manifest.c	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/manifest.c	Thu Dec 17 17:27:32 2015 -0600
@@ -242,7 +242,7 @@
 	hash = nodeof(l);
 	consumed = pl + 41;
 	flags = PyString_FromStringAndSize(l->start + consumed,
-									   l->len - consumed - 1);
+					   l->len - consumed - 1);
 	if (!path || !hash || !flags) {
 		goto done;
 	}
--- a/mercurial/manifest.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/manifest.py	Thu Dec 17 17:27:32 2015 -0600
@@ -334,36 +334,44 @@
         # zero copy representation of base as a buffer
         addbuf = util.buffer(base)
 
-        # start with a readonly loop that finds the offset of
-        # each line and creates the deltas
-        for f, todelete in changes:
-            # bs will either be the index of the item or the insert point
-            start, end = _msearch(addbuf, f, start)
-            if not todelete:
-                h, fl = self._lm[f]
-                l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
-            else:
-                if start == end:
-                    # item we want to delete was not found, error out
-                    raise AssertionError(
-                            _("failed to remove %s from manifest") % f)
-                l = ""
-            if dstart is not None and dstart <= start and dend >= start:
-                if dend < end:
+        changes = list(changes)
+        if len(changes) < 1000:
+            # start with a readonly loop that finds the offset of
+            # each line and creates the deltas
+            for f, todelete in changes:
+                # bs will either be the index of the item or the insert point
+                start, end = _msearch(addbuf, f, start)
+                if not todelete:
+                    h, fl = self._lm[f]
+                    l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
+                else:
+                    if start == end:
+                        # item we want to delete was not found, error out
+                        raise AssertionError(
+                                _("failed to remove %s from manifest") % f)
+                    l = ""
+                if dstart is not None and dstart <= start and dend >= start:
+                    if dend < end:
+                        dend = end
+                    if l:
+                        dline.append(l)
+                else:
+                    if dstart is not None:
+                        delta.append([dstart, dend, "".join(dline)])
+                    dstart = start
                     dend = end
-                if l:
-                    dline.append(l)
-            else:
-                if dstart is not None:
-                    delta.append([dstart, dend, "".join(dline)])
-                dstart = start
-                dend = end
-                dline = [l]
+                    dline = [l]
 
-        if dstart is not None:
-            delta.append([dstart, dend, "".join(dline)])
-        # apply the delta to the base, and get a delta for addrevision
-        deltatext, arraytext = _addlistdelta(base, delta)
+            if dstart is not None:
+                delta.append([dstart, dend, "".join(dline)])
+            # apply the delta to the base, and get a delta for addrevision
+            deltatext, arraytext = _addlistdelta(base, delta)
+        else:
+            # For large changes, it's much cheaper to just build the text and
+            # diff it.
+            arraytext = array.array('c', self.text())
+            deltatext = mdiff.textdiff(base, arraytext)
+
         return arraytext, deltatext
 
 def _msearch(m, s, lo=0, hi=None):
@@ -609,7 +617,7 @@
 
     def setflag(self, f, flags):
         """Set the flags (symlink, executable) for path f."""
-        assert 'd' not in flags
+        assert 't' not in flags
         self._load()
         dir, subpath = _splittopdir(f)
         if dir:
@@ -732,9 +740,12 @@
     def _matches(self, match):
         '''recursively generate a new manifest filtered by the match argument.
         '''
+
+        visit = match.visitdir(self._dir[:-1] or '.')
+        if visit == 'all':
+            return self.copy()
         ret = treemanifest(self._dir)
-
-        if not match.visitdir(self._dir[:-1] or '.'):
+        if not visit:
             return ret
 
         self._load()
@@ -807,7 +818,7 @@
 
     def parse(self, text, readsubtree):
         for f, n, fl in _parse(text):
-            if fl == 'd':
+            if fl == 't':
                 f = f + '/'
                 self._dirs[f] = readsubtree(self._subpath(f), n)
             elif '/' in f:
@@ -838,7 +849,7 @@
         """
         self._load()
         flags = self.flags
-        dirs = [(d[:-1], self._dirs[d]._node, 'd') for d in self._dirs]
+        dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
         files = [(f, self._files[f], flags(f)) for f in self._files]
         return _text(sorted(dirs + files), usemanifestv2)
 
--- a/mercurial/match.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/match.py	Thu Dec 17 17:27:32 2015 -0600
@@ -227,9 +227,15 @@
         has potential matches in it or one of its subdirectories. This is
         based on the match's primary, included, and excluded patterns.
 
+        Returns the string 'all' if the given directory and all subdirectories
+        should be visited. Otherwise returns True or False indicating whether
+        the given directory should be visited.
+
         This function's behavior is undefined if it has returned False for
         one of the dir's parent directories.
         '''
+        if self.prefix() and dir in self._fileroots:
+            return 'all'
         if dir in self._excluderoots:
             return False
         if (self._includeroots and
@@ -654,9 +660,11 @@
         if "#" in line:
             global _commentre
             if not _commentre:
-                _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
+                _commentre = util.re.compile(r'((?:^|[^\\])(?:\\\\)*)#.*')
             # remove comments prefixed by an even number of escapes
-            line = _commentre.sub(r'\1', line)
+            m = _commentre.search(line)
+            if m:
+                line = line[:m.end(1)]
             # fixup properly escaped comments that survived the above
             line = line.replace("\\#", "#")
         line = line.rstrip()
--- a/mercurial/merge.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/merge.py	Thu Dec 17 17:27:32 2015 -0600
@@ -16,6 +16,7 @@
 from .node import (
     bin,
     hex,
+    nullhex,
     nullid,
     nullrev,
 )
@@ -42,50 +43,69 @@
 class mergestate(object):
     '''track 3-way merge state of individual files
 
-    it is stored on disk when needed. Two file are used, one with an old
-    format, one with a new format. Both contains similar data, but the new
-    format can store new kinds of field.
-
-    Current new format is a list of arbitrary record of the form:
-
-        [type][length][content]
+    The merge state is stored on disk when needed. Two files are used: one with
+    an old format (version 1), and one with a new format (version 2). Version 2
+    stores a superset of the data in version 1, including new kinds of records
+    in the future. For more about the new format, see the documentation for
+    `_readrecordsv2`.
 
-    Type is a single character, length is a 4 bytes integer, content is an
-    arbitrary suites of bytes of length `length`.
+    Each record can contain arbitrary content, and has an associated type. This
+    `type` should be a letter. If `type` is uppercase, the record is mandatory:
+    versions of Mercurial that don't support it should abort. If `type` is
+    lowercase, the record can be safely ignored.
 
-    Type should be a letter. Capital letter are mandatory record, Mercurial
-    should abort if they are unknown. lower case record can be safely ignored.
-
-    Currently known record:
+    Currently known records:
 
     L: the node of the "local" part of the merge (hexified version)
     O: the node of the "other" part of the merge (hexified version)
     F: a file to be merged entry
+    C: a change/delete or delete/change conflict
     D: a file that the external merge driver will merge internally
        (experimental)
     m: the external merge driver defined for this merge plus its run state
        (experimental)
+    X: unsupported mandatory record type (used in tests)
+    x: unsupported advisory record type (used in tests)
 
     Merge driver run states (experimental):
     u: driver-resolved files unmarked -- needs to be run next time we're about
        to resolve or commit
     m: driver-resolved files marked -- only needs to be run before commit
     s: success/skipped -- does not need to be run any more
+
     '''
     statepathv1 = 'merge/state'
     statepathv2 = 'merge/state2'
 
+    @staticmethod
+    def clean(repo, node=None, other=None):
+        """Initialize a brand new merge state, removing any existing state on
+        disk."""
+        ms = mergestate(repo)
+        ms.reset(node, other)
+        return ms
+
+    @staticmethod
+    def read(repo):
+        """Initialize the merge state, reading it from disk."""
+        ms = mergestate(repo)
+        ms._read()
+        return ms
+
     def __init__(self, repo):
+        """Initialize the merge state.
+
+        Do not use this directly! Instead call read() or clean()."""
         self._repo = repo
         self._dirty = False
-        self._read()
 
     def reset(self, node=None, other=None):
         self._state = {}
         self._local = None
         self._other = None
-        if 'otherctx' in vars(self):
-            del self.otherctx
+        for var in ('localctx', 'otherctx'):
+            if var in vars(self):
+                delattr(self, var)
         if node:
             self._local = node
             self._other = other
@@ -95,6 +115,7 @@
         else:
             self._mdstate = 'u'
         shutil.rmtree(self._repo.join('merge'), True)
+        self._results = {}
         self._dirty = False
 
     def _read(self):
@@ -106,10 +127,12 @@
         self._state = {}
         self._local = None
         self._other = None
-        if 'otherctx' in vars(self):
-            del self.otherctx
+        for var in ('localctx', 'otherctx'):
+            if var in vars(self):
+                delattr(self, var)
         self._readmergedriver = None
         self._mdstate = 's'
+        unsupported = set()
         records = self._readrecords()
         for rtype, record in records:
             if rtype == 'L':
@@ -125,14 +148,17 @@
 
                 self._readmergedriver = bits[0]
                 self._mdstate = mdstate
-            elif rtype in 'FD':
+            elif rtype in 'FDC':
                 bits = record.split('\0')
                 self._state[bits[0]] = bits[1:]
             elif not rtype.islower():
-                raise error.Abort(_('unsupported merge state record: %s')
-                                   % rtype)
+                unsupported.add(rtype)
+        self._results = {}
         self._dirty = False
 
+        if unsupported:
+            raise error.UnsupportedMergeRecords(unsupported)
+
     def _readrecords(self):
         """Read merge state from disk and return a list of record (TYPE, data)
 
@@ -206,8 +232,21 @@
     def _readrecordsv2(self):
         """read on disk merge state for version 2 file
 
-        returns list of record [(TYPE, data), ...]
-        """
+        This format is a list of arbitrary records of the form:
+
+          [type][length][content]
+
+        `type` is a single character, `length` is a 4 byte integer, and
+        `content` is an arbitrary byte sequence of length `length`.
+
+        Mercurial versions prior to 3.7 have a bug where if there are
+        unsupported mandatory merge records, attempting to clear out the merge
+        state with hg update --clean or similar aborts. The 't' record type
+        works around that by writing out what those versions treat as an
+        advisory record, but later versions interpret as special: the first
+        character is the 'real' record type and everything onwards is the data.
+
+        Returns list of records [(TYPE, data), ...]."""
         records = []
         try:
             f = self._repo.vfs(self.statepathv2)
@@ -221,6 +260,8 @@
                 off += 4
                 record = data[off:(off + length)]
                 off += length
+                if rtype == 't':
+                    rtype, record = record[0], record[1:]
                 records.append((rtype, record))
             f.close()
         except IOError as err:
@@ -248,7 +289,15 @@
         return configmergedriver
 
     @util.propertycache
+    def localctx(self):
+        if self._local is None:
+            raise RuntimeError("localctx accessed but self._local isn't set")
+        return self._repo[self._local]
+
+    @util.propertycache
     def otherctx(self):
+        if self._other is None:
+            raise RuntimeError("localctx accessed but self._local isn't set")
         return self._repo[self._other]
 
     def active(self):
@@ -266,20 +315,28 @@
     def commit(self):
         """Write current state on disk (if necessary)"""
         if self._dirty:
-            records = []
-            records.append(('L', hex(self._local)))
-            records.append(('O', hex(self._other)))
-            if self.mergedriver:
-                records.append(('m', '\0'.join([
-                    self.mergedriver, self._mdstate])))
-            for d, v in self._state.iteritems():
-                if v[0] == 'd':
-                    records.append(('D', '\0'.join([d] + v)))
-                else:
-                    records.append(('F', '\0'.join([d] + v)))
+            records = self._makerecords()
             self._writerecords(records)
             self._dirty = False
 
+    def _makerecords(self):
+        records = []
+        records.append(('L', hex(self._local)))
+        records.append(('O', hex(self._other)))
+        if self.mergedriver:
+            records.append(('m', '\0'.join([
+                self.mergedriver, self._mdstate])))
+        for d, v in self._state.iteritems():
+            if v[0] == 'd':
+                records.append(('D', '\0'.join([d] + v)))
+            # v[1] == local ('cd'), v[6] == other ('dc') -- not supported by
+            # older versions of Mercurial
+            elif v[1] == nullhex or v[6] == nullhex:
+                records.append(('C', '\0'.join([d] + v)))
+            else:
+                records.append(('F', '\0'.join([d] + v)))
+        return records
+
     def _writerecords(self, records):
         """Write current state on disk (both v1 and v2)"""
         self._writerecordsv1(records)
@@ -298,10 +355,16 @@
         f.close()
 
     def _writerecordsv2(self, records):
-        """Write current state on disk in a version 2 file"""
+        """Write current state on disk in a version 2 file
+
+        See the docstring for _readrecordsv2 for why we use 't'."""
+        # these are the records that all version 2 clients can read
+        whitelist = 'LOF'
         f = self._repo.vfs(self.statepathv2, 'w')
         for key, data in records:
             assert len(key) == 1
+            if key not in whitelist:
+                key, data = 't', '%s%s' % (key, data)
             format = '>sI%is' % len(data)
             f.write(_pack(format, key, len(data), data))
         f.close()
@@ -315,8 +378,11 @@
 
         note: also write the local version to the `.hg/merge` directory.
         """
-        hash = util.sha1(fcl.path()).hexdigest()
-        self._repo.vfs.write('merge/' + hash, fcl.data())
+        if fcl.isabsent():
+            hash = nullhex
+        else:
+            hash = util.sha1(fcl.path()).hexdigest()
+            self._repo.vfs.write('merge/' + hash, fcl.data())
         self._state[fd] = ['u', hash, fcl.path(),
                            fca.path(), hex(fca.filenode()),
                            fco.path(), hex(fco.filenode()),
@@ -363,8 +429,9 @@
         stateentry = self._state[dfile]
         state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
         octx = self._repo[self._other]
-        fcd = wctx[dfile]
-        fco = octx[ofile]
+        fcd = self._filectxorabsent(hash, wctx, dfile)
+        fco = self._filectxorabsent(onode, octx, ofile)
+        # TODO: move this to filectxorabsent
         fca = self._repo.filectx(afile, fileid=anode)
         # "premerge" x flags
         flo = fco.flags()
@@ -378,29 +445,117 @@
                 flags = flo
         if preresolve:
             # restore local
-            f = self._repo.vfs('merge/' + hash)
-            self._repo.wwrite(dfile, f.read(), flags)
-            f.close()
-            complete, r = filemerge.premerge(self._repo, self._local, lfile,
-                                             fcd, fco, fca, labels=labels)
+            if hash != nullhex:
+                f = self._repo.vfs('merge/' + hash)
+                self._repo.wwrite(dfile, f.read(), flags)
+                f.close()
+            else:
+                self._repo.wvfs.unlinkpath(dfile, ignoremissing=True)
+            complete, r, deleted = filemerge.premerge(self._repo, self._local,
+                                                      lfile, fcd, fco, fca,
+                                                      labels=labels)
         else:
-            complete, r = filemerge.filemerge(self._repo, self._local, lfile,
-                                              fcd, fco, fca, labels=labels)
+            complete, r, deleted = filemerge.filemerge(self._repo, self._local,
+                                                       lfile, fcd, fco, fca,
+                                                       labels=labels)
         if r is None:
             # no real conflict
             del self._state[dfile]
             self._dirty = True
         elif not r:
             self.mark(dfile, 'r')
+
+        if complete:
+            action = None
+            if deleted:
+                if fcd.isabsent():
+                    # dc: local picked. Need to drop if present, which may
+                    # happen on re-resolves.
+                    action = 'f'
+                else:
+                    # cd: remote picked (or otherwise deleted)
+                    action = 'r'
+            else:
+                if fcd.isabsent(): # dc: remote picked
+                    action = 'g'
+                elif fco.isabsent(): # cd: local picked
+                    if dfile in self.localctx:
+                        action = 'am'
+                    else:
+                        action = 'a'
+                # else: regular merges (no action necessary)
+            self._results[dfile] = r, action
+
         return complete, r
 
+    def _filectxorabsent(self, hexnode, ctx, f):
+        if hexnode == nullhex:
+            return filemerge.absentfilectx(ctx, f)
+        else:
+            return ctx[f]
+
     def preresolve(self, dfile, wctx, labels=None):
+        """run premerge process for dfile
+
+        Returns whether the merge is complete, and the exit code."""
         return self._resolve(True, dfile, wctx, labels=labels)
 
     def resolve(self, dfile, wctx, labels=None):
-        """rerun merge process for file path `dfile`"""
+        """run merge process (assuming premerge was run) for dfile
+
+        Returns the exit code of the merge."""
         return self._resolve(False, dfile, wctx, labels=labels)[1]
 
+    def counts(self):
+        """return counts for updated, merged and removed files in this
+        session"""
+        updated, merged, removed = 0, 0, 0
+        for r, action in self._results.itervalues():
+            if r is None:
+                updated += 1
+            elif r == 0:
+                if action == 'r':
+                    removed += 1
+                else:
+                    merged += 1
+        return updated, merged, removed
+
+    def unresolvedcount(self):
+        """get unresolved count for this merge (persistent)"""
+        return len([True for f, entry in self._state.iteritems()
+                    if entry[0] == 'u'])
+
+    def actions(self):
+        """return lists of actions to perform on the dirstate"""
+        actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
+        for f, (r, action) in self._results.iteritems():
+            if action is not None:
+                actions[action].append((f, None, "merge result"))
+        return actions
+
+    def recordactions(self):
+        """record remove/add/get actions in the dirstate"""
+        branchmerge = self._repo.dirstate.p2() != nullid
+        recordupdates(self._repo, self.actions(), branchmerge)
+
+    def queueremove(self, f):
+        """queues a file to be removed from the dirstate
+
+        Meant for use by custom merge drivers."""
+        self._results[f] = 0, 'r'
+
+    def queueadd(self, f):
+        """queues a file to be added to the dirstate
+
+        Meant for use by custom merge drivers."""
+        self._results[f] = 0, 'a'
+
+    def queueget(self, f):
+        """queues a file to be marked modified in the dirstate
+
+        Meant for use by custom merge drivers."""
+        self._results[f] = 0, 'g'
+
 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
     if f2 is None:
         f2 = f
@@ -479,7 +634,7 @@
 
     if actions:
         # k, dr, e and rd are no-op
-        for m in 'a', 'f', 'g', 'cd', 'dc':
+        for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
             for f, args, msg in actions[m]:
                 pmmf.add(f)
         for f, args, msg in actions['r']:
@@ -528,15 +683,17 @@
     This is currently not implemented -- it's an extension point."""
     return True
 
-def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
+def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
                   acceptremote, followcopies):
     """
     Merge p1 and p2 with ancestor pa and generate merge action list
 
     branchmerge and force are as passed in to update
-    partial = function to filter file lists
+    matcher = matcher to filter file lists
     acceptremote = accept the incoming changes without prompting
     """
+    if matcher is not None and matcher.always():
+        matcher = None
 
     copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
 
@@ -550,7 +707,7 @@
 
     repo.ui.note(_("resolving manifests\n"))
     repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
-                  % (bool(branchmerge), bool(force), bool(partial)))
+                  % (bool(branchmerge), bool(force), bool(matcher)))
     repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
 
     m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
@@ -569,7 +726,7 @@
 
     actions = {}
     for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
-        if partial and not partial(f):
+        if matcher and not matcher(f):
             continue
         if n1 and n2: # file exists on both local and remote side
             if f not in ma:
@@ -618,7 +775,8 @@
                     if acceptremote:
                         actions[f] = ('r', None, "remote delete")
                     else:
-                        actions[f] = ('cd', None,  "prompt changed/deleted")
+                        actions[f] = ('cd', (f, None, f, False, pa.node()),
+                                      "prompt changed/deleted")
                 elif n1[20:] == 'a':
                     # This extra 'a' is added by working copy manifest to mark
                     # the file as locally added. We should forget it instead of
@@ -668,7 +826,8 @@
                 if acceptremote:
                     actions[f] = ('c', (fl2,), "remote recreating")
                 else:
-                    actions[f] = ('dc', (fl2,), "prompt deleted/changed")
+                    actions[f] = ('dc', (None, f, f, False, pa.node()),
+                                  "prompt deleted/changed")
 
     return actions, diverge, renamedelete
 
@@ -684,13 +843,12 @@
             # remote did change but ended up with same content
             del actions[f] # don't get = keep local deleted
 
-def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
-                     acceptremote, followcopies):
+def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
+                     acceptremote, followcopies, matcher=None):
     "Calculate the actions needed to merge mctx into wctx using ancestors"
-
     if len(ancestors) == 1: # default
         actions, diverge, renamedelete = manifestmerge(
-            repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
+            repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
             acceptremote, followcopies)
         _checkunknownfiles(repo, wctx, mctx, force, actions)
 
@@ -705,7 +863,7 @@
         for ancestor in ancestors:
             repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
             actions, diverge1, renamedelete1 = manifestmerge(
-                repo, wctx, mctx, ancestor, branchmerge, force, partial,
+                repo, wctx, mctx, ancestor, branchmerge, force, matcher,
                 acceptremote, followcopies)
             _checkunknownfiles(repo, wctx, mctx, force, actions)
 
@@ -834,25 +992,34 @@
     describes how many files were affected by the update.
     """
 
-    updated, merged, removed, unresolved = 0, 0, 0, 0
-    ms = mergestate(repo)
-    ms.reset(wctx.p1().node(), mctx.node())
+    updated, merged, removed = 0, 0, 0
+    ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
     moves = []
     for m, l in actions.items():
         l.sort()
 
-    # prescan for merges
-    for f, args, msg in actions['m']:
+    # 'cd' and 'dc' actions are treated like other merge conflicts
+    mergeactions = sorted(actions['cd'])
+    mergeactions.extend(sorted(actions['dc']))
+    mergeactions.extend(actions['m'])
+    for f, args, msg in mergeactions:
         f1, f2, fa, move, anc = args
         if f == '.hgsubstate': # merged internally
             continue
-        repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
-        fcl = wctx[f1]
-        fco = mctx[f2]
+        if f1 is None:
+            fcl = filemerge.absentfilectx(wctx, fa)
+        else:
+            repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
+            fcl = wctx[f1]
+        if f2 is None:
+            fco = filemerge.absentfilectx(mctx, fa)
+        else:
+            fco = mctx[f2]
         actx = repo[anc]
         if fa in actx:
             fca = actx[fa]
         else:
+            # TODO: move to absentfilectx
             fca = repo.filectx(f1, fileid=nullrev)
         ms.add(fcl, fco, fca, f)
         if f1 != f and move:
@@ -905,6 +1072,12 @@
         z += 1
         progress(_updating, z, item=f, total=numupdates, unit=_files)
 
+    # re-add/mark as modified (manifest only, just log it)
+    for f, args, msg in actions['am']:
+        repo.ui.debug(" %s: %s -> am\n" % (f, msg))
+        z += 1
+        progress(_updating, z, item=f, total=numupdates, unit=_files)
+
     # keep (noop, just log it)
     for f, args, msg in actions['k']:
         repo.ui.debug(" %s: %s -> k\n" % (f, msg))
@@ -942,7 +1115,6 @@
         util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
         updated += 1
 
-    mergeactions = actions['m']
     # the ordering is important here -- ms.mergedriver will raise if the merge
     # driver has changed, and we want to be able to bypass it when overwrite is
     # True
@@ -965,7 +1137,7 @@
 
     # premerge
     tocomplete = []
-    for f, args, msg in actions['m']:
+    for f, args, msg in mergeactions:
         repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
         z += 1
         progress(_updating, z, item=f, total=numupdates, unit=_files)
@@ -975,15 +1147,7 @@
             continue
         audit(f)
         complete, r = ms.preresolve(f, wctx, labels=labels)
-        if complete:
-            if r is not None and r > 0:
-                unresolved += 1
-            else:
-                if r is None:
-                    updated += 1
-                else:
-                    merged += 1
-        else:
+        if not complete:
             numupdates += 1
             tocomplete.append((f, args, msg))
 
@@ -992,25 +1156,29 @@
         repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
         z += 1
         progress(_updating, z, item=f, total=numupdates, unit=_files)
-        r = ms.resolve(f, wctx, labels=labels)
-        if r is not None and r > 0:
-            unresolved += 1
-        else:
-            if r is None:
-                updated += 1
-            else:
-                merged += 1
+        ms.resolve(f, wctx, labels=labels)
 
     ms.commit()
 
+    unresolved = ms.unresolvedcount()
+
     if usemergedriver and not unresolved and ms.mdstate() != 's':
         if not driverconclude(repo, ms, wctx, labels=labels):
             # XXX setting unresolved to at least 1 is a hack to make sure we
             # error out
-            return updated, merged, removed, max(unresolved, 1)
+            unresolved = max(unresolved, 1)
 
         ms.commit()
 
+    msupdated, msmerged, msremoved = ms.counts()
+    updated += msupdated
+    merged += msmerged
+    removed += msremoved
+
+    extraactions = ms.actions()
+    for k, acts in extraactions.iteritems():
+        actions[k].extend(acts)
+
     progress(_updating, None, total=numupdates, unit=_files)
 
     return updated, merged, removed, unresolved
@@ -1018,38 +1186,44 @@
 def recordupdates(repo, actions, branchmerge):
     "record merge actions to the dirstate"
     # remove (must come first)
-    for f, args, msg in actions['r']:
+    for f, args, msg in actions.get('r', []):
         if branchmerge:
             repo.dirstate.remove(f)
         else:
             repo.dirstate.drop(f)
 
     # forget (must come first)
-    for f, args, msg in actions['f']:
+    for f, args, msg in actions.get('f', []):
         repo.dirstate.drop(f)
 
     # re-add
-    for f, args, msg in actions['a']:
-        if not branchmerge:
+    for f, args, msg in actions.get('a', []):
+        repo.dirstate.add(f)
+
+    # re-add/mark as modified
+    for f, args, msg in actions.get('am', []):
+        if branchmerge:
+            repo.dirstate.normallookup(f)
+        else:
             repo.dirstate.add(f)
 
     # exec change
-    for f, args, msg in actions['e']:
+    for f, args, msg in actions.get('e', []):
         repo.dirstate.normallookup(f)
 
     # keep
-    for f, args, msg in actions['k']:
+    for f, args, msg in actions.get('k', []):
         pass
 
     # get
-    for f, args, msg in actions['g']:
+    for f, args, msg in actions.get('g', []):
         if branchmerge:
             repo.dirstate.otherparent(f)
         else:
             repo.dirstate.normal(f)
 
     # merge
-    for f, args, msg in actions['m']:
+    for f, args, msg in actions.get('m', []):
         f1, f2, fa, move, anc = args
         if branchmerge:
             # We've done a branch merge, mark this file as merged
@@ -1074,7 +1248,7 @@
                 repo.dirstate.drop(f1)
 
     # directory rename, move local
-    for f, args, msg in actions['dm']:
+    for f, args, msg in actions.get('dm', []):
         f0, flag = args
         if branchmerge:
             repo.dirstate.add(f)
@@ -1085,7 +1259,7 @@
             repo.dirstate.drop(f0)
 
     # directory rename, get
-    for f, args, msg in actions['dg']:
+    for f, args, msg in actions.get('dg', []):
         f0, flag = args
         if branchmerge:
             repo.dirstate.add(f)
@@ -1093,15 +1267,15 @@
         else:
             repo.dirstate.normal(f)
 
-def update(repo, node, branchmerge, force, partial, ancestor=None,
-           mergeancestor=False, labels=None):
+def update(repo, node, branchmerge, force, ancestor=None,
+           mergeancestor=False, labels=None, matcher=None):
     """
     Perform a merge between the working directory and the given node
 
     node = the node to update to, or None if unspecified
     branchmerge = whether to merge between branches
     force = whether to force branch merging or file overwriting
-    partial = a function to filter file lists (dirstate not updated)
+    matcher = a matcher to filter file lists (dirstate not updated)
     mergeancestor = whether it is merging with an ancestor. If true,
       we should accept the incoming changes for any prompts that occur.
       If false, merging with an ancestor (fast-forward) is only allowed
@@ -1140,6 +1314,13 @@
 
     onode = node
     wlock = repo.wlock()
+    # If we're doing a partial update, we need to skip updating
+    # the dirstate, so make a note of any partial-ness to the
+    # update here.
+    if matcher is None or matcher.always():
+        partial = False
+    else:
+        partial = True
     try:
         wc = repo[None]
         pl = wc.parents()
@@ -1168,8 +1349,12 @@
         fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
 
         ### check phase
-        if not overwrite and len(pl) > 1:
-            raise error.Abort(_("outstanding uncommitted merge"))
+        if not overwrite:
+            if len(pl) > 1:
+                raise error.Abort(_("outstanding uncommitted merge"))
+            ms = mergestate.read(repo)
+            if list(ms.unresolved()):
+                raise error.Abort(_("outstanding merge conflicts"))
         if branchmerge:
             if pas == [p2]:
                 raise error.Abort(_("merging with a working directory ancestor"
@@ -1231,10 +1416,10 @@
 
         ### calculate phase
         actionbyfile, diverge, renamedelete = calculateupdates(
-            repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
-            followcopies)
+            repo, wc, p2, pas, branchmerge, force, mergeancestor,
+            followcopies, matcher=matcher)
         # Convert to dictionary-of-lists format
-        actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
+        actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
         for f, (m, args, msg) in actionbyfile.iteritems():
             if m not in actions:
                 actions[m] = []
@@ -1248,35 +1433,32 @@
             else:
                 _checkcollision(repo, wc.manifest(), actions)
 
-        # Prompt and create actions. TODO: Move this towards resolve phase.
+        # Prompt and create actions. Most of this is in the resolve phase
+        # already, but we can't handle .hgsubstate in filemerge or
+        # subrepo.submerge yet so we have to keep prompting for it.
         for f, args, msg in sorted(actions['cd']):
+            if f != '.hgsubstate':
+                continue
             if repo.ui.promptchoice(
                 _("local changed %s which remote deleted\n"
                   "use (c)hanged version or (d)elete?"
                   "$$ &Changed $$ &Delete") % f, 0):
                 actions['r'].append((f, None, "prompt delete"))
+            elif f in p1:
+                actions['am'].append((f, None, "prompt keep"))
             else:
                 actions['a'].append((f, None, "prompt keep"))
-        del actions['cd'][:]
 
         for f, args, msg in sorted(actions['dc']):
-            flags, = args
+            if f != '.hgsubstate':
+                continue
+            f1, f2, fa, move, anc = args
+            flags = p2[f2].flags()
             if repo.ui.promptchoice(
                 _("remote changed %s which local deleted\n"
                   "use (c)hanged version or leave (d)eleted?"
                   "$$ &Changed $$ &Deleted") % f, 0) == 0:
                 actions['g'].append((f, (flags,), "prompt recreating"))
-        del actions['dc'][:]
-
-        ### apply phase
-        if not branchmerge: # just jump to the new rev
-            fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
-        if not partial:
-            repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
-            # note that we're in the middle of an update
-            repo.vfs.write('updatestate', p2.hex())
-
-        stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
 
         # divergent renames
         for f, fl in sorted(diverge.iteritems()):
@@ -1292,6 +1474,16 @@
             for nf in fl:
                 repo.ui.warn(" %s\n" % nf)
 
+        ### apply phase
+        if not branchmerge: # just jump to the new rev
+            fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
+        if not partial:
+            repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
+            # note that we're in the middle of an update
+            repo.vfs.write('updatestate', p2.hex())
+
+        stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
+
         if not partial:
             repo.dirstate.beginparentchange()
             repo.setparents(fp1, fp2)
@@ -1309,18 +1501,19 @@
         repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
     return stats
 
-def graft(repo, ctx, pctx, labels):
+def graft(repo, ctx, pctx, labels, keepparent=False):
     """Do a graft-like merge.
 
     This is a merge where the merge ancestor is chosen such that one
     or more changesets are grafted onto the current changeset. In
     addition to the merge, this fixes up the dirstate to include only
-    a single parent and tries to duplicate any renames/copies
-    appropriately.
+    a single parent (if keepparent is False) and tries to duplicate any
+    renames/copies appropriately.
 
     ctx - changeset to rebase
     pctx - merge base, usually ctx.p1()
     labels - merge labels eg ['local', 'graft']
+    keepparent - keep second parent if any
 
     """
     # If we're grafting a descendant onto an ancestor, be sure to pass
@@ -1331,12 +1524,17 @@
     # which local deleted".
     mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
 
-    stats = update(repo, ctx.node(), True, True, False, pctx.node(),
+    stats = update(repo, ctx.node(), True, True, pctx.node(),
                    mergeancestor=mergeancestor, labels=labels)
 
-    # drop the second merge parent
+    pother = nullid
+    parents = ctx.parents()
+    if keepparent and len(parents) == 2 and pctx in parents:
+        parents.remove(pctx)
+        pother = parents[0].node()
+
     repo.dirstate.beginparentchange()
-    repo.setparents(repo['.'].node(), nullid)
+    repo.setparents(repo['.'].node(), pother)
     repo.dirstate.write(repo.currenttransaction())
     # fix up dirstate for copies and renames
     copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
--- a/mercurial/node.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/node.py	Thu Dec 17 17:27:32 2015 -0600
@@ -9,17 +9,18 @@
 
 import binascii
 
+# This ugly style has a noticeable effect in manifest parsing
+hex = binascii.hexlify
+bin = binascii.unhexlify
+
 nullrev = -1
 nullid = "\0" * 20
+nullhex = hex(nullid)
 
 # pseudo identifiers for working directory
 # (they are experimental, so don't add too many dependencies on them)
 wdirrev = 0x7fffffff
 wdirid = "\xff" * 20
 
-# This ugly style has a noticeable effect in manifest parsing
-hex = binascii.hexlify
-bin = binascii.unhexlify
-
 def short(node):
     return hex(node[:6])
--- a/mercurial/obsolete.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/obsolete.py	Thu Dec 17 17:27:32 2015 -0600
@@ -67,10 +67,20 @@
 comment associated with each format for details.
 
 """
-import errno, struct
-import util, base85, node, parsers, error
-import phases
-from i18n import _
+from __future__ import absolute_import
+
+import errno
+import struct
+
+from .i18n import _
+from . import (
+    base85,
+    error,
+    node,
+    parsers,
+    phases,
+    util,
+)
 
 _pack = struct.pack
 _unpack = struct.unpack
--- a/mercurial/osutil.c	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/osutil.c	Thu Dec 17 17:27:32 2015 -0600
@@ -613,9 +613,14 @@
 		int ret, kind;
 		char *path;
 
+		/* With a large file count or on a slow filesystem,
+		   don't block signals for long (issue4878). */
+		if ((i % 1000) == 999 && PyErr_CheckSignals() == -1)
+			goto bail;
+
 		pypath = PySequence_GetItem(names, i);
 		if (!pypath)
-			return NULL;
+			goto bail;
 		path = PyString_AsString(pypath);
 		if (path == NULL) {
 			Py_DECREF(pypath);
--- a/mercurial/parsers.c	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/parsers.c	Thu Dec 17 17:27:32 2015 -0600
@@ -1286,19 +1286,21 @@
 	long phase;
 
 	if (!PyArg_ParseTuple(args, "O", &roots))
-		goto release_none;
+		goto done;
 	if (roots == NULL || !PyList_Check(roots))
-		goto release_none;
+		goto done;
 
 	phases = calloc(len, 1); /* phase per rev: {0: public, 1: draft, 2: secret} */
-	if (phases == NULL)
-		goto release_none;
+	if (phases == NULL) {
+		PyErr_NoMemory();
+		goto done;
+	}
 	/* Put the phase information of all the roots in phases */
 	numphase = PyList_GET_SIZE(roots)+1;
 	minrevallphases = len + 1;
 	phasessetlist = PyList_New(numphase);
 	if (phasessetlist == NULL)
-		goto release_none;
+		goto done;
 
 	PyList_SET_ITEM(phasessetlist, 0, Py_None);
 	Py_INCREF(Py_None);
@@ -1307,13 +1309,13 @@
 		phaseroots = PyList_GET_ITEM(roots, i);
 		phaseset = PySet_New(NULL);
 		if (phaseset == NULL)
-			goto release_phasesetlist;
+			goto release;
 		PyList_SET_ITEM(phasessetlist, i+1, phaseset);
 		if (!PyList_Check(phaseroots))
-			goto release_phasesetlist;
+			goto release;
 		minrevphase = add_roots_get_min(self, phaseroots, i+1, phases);
 		if (minrevphase == -2) /* Error from add_roots_get_min */
-			goto release_phasesetlist;
+			goto release;
 		minrevallphases = MIN(minrevallphases, minrevphase);
 	}
 	/* Propagate the phase information from the roots to the revs */
@@ -1322,43 +1324,48 @@
 		for (i = minrevallphases; i < len; i++) {
 			if (index_get_parents(self, i, parents,
 					      (int)len - 1) < 0)
-				goto release_phasesetlist;
+				goto release;
 			set_phase_from_parents(phases, parents[0], parents[1], i);
 		}
 	}
 	/* Transform phase list to a python list */
 	phaseslist = PyList_New(len);
 	if (phaseslist == NULL)
-		goto release_phasesetlist;
+		goto release;
 	for (i = 0; i < len; i++) {
+		PyObject *phaseval;
+
 		phase = phases[i];
 		/* We only store the sets of phase for non public phase, the public phase
 		 * is computed as a difference */
 		if (phase != 0) {
 			phaseset = PyList_GET_ITEM(phasessetlist, phase);
 			rev = PyInt_FromLong(i);
+			if (rev == NULL)
+				goto release;
 			PySet_Add(phaseset, rev);
 			Py_XDECREF(rev);
 		}
-		PyList_SET_ITEM(phaseslist, i, PyInt_FromLong(phase));
+		phaseval = PyInt_FromLong(phase);
+		if (phaseval == NULL)
+			goto release;
+		PyList_SET_ITEM(phaseslist, i, phaseval);
 	}
 	ret = PyList_New(2);
 	if (ret == NULL)
-		goto release_phaseslist;
+		goto release;
 
 	PyList_SET_ITEM(ret, 0, phaseslist);
 	PyList_SET_ITEM(ret, 1, phasessetlist);
 	/* We don't release phaseslist and phasessetlist as we return them to
 	 * python */
-	goto release_phases;
-
-release_phaseslist:
+	goto done;
+
+release:
 	Py_XDECREF(phaseslist);
-release_phasesetlist:
 	Py_XDECREF(phasessetlist);
-release_phases:
+done:
 	free(phases);
-release_none:
 	return ret;
 }
 
@@ -1404,8 +1411,10 @@
 	}
 
 	nothead = calloc(len, 1);
-	if (nothead == NULL)
+	if (nothead == NULL) {
+		PyErr_NoMemory();
 		goto bail;
+	}
 
 	for (i = 0; i < len; i++) {
 		int isfiltered;
@@ -1995,19 +2004,19 @@
 
 		for (i = 0; i < 2; i++) {
 			int p = parents[i];
-			long nsp, sp;
+			long sp;
 			int dp;
 
 			if (p == -1)
 				continue;
 
 			dp = depth[p];
-			nsp = sp = seen[p];
+			sp = seen[p];
 			if (dp <= dv) {
 				depth[p] = dv + 1;
 				if (sp != sv) {
 					interesting[sv] += 1;
-					nsp = seen[p] = sv;
+					seen[p] = sv;
 					if (sp) {
 						interesting[sp] -= 1;
 						if (interesting[sp] == 0)
@@ -2016,7 +2025,7 @@
 				}
 			}
 			else if (dv == dp - 1) {
-				nsp = sp | sv;
+				long nsp = sp | sv;
 				if (nsp == sp)
 					continue;
 				seen[p] = nsp;
--- a/mercurial/patch.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/patch.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1106,8 +1106,8 @@
                         applied[newhunk.filename()].append(newhunk)
             else:
                 fixoffset += chunk.removed - chunk.added
-    return sum([h for h in applied.itervalues()
-               if h[0].special() or len(h) > 1], [])
+    return (sum([h for h in applied.itervalues()
+               if h[0].special() or len(h) > 1], []), {})
 class hunk(object):
     def __init__(self, desc, num, lr, context):
         self.number = num
@@ -2146,7 +2146,7 @@
                                             'ignoreblanklines')
     if formatchanging:
         buildopts['text'] = opts and opts.get('text')
-        buildopts['nobinary'] = get('nobinary')
+        buildopts['nobinary'] = get('nobinary', forceplain=False)
         buildopts['noprefix'] = get('noprefix', forceplain=False)
 
     return mdiff.diffopts(**buildopts)
--- a/mercurial/pathencode.c	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/pathencode.c	Thu Dec 17 17:27:32 2015 -0600
@@ -517,8 +517,7 @@
 	newlen = _lowerencode(NULL, 0, path, len);
 	ret = PyString_FromStringAndSize(NULL, newlen);
 	if (ret)
-		newlen = _lowerencode(PyString_AS_STRING(ret), newlen,
-				      path, len);
+		_lowerencode(PyString_AS_STRING(ret), newlen, path, len);
 
 	return ret;
 }
--- a/mercurial/pathutil.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/pathutil.py	Thu Dec 17 17:27:32 2015 -0600
@@ -23,15 +23,22 @@
     - under top-level .hg
     - starts at the root of a windows drive
     - contains ".."
+
+    More check are also done about the file system states:
     - traverses a symlink (e.g. a/symlink_here/b)
     - inside a nested repository (a callback can be used to approve
       some nested repositories, e.g., subrepositories)
+
+    The file system checks are only done when 'realfs' is set to True (the
+    default). They should be disable then we are auditing path for operation on
+    stored history.
     '''
 
-    def __init__(self, root, callback=None):
+    def __init__(self, root, callback=None, realfs=True):
         self.audited = set()
         self.auditeddir = set()
         self.root = root
+        self._realfs = realfs
         self.callback = callback
         if os.path.lexists(root) and not util.checkcase(root):
             self.normcase = util.normcase
@@ -81,25 +88,8 @@
             normprefix = os.sep.join(normparts)
             if normprefix in self.auditeddir:
                 break
-            curpath = os.path.join(self.root, prefix)
-            try:
-                st = os.lstat(curpath)
-            except OSError as err:
-                # EINVAL can be raised as invalid path syntax under win32.
-                # They must be ignored for patterns can be checked too.
-                if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
-                    raise
-            else:
-                if stat.S_ISLNK(st.st_mode):
-                    raise error.Abort(
-                        _('path %r traverses symbolic link %r')
-                        % (path, prefix))
-                elif (stat.S_ISDIR(st.st_mode) and
-                      os.path.isdir(os.path.join(curpath, '.hg'))):
-                    if not self.callback or not self.callback(curpath):
-                        raise error.Abort(_("path '%s' is inside nested "
-                                           "repo %r")
-                                         % (path, prefix))
+            if self._realfs:
+                self._checkfs(prefix, path)
             prefixes.append(normprefix)
             parts.pop()
             normparts.pop()
@@ -109,6 +99,26 @@
         # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
         self.auditeddir.update(prefixes)
 
+    def _checkfs(self, prefix, path):
+        """raise exception if a file system backed check fails"""
+        curpath = os.path.join(self.root, prefix)
+        try:
+            st = os.lstat(curpath)
+        except OSError as err:
+            # EINVAL can be raised as invalid path syntax under win32.
+            # They must be ignored for patterns can be checked too.
+            if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
+                raise
+        else:
+            if stat.S_ISLNK(st.st_mode):
+                msg = _('path %r traverses symbolic link %r') % (path, prefix)
+                raise error.Abort(msg)
+            elif (stat.S_ISDIR(st.st_mode) and
+                  os.path.isdir(os.path.join(curpath, '.hg'))):
+                if not self.callback or not self.callback(curpath):
+                    msg = _("path '%s' is inside nested repo %r")
+                    raise error.Abort(msg % (path, prefix))
+
     def check(self, path):
         try:
             self(path)
--- a/mercurial/phases.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/phases.py	Thu Dec 17 17:27:32 2015 -0600
@@ -308,9 +308,19 @@
                 raise error.Abort(_('cannot change null revision phase'))
             currentroots = currentroots.copy()
             currentroots.update(newroots)
-            ctxs = repo.set('roots(%ln::)', currentroots)
-            currentroots.intersection_update(ctx.node() for ctx in ctxs)
-            self._updateroots(targetphase, currentroots, tr)
+
+            # Only compute new roots for revs above the roots that are being
+            # retracted.
+            minnewroot = min(repo[n].rev() for n in newroots)
+            aboveroots = [n for n in currentroots
+                          if repo[n].rev() >= minnewroot]
+            updatedroots = repo.set('roots(%ln::)', aboveroots)
+
+            finalroots = set(n for n in currentroots if repo[n].rev() <
+                             minnewroot)
+            finalroots.update(ctx.node() for ctx in updatedroots)
+
+            self._updateroots(targetphase, finalroots, tr)
         repo.invalidatevolatilesets()
 
     def filterunknown(self, repo):
--- a/mercurial/posix.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/posix.py	Thu Dec 17 17:27:32 2015 -0600
@@ -15,7 +15,6 @@
 import pwd
 import re
 import select
-import socket
 import stat
 import sys
 import tempfile
@@ -29,7 +28,16 @@
 posixfile = open
 normpath = os.path.normpath
 samestat = os.path.samestat
-oslink = os.link
+try:
+    oslink = os.link
+except AttributeError:
+    # Some platforms build Python without os.link on systems that are
+    # vaguely unix-like but don't have hardlink support. For those
+    # poor souls, just say we tried and that it failed so we fall back
+    # to copies.
+    def oslink(src, dst):
+        raise OSError(errno.EINVAL,
+                      'hardlinks not supported: %s to %s' % (src, dst))
 unlink = os.unlink
 rename = os.rename
 removedirs = os.removedirs
@@ -261,40 +269,17 @@
         except UnicodeDecodeError:
             # OS X percent-encodes any bytes that aren't valid utf-8
             s = ''
-            g = ''
-            l = 0
-            for c in path:
-                o = ord(c)
-                if l and o < 128 or o >= 192:
-                    # we want a continuation byte, but didn't get one
-                    s += ''.join(["%%%02X" % ord(x) for x in g])
-                    g = ''
-                    l = 0
-                if l == 0 and o < 128:
-                    # ascii
-                    s += c
-                elif l == 0 and 194 <= o < 245:
-                    # valid leading bytes
-                    if o < 224:
-                        l = 1
-                    elif o < 240:
-                        l = 2
-                    else:
-                        l = 3
-                    g = c
-                elif l > 0 and 128 <= o < 192:
-                    # valid continuations
-                    g += c
-                    l -= 1
-                    if not l:
-                        s += g
-                        g = ''
-                else:
-                    # invalid
-                    s += "%%%02X" % o
+            pos = 0
+            l = len(path)
+            while pos < l:
+                try:
+                    c = encoding.getutf8char(path, pos)
+                    pos += len(c)
+                except ValueError:
+                    c = '%%%02X' % ord(path[pos])
+                    pos += 1
+                s += c
 
-            # any remaining partial characters
-            s += ''.join(["%%%02X" % ord(x) for x in g])
             u = s.decode('utf-8')
 
         # Decompose then lowercase (HFS+ technote specifies lower)
@@ -569,46 +554,6 @@
 def executablepath():
     return None # available on Windows only
 
-class unixdomainserver(socket.socket):
-    def __init__(self, join, subsystem):
-        '''Create a unix domain socket with the given prefix.'''
-        super(unixdomainserver, self).__init__(socket.AF_UNIX)
-        sockname = subsystem + '.sock'
-        self.realpath = self.path = join(sockname)
-        if os.path.islink(self.path):
-            if os.path.exists(self.path):
-                self.realpath = os.readlink(self.path)
-            else:
-                os.unlink(self.path)
-        try:
-            self.bind(self.realpath)
-        except socket.error as err:
-            if err.args[0] == 'AF_UNIX path too long':
-                tmpdir = tempfile.mkdtemp(prefix='hg-%s-' % subsystem)
-                self.realpath = os.path.join(tmpdir, sockname)
-                try:
-                    self.bind(self.realpath)
-                    os.symlink(self.realpath, self.path)
-                except (OSError, socket.error):
-                    self.cleanup()
-                    raise
-            else:
-                raise
-        self.listen(5)
-
-    def cleanup(self):
-        def okayifmissing(f, path):
-            try:
-                f(path)
-            except OSError as err:
-                if err.errno != errno.ENOENT:
-                    raise
-
-        okayifmissing(os.unlink, self.path)
-        if self.realpath != self.path:
-            okayifmissing(os.unlink, self.realpath)
-            okayifmissing(os.rmdir, os.path.dirname(self.realpath))
-
 def statislink(st):
     '''check whether a stat result is a symlink'''
     return st and stat.S_ISLNK(st.st_mode)
--- a/mercurial/pure/base85.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/pure/base85.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,6 +5,8 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import struct
 
 _b85chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
--- a/mercurial/pure/bdiff.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/pure/bdiff.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,7 +5,11 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import struct, difflib, re
+from __future__ import absolute_import
+
+import difflib
+import re
+import struct
 
 def splitnewlines(text):
     '''like str.splitlines, but only split on newlines.'''
--- a/mercurial/pure/diffhelpers.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/pure/diffhelpers.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,6 +5,8 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 def addlines(fp, hunk, lena, lenb, a, b):
     while True:
         todoa = lena - len(a)
--- a/mercurial/pure/mpatch.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/pure/mpatch.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,11 +5,12 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
+import cStringIO
 import struct
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
+
+StringIO = cStringIO.StringIO
 
 # This attempts to apply a series of patches in time proportional to
 # the total size of the patches, rather than patches * len(text). This
--- a/mercurial/pure/osutil.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/pure/osutil.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,6 +5,8 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import os
 import stat as statmod
 
@@ -58,7 +60,8 @@
 if os.name != 'nt':
     posixfile = open
 else:
-    import ctypes, msvcrt
+    import ctypes
+    import msvcrt
 
     _kernel32 = ctypes.windll.kernel32
 
--- a/mercurial/pure/parsers.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/pure/parsers.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,8 +5,13 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from mercurial.node import nullid
-import struct, zlib, cStringIO
+from __future__ import absolute_import
+
+import cStringIO
+import struct
+import zlib
+
+from .node import nullid
 
 _pack = struct.pack
 _unpack = struct.unpack
--- a/mercurial/repair.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/repair.py	Thu Dec 17 17:27:32 2015 -0600
@@ -203,6 +203,18 @@
                 repo.ui.popbuffer()
             f.close()
 
+        for m in updatebm:
+            bm[m] = repo[newbmtarget].node()
+        lock = tr = None
+        try:
+            lock = repo.lock()
+            tr = repo.transaction('repair')
+            bm.recordchange(tr)
+            tr.close()
+        finally:
+            tr.release()
+            lock.release()
+
         # remove undo files
         for undovfs, undofile in repo.undofiles():
             try:
@@ -212,9 +224,6 @@
                     ui.warn(_('error removing %s: %s\n') %
                             (undovfs.join(undofile), str(e)))
 
-        for m in updatebm:
-            bm[m] = repo[newbmtarget].node()
-        bm.write()
     except: # re-raises
         if backupfile:
             ui.warn(_("strip failed, full bundle stored in '%s'\n")
--- a/mercurial/repoview.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/repoview.py	Thu Dec 17 17:27:32 2015 -0600
@@ -300,22 +300,16 @@
         # some cache may be implemented later
         unfi = self._unfilteredrepo
         unfichangelog = unfi.changelog
+        # bypass call to changelog.method
+        unfiindex = unfichangelog.index
+        unfilen = len(unfiindex) - 1
+        unfinode = unfiindex[unfilen - 1][7]
+
         revs = filterrevs(unfi, self.filtername)
         cl = self._clcache
-        newkey = (len(unfichangelog), unfichangelog.tip(), hash(revs),
-                  unfichangelog._delayed)
-        if cl is not None:
-            # we need to check curkey too for some obscure reason.
-            # MQ test show a corruption of the underlying repo (in _clcache)
-            # without change in the cachekey.
-            oldfilter = cl.filteredrevs
-            try:
-                cl.filteredrevs = ()  # disable filtering for tip
-                curkey = (len(cl), cl.tip(), hash(oldfilter), cl._delayed)
-            finally:
-                cl.filteredrevs = oldfilter
-            if newkey != self._clcachekey or newkey != curkey:
-                cl = None
+        newkey = (unfilen, unfinode, hash(revs), unfichangelog._delayed)
+        if cl is not None and newkey != self._clcachekey:
+            cl = None
         # could have been made None by the previous if
         if cl is None:
             cl = copy.copy(unfichangelog)
--- a/mercurial/revlog.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/revlog.py	Thu Dec 17 17:27:32 2015 -0600
@@ -11,12 +11,29 @@
 and O(changes) merge between branches.
 """
 
-# import stuff from node for others to import from revlog
+from __future__ import absolute_import
+
 import collections
-from node import bin, hex, nullid, nullrev
-from i18n import _
-import ancestor, mdiff, parsers, error, util, templatefilters
-import struct, zlib, errno
+import errno
+import struct
+import zlib
+
+# import stuff from node for others to import from revlog
+from .node import (
+    bin,
+    hex,
+    nullid,
+    nullrev,
+)
+from .i18n import _
+from . import (
+    ancestor,
+    error,
+    mdiff,
+    parsers,
+    templatefilters,
+    util,
+)
 
 _pack = struct.pack
 _unpack = struct.unpack
@@ -205,14 +222,21 @@
         self.indexfile = indexfile
         self.datafile = indexfile[:-2] + ".d"
         self.opener = opener
+        # 3-tuple of (node, rev, text) for a raw revision.
         self._cache = None
+        # 2-tuple of (rev, baserev) defining the base revision the delta chain
+        # begins at for a revision.
         self._basecache = None
+        # 2-tuple of (offset, data) of raw data from the revlog at an offset.
         self._chunkcache = (0, '')
+        # How much data to read and cache into the raw revlog data cache.
         self._chunkcachesize = 65536
         self._maxchainlen = None
         self._aggressivemergedeltas = False
         self.index = []
+        # Mapping of partial identifiers to full nodes.
         self._pcache = {}
+        # Mapping of revision integer to full node.
         self._nodecache = {nullid: nullrev}
         self._nodepos = None
 
@@ -230,6 +254,7 @@
                 self._maxchainlen = opts['maxchainlen']
             if 'aggressivemergedeltas' in opts:
                 self._aggressivemergedeltas = opts['aggressivemergedeltas']
+            self._lazydeltabase = bool(opts.get('lazydeltabase', False))
 
         if self._chunkcachesize <= 0:
             raise RevlogError(_('revlog chunk cache size %r is not greater '
@@ -925,6 +950,10 @@
         return hash(text, p1, p2) != node
 
     def _addchunk(self, offset, data):
+        """Add a segment to the revlog cache.
+
+        Accepts an absolute offset and the data that is at that location.
+        """
         o, d = self._chunkcache
         # try to add to existing cache
         if o + len(d) == offset and len(d) + len(data) < _chunksize:
@@ -933,13 +962,15 @@
             self._chunkcache = offset, data
 
     def _loadchunk(self, offset, length, df=None):
-        """Load a chunk/segment from the revlog.
+        """Load a segment of raw data from the revlog.
 
-        Accepts absolute offset, length to read, and an optional existing
+        Accepts an absolute offset, length to read, and an optional existing
         file handle to read from.
 
         If an existing file handle is passed, it will be seeked and the
         original seek position will NOT be restored.
+
+        Returns a str or buffer of raw byte data.
         """
         if df is not None:
             closehandle = False
@@ -967,6 +998,16 @@
         return d
 
     def _getchunk(self, offset, length, df=None):
+        """Obtain a segment of raw data from the revlog.
+
+        Accepts an absolute offset, length of bytes to obtain, and an
+        optional file handle to the already-opened revlog. If the file
+        handle is used, it's original seek position will not be preserved.
+
+        Requests for data may be returned from a cache.
+
+        Returns a str or a buffer instance of raw byte data.
+        """
         o, d = self._chunkcache
         l = len(d)
 
@@ -981,6 +1022,18 @@
         return self._loadchunk(offset, length, df=df)
 
     def _chunkraw(self, startrev, endrev, df=None):
+        """Obtain a segment of raw data corresponding to a range of revisions.
+
+        Accepts the start and end revisions and an optional already-open
+        file handle to be used for reading. If the file handle is read, its
+        seek position will not be preserved.
+
+        Requests for data may be satisfied by a cache.
+
+        Returns a str or a buffer instance of raw byte data. Callers will
+        need to call ``self.start(rev)`` and ``self.length()`` to determine
+        where each revision's data begins and ends.
+        """
         start = self.start(startrev)
         end = self.end(endrev)
         if self._inline:
@@ -990,12 +1043,29 @@
         return self._getchunk(start, length, df=df)
 
     def _chunk(self, rev, df=None):
+        """Obtain a single decompressed chunk for a revision.
+
+        Accepts an integer revision and an optional already-open file handle
+        to be used for reading. If used, the seek position of the file will not
+        be preserved.
+
+        Returns a str holding uncompressed data for the requested revision.
+        """
         return decompress(self._chunkraw(rev, rev, df=df))
 
     def _chunks(self, revs, df=None):
-        '''faster version of [self._chunk(rev) for rev in revs]
+        """Obtain decompressed chunks for the specified revisions.
 
-        Assumes that revs is in ascending order.'''
+        Accepts an iterable of numeric revisions that are assumed to be in
+        ascending order. Also accepts an optional already-open file handle
+        to be used for reading. If used, the seek position of the file will
+        not be preserved.
+
+        This function is similar to calling ``self._chunk()`` multiple times,
+        but is faster.
+
+        Returns a list with decompressed data for each requested revision.
+        """
         if not revs:
             return []
         start = self.start
@@ -1031,6 +1101,7 @@
         return l
 
     def _chunkclear(self):
+        """Clear the raw chunk cache."""
         self._chunkcache = (0, '')
 
     def deltaparent(self, rev):
@@ -1352,9 +1423,8 @@
         curr = len(self)
         prev = curr - 1
         base = chainbase = curr
-        chainlen = None
         offset = self.end(prev)
-        d = None
+        delta = None
         if self._basecache is None:
             self._basecache = (prev, self.chainbase(prev))
         basecache = self._basecache
@@ -1370,40 +1440,39 @@
 
         # should we try to build a delta?
         if prev != nullrev:
-            if self._generaldelta:
-                if p2r != nullrev and self._aggressivemergedeltas:
-                    d = builddelta(p1r)
-                    d2 = builddelta(p2r)
-                    p1good = self._isgooddelta(d, textlen)
-                    p2good = self._isgooddelta(d2, textlen)
-                    if p1good and p2good:
-                        # If both are good deltas, choose the smallest
-                        if d2[1] < d[1]:
-                            d = d2
-                    elif p2good:
-                        # If only p2 is good, use it
-                        d = d2
-                    elif p1good:
-                        pass
-                    else:
-                        # Neither is good, try against prev to hopefully save us
-                        # a fulltext.
-                        d = builddelta(prev)
-                else:
+            tested = set()
+            if cachedelta and self._generaldelta and self._lazydeltabase:
+                # Assume what we received from the server is a good choice
+                # build delta will reuse the cache
+                candidatedelta = builddelta(cachedelta[0])
+                tested.add(cachedelta[0])
+                if self._isgooddelta(candidatedelta, textlen):
+                    delta = candidatedelta
+            if delta is None and self._generaldelta:
+                # exclude already lazy tested base if any
+                parents = [p for p in (p1r, p2r)
+                           if p != nullrev and p not in tested]
+                if parents and not self._aggressivemergedeltas:
                     # Pick whichever parent is closer to us (to minimize the
-                    # chance of having to build a fulltext). Since
-                    # nullrev == -1, any non-merge commit will always pick p1r.
-                    drev = p2r if p2r > p1r else p1r
-                    d = builddelta(drev)
-                    # If the chosen delta will result in us making a full text,
-                    # give it one last try against prev.
-                    if drev != prev and not self._isgooddelta(d, textlen):
-                        d = builddelta(prev)
-            else:
-                d = builddelta(prev)
-            dist, l, data, base, chainbase, chainlen, compresseddeltalen = d
-
-        if not self._isgooddelta(d, textlen):
+                    # chance of having to build a fulltext).
+                    parents = [max(parents)]
+                tested.update(parents)
+                pdeltas = []
+                for p in parents:
+                    pd = builddelta(p)
+                    if self._isgooddelta(pd, textlen):
+                        pdeltas.append(pd)
+                if pdeltas:
+                    delta = min(pdeltas, key=lambda x: x[1])
+            if delta is None and prev not in tested:
+                # other approach failed try against prev to hopefully save us a
+                # fulltext.
+                candidatedelta = builddelta(prev)
+                if self._isgooddelta(candidatedelta, textlen):
+                    delta = candidatedelta
+        if delta is not None:
+            dist, l, data, base, chainbase, chainlen, compresseddeltalen = delta
+        else:
             text = buildtext()
             data = self.compress(text)
             l = len(data[1]) + len(data[0])
--- a/mercurial/revset.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/revset.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1164,8 +1164,16 @@
     m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
                        exclude=exc, ctx=repo[rev], default=default)
 
+    # This directly read the changelog data as creating changectx for all
+    # revisions is quite expensive.
+    getchangeset = repo.changelog.read
+    wdirrev = node.wdirrev
     def matches(x):
-        for f in repo[x].files():
+        if x == wdirrev:
+            files = repo[x].files()
+        else:
+            files = getchangeset(x)[3]
+        for f in files:
             if m(f):
                 return True
         return False
@@ -1684,7 +1692,7 @@
 
     from . import hg # avoid start-up nasties
     # i18n: "remote" is a keyword
-    l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
+    l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
 
     q = '.'
     if len(l) > 0:
--- a/mercurial/scmutil.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/scmutil.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1170,3 +1170,16 @@
     subprocess."""
     return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args,
                     **kwargs)
+
+def gdinitconfig(ui):
+    """helper function to know if a repo should be created as general delta
+    """
+    # experimental config: format.generaldelta
+    return (ui.configbool('format', 'generaldelta', False)
+            or ui.configbool('format', 'usegeneraldelta', True))
+
+def gddeltaconfig(ui):
+    """helper function to know if incoming delta should be optimised
+    """
+    # experimental config: format.generaldelta
+    return ui.configbool('format', 'generaldelta', False)
--- a/mercurial/similar.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/similar.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,10 +5,14 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import util
-import mdiff
-import bdiff
+from __future__ import absolute_import
+
+from .i18n import _
+from . import (
+    bdiff,
+    mdiff,
+    util,
+)
 
 def _findexactmatches(repo, added, removed):
     '''find renamed files that have no changes
--- a/mercurial/subrepo.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/subrepo.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1910,7 +1910,7 @@
             status = self.status(None)
             names = status.modified
             for name in names:
-                bakname = "%s.orig" % name
+                bakname = cmdutil.origpath(self.ui, self._subparent, name)
                 self.ui.note(_('saving current version of %s as %s\n') %
                         (name, bakname))
                 self.wvfs.rename(name, bakname)
--- a/mercurial/templatefilters.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/templatefilters.py	Thu Dec 17 17:27:32 2015 -0600
@@ -223,7 +223,7 @@
         raise TypeError('cannot encode type %s' % obj.__class__.__name__)
 
 def _uescape(c):
-    if ord(c) < 0x80:
+    if 0x20 <= ord(c) < 0x80:
         return c
     else:
         return '\\u%04x' % ord(c)
--- a/mercurial/templatekw.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/templatekw.py	Thu Dec 17 17:27:32 2015 -0600
@@ -7,7 +7,7 @@
 
 from __future__ import absolute_import
 
-from .node import hex
+from .node import hex, nullid
 from . import (
     error,
     hbisect,
@@ -340,6 +340,21 @@
     """
     return showlist('file', args['ctx'].files(), **args)
 
+def showgraphnode(repo, ctx, **args):
+    """:graphnode: String. The character representing the changeset node in
+    an ASCII revision graph"""
+    wpnodes = repo.dirstate.parents()
+    if wpnodes[1] == nullid:
+        wpnodes = wpnodes[:1]
+    if ctx.node() in wpnodes:
+        return '@'
+    elif ctx.obsolete():
+        return 'x'
+    elif ctx.closesbranch():
+        return '_'
+    else:
+        return 'o'
+
 def showlatesttag(**args):
     """:latesttag: List of strings. The global tags on the most recent globally
     tagged ancestor of this changeset.
@@ -518,6 +533,7 @@
     'file_dels': showfiledels,
     'file_mods': showfilemods,
     'files': showfiles,
+    'graphnode': showgraphnode,
     'latesttag': showlatesttag,
     'latesttagdistance': showlatesttagdistance,
     'manifest': showmanifest,
--- a/mercurial/templater.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/templater.py	Thu Dec 17 17:27:32 2015 -0600
@@ -336,7 +336,7 @@
     specifying files to include or exclude."""
     if len(args) > 2:
         # i18n: "diff" is a keyword
-        raise error.ParseError(_("diff expects one, two or no arguments"))
+        raise error.ParseError(_("diff expects zero, one, or two arguments"))
 
     def getpatterns(i):
         if i < len(args):
--- a/mercurial/templates/monoblue/map	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/templates/monoblue/map	Thu Dec 17 17:27:32 2015 -0600
@@ -23,7 +23,7 @@
 naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
 navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
 navgraphentry = '<a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-filenaventry = '<a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a>'
+filenaventry = '<a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
 filedifflink = '<a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
 filenodelink = '
   <tr class="parity{parity}">
--- a/mercurial/templates/paper/filelog.tmpl	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/templates/paper/filelog.tmpl	Thu Dec 17 17:27:32 2015 -0600
@@ -44,7 +44,10 @@
 
 <div class="main">
 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
-<h3>log {file|escape}</h3>
+<h3>
+ log {file|escape} @ {rev}:<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
+ {branch%changelogbranchname}{tags%changelogtag}{bookmarks%changelogtag}
+</h3>
 
 <form class="search" action="{url|urlescape}log">
 {sessionvars%hiddenformentry}
--- a/mercurial/templates/static/mercurial.js	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/templates/static/mercurial.js	Thu Dec 17 17:27:32 2015 -0600
@@ -50,18 +50,6 @@
 		this.cell_height = this.box_size;
 	}
 
-	function colorPart(num) {
-		num *= 255
-		num = num < 0 ? 0 : num;
-		num = num > 255 ? 255 : num;
-		var digits = Math.round(num).toString(16);
-		if (num < 16) {
-			return '0' + digits;
-		} else {
-			return digits;
-		}
-	}
-
 	this.setColor = function(color, bg, fg) {
 
 		// Set the colour.
--- a/mercurial/ui.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/ui.py	Thu Dec 17 17:27:32 2015 -0600
@@ -95,9 +95,12 @@
     def __init__(self, src=None):
         # _buffers: used for temporary capture of output
         self._buffers = []
-        # _bufferstates:
-        #   should the temporary capture include stderr and subprocess output
+        # 3-tuple describing how each buffer in the stack behaves.
+        # Values are (capture stderr, capture subprocesses, apply labels).
         self._bufferstates = []
+        # When a buffer is active, defines whether we are expanding labels.
+        # This exists to prevent an extra list lookup.
+        self._bufferapplylabels = None
         self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
         self._reportuntrusted = True
         self._ocfg = config.config() # overlay
@@ -278,6 +281,39 @@
                                "%s.%s = %s\n" % (section, n, uvalue))
         return value
 
+    def configsuboptions(self, section, name, default=None, untrusted=False):
+        """Get a config option and all sub-options.
+
+        Some config options have sub-options that are declared with the
+        format "key:opt = value". This method is used to return the main
+        option and all its declared sub-options.
+
+        Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
+        is a dict of defined sub-options where keys and values are strings.
+        """
+        data = self._data(untrusted)
+        main = data.get(section, name, default)
+        if self.debugflag and not untrusted and self._reportuntrusted:
+            uvalue = self._ucfg.get(section, name)
+            if uvalue is not None and uvalue != main:
+                self.debug('ignoring untrusted configuration option '
+                           '%s.%s = %s\n' % (section, name, uvalue))
+
+        sub = {}
+        prefix = '%s:' % name
+        for k, v in data.items(section):
+            if k.startswith(prefix):
+                sub[k[len(prefix):]] = v
+
+        if self.debugflag and not untrusted and self._reportuntrusted:
+            for k, v in sub.items():
+                uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
+                if uvalue is not None and uvalue != v:
+                    self.debug('ignoring untrusted configuration option '
+                               '%s:%s.%s = %s\n' % (section, name, k, uvalue))
+
+        return main, sub
+
     def configpath(self, section, name, default=None, untrusted=False):
         'get a path config item, expanded relative to repo root or config file'
         v = self.config(section, name, default, untrusted)
@@ -475,8 +511,14 @@
         '''tell whether section exists in config.'''
         return section in self._data(untrusted)
 
-    def configitems(self, section, untrusted=False):
+    def configitems(self, section, untrusted=False, ignoresub=False):
         items = self._data(untrusted).items(section)
+        if ignoresub:
+            newitems = {}
+            for k, v in items:
+                if ':' not in k:
+                    newitems[k] = v
+            items = newitems.items()
         if self.debugflag and not untrusted and self._reportuntrusted:
             for k, v in self._ucfg.items(section):
                 if self._tcfg.get(section, k) != v:
@@ -573,18 +615,13 @@
     def paths(self):
         return paths(self)
 
-    def pushbuffer(self, error=False, subproc=False):
+    def pushbuffer(self, error=False, subproc=False, labeled=False):
         """install a buffer to capture standard output of the ui object
 
         If error is True, the error output will be captured too.
 
         If subproc is True, output from subprocesses (typically hooks) will be
-        captured too."""
-        self._buffers.append([])
-        self._bufferstates.append((error, subproc))
-
-    def popbuffer(self, labeled=False):
-        '''pop the last buffer and return the buffered output
+        captured too.
 
         If labeled is True, any labels associated with buffered
         output will be handled. By default, this has no effect
@@ -592,8 +629,19 @@
         handle this argument and returned styled output. If output
         is being buffered so it can be captured and parsed or
         processed, labeled should not be set to True.
-        '''
+        """
+        self._buffers.append([])
+        self._bufferstates.append((error, subproc, labeled))
+        self._bufferapplylabels = labeled
+
+    def popbuffer(self):
+        '''pop the last buffer and return the buffered output'''
         self._bufferstates.pop()
+        if self._bufferstates:
+            self._bufferapplylabels = self._bufferstates[-1][2]
+        else:
+            self._bufferapplylabels = None
+
         return "".join(self._buffers.pop())
 
     def write(self, *args, **opts):
@@ -613,12 +661,12 @@
         "cmdname.type" is recommended. For example, status issues
         a label of "status.modified" for modified files.
         '''
-        self._progclear()
         if self._buffers:
-            self._buffers[-1].extend([str(a) for a in args])
+            self._buffers[-1].extend(a for a in args)
         else:
+            self._progclear()
             for a in args:
-                self.fout.write(str(a))
+                self.fout.write(a)
 
     def write_err(self, *args, **opts):
         self._progclear()
@@ -628,7 +676,7 @@
             if not getattr(self.fout, 'closed', False):
                 self.fout.flush()
             for a in args:
-                self.ferr.write(str(a))
+                self.ferr.write(a)
             # stderr may be buffered under win32 when redirected to files,
             # including stdout.
             if not getattr(self.ferr, 'closed', False):
@@ -757,7 +805,7 @@
                 self.write(r, "\n")
             return r
         except EOFError:
-            raise error.Abort(_('response expected'))
+            raise error.ResponseExpected()
 
     @staticmethod
     def extractchoices(prompt):
@@ -817,7 +865,7 @@
             else:
                 return getpass.getpass('')
         except EOFError:
-            raise error.Abort(_('response expected'))
+            raise error.ResponseExpected()
     def status(self, *msg, **opts):
         '''write status message to output (if ui.quiet is False)
 
@@ -851,10 +899,12 @@
             self.write(*msg, **opts)
 
     def edit(self, text, user, extra=None, editform=None, pending=None):
-        if extra is None:
-            extra = {}
-        (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
-                                      text=True)
+        extra_defaults = { 'prefix': 'editor' }
+        if extra is not None:
+            extra_defaults.update(extra)
+        extra = extra_defaults
+        (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
+                                      suffix=".txt", text=True)
         try:
             f = os.fdopen(fd, "w")
             f.write(text)
@@ -1008,15 +1058,31 @@
         '''
         return msg
 
-    def develwarn(self, msg):
-        """issue a developer warning message"""
+    def develwarn(self, msg, stacklevel=1):
+        """issue a developer warning message
+
+        Use 'stacklevel' to report the offender some layers further up in the
+        stack.
+        """
         msg = 'devel-warn: ' + msg
+        stacklevel += 1 # get in develwarn
         if self.tracebackflag:
-            util.debugstacktrace(msg, 2, self.ferr, self.fout)
+            util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
         else:
             curframe = inspect.currentframe()
             calframe = inspect.getouterframes(curframe, 2)
-            self.write_err('%s at: %s:%s (%s)\n' % ((msg,) + calframe[2][1:4]))
+            self.write_err('%s at: %s:%s (%s)\n'
+                           % ((msg,) + calframe[stacklevel][1:4]))
+
+    def deprecwarn(self, msg, version):
+        """issue a deprecation warning
+
+        - msg: message explaining what is deprecated and how to upgrade,
+        - version: last version where the API will be supported,
+        """
+        msg += ("\n(compatibility will be dropped after Mercurial-%s,"
+                " update your code.)") % version
+        self.develwarn(msg, stacklevel=2)
 
 class paths(dict):
     """Represents a collection of paths and their configs.
@@ -1027,7 +1093,7 @@
     def __init__(self, ui):
         dict.__init__(self)
 
-        for name, loc in ui.configitems('paths'):
+        for name, loc in ui.configitems('paths', ignoresub=True):
             # No location is the same as not existing.
             if not loc:
                 continue
@@ -1035,7 +1101,8 @@
             # TODO ignore default-push once all consumers stop referencing it
             # since it is handled specifically below.
 
-            self[name] = path(name, rawloc=loc)
+            loc, sub = ui.configsuboptions('paths', name)
+            self[name] = path(ui, name, rawloc=loc, suboptions=sub)
 
         # Handle default-push, which is a one-off that defines the push URL for
         # the "default" path.
@@ -1044,8 +1111,8 @@
             # "default-push" can be defined without "default" entry. This is a
             # bit weird, but is allowed for backwards compatibility.
             if 'default' not in self:
-                self['default'] = path('default', rawloc=defaultpush)
-            self['default']._pushloc = defaultpush
+                self['default'] = path(ui, 'default', rawloc=defaultpush)
+            self['default'].pushloc = defaultpush
 
     def getpath(self, name, default=None):
         """Return a ``path`` from a string, falling back to a default.
@@ -1076,19 +1143,59 @@
         except KeyError:
             # Try to resolve as a local path or URI.
             try:
-                return path(None, rawloc=name)
+                # We don't pass sub-options in, so no need to pass ui instance.
+                return path(None, None, rawloc=name)
             except ValueError:
                 raise error.RepoError(_('repository %s does not exist') %
                                         name)
 
         assert False
 
+_pathsuboptions = {}
+
+def pathsuboption(option, attr):
+    """Decorator used to declare a path sub-option.
+
+    Arguments are the sub-option name and the attribute it should set on
+    ``path`` instances.
+
+    The decorated function will receive as arguments a ``ui`` instance,
+    ``path`` instance, and the string value of this option from the config.
+    The function should return the value that will be set on the ``path``
+    instance.
+
+    This decorator can be used to perform additional verification of
+    sub-options and to change the type of sub-options.
+    """
+    def register(func):
+        _pathsuboptions[option] = (attr, func)
+        return func
+    return register
+
+@pathsuboption('pushurl', 'pushloc')
+def pushurlpathoption(ui, path, value):
+    u = util.url(value)
+    # Actually require a URL.
+    if not u.scheme:
+        ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
+        return None
+
+    # Don't support the #foo syntax in the push URL to declare branch to
+    # push.
+    if u.fragment:
+        ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
+                  'ignoring)\n') % path.name)
+        u.fragment = None
+
+    return str(u)
+
 class path(object):
     """Represents an individual path and its configuration."""
 
-    def __init__(self, name, rawloc=None, pushloc=None):
+    def __init__(self, ui, name, rawloc=None, suboptions=None):
         """Construct a path from its config options.
 
+        ``ui`` is the ``ui`` instance the path is coming from.
         ``name`` is the symbolic name of the path.
         ``rawloc`` is the raw location, as defined in the config.
         ``pushloc`` is the raw locations pushes should be made to.
@@ -1113,7 +1220,6 @@
         self.name = name
         self.rawloc = rawloc
         self.loc = str(u)
-        self._pushloc = pushloc
 
         # When given a raw location but not a symbolic name, validate the
         # location is valid.
@@ -1121,6 +1227,19 @@
             raise ValueError('location is not a URL or path to a local '
                              'repo: %s' % rawloc)
 
+        suboptions = suboptions or {}
+
+        # Now process the sub-options. If a sub-option is registered, its
+        # attribute will always be present. The value will be None if there
+        # was no valid sub-option.
+        for suboption, (attr, func) in _pathsuboptions.iteritems():
+            if suboption not in suboptions:
+                setattr(self, attr, None)
+                continue
+
+            value = func(ui, self, suboptions[suboption])
+            setattr(self, attr, value)
+
     def _isvalidlocalpath(self, path):
         """Returns True if the given path is a potentially valid repository.
         This is its own function so that extensions can change the definition of
@@ -1129,8 +1248,17 @@
         return os.path.isdir(os.path.join(path, '.hg'))
 
     @property
-    def pushloc(self):
-        return self._pushloc or self.loc
+    def suboptions(self):
+        """Return sub-options and their values for this path.
+
+        This is intended to be used for presentation purposes.
+        """
+        d = {}
+        for subopt, (attr, _func) in _pathsuboptions.iteritems():
+            value = getattr(self, attr)
+            if value is not None:
+                d[subopt] = value
+        return d
 
 # we instantiate one globally shared progress bar to avoid
 # competing progress bars when multiple UI objects get created
--- a/mercurial/util.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/util.py	Thu Dec 17 17:27:32 2015 -0600
@@ -13,22 +13,47 @@
 hide platform-specific details from the core.
 """
 
-import i18n
-_ = i18n._
-import error, osutil, encoding, parsers
-import errno, shutil, sys, tempfile, traceback
+from __future__ import absolute_import
+
+import bz2
+import calendar
+import collections
+import datetime
+import errno
+import gc
+import hashlib
+import imp
+import os
 import re as remod
-import os, time, datetime, calendar, textwrap, signal, collections
-import stat
-import imp, socket, urllib
-import gc
-import bz2
+import shutil
+import signal
+import socket
+import subprocess
+import sys
+import tempfile
+import textwrap
+import time
+import traceback
+import urllib
 import zlib
 
+from . import (
+    encoding,
+    error,
+    i18n,
+    osutil,
+    parsers,
+)
+
 if os.name == 'nt':
-    import windows as platform
+    from . import windows as platform
 else:
-    import posix as platform
+    from . import posix as platform
+
+md5 = hashlib.md5
+sha1 = hashlib.sha1
+sha512 = hashlib.sha512
+_ = i18n._
 
 cachestat = platform.cachestat
 checkexec = platform.checkexec
@@ -88,58 +113,21 @@
 
 _notset = object()
 
+# disable Python's problematic floating point timestamps (issue4836)
+# (Python hypocritically says you shouldn't change this behavior in
+# libraries, and sure enough Mercurial is not a library.)
+os.stat_float_times(False)
+
 def safehasattr(thing, attr):
     return getattr(thing, attr, _notset) is not _notset
 
-def sha1(s=''):
-    '''
-    Low-overhead wrapper around Python's SHA support
-
-    >>> f = _fastsha1
-    >>> a = sha1()
-    >>> a = f()
-    >>> a.hexdigest()
-    'da39a3ee5e6b4b0d3255bfef95601890afd80709'
-    '''
-
-    return _fastsha1(s)
-
-def _fastsha1(s=''):
-    # This function will import sha1 from hashlib or sha (whichever is
-    # available) and overwrite itself with it on the first call.
-    # Subsequent calls will go directly to the imported function.
-    if sys.version_info >= (2, 5):
-        from hashlib import sha1 as _sha1
-    else:
-        from sha import sha as _sha1
-    global _fastsha1, sha1
-    _fastsha1 = sha1 = _sha1
-    return _sha1(s)
-
-def md5(s=''):
-    try:
-        from hashlib import md5 as _md5
-    except ImportError:
-        from md5 import md5 as _md5
-    global md5
-    md5 = _md5
-    return _md5(s)
-
 DIGESTS = {
     'md5': md5,
     'sha1': sha1,
+    'sha512': sha512,
 }
 # List of digest types from strongest to weakest
-DIGESTS_BY_STRENGTH = ['sha1', 'md5']
-
-try:
-    import hashlib
-    DIGESTS.update({
-        'sha512': hashlib.sha512,
-    })
-    DIGESTS_BY_STRENGTH.insert(0, 'sha512')
-except ImportError:
-    pass
+DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
 
 for k in DIGESTS_BY_STRENGTH:
     assert k in DIGESTS
@@ -233,7 +221,6 @@
         def buffer(sliceable, offset=0):
             return memoryview(sliceable)[offset:]
 
-import subprocess
 closefds = os.name == 'posix'
 
 _chunksize = 4096
@@ -359,11 +346,64 @@
 def version():
     """Return version information if available."""
     try:
-        import __version__
+        from . import __version__
         return __version__.version
     except ImportError:
         return 'unknown'
 
+def versiontuple(v=None, n=4):
+    """Parses a Mercurial version string into an N-tuple.
+
+    The version string to be parsed is specified with the ``v`` argument.
+    If it isn't defined, the current Mercurial version string will be parsed.
+
+    ``n`` can be 2, 3, or 4. Here is how some version strings map to
+    returned values:
+
+    >>> v = '3.6.1+190-df9b73d2d444'
+    >>> versiontuple(v, 2)
+    (3, 6)
+    >>> versiontuple(v, 3)
+    (3, 6, 1)
+    >>> versiontuple(v, 4)
+    (3, 6, 1, '190-df9b73d2d444')
+
+    >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
+    (3, 6, 1, '190-df9b73d2d444+20151118')
+
+    >>> v = '3.6'
+    >>> versiontuple(v, 2)
+    (3, 6)
+    >>> versiontuple(v, 3)
+    (3, 6, None)
+    >>> versiontuple(v, 4)
+    (3, 6, None, None)
+    """
+    if not v:
+        v = version()
+    parts = v.split('+', 1)
+    if len(parts) == 1:
+        vparts, extra = parts[0], None
+    else:
+        vparts, extra = parts
+
+    vints = []
+    for i in vparts.split('.'):
+        try:
+            vints.append(int(i))
+        except ValueError:
+            break
+    # (3, 6) -> (3, 6, None)
+    while len(vints) < 3:
+        vints.append(None)
+
+    if n == 2:
+        return (vints[0], vints[1])
+    if n == 3:
+        return (vints[0], vints[1], vints[2])
+    if n == 4:
+        return (vints[0], vints[1], vints[2], extra)
+
 # used by parsedate
 defaultdateformats = (
     '%Y-%m-%d %H:%M:%S',
@@ -470,34 +510,178 @@
         self._list.insert(index, key)
         dict.__setitem__(self, key, val)
 
+class _lrucachenode(object):
+    """A node in a doubly linked list.
+
+    Holds a reference to nodes on either side as well as a key-value
+    pair for the dictionary entry.
+    """
+    __slots__ = ('next', 'prev', 'key', 'value')
+
+    def __init__(self):
+        self.next = None
+        self.prev = None
+
+        self.key = _notset
+        self.value = None
+
+    def markempty(self):
+        """Mark the node as emptied."""
+        self.key = _notset
+
 class lrucachedict(object):
-    '''cache most recent gets from or sets to this dictionary'''
-    def __init__(self, maxsize):
+    """Dict that caches most recent accesses and sets.
+
+    The dict consists of an actual backing dict - indexed by original
+    key - and a doubly linked circular list defining the order of entries in
+    the cache.
+
+    The head node is the newest entry in the cache. If the cache is full,
+    we recycle head.prev and make it the new head. Cache accesses result in
+    the node being moved to before the existing head and being marked as the
+    new head node.
+
+    NOTE: construction of this class doesn't scale well if the cache size
+    is in the thousands. Avoid creating hundreds or thousands of instances
+    with large capacities.
+    """
+    def __init__(self, max):
         self._cache = {}
-        self._maxsize = maxsize
-        self._order = collections.deque()
+
+        self._head = head = _lrucachenode()
+        head.prev = head
+        head.next = head
+        self._size = 1
+        self._capacity = max
+
+    def __len__(self):
+        return len(self._cache)
+
+    def __contains__(self, k):
+        return k in self._cache
 
-    def __getitem__(self, key):
-        value = self._cache[key]
-        self._order.remove(key)
-        self._order.append(key)
-        return value
+    def __iter__(self):
+        # We don't have to iterate in cache order, but why not.
+        n = self._head
+        for i in range(len(self._cache)):
+            yield n.key
+            n = n.next
+
+    def __getitem__(self, k):
+        node = self._cache[k]
+        self._movetohead(node)
+        return node.value
+
+    def __setitem__(self, k, v):
+        node = self._cache.get(k)
+        # Replace existing value and mark as newest.
+        if node is not None:
+            node.value = v
+            self._movetohead(node)
+            return
+
+        if self._size < self._capacity:
+            node = self._addcapacity()
+        else:
+            # Grab the last/oldest item.
+            node = self._head.prev
 
-    def __setitem__(self, key, value):
-        if key not in self._cache:
-            if len(self._cache) >= self._maxsize:
-                del self._cache[self._order.popleft()]
-        else:
-            self._order.remove(key)
-        self._cache[key] = value
-        self._order.append(key)
+        # At capacity. Kill the old entry.
+        if node.key is not _notset:
+            del self._cache[node.key]
+
+        node.key = k
+        node.value = v
+        self._cache[k] = node
+        # And mark it as newest entry. No need to adjust order since it
+        # is already self._head.prev.
+        self._head = node
 
-    def __contains__(self, key):
-        return key in self._cache
+    def __delitem__(self, k):
+        node = self._cache.pop(k)
+        node.markempty()
+
+        # Temporarily mark as newest item before re-adjusting head to make
+        # this node the oldest item.
+        self._movetohead(node)
+        self._head = node.next
+
+    # Additional dict methods.
+
+    def get(self, k, default=None):
+        try:
+            return self._cache[k]
+        except KeyError:
+            return default
 
     def clear(self):
+        n = self._head
+        while n.key is not _notset:
+            n.markempty()
+            n = n.next
+
         self._cache.clear()
-        self._order = collections.deque()
+
+    def _movetohead(self, node):
+        """Mark a node as the newest, making it the new head.
+
+        When a node is accessed, it becomes the freshest entry in the LRU
+        list, which is denoted by self._head.
+
+        Visually, let's make ``N`` the new head node (* denotes head):
+
+            previous/oldest <-> head <-> next/next newest
+
+            ----<->--- A* ---<->-----
+            |                       |
+            E <-> D <-> N <-> C <-> B
+
+        To:
+
+            ----<->--- N* ---<->-----
+            |                       |
+            E <-> D <-> C <-> B <-> A
+
+        This requires the following moves:
+
+           C.next = D  (node.prev.next = node.next)
+           D.prev = C  (node.next.prev = node.prev)
+           E.next = N  (head.prev.next = node)
+           N.prev = E  (node.prev = head.prev)
+           N.next = A  (node.next = head)
+           A.prev = N  (head.prev = node)
+        """
+        head = self._head
+        # C.next = D
+        node.prev.next = node.next
+        # D.prev = C
+        node.next.prev = node.prev
+        # N.prev = E
+        node.prev = head.prev
+        # N.next = A
+        # It is tempting to do just "head" here, however if node is
+        # adjacent to head, this will do bad things.
+        node.next = head.prev.next
+        # E.next = N
+        node.next.prev = node
+        # A.prev = N
+        node.prev.next = node
+
+        self._head = node
+
+    def _addcapacity(self):
+        """Add a node to the circular linked list.
+
+        The new node is inserted before the head node.
+        """
+        head = self._head
+        node = _lrucachenode()
+        head.prev.next = node
+        node.prev = head.prev
+        node.next = head
+        head.prev = node
+        self._size += 1
+        return node
 
 def lrucachefunc(func):
     '''cache most recent results of function calls'''
@@ -960,20 +1144,6 @@
     except AttributeError:
         return os.stat(fp.name)
 
-def statmtimesec(st):
-    """Get mtime as integer of seconds
-
-    'int(st.st_mtime)' cannot be used because st.st_mtime is computed as
-    'sec + 1e-9 * nsec' and double-precision floating-point type is too narrow
-    to represent nanoseconds. If 'nsec' is close to 1 sec, 'int(st.st_mtime)'
-    can be 'sec + 1'. (issue4836)
-    """
-    try:
-        return st[stat.ST_MTIME]
-    except (TypeError, IndexError):
-        # osutil.stat doesn't allow index access and its st_mtime is int
-        return st.st_mtime
-
 # File system features
 
 def checkcase(path):
@@ -1416,9 +1586,10 @@
     if "%1" in format or "%2" in format or "%z" in format:
         sign = (tz > 0) and "-" or "+"
         minutes = abs(tz) // 60
+        q, r = divmod(minutes, 60)
         format = format.replace("%z", "%1%2")
-        format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
-        format = format.replace("%2", "%02d" % (minutes % 60))
+        format = format.replace("%1", "%c%02d" % (sign, q))
+        format = format.replace("%2", "%02d" % r)
     try:
         t = time.gmtime(float(t) - tz)
     except ValueError:
@@ -2288,9 +2459,9 @@
     u.user = u.passwd = None
     return str(u)
 
-def isatty(fd):
+def isatty(fp):
     try:
-        return fd.isatty()
+        return fp.isatty()
     except AttributeError:
         return False
 
--- a/mercurial/verify.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/verify.py	Thu Dec 17 17:27:32 2015 -0600
@@ -35,6 +35,17 @@
         f = f.replace('//', '/')
     return f
 
+def _validpath(repo, path):
+    """Returns False if a path should NOT be treated as part of a repo.
+
+    For all in-core cases, this returns True, as we have no way for a
+    path to be mentioned in the history but not actually be
+    relevant. For narrow clones, this is important because many
+    filelogs will be missing, and changelog entries may mention
+    modified files that are outside the narrow scope.
+    """
+    return True
+
 def _verify(repo):
     repo = repo.unfiltered()
     mflinkrevs = {}
@@ -154,7 +165,8 @@
                 mflinkrevs.setdefault(changes[0], []).append(i)
                 refersmf = True
             for f in changes[3]:
-                filelinkrevs.setdefault(_normpath(f), []).append(i)
+                if _validpath(repo, f):
+                    filelinkrevs.setdefault(_normpath(f), []).append(i)
         except Exception as inst:
             refersmf = True
             exc(i, _("unpacking changeset %s") % short(n), inst)
@@ -181,7 +193,9 @@
                 if not f:
                     err(lr, _("file without name in manifest"))
                 elif f != "/dev/null": # ignore this in very old repos
-                    filenodes.setdefault(_normpath(f), {}).setdefault(fn, lr)
+                    if _validpath(repo, f):
+                        filenodes.setdefault(
+                            _normpath(f), {}).setdefault(fn, lr)
         except Exception as inst:
             exc(lr, _("reading manifest delta %s") % short(n), inst)
     ui.progress(_('checking'), None)
--- a/mercurial/windows.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/windows.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,11 +5,23 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import osutil, encoding
-import errno, msvcrt, os, re, stat, sys, _winreg
+from __future__ import absolute_import
 
+import _winreg
+import errno
+import msvcrt
+import os
+import re
+import stat
+import sys
 import win32
+
+from .i18n import _
+from . import (
+    encoding,
+    osutil,
+)
+
 executablepath = win32.executablepath
 getuser = win32.getuser
 hidewindow = win32.hidewindow
--- a/mercurial/wireproto.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/mercurial/wireproto.py	Thu Dec 17 17:27:32 2015 -0600
@@ -30,6 +30,10 @@
     util,
 )
 
+bundle2required = _(
+    'incompatible Mercurial client; bundle2 required\n'
+    '(see https://www.mercurial-scm.org/wiki/IncompatibleClient)\n')
+
 class abstractserverproto(object):
     """abstract class that summarizes the protocol API
 
@@ -166,7 +170,13 @@
 # client side
 
 class wirepeer(peer.peerrepository):
+    """Client-side interface for communicating with a peer repository.
 
+    Methods commonly call wire protocol commands of the same name.
+
+    See also httppeer.py and sshpeer.py for protocol-specific
+    implementations of this interface.
+    """
     def batch(self):
         if self.capable('batch'):
             return remotebatch(self)
@@ -481,6 +491,14 @@
                          % (cmd, ",".join(others)))
     return opts
 
+def bundle1allowed(ui, action):
+    """Whether a bundle1 operation is allowed from the server."""
+    v = ui.configbool('server', 'bundle1.%s' % action, None)
+    if v is not None:
+        return v
+
+    return ui.configbool('server', 'bundle1', True)
+
 # list of commands
 commands = {}
 
@@ -585,7 +603,7 @@
             caps.append('stream')
         # otherwise, add 'streamreqs' detailing our local revlog format
         else:
-            caps.append('streamreqs=%s' % ','.join(requiredformats))
+            caps.append('streamreqs=%s' % ','.join(sorted(requiredformats)))
     if repo.ui.configbool('experimental', 'bundle2-advertise', True):
         capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
         caps.append('bundle2=' + urllib.quote(capsblob))
@@ -646,6 +664,11 @@
         elif keytype != 'plain':
             raise KeyError('unknown getbundle option type %s'
                            % keytype)
+
+    if not bundle1allowed(repo.ui, 'pull'):
+        if not exchange.bundle2requested(opts.get('bundlecaps')):
+            return ooberror(bundle2required)
+
     cg = exchange.getbundle(repo, 'serve', **opts)
     return streamres(proto.groupchunks(cg))
 
@@ -757,6 +780,10 @@
             proto.getfile(fp)
             fp.seek(0)
             gen = exchange.readbundle(repo.ui, fp, None)
+            if (isinstance(gen, changegroupmod.cg1unpacker)
+                and not bundle1allowed(repo.ui, 'push')):
+                return ooberror(bundle2required)
+
             r = exchange.unbundle(repo, gen, their_heads, 'serve',
                                   proto._client())
             if util.safehasattr(r, 'addpart'):
--- a/setup.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/setup.py	Thu Dec 17 17:27:32 2015 -0600
@@ -9,22 +9,14 @@
     raise SystemExit("Mercurial requires Python 2.6 or later.")
 
 if sys.version_info[0] >= 3:
-    def b(s):
-        '''A helper function to emulate 2.6+ bytes literals using string
-        literals.'''
-        return s.encode('latin1')
     printf = eval('print')
     libdir_escape = 'unicode_escape'
 else:
     libdir_escape = 'string_escape'
-    def b(s):
-        '''A helper function to emulate 2.6+ bytes literals using string
-        literals.'''
-        return s
     def printf(*args, **kwargs):
         f = kwargs.get('file', sys.stdout)
         end = kwargs.get('end', '\n')
-        f.write(b(' ').join(args) + end)
+        f.write(b' '.join(args) + end)
 
 # Solaris Python packaging brain damage
 try:
@@ -79,11 +71,16 @@
 from distutils.command.build import build
 from distutils.command.build_ext import build_ext
 from distutils.command.build_py import build_py
+from distutils.command.build_scripts import build_scripts
 from distutils.command.install_lib import install_lib
 from distutils.command.install_scripts import install_scripts
 from distutils.spawn import spawn, find_executable
 from distutils import file_util
-from distutils.errors import CCompilerError, DistutilsExecError
+from distutils.errors import (
+    CCompilerError,
+    DistutilsError,
+    DistutilsExecError,
+)
 from distutils.sysconfig import get_python_inc, get_config_var
 from distutils.version import StrictVersion
 
@@ -102,6 +99,7 @@
 
 scripts = ['hg']
 if os.name == 'nt':
+    # We remove hg.bat if we are able to build hg.exe.
     scripts.append('contrib/win32/hg.bat')
 
 # simplified version of distutils.ccompiler.CCompiler.has_function
@@ -166,22 +164,20 @@
     # fine, we don't want to load it anyway.  Python may warn about
     # a missing __init__.py in mercurial/locale, we also ignore that.
     err = [e for e in err.splitlines()
-           if not e.startswith(b('not trusting file')) \
-              and not e.startswith(b('warning: Not importing')) \
-              and not e.startswith(b('obsolete feature not enabled'))]
+           if not e.startswith(b'not trusting file') \
+              and not e.startswith(b'warning: Not importing') \
+              and not e.startswith(b'obsolete feature not enabled')]
     if err:
         printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
-        printf(b('\n').join([b('  ') + e for e in err]), file=sys.stderr)
+        printf(b'\n'.join([b'  ' + e for e in err]), file=sys.stderr)
         return ''
     return out
 
 version = ''
 
-# Execute hg out of this directory with a custom environment which
-# includes the pure Python modules in mercurial/pure. We also take
-# care to not use any hgrc files and do no localization.
-pypath = ['mercurial', os.path.join('mercurial', 'pure')]
-env = {'PYTHONPATH': os.pathsep.join(pypath),
+# Execute hg out of this directory with a custom environment which takes care
+# to not use any hgrc files and do no localization.
+env = {'HGMODULEPOLICY': 'py',
        'HGRCPATH': '',
        'LANGUAGE': 'C'}
 if 'LD_LIBRARY_PATH' in os.environ:
@@ -306,6 +302,31 @@
             log.warn("Failed to build optional extension '%s' (skipping)",
                      ext.name)
 
+class hgbuildscripts(build_scripts):
+    def run(self):
+        if os.name != 'nt':
+            return build_scripts.run(self)
+
+        exebuilt = False
+        try:
+            self.run_command('build_hgexe')
+            exebuilt = True
+        except (DistutilsError, CCompilerError):
+            log.warn('failed to build optional hg.exe')
+
+        if exebuilt:
+            # Copying hg.exe to the scripts build directory ensures it is
+            # installed by the install_scripts command.
+            hgexecommand = self.get_finalized_command('build_hgexe')
+            dest = os.path.join(self.build_dir, 'hg.exe')
+            self.mkpath(self.build_dir)
+            self.copy_file(hgexecommand.hgexepath, dest)
+
+            # Remove hg.bat because it is redundant with hg.exe.
+            self.scripts.remove('contrib/win32/hg.bat')
+
+        return build_scripts.run(self)
+
 class hgbuildpy(build_py):
     if convert2to3:
         fixer_names = sorted(set(getfixers("lib2to3.fixes") +
@@ -315,11 +336,6 @@
         build_py.finalize_options(self)
 
         if self.distribution.pure:
-            if self.py_modules is None:
-                self.py_modules = []
-            for ext in self.distribution.ext_modules:
-                if ext.name.startswith("mercurial."):
-                    self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
             self.distribution.ext_modules = []
         else:
             h = os.path.join(get_python_inc(), 'Python.h')
@@ -327,14 +343,21 @@
                 raise SystemExit('Python headers are required to build '
                                  'Mercurial but weren\'t found in %s' % h)
 
-    def find_modules(self):
-        modules = build_py.find_modules(self)
-        for module in modules:
-            if module[0] == "mercurial.pure":
-                if module[1] != "__init__":
-                    yield ("mercurial", module[1], module[2])
+    def copy_file(self, *args, **kwargs):
+        dst, copied = build_py.copy_file(self, *args, **kwargs)
+
+        if copied and dst.endswith('__init__.py'):
+            if self.distribution.pure:
+                modulepolicy = 'py'
             else:
-                yield module
+                modulepolicy = 'c'
+            content = open(dst, 'rb').read()
+            content = content.replace(b'@MODULELOADPOLICY@',
+                                      modulepolicy.encode(libdir_escape))
+            with open(dst, 'wb') as fh:
+                fh.write(content)
+
+        return dst, copied
 
 class buildhgextindex(Command):
     description = 'generate prebuilt index of hgext (for frozen package)'
@@ -389,6 +412,11 @@
                                       libraries=[],
                                       output_dir=self.build_temp)
 
+    @property
+    def hgexepath(self):
+        dir = os.path.dirname(self.get_ext_fullpath('dummy'))
+        return os.path.join(self.build_temp, dir, 'hg.exe')
+
 class hginstalllib(install_lib):
     '''
     This is a specialization of install_lib that replaces the copy_file used
@@ -443,6 +471,25 @@
     def run(self):
         install_scripts.run(self)
 
+        # It only makes sense to replace @LIBDIR@ with the install path if
+        # the install path is known. For wheels, the logic below calculates
+        # the libdir to be "../..". This is because the internal layout of a
+        # wheel archive looks like:
+        #
+        #   mercurial-3.6.1.data/scripts/hg
+        #   mercurial/__init__.py
+        #
+        # When installing wheels, the subdirectories of the "<pkg>.data"
+        # directory are translated to system local paths and files therein
+        # are copied in place. The mercurial/* files are installed into the
+        # site-packages directory. However, the site-packages directory
+        # isn't known until wheel install time. This means we have no clue
+        # at wheel generation time what the installed site-packages directory
+        # will be. And, wheels don't appear to provide the ability to register
+        # custom code to run during wheel installation. This all means that
+        # we can't reliably set the libdir in wheels: the default behavior
+        # of looking in sys.path must do.
+
         if (os.path.splitdrive(self.install_dir)[0] !=
             os.path.splitdrive(self.install_lib)[0]):
             # can't make relative paths from one drive to another, so use an
@@ -461,10 +508,18 @@
             fp.close()
 
             # skip binary files
-            if b('\0') in data:
+            if b'\0' in data:
                 continue
 
-            data = data.replace(b('@LIBDIR@'), libdir.encode(libdir_escape))
+            # During local installs, the shebang will be rewritten to the final
+            # install path. During wheel packaging, the shebang has a special
+            # value.
+            if data.startswith(b'#!python'):
+                log.info('not rewriting @LIBDIR@ in %s because install path '
+                         'not known' % outfile)
+                continue
+
+            data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
             fp = open(outfile, 'wb')
             fp.write(data)
             fp.close()
@@ -473,6 +528,7 @@
             'build_mo': hgbuildmo,
             'build_ext': hgbuildext,
             'build_py': hgbuildpy,
+            'build_scripts': hgbuildscripts,
             'build_hgextindex': buildhgextindex,
             'install_lib': hginstalllib,
             'install_scripts': hginstallscripts,
@@ -480,11 +536,10 @@
             }
 
 packages = ['mercurial', 'mercurial.hgweb', 'mercurial.httpclient',
+            'mercurial.pure',
             'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf',
             'hgext.largefiles']
 
-pymodules = []
-
 common_depends = ['mercurial/util.h']
 
 osutil_ldflags = []
@@ -536,6 +591,7 @@
 
 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
                              'help/*.txt',
+                             'help/internals/*.txt',
                              'default.d/*.rc',
                              'dummycert.pem']}
 
@@ -636,7 +692,6 @@
       ],
       scripts=scripts,
       packages=packages,
-      py_modules=pymodules,
       ext_modules=extmodules,
       data_files=datafiles,
       package_data=packagedata,
--- a/tests/autodiff.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/autodiff.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,7 +1,13 @@
 # Extension dedicated to test patch.diff() upgrade modes
-#
-#
-from mercurial import cmdutil, scmutil, patch, error
+
+from __future__ import absolute_import
+
+from mercurial import (
+    cmdutil,
+    error,
+    patch,
+    scmutil,
+)
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
--- a/tests/dumbhttp.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/dumbhttp.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,13 +1,22 @@
 #!/usr/bin/env python
 
+from __future__ import absolute_import
+
 """
 Small and dumb HTTP server for use in tests.
 """
 
-from optparse import OptionParser
-import BaseHTTPServer, SimpleHTTPServer, signal, sys
+import optparse
+import BaseHTTPServer
+import signal
+import SimpleHTTPServer
+import sys
 
-from mercurial import cmdutil
+from mercurial import (
+    cmdutil,
+)
+
+OptionParser = optparse.OptionParser
 
 class simplehttpservice(object):
     def __init__(self, host, port):
--- a/tests/f	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/f	Thu Dec 17 17:27:32 2015 -0600
@@ -44,7 +44,7 @@
             if opts.type:
                 facts.append('file')
             if opts.hexdump or opts.dump or opts.md5:
-                content = file(f).read()
+                content = file(f, 'rb').read()
         elif islink:
             if opts.type:
                 facts.append('link')
--- a/tests/fakedirstatewritetime.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/fakedirstatewritetime.py	Thu Dec 17 17:27:32 2015 -0600
@@ -5,7 +5,15 @@
 #   - 'workingctx._checklookup()' (= 'repo.status()')
 #   - 'committablectx.markcommitted()'
 
-from mercurial import context, dirstate, extensions, parsers, util
+from __future__ import absolute_import
+
+from mercurial import (
+    context,
+    dirstate,
+    extensions,
+    parsers,
+    util,
+)
 
 def pack_dirstate(fakenow, orig, dmap, copymap, pl, now):
     # execute what original parsers.pack_dirstate should do actually
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/fakemergerecord.py	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,25 @@
+# Extension to write out fake unsupported records into the merge state
+#
+#
+
+from __future__ import absolute_import
+
+from mercurial import (
+    cmdutil,
+    merge,
+)
+
+cmdtable = {}
+command = cmdutil.command(cmdtable)
+
+@command('fakemergerecord',
+         [('X', 'mandatory', None, 'add a fake mandatory record'),
+          ('x', 'advisory', None, 'add a fake advisory record')], '')
+def fakemergerecord(ui, repo, *pats, **opts):
+    ms = merge.mergestate.read(repo)
+    records = ms._makerecords()
+    if opts.get('mandatory'):
+        records.append(('X', 'mandatory record'))
+    if opts.get('advisory'):
+        records.append(('x', 'advisory record'))
+    ms._writerecords(records)
--- a/tests/fakepatchtime.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/fakepatchtime.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,7 +1,13 @@
 # extension to emulate invoking 'patch.internalpatch()' at the time
 # specified by '[fakepatchtime] fakenow'
 
-from mercurial import extensions, patch as patchmod, util
+from __future__ import absolute_import
+
+from mercurial import (
+    extensions,
+    patch as patchmod,
+    util,
+)
 
 def internalpatch(orig, ui, repo, patchobj, strip,
                   prefix='', files=None,
--- a/tests/filterpyflakes.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/filterpyflakes.py	Thu Dec 17 17:27:32 2015 -0600
@@ -2,7 +2,10 @@
 
 # Filter output by pyflakes to control which warnings we check
 
-import sys, re
+from __future__ import absolute_import
+
+import re
+import sys
 
 def makekey(typeandline):
     """
--- a/tests/generate-working-copy-states.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/generate-working-copy-states.py	Thu Dec 17 17:27:32 2015 -0600
@@ -29,8 +29,10 @@
 # $ hg forget *_*_*-untracked
 # $ rm *_*_missing-*
 
+from __future__ import absolute_import
+
+import os
 import sys
-import os
 
 # Generates pairs of (filename, contents), where 'contents' is a list
 # describing the file's content at each revision (or in the working copy).
--- a/tests/get-with-headers.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/get-with-headers.py	Thu Dec 17 17:27:32 2015 -0600
@@ -3,18 +3,15 @@
 """This does HTTP GET requests given a host:port and path and returns
 a subset of the headers plus the body of the result."""
 
-import httplib, sys
+from __future__ import absolute_import
+
+import httplib
+import json
+import os
+import sys
 
 try:
-    import json
-except ImportError:
-    try:
-        import simplejson as json
-    except ImportError:
-        json = None
-
-try:
-    import msvcrt, os
+    import msvcrt
     msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
     msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
 except ImportError:
@@ -58,11 +55,6 @@
         # Pretty print JSON. This also has the beneficial side-effect
         # of verifying emitted JSON is well-formed.
         if formatjson:
-            if not json:
-                print 'no json module not available'
-                print 'did you forget a #require json?'
-                sys.exit(1)
-
             # json.dumps() will print trailing newlines. Eliminate them
             # to make tests easier to write.
             data = json.loads(data)
--- a/tests/heredoctest.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/heredoctest.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+
 import sys
 
 globalvars = {}
--- a/tests/hghave.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/hghave.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+
 import errno
 import os
 import re
@@ -68,7 +70,7 @@
         sys.exit(1)
 
 def matchoutput(cmd, regexp, ignorestatus=False):
-    """Return True if cmd executes successfully and its output
+    """Return the match object if cmd executes successfully and its output
     is matched by the supplied regular expression.
     """
     r = re.compile(regexp)
@@ -463,3 +465,12 @@
 @check("slow", "allow slow tests")
 def has_slow():
     return os.environ.get('HGTEST_SLOW') == 'slow'
+
+@check("hypothesis", "is Hypothesis installed")
+def has_hypothesis():
+    try:
+        import hypothesis
+        hypothesis.given
+        return True
+    except ImportError:
+        return False
--- a/tests/hgweberror.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/hgweberror.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,6 +1,10 @@
 # A dummy extension that installs an hgweb command that throws an Exception.
 
-from mercurial.hgweb import webcommands
+from __future__ import absolute_import
+
+from mercurial.hgweb import (
+    webcommands,
+)
 
 def raiseerror(web, req, tmpl):
     '''Dummy web command that raises an uncaught Exception.'''
--- a/tests/histedit-helpers.sh	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/histedit-helpers.sh	Thu Dec 17 17:27:32 2015 -0600
@@ -2,5 +2,6 @@
     grep -v 'saving bundle' | grep -v 'saved backup' | \
         grep -v added | grep -v adding | \
         grep -v "unable to find 'e' for patching" | \
-        grep -v "e: No such file or directory"
+        grep -v "e: No such file or directory" | \
+    cat
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/hypothesishelpers.py	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,62 @@
+# Helper module to use the Hypothesis tool in tests
+#
+# Copyright 2015 David R. MacIver
+#
+# For details see http://hypothesis.readthedocs.org
+
+import os
+import sys
+import traceback
+
+from hypothesis.settings import set_hypothesis_home_dir
+import hypothesis.strategies as st
+from hypothesis import given, Settings
+
+# hypothesis store data regarding generate example and code
+set_hypothesis_home_dir(os.path.join(
+    os.getenv('TESTTMP'), ".hypothesis"
+))
+
+def check(*args, **kwargs):
+    """decorator to make a function a hypothesis test
+
+    Decorated function are run immediately (to be used doctest style)"""
+    def accept(f):
+        # Workaround for https://github.com/DRMacIver/hypothesis/issues/206
+        # Fixed in version 1.13 (released 2015 october 29th)
+        f.__module__ = '__anon__'
+        try:
+            given(*args, settings=Settings(max_examples=2000), **kwargs)(f)()
+        except Exception:
+            traceback.print_exc(file=sys.stdout)
+            sys.exit(1)
+    return accept
+
+
+def roundtrips(data, decode, encode):
+    """helper to tests function that must do proper encode/decode roundtripping
+    """
+    @given(data)
+    def testroundtrips(value):
+        encoded = encode(value)
+        decoded = decode(encoded)
+        if decoded != value:
+            raise ValueError(
+                "Round trip failed: %s(%r) -> %s(%r) -> %r" % (
+                    encode.__name__, value, decode.__name__, encoded,
+                    decoded
+                ))
+    try:
+        testroundtrips()
+    except Exception:
+        # heredoc swallow traceback, we work around it
+        traceback.print_exc(file=sys.stdout)
+        raise
+    print("Round trip OK")
+
+
+# strategy for generating bytestring that might be an issue for Mercurial
+bytestrings = (
+    st.builds(lambda s, e: s.encode(e), st.text(), st.sampled_from([
+        'utf-8', 'utf-16',
+    ]))) | st.binary()
--- a/tests/run-tests.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/run-tests.py	Thu Dec 17 17:27:32 2015 -0600
@@ -154,6 +154,7 @@
 defaults = {
     'jobs': ('HGTEST_JOBS', 1),
     'timeout': ('HGTEST_TIMEOUT', 180),
+    'slowtimeout': ('HGTEST_SLOWTIMEOUT', 500),
     'port': ('HGTEST_PORT', 20059),
     'shell': ('HGTEST_SHELL', 'sh'),
 }
@@ -236,6 +237,9 @@
     parser.add_option("-t", "--timeout", type="int",
         help="kill errant tests after TIMEOUT seconds"
              " (default: $%s or %d)" % defaults['timeout'])
+    parser.add_option("--slowtimeout", type="int",
+        help="kill errant slow tests after SLOWTIMEOUT seconds"
+             " (default: $%s or %d)" % defaults['slowtimeout'])
     parser.add_option("--time", action="store_true",
         help="time how long each test takes")
     parser.add_option("--json", action="store_true",
@@ -263,6 +267,8 @@
                       help='run statprof on run-tests')
     parser.add_option('--allow-slow-tests', action='store_true',
                       help='allow extremely slow tests')
+    parser.add_option('--showchannels', action='store_true',
+                      help='show scheduling channels')
 
     for option, (envvar, default) in defaults.items():
         defaults[option] = type(default)(os.environ.get(envvar, default))
@@ -327,7 +333,11 @@
         if options.timeout != defaults['timeout']:
             sys.stderr.write(
                 'warning: --timeout option ignored with --debug\n')
+        if options.slowtimeout != defaults['slowtimeout']:
+            sys.stderr.write(
+                'warning: --slowtimeout option ignored with --debug\n')
         options.timeout = 0
+        options.slowtimeout = 0
     if options.py3k_warnings:
         if PYTHON3:
             parser.error(
@@ -339,6 +349,9 @@
     else:
         options.whitelisted = {}
 
+    if options.showchannels:
+        options.nodiff = True
+
     return (options, args)
 
 def rename(src, dst):
@@ -430,7 +443,8 @@
                  debug=False,
                  timeout=defaults['timeout'],
                  startport=defaults['port'], extraconfigopts=None,
-                 py3kwarnings=False, shell=None):
+                 py3kwarnings=False, shell=None,
+                 slowtimeout=defaults['slowtimeout']):
         """Create a test from parameters.
 
         path is the full path to the file defining the test.
@@ -444,7 +458,9 @@
         output.
 
         timeout controls the maximum run time of the test. It is ignored when
-        debug is True.
+        debug is True. See slowtimeout for tests with #require slow.
+
+        slowtimeout overrides timeout if the test has #require slow.
 
         startport controls the starting port number to use for this test. Each
         test will reserve 3 port numbers for execution. It is the caller's
@@ -469,6 +485,7 @@
         self._keeptmpdir = keeptmpdir
         self._debug = debug
         self._timeout = timeout
+        self._slowtimeout = slowtimeout
         self._startport = startport
         self._extraconfigopts = extraconfigopts or []
         self._py3kwarnings = py3kwarnings
@@ -922,7 +939,12 @@
             print(stdout)
             sys.exit(1)
 
-        return ret == 0
+        if ret != 0:
+            return False
+
+        if 'slow' in reqs:
+            self._timeout = self._slowtimeout
+        return True
 
     def _parsetest(self, lines):
         # We generate a shell script which outputs unique markers to line
@@ -1256,10 +1278,13 @@
             self.stop()
         else:
             with iolock:
-                if not self._options.nodiff:
-                    self.stream.write('\nERROR: %s output changed\n' % test)
+                if reason == "timed out":
+                    self.stream.write('t')
+                else:
+                    if not self._options.nodiff:
+                        self.stream.write('\nERROR: %s output changed\n' % test)
+                    self.stream.write('!')
 
-                self.stream.write('!')
                 self.stream.flush()
 
     def addSuccess(self, test):
@@ -1398,7 +1423,7 @@
 
     def __init__(self, testdir, jobs=1, whitelist=None, blacklist=None,
                  retest=False, keywords=None, loop=False, runs_per_test=1,
-                 loadtest=None,
+                 loadtest=None, showchannels=False,
                  *args, **kwargs):
         """Create a new instance that can run tests with a configuration.
 
@@ -1435,6 +1460,7 @@
         self._loop = loop
         self._runs_per_test = runs_per_test
         self._loadtest = loadtest
+        self._showchannels = showchannels
 
     def run(self, result):
         # We have a number of filters that need to be applied. We do this
@@ -1481,7 +1507,14 @@
         done = queue.Queue()
         running = 0
 
+        channels = [""] * self._jobs
+
         def job(test, result):
+            for n, v in enumerate(channels):
+                if not v:
+                    channel = n
+                    break
+            channels[channel] = "=" + test.name[5:].split(".")[0]
             try:
                 test(result)
                 done.put(None)
@@ -1490,9 +1523,33 @@
             except: # re-raises
                 done.put(('!', test, 'run-test raised an error, see traceback'))
                 raise
+            channels[channel] = ''
+
+        def stat():
+            count = 0
+            while channels:
+                d = '\n%03s  ' % count
+                for n, v in enumerate(channels):
+                    if v:
+                        d += v[0]
+                        channels[n] = v[1:] or '.'
+                    else:
+                        d += ' '
+                    d += ' '
+                with iolock:
+                    sys.stdout.write(d + '  ')
+                    sys.stdout.flush()
+                for x in xrange(10):
+                    if channels:
+                        time.sleep(.1)
+                count += 1
 
         stoppedearly = False
 
+        if self._showchannels:
+            statthread = threading.Thread(target=stat, name="stat")
+            statthread.start()
+
         try:
             while tests or running:
                 if not done.empty() or running == self._jobs or not tests:
@@ -1533,6 +1590,8 @@
             for test in runtests:
                 test.abort()
 
+        channels = []
+
         return result
 
 class TextTestRunner(unittest.TextTestRunner):
@@ -1724,8 +1783,16 @@
         else:
             # keywords for slow tests
             slow = {b'svn': 10,
-                    b'gendoc': 10,
-                    b'check-code-hg': 100,
+                    b'cvs': 10,
+                    b'hghave': 10,
+                    b'largefiles-update': 10,
+                    b'run-tests': 10,
+                    b'corruption': 10,
+                    b'race': 10,
+                    b'i18n': 10,
+                    b'check': 100,
+                    b'gendoc': 100,
+                    b'contrib-perf': 200,
                    }
             def sortkey(f):
                 # run largest tests first, as they tend to take the longest
@@ -1914,6 +1981,7 @@
                               keywords=kws,
                               loop=self.options.loop,
                               runs_per_test=self.options.runs_per_test,
+                              showchannels=self.options.showchannels,
                               tests=tests, loadtest=self._gettest)
             verbosity = 1
             if self.options.verbose:
--- a/tests/test-ancestor.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-ancestor.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,6 +1,21 @@
-from mercurial import ancestor, commands, hg, ui, util
+from __future__ import absolute_import
+
+import binascii
+import getopt
+import math
+import os
+import random
+import sys
+import time
+
 from mercurial.node import nullrev
-import binascii, getopt, math, os, random, sys, time
+from mercurial import (
+    ancestor,
+    commands,
+    hg,
+    ui,
+    util,
+)
 
 def buildgraph(rng, nodes=100, rootprob=0.05, mergeprob=0.2, prevprob=0.7):
     '''nodes: total number of nodes in the graph
--- a/tests/test-audit-path.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-audit-path.t	Thu Dec 17 17:27:32 2015 -0600
@@ -27,6 +27,45 @@
   abort: path 'b/b' traverses symbolic link 'b' (glob)
   [255]
 
+  $ hg commit -m 'add symlink b'
+
+
+Test symlink traversing when accessing history:
+-----------------------------------------------
+
+(build a changeset where the path exists as a directory)
+
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkdir b
+  $ echo c > b/a
+  $ hg add b/a
+  $ hg ci -m 'add directory b'
+  created new head
+
+Test that hg cat does not do anything wrong the working copy has 'b' as directory
+
+  $ hg cat b/a
+  c
+  $ hg cat -r "desc(directory)" b/a
+  c
+  $ hg cat -r "desc(symlink)" b/a
+  b/a: no such file in rev bc151a1f53bd
+  [1]
+
+Test that hg cat does not do anything wrong the working copy has 'b' as a symlink (issue4749)
+
+  $ hg up 'desc(symlink)'
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg cat b/a
+  b/a: no such file in rev bc151a1f53bd
+  [1]
+  $ hg cat -r "desc(directory)" b/a
+  c
+  $ hg cat -r "desc(symlink)" b/a
+  b/a: no such file in rev bc151a1f53bd
+  [1]
+
 #endif
 
 
--- a/tests/test-backout.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-backout.t	Thu Dec 17 17:27:32 2015 -0600
@@ -686,7 +686,7 @@
   * version 2 records
   local: b71750c4b0fdf719734971e3ef90dbeab5919a2d
   other: a30dd8addae3ce71b8667868478542bc417439e6
-  file: foo (state "u", hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33)
+  file: foo (record type "F", state "u", hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33)
     local path: foo (flags "")
     ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708)
     other path: foo (node f50039b486d6fa1a90ae51778388cad161f425ee)
@@ -694,7 +694,7 @@
   $ hg debugmergestate
   * version 1 records
   local: b71750c4b0fdf719734971e3ef90dbeab5919a2d
-  file: foo (state "u", hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33)
+  file: foo (record type "F", state "u", hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33)
     local path: foo (flags "")
     ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708)
     other path: foo (node not stored in v1 format)
@@ -709,7 +709,7 @@
   update: (current)
   phases: 3 draft
   $ hg resolve --all --debug
-  picked tool ':merge' for foo (binary False symlink False)
+  picked tool ':merge' for foo (binary False symlink False changedelete False)
   merging foo
   my foo@b71750c4b0fd+ other foo@a30dd8addae3 ancestor foo@913609522437
    premerge successful
--- a/tests/test-bundle-type.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-bundle-type.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,3 +1,9 @@
+
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > usegeneraldelta=yes
+  > EOF
+
 bundle w/o type option
 
   $ hg init t1
@@ -43,20 +49,26 @@
   % test bundle type None
   searching for changes
   1 changesets found
-  HG10UN
-  c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  HG20\x00\x00 (esc)
+  Stream params: {}
+  changegroup -- "{'version': '02'}"
+      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
   
   % test bundle type bzip2
   searching for changes
   1 changesets found
-  HG10BZ
-  c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  HG20\x00\x00 (esc)
+  Stream params: {'Compression': 'BZ'}
+  changegroup -- "{'version': '02'}"
+      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
   
   % test bundle type gzip
   searching for changes
   1 changesets found
-  HG10GZ
-  c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
+  HG20\x00\x00 (esc)
+  Stream params: {'Compression': 'GZ'}
+  changegroup -- "{'version': '02'}"
+      c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
   
   % test bundle type none-v2
   searching for changes
--- a/tests/test-bundle.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-bundle.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,3 +1,9 @@
+
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > usegeneraldelta=yes
+  > EOF
+
 Setting up test
 
   $ hg init test
@@ -260,15 +266,15 @@
 packed1 is produced properly
 
   $ hg -R test debugcreatestreamclonebundle packed.hg
-  writing 2608 bytes for 6 files
-  bundle requirements: revlogv1
+  writing 2663 bytes for 6 files
+  bundle requirements: generaldelta, revlogv1
 
   $ f -B 64 --size --sha1 --hexdump packed.hg
-  packed.hg: size=2758, sha1=864c1c7b490bac9f2950ef5a660668378ac0524e
+  packed.hg: size=2826, sha1=e139f97692a142b19cdcff64a69697d5307ce6d4
   0000: 48 47 53 31 55 4e 00 00 00 00 00 00 00 06 00 00 |HGS1UN..........|
-  0010: 00 00 00 00 0a 30 00 09 72 65 76 6c 6f 67 76 31 |.....0..revlogv1|
-  0020: 00 64 61 74 61 2f 61 64 69 66 66 65 72 65 6e 74 |.data/adifferent|
-  0030: 66 69 6c 65 2e 69 00 31 33 39 0a 00 01 00 01 00 |file.i.139......|
+  0010: 00 00 00 00 0a 67 00 16 67 65 6e 65 72 61 6c 64 |.....g..generald|
+  0020: 65 6c 74 61 2c 72 65 76 6c 6f 67 76 31 00 64 61 |elta,revlogv1.da|
+  0030: 74 61 2f 61 64 69 66 66 65 72 65 6e 74 66 69 6c |ta/adifferentfil|
 
 generaldelta requirement is listed in stream clone bundles
 
@@ -299,8 +305,8 @@
 packed1 can be consumed from debug command
 
   $ hg -R packed debugapplystreamclonebundle packed.hg
-  6 files to transfer, 2.55 KB of data
-  transferred 2.55 KB in *.* seconds (*) (glob)
+  6 files to transfer, 2.60 KB of data
+  transferred 2.60 KB in *.* seconds (* */sec) (glob)
 
 Does not work on non-empty repo
 
@@ -695,6 +701,8 @@
   list of changesets:
   1a38c1b849e8b70c756d2d80b0b9a3ac0b7ea11a
   057f4db07f61970e1c11e83be79e9d08adc4dc31
+  bundle2-output-bundle: "HG20", (1 params) 1 parts total
+  bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
   bundling: 1/2 changesets (50.00%)
   bundling: 2/2 changesets (100.00%)
   bundling: 1/2 manifests (50.00%)
--- a/tests/test-bundle2-exchange.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-bundle2-exchange.t	Thu Dec 17 17:27:32 2015 -0600
@@ -951,3 +951,86 @@
   remote: adding manifests
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files
+
+  $ cd ..
+
+Servers can disable bundle1 for clone/pull operations
+
+  $ killdaemons.py
+  $ hg init bundle2onlyserver
+  $ cd bundle2onlyserver
+  $ cat > .hg/hgrc << EOF
+  > [server]
+  > bundle1.pull = false
+  > EOF
+
+  $ touch foo
+  $ hg -q commit -A -m initial
+
+  $ hg serve -p $HGPORT -d --pid-file=hg.pid
+  $ cat hg.pid >> $DAEMON_PIDS
+
+  $ hg --config experimental.bundle2-exp=false clone http://localhost:$HGPORT/ not-bundle2
+  requesting all changes
+  abort: remote error:
+  incompatible Mercurial client; bundle2 required
+  (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
+  [255]
+  $ killdaemons.py
+
+Verify the global server.bundle1 option works
+
+  $ cat > .hg/hgrc << EOF
+  > [server]
+  > bundle1 = false
+  > EOF
+  $ hg serve -p $HGPORT -d --pid-file=hg.pid
+  $ cat hg.pid >> $DAEMON_PIDS
+  $ hg --config experimental.bundle2-exp=false clone http://localhost:$HGPORT not-bundle2
+  requesting all changes
+  abort: remote error:
+  incompatible Mercurial client; bundle2 required
+  (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
+  [255]
+  $ killdaemons.py
+
+Verify bundle1 pushes can be disabled
+
+  $ cat > .hg/hgrc << EOF
+  > [server]
+  > bundle1.push = false
+  > [web]
+  > allow_push = *
+  > push_ssl = false
+  > EOF
+
+  $ hg serve -p $HGPORT -d --pid-file=hg.pid -E error.log
+  $ cat hg.pid >> $DAEMON_PIDS
+  $ cd ..
+
+  $ hg clone http://localhost:$HGPORT bundle2-only
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd bundle2-only
+  $ echo commit > foo
+  $ hg commit -m commit
+  $ hg --config experimental.bundle2-exp=false push
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  abort: remote error:
+  incompatible Mercurial client; bundle2 required
+  (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
+  [255]
+
+  $ hg push
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
--- a/tests/test-check-code-hg.t	Mon Nov 30 13:47:29 2015 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-#require test-repo
-
-  $ check_code="$TESTDIR"/../contrib/check-code.py
-  $ cd "$TESTDIR"/..
-
-New errors are not allowed. Warnings are strongly discouraged.
-(The writing "no-che?k-code" is for not skipping this file when checking.)
-
-  $ hg locate | sed 's-\\-/-g' |
-  >   xargs "$check_code" --warnings --per-file=0 || false
-  Skipping hgext/zeroconf/Zeroconf.py it has no-che?k-code (glob)
-  Skipping i18n/polib.py it has no-che?k-code (glob)
-  Skipping mercurial/httpclient/__init__.py it has no-che?k-code (glob)
-  Skipping mercurial/httpclient/_readers.py it has no-che?k-code (glob)
-  Skipping mercurial/httpclient/socketutil.py it has no-che?k-code (glob)
--- a/tests/test-check-code.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-check-code.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,234 +1,15 @@
-  $ cat > correct.py <<EOF
-  > def toto(arg1, arg2):
-  >     del arg2
-  >     return (5 + 6, 9)
-  > EOF
-  $ cat > wrong.py <<EOF
-  > def toto( arg1, arg2):
-  >     del(arg2)
-  >     return ( 5+6, 9)
-  > EOF
-  $ cat > quote.py <<EOF
-  > # let's use quote in comments
-  > (''' ( 4x5 )
-  > but """\\''' and finally''',
-  > """let's fool checkpatch""", '1+2',
-  > '"""', 42+1, """and
-  > ( 4-1 ) """, "( 1+1 )\" and ")
-  > a, '\\\\\\\\', "\\\\\\" x-2", "c-1"
-  > EOF
-  $ cat > classstyle.py <<EOF
-  > class newstyle_class(object):
-  >     pass
-  > 
-  > class oldstyle_class:
-  >     pass
-  > 
-  > class empty():
-  >     pass
-  > 
-  > no_class = 1:
-  >     pass
-  > EOF
+#require test-repo
+
   $ check_code="$TESTDIR"/../contrib/check-code.py
-  $ "$check_code" ./wrong.py ./correct.py ./quote.py ./classstyle.py
-  ./wrong.py:1:
-   > def toto( arg1, arg2):
-   gratuitous whitespace in () or []
-  ./wrong.py:2:
-   >     del(arg2)
-   Python keyword is not a function
-  ./wrong.py:3:
-   >     return ( 5+6, 9)
-   gratuitous whitespace in () or []
-   missing whitespace in expression
-  ./quote.py:5:
-   > '"""', 42+1, """and
-   missing whitespace in expression
-  ./classstyle.py:4:
-   > class oldstyle_class:
-   old-style class, use class foo(object)
-  ./classstyle.py:7:
-   > class empty():
-   class foo() creates old style object, use class foo(object)
-  [1]
-  $ cat > python3-compat.py << EOF
-  > foo <> bar
-  > reduce(lambda a, b: a + b, [1, 2, 3, 4])
-  > dict(key=value)
-  > EOF
-  $ "$check_code" python3-compat.py
-  python3-compat.py:1:
-   > foo <> bar
-   <> operator is not available in Python 3+, use !=
-  python3-compat.py:2:
-   > reduce(lambda a, b: a + b, [1, 2, 3, 4])
-   reduce is not available in Python 3+
-  python3-compat.py:3:
-   > dict(key=value)
-   dict() is different in Py2 and 3 and is slower than {}
-  [1]
+  $ cd "$TESTDIR"/..
 
-  $ cat > is-op.py <<EOF
-  > # is-operator comparing number or string literal
-  > x = None
-  > y = x is 'foo'
-  > y = x is "foo"
-  > y = x is 5346
-  > y = x is -6
-  > y = x is not 'foo'
-  > y = x is not "foo"
-  > y = x is not 5346
-  > y = x is not -6
-  > EOF
-
-  $ "$check_code" ./is-op.py
-  ./is-op.py:3:
-   > y = x is 'foo'
-   object comparison with literal
-  ./is-op.py:4:
-   > y = x is "foo"
-   object comparison with literal
-  ./is-op.py:5:
-   > y = x is 5346
-   object comparison with literal
-  ./is-op.py:6:
-   > y = x is -6
-   object comparison with literal
-  ./is-op.py:7:
-   > y = x is not 'foo'
-   object comparison with literal
-  ./is-op.py:8:
-   > y = x is not "foo"
-   object comparison with literal
-  ./is-op.py:9:
-   > y = x is not 5346
-   object comparison with literal
-  ./is-op.py:10:
-   > y = x is not -6
-   object comparison with literal
-  [1]
+New errors are not allowed. Warnings are strongly discouraged.
+(The writing "no-che?k-code" is for not skipping this file when checking.)
 
-  $ cat > for-nolineno.py <<EOF
-  > except:
-  > EOF
-  $ "$check_code" for-nolineno.py --nolineno
-  for-nolineno.py:0:
-   > except:
-   naked except clause
-  [1]
-
-  $ cat > warning.t <<EOF
-  >   $ function warnonly {
-  >   > }
-  >   $ diff -N aaa
-  >   $ function onwarn {}
-  > EOF
-  $ "$check_code" warning.t
-  $ "$check_code" --warn warning.t
-  warning.t:1:
-   >   $ function warnonly {
-   warning: don't use 'function', use old style
-  warning.t:3:
-   >   $ diff -N aaa
-   warning: don't use 'diff -N'
-  warning.t:4:
-   >   $ function onwarn {}
-   warning: don't use 'function', use old style
-  [1]
-  $ cat > raise-format.py <<EOF
-  > raise SomeException, message
-  > # this next line is okay
-  > raise SomeException(arg1, arg2)
-  > EOF
-  $ "$check_code" not-existing.py raise-format.py
-  Skipping*not-existing.py* (glob)
-  raise-format.py:1:
-   > raise SomeException, message
-   don't use old-style two-argument raise, use Exception(message)
-  [1]
-
-  $ cat > rst.py <<EOF
-  > """problematic rst text
-  > 
-  > .. note::
-  >     wrong
-  > """
-  > 
-  > '''
-  > 
-  > .. note::
-  > 
-  >     valid
-  > 
-  > new text
-  > 
-  >     .. note::
-  > 
-  >         also valid
-  > '''
-  > 
-  > """mixed
-  > 
-  > .. note::
-  > 
-  >   good
-  > 
-  >     .. note::
-  >         plus bad
-  > """
-  > EOF
-  $ $check_code -w rst.py
-  rst.py:3:
-   > .. note::
-   warning: add two newlines after '.. note::'
-  rst.py:26:
-   >     .. note::
-   warning: add two newlines after '.. note::'
-  [1]
-
-  $ cat > ./map-inside-gettext.py <<EOF
-  > print _("map inside gettext %s" % v)
-  > 
-  > print _("concatenating " " by " " space %s" % v)
-  > print _("concatenating " + " by " + " '+' %s" % v)
-  > 
-  > print _("mapping operation in different line %s"
-  >         % v)
-  > 
-  > print _(
-  >         "leading spaces inside of '(' %s" % v)
-  > EOF
-  $ "$check_code" ./map-inside-gettext.py
-  ./map-inside-gettext.py:1:
-   > print _("map inside gettext %s" % v)
-   don't use % inside _()
-  ./map-inside-gettext.py:3:
-   > print _("concatenating " " by " " space %s" % v)
-   don't use % inside _()
-  ./map-inside-gettext.py:4:
-   > print _("concatenating " + " by " + " '+' %s" % v)
-   don't use % inside _()
-  ./map-inside-gettext.py:6:
-   > print _("mapping operation in different line %s"
-   don't use % inside _()
-  ./map-inside-gettext.py:9:
-   > print _(
-   don't use % inside _()
-  [1]
-
-web templates
-
-  $ mkdir -p mercurial/templates
-  $ cat > mercurial/templates/example.tmpl <<EOF
-  > {desc}
-  > {desc|escape}
-  > {desc|firstline}
-  > {desc|websub}
-  > EOF
-
-  $ "$check_code" --warnings mercurial/templates/example.tmpl
-  mercurial/templates/example.tmpl:2:
-   > {desc|escape}
-   warning: follow desc keyword with either firstline or websub
-  [1]
+  $ hg locate | sed 's-\\-/-g' |
+  >   xargs "$check_code" --warnings --per-file=0 || false
+  Skipping hgext/zeroconf/Zeroconf.py it has no-che?k-code (glob)
+  Skipping i18n/polib.py it has no-che?k-code (glob)
+  Skipping mercurial/httpclient/__init__.py it has no-che?k-code (glob)
+  Skipping mercurial/httpclient/_readers.py it has no-che?k-code (glob)
+  Skipping mercurial/httpclient/socketutil.py it has no-che?k-code (glob)
--- a/tests/test-check-commit-hg.t	Mon Nov 30 13:47:29 2015 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-#require test-repo
-
-Enable obsolescence to avoid the warning issue when obsmarker are found
-
-  $ cat >> $HGRCPATH << EOF
-  > [experimental]
-  > evolution=createmarkers
-  > EOF
-
-Go back in the hg repo
-
-  $ cd $TESTDIR/..
-
-  $ for node in `hg log --rev 'draft() and ::.' --template '{node|short}\n'`; do
-  >    hg export $node | contrib/check-commit > ${TESTTMP}/check-commit.out
-  >    if [ $? -ne 0 ]; then
-  >        echo "Revision $node does not comply to rules"
-  >        echo '------------------------------------------------------'
-  >        cat ${TESTTMP}/check-commit.out
-  >        echo
-  >   fi
-  > done
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-check-commit.t	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,24 @@
+#require test-repo
+
+Enable obsolescence to avoid the warning issue when obsmarker are found
+
+  $ cat >> $HGRCPATH << EOF
+  > [experimental]
+  > evolution=createmarkers
+  > EOF
+
+Go back in the hg repo
+
+  $ cd $TESTDIR/..
+
+  $ for node in `hg log --rev 'draft() and ::.' --template '{node|short}\n'`; do
+  >    hg export $node | contrib/check-commit > ${TESTTMP}/check-commit.out
+  >    if [ $? -ne 0 ]; then
+  >        echo "Revision $node does not comply to rules"
+  >        echo '------------------------------------------------------'
+  >        cat ${TESTTMP}/check-commit.out
+  >        echo
+  >   fi
+  > done
+
+
--- a/tests/test-check-config-hg.t	Mon Nov 30 13:47:29 2015 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-#require test-repo
-
-  $ cd "$TESTDIR"/..
-
-New errors are not allowed. Warnings are strongly discouraged.
-
-  $ hg files "set:(**.py or **.txt) - tests/**" | sed 's|\\|/|g' |
-  >   xargs python contrib/check-config.py
-  undocumented: convert.cvsps.cache (bool) [True]
-  undocumented: convert.cvsps.fuzz (str) [60]
-  undocumented: convert.cvsps.mergefrom (str)
-  undocumented: convert.cvsps.mergeto (str)
-  undocumented: convert.git.remoteprefix (str) ['remote']
-  undocumented: convert.git.similarity (int) [50]
-  undocumented: convert.hg.clonebranches (bool)
-  undocumented: convert.hg.ignoreerrors (bool)
-  undocumented: convert.hg.revs (str)
-  undocumented: convert.hg.saverev (bool)
-  undocumented: convert.hg.sourcename (str)
-  undocumented: convert.hg.startrev (str)
-  undocumented: convert.hg.tagsbranch (str) ['default']
-  undocumented: convert.hg.usebranchnames (bool) [True]
-  undocumented: convert.localtimezone (bool)
-  undocumented: convert.p4.startrev (str)
-  undocumented: convert.skiptags (bool)
-  undocumented: convert.svn.debugsvnlog (bool) [True]
-  undocumented: convert.svn.startrev (str)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-check-config.t	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,8 @@
+#require test-repo
+
+  $ cd "$TESTDIR"/..
+
+New errors are not allowed. Warnings are strongly discouraged.
+
+  $ hg files "set:(**.py or **.txt) - tests/**" | sed 's|\\|/|g' |
+  >   xargs python contrib/check-config.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-check-py3-compat.t	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,207 @@
+#require test-repo
+
+  $ cd "$TESTDIR"/..
+
+  $ hg files 'set:(**.py)' | xargs python contrib/check-py3-compat.py
+  contrib/casesmash.py not using absolute_import
+  contrib/check-code.py not using absolute_import
+  contrib/check-code.py requires print_function
+  contrib/check-config.py not using absolute_import
+  contrib/check-config.py requires print_function
+  contrib/debugcmdserver.py not using absolute_import
+  contrib/debugcmdserver.py requires print_function
+  contrib/debugshell.py not using absolute_import
+  contrib/fixpax.py not using absolute_import
+  contrib/fixpax.py requires print_function
+  contrib/hgclient.py not using absolute_import
+  contrib/hgclient.py requires print_function
+  contrib/hgfixes/fix_bytes.py not using absolute_import
+  contrib/hgfixes/fix_bytesmod.py not using absolute_import
+  contrib/hgfixes/fix_leftover_imports.py not using absolute_import
+  contrib/import-checker.py not using absolute_import
+  contrib/import-checker.py requires print_function
+  contrib/memory.py not using absolute_import
+  contrib/perf.py not using absolute_import
+  contrib/python-hook-examples.py not using absolute_import
+  contrib/revsetbenchmarks.py not using absolute_import
+  contrib/revsetbenchmarks.py requires print_function
+  contrib/showstack.py not using absolute_import
+  contrib/synthrepo.py not using absolute_import
+  contrib/win32/hgwebdir_wsgi.py not using absolute_import
+  doc/check-seclevel.py not using absolute_import
+  doc/gendoc.py not using absolute_import
+  doc/hgmanpage.py not using absolute_import
+  hgext/__init__.py not using absolute_import
+  hgext/acl.py not using absolute_import
+  hgext/blackbox.py not using absolute_import
+  hgext/bugzilla.py not using absolute_import
+  hgext/censor.py not using absolute_import
+  hgext/children.py not using absolute_import
+  hgext/churn.py not using absolute_import
+  hgext/clonebundles.py not using absolute_import
+  hgext/color.py not using absolute_import
+  hgext/convert/__init__.py not using absolute_import
+  hgext/convert/bzr.py not using absolute_import
+  hgext/convert/common.py not using absolute_import
+  hgext/convert/convcmd.py not using absolute_import
+  hgext/convert/cvs.py not using absolute_import
+  hgext/convert/cvsps.py not using absolute_import
+  hgext/convert/darcs.py not using absolute_import
+  hgext/convert/filemap.py not using absolute_import
+  hgext/convert/git.py not using absolute_import
+  hgext/convert/gnuarch.py not using absolute_import
+  hgext/convert/hg.py not using absolute_import
+  hgext/convert/monotone.py not using absolute_import
+  hgext/convert/p4.py not using absolute_import
+  hgext/convert/subversion.py not using absolute_import
+  hgext/convert/transport.py not using absolute_import
+  hgext/eol.py not using absolute_import
+  hgext/extdiff.py not using absolute_import
+  hgext/factotum.py not using absolute_import
+  hgext/fetch.py not using absolute_import
+  hgext/gpg.py not using absolute_import
+  hgext/graphlog.py not using absolute_import
+  hgext/hgcia.py not using absolute_import
+  hgext/hgk.py not using absolute_import
+  hgext/highlight/__init__.py not using absolute_import
+  hgext/highlight/highlight.py not using absolute_import
+  hgext/histedit.py not using absolute_import
+  hgext/keyword.py not using absolute_import
+  hgext/largefiles/__init__.py not using absolute_import
+  hgext/largefiles/basestore.py not using absolute_import
+  hgext/largefiles/lfcommands.py not using absolute_import
+  hgext/largefiles/lfutil.py not using absolute_import
+  hgext/largefiles/localstore.py not using absolute_import
+  hgext/largefiles/overrides.py not using absolute_import
+  hgext/largefiles/proto.py not using absolute_import
+  hgext/largefiles/remotestore.py not using absolute_import
+  hgext/largefiles/reposetup.py not using absolute_import
+  hgext/largefiles/uisetup.py not using absolute_import
+  hgext/largefiles/wirestore.py not using absolute_import
+  hgext/mq.py not using absolute_import
+  hgext/notify.py not using absolute_import
+  hgext/pager.py not using absolute_import
+  hgext/patchbomb.py not using absolute_import
+  hgext/purge.py not using absolute_import
+  hgext/rebase.py not using absolute_import
+  hgext/record.py not using absolute_import
+  hgext/relink.py not using absolute_import
+  hgext/schemes.py not using absolute_import
+  hgext/share.py not using absolute_import
+  hgext/shelve.py not using absolute_import
+  hgext/strip.py not using absolute_import
+  hgext/transplant.py not using absolute_import
+  hgext/win32mbcs.py not using absolute_import
+  hgext/win32text.py not using absolute_import
+  hgext/zeroconf/Zeroconf.py not using absolute_import
+  hgext/zeroconf/Zeroconf.py requires print_function
+  hgext/zeroconf/__init__.py not using absolute_import
+  i18n/check-translation.py not using absolute_import
+  i18n/polib.py not using absolute_import
+  mercurial/byterange.py not using absolute_import
+  mercurial/cmdutil.py not using absolute_import
+  mercurial/commands.py not using absolute_import
+  mercurial/context.py not using absolute_import
+  mercurial/dirstate.py not using absolute_import
+  mercurial/dispatch.py requires print_function
+  mercurial/exchange.py not using absolute_import
+  mercurial/help.py not using absolute_import
+  mercurial/httpclient/__init__.py not using absolute_import
+  mercurial/httpclient/_readers.py not using absolute_import
+  mercurial/httpclient/socketutil.py not using absolute_import
+  mercurial/httpconnection.py not using absolute_import
+  mercurial/keepalive.py not using absolute_import
+  mercurial/keepalive.py requires print_function
+  mercurial/localrepo.py not using absolute_import
+  mercurial/lsprof.py requires print_function
+  mercurial/lsprofcalltree.py not using absolute_import
+  mercurial/lsprofcalltree.py requires print_function
+  mercurial/mail.py requires print_function
+  mercurial/manifest.py not using absolute_import
+  mercurial/mdiff.py not using absolute_import
+  mercurial/patch.py not using absolute_import
+  mercurial/pvec.py not using absolute_import
+  mercurial/py3kcompat.py not using absolute_import
+  mercurial/scmposix.py not using absolute_import
+  mercurial/scmutil.py not using absolute_import
+  mercurial/scmwindows.py not using absolute_import
+  mercurial/store.py not using absolute_import
+  setup.py not using absolute_import
+  tests/filterpyflakes.py requires print_function
+  tests/generate-working-copy-states.py requires print_function
+  tests/get-with-headers.py requires print_function
+  tests/heredoctest.py requires print_function
+  tests/hypothesishelpers.py not using absolute_import
+  tests/hypothesishelpers.py requires print_function
+  tests/killdaemons.py not using absolute_import
+  tests/md5sum.py not using absolute_import
+  tests/mockblackbox.py not using absolute_import
+  tests/printenv.py not using absolute_import
+  tests/readlink.py not using absolute_import
+  tests/readlink.py requires print_function
+  tests/revlog-formatv0.py not using absolute_import
+  tests/run-tests.py not using absolute_import
+  tests/seq.py not using absolute_import
+  tests/seq.py requires print_function
+  tests/silenttestrunner.py not using absolute_import
+  tests/silenttestrunner.py requires print_function
+  tests/sitecustomize.py not using absolute_import
+  tests/svn-safe-append.py not using absolute_import
+  tests/svnxml.py not using absolute_import
+  tests/test-ancestor.py requires print_function
+  tests/test-atomictempfile.py not using absolute_import
+  tests/test-batching.py not using absolute_import
+  tests/test-batching.py requires print_function
+  tests/test-bdiff.py not using absolute_import
+  tests/test-bdiff.py requires print_function
+  tests/test-context.py not using absolute_import
+  tests/test-context.py requires print_function
+  tests/test-demandimport.py not using absolute_import
+  tests/test-demandimport.py requires print_function
+  tests/test-dispatch.py not using absolute_import
+  tests/test-dispatch.py requires print_function
+  tests/test-doctest.py not using absolute_import
+  tests/test-duplicateoptions.py not using absolute_import
+  tests/test-duplicateoptions.py requires print_function
+  tests/test-filecache.py not using absolute_import
+  tests/test-filecache.py requires print_function
+  tests/test-filelog.py not using absolute_import
+  tests/test-filelog.py requires print_function
+  tests/test-hg-parseurl.py not using absolute_import
+  tests/test-hg-parseurl.py requires print_function
+  tests/test-hgweb-auth.py not using absolute_import
+  tests/test-hgweb-auth.py requires print_function
+  tests/test-hgwebdir-paths.py not using absolute_import
+  tests/test-hybridencode.py not using absolute_import
+  tests/test-hybridencode.py requires print_function
+  tests/test-lrucachedict.py not using absolute_import
+  tests/test-lrucachedict.py requires print_function
+  tests/test-manifest.py not using absolute_import
+  tests/test-minirst.py not using absolute_import
+  tests/test-minirst.py requires print_function
+  tests/test-parseindex2.py not using absolute_import
+  tests/test-parseindex2.py requires print_function
+  tests/test-pathencode.py not using absolute_import
+  tests/test-pathencode.py requires print_function
+  tests/test-propertycache.py not using absolute_import
+  tests/test-propertycache.py requires print_function
+  tests/test-revlog-ancestry.py not using absolute_import
+  tests/test-revlog-ancestry.py requires print_function
+  tests/test-run-tests.py not using absolute_import
+  tests/test-simplemerge.py not using absolute_import
+  tests/test-status-inprocess.py not using absolute_import
+  tests/test-status-inprocess.py requires print_function
+  tests/test-symlink-os-yes-fs-no.py not using absolute_import
+  tests/test-trusted.py not using absolute_import
+  tests/test-trusted.py requires print_function
+  tests/test-ui-color.py not using absolute_import
+  tests/test-ui-color.py requires print_function
+  tests/test-ui-config.py not using absolute_import
+  tests/test-ui-config.py requires print_function
+  tests/test-ui-verbosity.py not using absolute_import
+  tests/test-ui-verbosity.py requires print_function
+  tests/test-url.py not using absolute_import
+  tests/test-url.py requires print_function
+  tests/test-walkrepo.py requires print_function
+  tests/test-wireproto.py requires print_function
+  tests/tinyproxy.py requires print_function
--- a/tests/test-check-pyflakes.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-check-pyflakes.t	Thu Dec 17 17:27:32 2015 -0600
@@ -7,6 +7,6 @@
 
   $ hg locate 'set:**.py or grep("^!#.*python")' 2>/dev/null \
   > | xargs pyflakes 2>/dev/null | "$TESTDIR/filterpyflakes.py"
-  tests/filterpyflakes.py:58: undefined name 'undefinedname'
+  tests/filterpyflakes.py:61: undefined name 'undefinedname'
   
 
--- a/tests/test-clone-r.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-clone-r.t	Thu Dec 17 17:27:32 2015 -0600
@@ -63,7 +63,7 @@
        2        96      48  .....       2 626a32663c2f 8b89697eba2c 000000000000 (re)
        3       144      48  .....       3 f54c32f13478 626a32663c2f 000000000000 (re)
        4       192      ..  .....       6 de68e904d169 626a32663c2f 000000000000 (re)
-       5       2..      68  .....       7 09bb521d218d de68e904d169 000000000000 (re)
+       5       2..      ..  .....       7 09bb521d218d de68e904d169 000000000000 (re)
        6       3..      54  .....       8 1fde233dfb0f f54c32f13478 000000000000 (re)
 
   $ hg verify
--- a/tests/test-clonebundles.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-clonebundles.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,5 +1,9 @@
 Set up a server
 
+  $ cat >> $HGRCPATH << EOF
+  > [format]
+  > usegeneraldelta=yes
+  > EOF
   $ hg init server
   $ cd server
   $ cat >> .hg/hgrc << EOF
@@ -80,7 +84,7 @@
   $ echo 'http://does.not.exist/bundle.hg' > server/.hg/clonebundles.manifest
   $ hg clone http://localhost:$HGPORT 404-url
   applying clone bundle from http://does.not.exist/bundle.hg
-  error fetching bundle: * not known (glob)
+  error fetching bundle: (.* not known|getaddrinfo failed) (re)
   abort: error applying bundle
   (if this error persists, consider contacting the server operator or disable clone bundles via "--config experimental.clonebundles=false")
   [255]
@@ -90,7 +94,7 @@
   $ echo "http://localhost:$HGPORT1/bundle.hg" > server/.hg/clonebundles.manifest
   $ hg clone http://localhost:$HGPORT server-not-runner
   applying clone bundle from http://localhost:$HGPORT1/bundle.hg
-  error fetching bundle: Connection refused
+  error fetching bundle: * refused* (glob)
   abort: error applying bundle
   (if this error persists, consider contacting the server operator or disable clone bundles via "--config experimental.clonebundles=false")
   [255]
@@ -308,7 +312,7 @@
 
   $ hg -R server debugcreatestreamclonebundle packed.hg
   writing 613 bytes for 4 files
-  bundle requirements: revlogv1
+  bundle requirements: generaldelta, revlogv1
 
 No bundle spec should work
 
--- a/tests/test-commit-amend.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-commit-amend.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,3 +1,8 @@
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > usegeneraldelta=yes
+  > EOF
+
   $ hg init
 
 Setup:
@@ -115,15 +120,15 @@
   stripping amended changeset 74609c7f506e
   1 changesets found
   uncompressed size of bundle content:
-       250 (changelog)
-       143 (manifests)
-       109  a
+       270 (changelog)
+       163 (manifests)
+       129  a
   saved backup bundle to $TESTTMP/.hg/strip-backup/74609c7f506e-1bfde511-amend-backup.hg (glob)
   1 changesets found
   uncompressed size of bundle content:
-       246 (changelog)
-       143 (manifests)
-       109  a
+       266 (changelog)
+       163 (manifests)
+       129  a
   adding branch
   adding changesets
   adding manifests
@@ -259,15 +264,15 @@
   stripping amended changeset 5f357c7560ab
   1 changesets found
   uncompressed size of bundle content:
-       238 (changelog)
-       143 (manifests)
-       111  a
+       258 (changelog)
+       163 (manifests)
+       131  a
   saved backup bundle to $TESTTMP/.hg/strip-backup/5f357c7560ab-e7c84ade-amend-backup.hg (glob)
   1 changesets found
   uncompressed size of bundle content:
-       246 (changelog)
-       143 (manifests)
-       111  a
+       266 (changelog)
+       163 (manifests)
+       131  a
   adding branch
   adding changesets
   adding manifests
@@ -302,15 +307,15 @@
   stripping amended changeset 7ab3bf440b54
   2 changesets found
   uncompressed size of bundle content:
-       450 (changelog)
-       282 (manifests)
-       209  a
+       490 (changelog)
+       322 (manifests)
+       249  a
   saved backup bundle to $TESTTMP/.hg/strip-backup/7ab3bf440b54-8e3b5088-amend-backup.hg (glob)
   1 changesets found
   uncompressed size of bundle content:
-       246 (changelog)
-       143 (manifests)
-       113  a
+       266 (changelog)
+       163 (manifests)
+       133  a
   adding branch
   adding changesets
   adding manifests
@@ -807,7 +812,7 @@
   $ hg ci -m aa
   $ hg merge -q bar
   local changed aa which remote deleted
-  use (c)hanged version or (d)elete? c
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
   $ hg ci -m 'merge bar (with conflicts)'
   $ hg log --config diff.git=1 -pr .
   changeset:   33:97a298b0c59f
@@ -1138,14 +1143,14 @@
   R olddirname/commonfile.py
   R olddirname/newfile.py
   $ hg debugindex newdirname/newfile.py
-     rev    offset  length   base linkrev nodeid       p1           p2
-       0         0      88      0       3 34a4d536c0c0 000000000000 000000000000
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0      88     -1       3 34a4d536c0c0 000000000000 000000000000
 
   $ echo a >> newdirname/commonfile.py
   $ hg ci --amend -m bug
   $ hg debugrename newdirname/newfile.py
   newdirname/newfile.py renamed from olddirname/newfile.py:690b295714aed510803d3020da9c70fca8336def (glob)
   $ hg debugindex newdirname/newfile.py
-     rev    offset  length   base linkrev nodeid       p1           p2
-       0         0      88      0       3 34a4d536c0c0 000000000000 000000000000
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0      88     -1       3 34a4d536c0c0 000000000000 000000000000
 
--- a/tests/test-commit-interactive-curses.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-commit-interactive-curses.t	Thu Dec 17 17:27:32 2015 -0600
@@ -41,6 +41,10 @@
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     a
   
+Check that commit -i works with no changes
+  $ hg commit -i
+  no changes to record
+
 Committing only one file
 
   $ echo "a" >> a
@@ -71,6 +75,7 @@
 - unfold it
 - go down to second hunk (1 for the first hunk, 1 for the first hunkline, 1 for the second hunk, 1 for the second hunklike)
 - toggle the second hunk
+- toggle on and off the amend mode (to check that it toggles off)
 - edit the hunk and quit the editor immediately with non-zero status
 - commit
 
@@ -88,6 +93,8 @@
   > KEY_DOWN
   > KEY_DOWN
   > TOGGLE
+  > a
+  > a
   > e
   > X
   > EOF
@@ -166,3 +173,23 @@
   $ hg st
   ? testModeCommands
 
+Amend option works
+  $ echo "hello world" > x
+  $ hg diff -c .
+  diff -r a6735021574d -r 2b0e9be4d336 x
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/x	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +hello
+  $ cat <<EOF >testModeCommands
+  > a
+  > X
+  > EOF
+  $ hg commit -i  -m "newly added file" -d "0 0"
+  saved backup bundle to $TESTTMP/a/.hg/strip-backup/2b0e9be4d336-28bbe4e2-amend-backup.hg (glob)
+  $ hg diff -c .
+  diff -r a6735021574d -r c1d239d165ae x
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/x	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +hello world
--- a/tests/test-commit-interactive.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-commit-interactive.t	Thu Dec 17 17:27:32 2015 -0600
@@ -153,7 +153,7 @@
 
 Add binary file
 
-  $ hg bundle --base -2 tip.bundle
+  $ hg bundle --type v1 --base -2 tip.bundle
   1 changesets found
   $ hg add tip.bundle
   $ hg commit -i -d '4 0' -m binary<<EOF
@@ -178,7 +178,7 @@
 
 Change binary file
 
-  $ hg bundle --base -2 tip.bundle
+  $ hg bundle --base -2 --type v1 tip.bundle
   1 changesets found
   $ hg commit -i -d '5 0' -m binary-change<<EOF
   > y
@@ -202,7 +202,7 @@
 Rename and change binary file
 
   $ hg mv tip.bundle top.bundle
-  $ hg bundle --base -2 top.bundle
+  $ hg bundle --base -2 --type v1 top.bundle
   1 changesets found
   $ hg commit -i -d '6 0' -m binary-change-rename<<EOF
   > y
--- a/tests/test-completion.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-completion.t	Thu Dec 17 17:27:32 2015 -0600
@@ -80,6 +80,7 @@
   debugdag
   debugdata
   debugdate
+  debugdeltachain
   debugdirstate
   debugdiscovery
   debugextensions
@@ -243,6 +244,7 @@
   debugdag: tags, branches, dots, spaces
   debugdata: changelog, manifest, dir
   debugdate: extended
+  debugdeltachain: changelog, manifest, dir, template
   debugdirstate: nodates, datesort
   debugdiscovery: old, nonheads, ssh, remotecmd, insecure
   debugextensions: template
@@ -251,7 +253,7 @@
   debuggetbundle: head, common, type
   debugignore: 
   debugindex: changelog, manifest, dir, format
-  debugindexdot: 
+  debugindexdot: changelog, manifest, dir
   debuginstall: 
   debugknown: 
   debuglabelcomplete: 
--- a/tests/test-context.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-context.py	Thu Dec 17 17:27:32 2015 -0600
@@ -16,7 +16,11 @@
 repo[None].add(['foo'])
 repo.commit(text='commit1', date="0 0")
 
-print "workingfilectx.date =", repo[None]['foo'].date()
+if os.name == 'nt':
+    d = repo[None]['foo'].date()
+    print "workingfilectx.date = (%d, %d)" % (d[0], d[1])
+else:
+    print "workingfilectx.date =", repo[None]['foo'].date()
 
 # test memctx with non-ASCII commit message
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-contrib-check-code.t	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,234 @@
+  $ cat > correct.py <<EOF
+  > def toto(arg1, arg2):
+  >     del arg2
+  >     return (5 + 6, 9)
+  > EOF
+  $ cat > wrong.py <<EOF
+  > def toto( arg1, arg2):
+  >     del(arg2)
+  >     return ( 5+6, 9)
+  > EOF
+  $ cat > quote.py <<EOF
+  > # let's use quote in comments
+  > (''' ( 4x5 )
+  > but """\\''' and finally''',
+  > """let's fool checkpatch""", '1+2',
+  > '"""', 42+1, """and
+  > ( 4-1 ) """, "( 1+1 )\" and ")
+  > a, '\\\\\\\\', "\\\\\\" x-2", "c-1"
+  > EOF
+  $ cat > classstyle.py <<EOF
+  > class newstyle_class(object):
+  >     pass
+  > 
+  > class oldstyle_class:
+  >     pass
+  > 
+  > class empty():
+  >     pass
+  > 
+  > no_class = 1:
+  >     pass
+  > EOF
+  $ check_code="$TESTDIR"/../contrib/check-code.py
+  $ "$check_code" ./wrong.py ./correct.py ./quote.py ./classstyle.py
+  ./wrong.py:1:
+   > def toto( arg1, arg2):
+   gratuitous whitespace in () or []
+  ./wrong.py:2:
+   >     del(arg2)
+   Python keyword is not a function
+  ./wrong.py:3:
+   >     return ( 5+6, 9)
+   gratuitous whitespace in () or []
+   missing whitespace in expression
+  ./quote.py:5:
+   > '"""', 42+1, """and
+   missing whitespace in expression
+  ./classstyle.py:4:
+   > class oldstyle_class:
+   old-style class, use class foo(object)
+  ./classstyle.py:7:
+   > class empty():
+   class foo() creates old style object, use class foo(object)
+  [1]
+  $ cat > python3-compat.py << EOF
+  > foo <> bar
+  > reduce(lambda a, b: a + b, [1, 2, 3, 4])
+  > dict(key=value)
+  > EOF
+  $ "$check_code" python3-compat.py
+  python3-compat.py:1:
+   > foo <> bar
+   <> operator is not available in Python 3+, use !=
+  python3-compat.py:2:
+   > reduce(lambda a, b: a + b, [1, 2, 3, 4])
+   reduce is not available in Python 3+
+  python3-compat.py:3:
+   > dict(key=value)
+   dict() is different in Py2 and 3 and is slower than {}
+  [1]
+
+  $ cat > is-op.py <<EOF
+  > # is-operator comparing number or string literal
+  > x = None
+  > y = x is 'foo'
+  > y = x is "foo"
+  > y = x is 5346
+  > y = x is -6
+  > y = x is not 'foo'
+  > y = x is not "foo"
+  > y = x is not 5346
+  > y = x is not -6
+  > EOF
+
+  $ "$check_code" ./is-op.py
+  ./is-op.py:3:
+   > y = x is 'foo'
+   object comparison with literal
+  ./is-op.py:4:
+   > y = x is "foo"
+   object comparison with literal
+  ./is-op.py:5:
+   > y = x is 5346
+   object comparison with literal
+  ./is-op.py:6:
+   > y = x is -6
+   object comparison with literal
+  ./is-op.py:7:
+   > y = x is not 'foo'
+   object comparison with literal
+  ./is-op.py:8:
+   > y = x is not "foo"
+   object comparison with literal
+  ./is-op.py:9:
+   > y = x is not 5346
+   object comparison with literal
+  ./is-op.py:10:
+   > y = x is not -6
+   object comparison with literal
+  [1]
+
+  $ cat > for-nolineno.py <<EOF
+  > except:
+  > EOF
+  $ "$check_code" for-nolineno.py --nolineno
+  for-nolineno.py:0:
+   > except:
+   naked except clause
+  [1]
+
+  $ cat > warning.t <<EOF
+  >   $ function warnonly {
+  >   > }
+  >   $ diff -N aaa
+  >   $ function onwarn {}
+  > EOF
+  $ "$check_code" warning.t
+  $ "$check_code" --warn warning.t
+  warning.t:1:
+   >   $ function warnonly {
+   warning: don't use 'function', use old style
+  warning.t:3:
+   >   $ diff -N aaa
+   warning: don't use 'diff -N'
+  warning.t:4:
+   >   $ function onwarn {}
+   warning: don't use 'function', use old style
+  [1]
+  $ cat > raise-format.py <<EOF
+  > raise SomeException, message
+  > # this next line is okay
+  > raise SomeException(arg1, arg2)
+  > EOF
+  $ "$check_code" not-existing.py raise-format.py
+  Skipping*not-existing.py* (glob)
+  raise-format.py:1:
+   > raise SomeException, message
+   don't use old-style two-argument raise, use Exception(message)
+  [1]
+
+  $ cat > rst.py <<EOF
+  > """problematic rst text
+  > 
+  > .. note::
+  >     wrong
+  > """
+  > 
+  > '''
+  > 
+  > .. note::
+  > 
+  >     valid
+  > 
+  > new text
+  > 
+  >     .. note::
+  > 
+  >         also valid
+  > '''
+  > 
+  > """mixed
+  > 
+  > .. note::
+  > 
+  >   good
+  > 
+  >     .. note::
+  >         plus bad
+  > """
+  > EOF
+  $ $check_code -w rst.py
+  rst.py:3:
+   > .. note::
+   warning: add two newlines after '.. note::'
+  rst.py:26:
+   >     .. note::
+   warning: add two newlines after '.. note::'
+  [1]
+
+  $ cat > ./map-inside-gettext.py <<EOF
+  > print _("map inside gettext %s" % v)
+  > 
+  > print _("concatenating " " by " " space %s" % v)
+  > print _("concatenating " + " by " + " '+' %s" % v)
+  > 
+  > print _("mapping operation in different line %s"
+  >         % v)
+  > 
+  > print _(
+  >         "leading spaces inside of '(' %s" % v)
+  > EOF
+  $ "$check_code" ./map-inside-gettext.py
+  ./map-inside-gettext.py:1:
+   > print _("map inside gettext %s" % v)
+   don't use % inside _()
+  ./map-inside-gettext.py:3:
+   > print _("concatenating " " by " " space %s" % v)
+   don't use % inside _()
+  ./map-inside-gettext.py:4:
+   > print _("concatenating " + " by " + " '+' %s" % v)
+   don't use % inside _()
+  ./map-inside-gettext.py:6:
+   > print _("mapping operation in different line %s"
+   don't use % inside _()
+  ./map-inside-gettext.py:9:
+   > print _(
+   don't use % inside _()
+  [1]
+
+web templates
+
+  $ mkdir -p mercurial/templates
+  $ cat > mercurial/templates/example.tmpl <<EOF
+  > {desc}
+  > {desc|escape}
+  > {desc|firstline}
+  > {desc|websub}
+  > EOF
+
+  $ "$check_code" --warnings mercurial/templates/example.tmpl
+  mercurial/templates/example.tmpl:2:
+   > {desc|escape}
+   warning: follow desc keyword with either firstline or websub
+  [1]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-contrib-check-commit.t	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,28 @@
+Test the 'check-commit' script
+==============================
+
+Test long lines in header (should not be reported as too long description)
+
+  $ cat > patch-with-long-header.diff << EOF
+  > # HG changeset patch
+  > # User timeless <timeless@mozdev.org>
+  > # Date 1448911706 0
+  > #      Mon Nov 30 19:28:26 2015 +0000
+  > # Node ID c41cb6d2b7dbd62b1033727f8606b8c09fc4aa88
+  > # Parent  42aa0e570eaa364a622bc4443b0bcb79b1100a58
+  > # ClownJoke This is a veryly long header that should not be warned about because its not the description
+  > transplant: use Oxford comma
+  > 
+  > diff --git a/hgext/transplant.py b/hgext/transplant.py
+  > --- a/hgext/transplant.py
+  > +++ b/hgext/transplant.py
+  > @@ -599,7 +599,7 @@
+  >              return
+  >          if not (opts.get('source') or revs or
+  >                  opts.get('merge') or opts.get('branch')):
+  > -            raise error.Abort(_('no source URL, branch revision or revision '
+  > +            raise error.Abort(_('no source URL, branch revision, or revision '
+  >                                 'list provided'))
+  >          if opts.get('all'):
+  > EOF
+  $ cat patch-with-long-header.diff | $TESTDIR/../contrib/check-commit
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-contrib-perf.t	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,148 @@
+#require test-repo
+
+Set vars:
+
+  $ CONTRIBDIR="$TESTDIR/../contrib"
+
+Prepare repo:
+
+  $ hg init
+
+  $ echo this is file a > a
+  $ hg add a
+  $ hg commit -m first
+
+  $ echo adding to file a >> a
+  $ hg commit -m second
+
+  $ echo adding more to file a >> a
+  $ hg commit -m third
+
+  $ hg up -r 0
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo merge-this >> a
+  $ hg commit -m merge-able
+  created new head
+
+  $ hg up -r 2
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+perfstatus
+
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > perfstatusext=$CONTRIBDIR/perf.py
+  > [perf]
+  > presleep=0
+  > stub=on
+  > parentscount=1
+  > EOF
+  $ hg help perfstatusext
+  perfstatusext extension - helper extension to measure performance
+  
+  list of commands:
+  
+   perfaddremove
+                 (no help text available)
+   perfancestors
+                 (no help text available)
+   perfancestorset
+                 (no help text available)
+   perfannotate  (no help text available)
+   perfbranchmap
+                 benchmark the update of a branchmap
+   perfcca       (no help text available)
+   perfchangeset
+                 (no help text available)
+   perfctxfiles  (no help text available)
+   perfdiffwd    Profile diff of working directory changes
+   perfdirfoldmap
+                 (no help text available)
+   perfdirs      (no help text available)
+   perfdirstate  (no help text available)
+   perfdirstatedirs
+                 (no help text available)
+   perfdirstatefoldmap
+                 (no help text available)
+   perfdirstatewrite
+                 (no help text available)
+   perffncacheencode
+                 (no help text available)
+   perffncacheload
+                 (no help text available)
+   perffncachewrite
+                 (no help text available)
+   perfheads     (no help text available)
+   perfindex     (no help text available)
+   perfloadmarkers
+                 benchmark the time to parse the on-disk markers for a repo
+   perflog       (no help text available)
+   perflookup    (no help text available)
+   perflrucachedict
+                 (no help text available)
+   perfmanifest  (no help text available)
+   perfmergecalculate
+                 (no help text available)
+   perfmoonwalk  benchmark walking the changelog backwards
+   perfnodelookup
+                 (no help text available)
+   perfparents   (no help text available)
+   perfpathcopies
+                 (no help text available)
+   perfrawfiles  (no help text available)
+   perfrevlog    (no help text available)
+   perfrevrange  (no help text available)
+   perfrevset    benchmark the execution time of a revset
+   perfstartup   (no help text available)
+   perfstatus    (no help text available)
+   perftags      (no help text available)
+   perftemplating
+                 (no help text available)
+   perfvolatilesets
+                 benchmark the computation of various volatile set
+   perfwalk      (no help text available)
+  
+  (use "hg help -v perfstatusext" to show built-in aliases and global options)
+  $ hg perfaddremove
+  $ hg perfancestors
+  $ hg perfancestorset 2
+  $ hg perfannotate a
+  $ hg perfbranchmap
+  $ hg perfcca
+  $ hg perfchangeset 2
+  $ hg perfctxfiles 2
+  $ hg perfdiffwd
+  $ hg perfdirfoldmap
+  $ hg perfdirs
+  $ hg perfdirstate
+  $ hg perfdirstatedirs
+  $ hg perfdirstatefoldmap
+  $ hg perfdirstatewrite
+  $ hg perffncacheencode
+  $ hg perffncacheload
+  $ hg perffncachewrite
+  transaction abort!
+  rollback completed
+  $ hg perfheads
+  $ hg perfindex
+  $ hg perfloadmarkers
+  $ hg perflog
+  $ hg perflookup 2
+  $ hg perflrucache
+  $ hg perfmanifest 2
+  $ hg perfmergecalculate -r 3
+  $ hg perfmoonwalk
+  $ hg perfnodelookup 2
+  $ hg perfpathcopies 1 2
+  $ hg perfrawfiles 2
+  $ hg perfrevlog .hg/store/data/a.i
+  $ hg perfrevrange
+  $ hg perfrevset 'all()'
+  $ hg perfstartup
+  $ hg perfstatus
+  $ hg perftags
+  $ hg perftemplating
+  $ hg perfvolatilesets
+  $ hg perfwalk
+  $ hg perfparents
+
--- a/tests/test-convert-filemap.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-convert-filemap.t	Thu Dec 17 17:27:32 2015 -0600
@@ -740,4 +740,48 @@
      - converted/a
      - toberemoved
   
+  $ cd ..
 
+Test case where cleanp2 contains a file that doesn't exist in p2 - for
+example because filemap changed.
+
+  $ hg init cleanp2
+  $ cd cleanp2
+  $ touch f f1 f2 && hg ci -Aqm '0'
+  $ echo f1 > f1 && echo >> f && hg ci -m '1'
+  $ hg up -qr0 && echo f2 > f2 && echo >> f && hg ci -qm '2'
+  $ echo "include f" > filemap
+  $ hg convert --filemap filemap .
+  assuming destination .-hg
+  initializing destination .-hg repository
+  scanning source...
+  sorting...
+  converting...
+  2 0
+  1 1
+  0 2
+  $ hg merge && hg ci -qm '3'
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ echo "include ." > filemap
+  $ hg convert --filemap filemap .
+  assuming destination .-hg
+  scanning source...
+  sorting...
+  converting...
+  0 3
+  $ hg -R .-hg log -G -T '{shortest(node)} {desc}\n{files % "- {file}\n"}\n'
+  o    e9ed 3
+  |\
+  | o  33a0 2
+  | |  - f
+  | |
+  o |  f73e 1
+  |/   - f
+  |
+  o  d681 0
+     - f
+  
+  $ hg -R .-hg mani -r tip
+  f
+  $ cd ..
--- a/tests/test-convert-git.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-convert-git.t	Thu Dec 17 17:27:32 2015 -0600
@@ -54,7 +54,7 @@
   $ hg convert --config extensions.progress= --config progress.assume-tty=1 \
   >   --config progress.delay=0 --config progress.changedelay=0 \
   >   --config progress.refresh=0 --config progress.width=60 \
-  > --datesort git-repo
+  >   --config progress.format='topic, bar, number' --datesort git-repo
   \r (no-eol) (esc)
   scanning [======>                                     ] 1/6\r (no-eol) (esc)
   scanning [=============>                              ] 2/6\r (no-eol) (esc)
@@ -173,7 +173,8 @@
   $ hg convert --datesort git-repo2 fullrepo \
   > --config extensions.progress= --config progress.assume-tty=1 \
   > --config progress.delay=0 --config progress.changedelay=0 \
-  > --config progress.refresh=0 --config progress.width=60
+  > --config progress.refresh=0 --config progress.width=60 \
+  > --config progress.format='topic, bar, number'
   \r (no-eol) (esc)
   scanning [===>                                        ] 1/9\r (no-eol) (esc)
   scanning [========>                                   ] 2/9\r (no-eol) (esc)
@@ -533,8 +534,7 @@
   $ git commit -q -m "remove .gitmodules" .gitmodules
   $ git commit -q -m "missing .gitmodules"
   $ cd ..
-  $ hg convert git-repo6 hg-repo6 --traceback
-  fatal: Path '.gitmodules' does not exist in '*' (glob)
+  $ hg convert git-repo6 hg-repo6 --traceback 2>&1 | grep -v "fatal: Path '.gitmodules' does not exist"
   initializing destination hg-repo6 repository
   scanning source...
   sorting...
@@ -689,10 +689,7 @@
 
   $ cd git-repo7
   $ echo a >> a
-  $ git commit -am "move master forward"
-  [master 0c81947] move master forward
-   Author: nottest <test@example.org>
-   1 file changed, 1 insertion(+)
+  $ git commit -q -am "move master forward"
   $ cd ..
   $ rm -rf hg-repo7
   $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
--- a/tests/test-copy-move-merge.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-copy-move-merge.t	Thu Dec 17 17:27:32 2015 -0600
@@ -35,12 +35,12 @@
    preserving a for resolve of c
   removing a
    b: remote moved from a -> m (premerge)
-  picked tool ':merge' for b (binary False symlink False)
+  picked tool ':merge' for b (binary False symlink False changedelete False)
   merging a and b to b
   my b@add3f11052fa+ other b@17c05bb7fcb6 ancestor a@b8bf91eeebbc
    premerge successful
    c: remote moved from a -> m (premerge)
-  picked tool ':merge' for c (binary False symlink False)
+  picked tool ':merge' for c (binary False symlink False changedelete False)
   merging a and c to c
   my c@add3f11052fa+ other c@17c05bb7fcb6 ancestor a@b8bf91eeebbc
    premerge successful
@@ -83,7 +83,7 @@
   $ hg rebase --keep -d 1 -b 2 --config extensions.rebase= --config experimental.disablecopytrace=True
   rebasing 2:add3f11052fa "other" (tip)
   remote changed a which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
 
   $ cat b
   1
--- a/tests/test-copy.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-copy.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,3 +1,9 @@
+# enable bundle2 in advance
+
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > usegeneraldelta=yes
+  > EOF
 
   $ mkdir part1
   $ cd part1
@@ -87,7 +93,7 @@
   copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
 
   $ md5sum.py .hg/store/data/b.i
-  4999f120a3b88713bbefddd195cf5133  .hg/store/data/b.i
+  44913824c8f5890ae218f9829535922e  .hg/store/data/b.i
   $ hg cat b > bsum
   $ md5sum.py bsum
   60b725f10c9c85c70d97880dfe8191b3  bsum
--- a/tests/test-debugbundle.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-debugbundle.t	Thu Dec 17 17:27:32 2015 -0600
@@ -6,7 +6,7 @@
   $ touch a ; hg add a ; hg ci -ma
   $ touch b ; hg add b ; hg ci -mb
   $ touch c ; hg add c ; hg ci -mc
-  $ hg bundle --base 0 --rev tip bundle.hg -v
+  $ hg bundle --base 0 --rev tip bundle.hg -v --type v1
   2 changesets found
   uncompressed size of bundle content:
        332 (changelog)
--- a/tests/test-debugcommands.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-debugcommands.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,3 +1,8 @@
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > usegeneraldelta=yes
+  > EOF
+
   $ hg init debugrevlog
   $ cd debugrevlog
   $ echo a > a
@@ -5,7 +10,7 @@
   adding a
   $ hg debugrevlog -m
   format : 1
-  flags  : inline
+  flags  : inline, generaldelta
   
   revisions     :  1
       merges    :  0 ( 0.00%)
@@ -27,18 +32,44 @@
 
 Test debugindex, with and without the --debug flag
   $ hg debugindex a
-     rev    offset  length   .... linkrev nodeid       p1           p2 (re)
+     rev    offset  length  ..... linkrev nodeid       p1           p2 (re)
        0         0       3   ....       0 b789fdd96dc2 000000000000 000000000000 (re)
   $ hg --debug debugindex a
-     rev    offset  length   .... linkrev nodeid                                   p1                                       p2 (re)
+     rev    offset  length  ..... linkrev nodeid                                   p1                                       p2 (re)
        0         0       3   ....       0 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 (re)
   $ hg debugindex -f 1 a
-     rev flag   offset   length     size   ....   link     p1     p2       nodeid (re)
+     rev flag   offset   length     size  .....   link     p1     p2       nodeid (re)
        0 0000        0        3        2   ....      0     -1     -1 b789fdd96dc2 (re)
   $ hg --debug debugindex -f 1 a
-     rev flag   offset   length     size   ....   link     p1     p2                                   nodeid (re)
+     rev flag   offset   length     size  .....   link     p1     p2                                   nodeid (re)
        0 0000        0        3        2   ....      0     -1     -1 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 (re)
 
+debugdelta chain basic output
+
+  $ hg debugdeltachain -m
+      rev  chain# chainlen     prev   delta       size    rawsize  chainsize     ratio   lindist extradist extraratio
+        0       1        1       -1    base         44         43         44   1.02326        44         0    0.00000
+
+  $ hg debugdeltachain -m -T '{rev} {chainid} {chainlen}\n'
+  0 1 1
+
+  $ hg debugdeltachain -m -Tjson
+  [
+   {
+    "chainid": 1,
+    "chainlen": 1,
+    "chainratio": 1.02325581395,
+    "chainsize": 44,
+    "compsize": 44,
+    "deltatype": "base",
+    "extradist": 0,
+    "extraratio": 0.0,
+    "lindist": 44,
+    "prevrev": -1,
+    "rev": 0,
+    "uncompsize": 43
+   }
+  ]
 
 Test max chain len
   $ cat >> $HGRCPATH << EOF
--- a/tests/test-default-push.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-default-push.t	Thu Dec 17 17:27:32 2015 -0600
@@ -69,3 +69,26 @@
   $ hg --cwd b push doesnotexist
   abort: repository doesnotexist does not exist!
   [255]
+
+:pushurl is used when defined
+
+  $ hg -q clone a pushurlsource
+  $ hg -q clone a pushurldest
+  $ cd pushurlsource
+  $ cat > .hg/hgrc << EOF
+  > [paths]
+  > default = https://example.com/not/relevant
+  > default:pushurl = file://`pwd`/../pushurldest
+  > EOF
+
+  $ touch pushurl
+  $ hg -q commit -A -m 'add pushurl'
+  $ hg push
+  pushing to file:/*/$TESTTMP/pushurlsource/../pushurldest (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+  $ cd ..
--- a/tests/test-devel-warnings.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-devel-warnings.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,6 +1,6 @@
 
   $ cat << EOF > buggylocking.py
-  > """A small extension that acquire locks in the wrong order
+  > """A small extension that tests our developer warnings
   > """
   > 
   > from mercurial import cmdutil, repair, revset
@@ -47,6 +47,12 @@
   >         repair.strip(repo.ui, repo, [repo['.'].node()])
   >     finally:
   >         lo.release()
+  > @command('oldanddeprecated', [], '')
+  > def oldanddeprecated(ui, repo):
+  >     """test deprecation warning API"""
+  >     def foobar(ui):
+  >         ui.deprecwarn('foorbar is deprecated, go shopping', '42.1337')
+  >     foobar(ui)
   > 
   > def oldstylerevset(repo, subset, x):
   >     return list(subset)
@@ -114,5 +120,22 @@
   $ hg log -r "oldstyle()" -T '{rev}\n'
   devel-warn: revset "oldstyle" use list instead of smartset, (upgrade your code) at: */mercurial/revset.py:* (mfunc) (glob)
   0
+  $ hg oldanddeprecated
+  devel-warn: foorbar is deprecated, go shopping
+  (compatibility will be dropped after Mercurial-42.1337, update your code.) at: $TESTTMP/buggylocking.py:53 (oldanddeprecated)
 
+  $ hg oldanddeprecated --traceback
+  devel-warn: foorbar is deprecated, go shopping
+  (compatibility will be dropped after Mercurial-42.1337, update your code.) at:
+   */hg:* in <module> (glob)
+   */mercurial/dispatch.py:* in run (glob)
+   */mercurial/dispatch.py:* in dispatch (glob)
+   */mercurial/dispatch.py:* in _runcatch (glob)
+   */mercurial/dispatch.py:* in _dispatch (glob)
+   */mercurial/dispatch.py:* in runcommand (glob)
+   */mercurial/dispatch.py:* in _runcommand (glob)
+   */mercurial/dispatch.py:* in checkargs (glob)
+   */mercurial/dispatch.py:* in <lambda> (glob)
+   */mercurial/util.py:* in check (glob)
+   $TESTTMP/buggylocking.py:* in oldanddeprecated (glob)
   $ cd ..
--- a/tests/test-diff-binary-file.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-diff-binary-file.t	Thu Dec 17 17:27:32 2015 -0600
@@ -44,6 +44,26 @@
   diff --git a/binfile.bin b/binfile.bin
   Binary file binfile.bin has changed
 
+  $ HGPLAIN=1 hg diff --config diff.nobinary=True --git -r 0 -r 1
+  diff --git a/binfile.bin b/binfile.bin
+  index 37ba3d1c6f17137d9c5f5776fa040caf5fe73ff9..58dc31a9e2f40f74ff3b45903f7d620b8e5b7356
+  GIT binary patch
+  literal 594
+  zc$@)J0<HatP)<h;3K|Lk000e1NJLTq000mG000mO0ssI2kdbIM00009a7bBm000XU
+  z000XU0RWnu7ytkO2XskIMF-Uh9TW;VpMjwv0005-Nkl<ZD9@FWPs=e;7{<>W$NUkd
+  zX$nnYLt$-$V!?uy+1V%`z&Eh=ah|duER<4|QWhju3gb^nF*8iYobxWG-qqXl=2~5M
+  z*IoDB)sG^CfNuoBmqLTVU^<;@nwHP!1wrWd`{(mHo6VNXWtyh{alzqmsH*yYzpvLT
+  zLdY<T=ks|woh-`&01!ej#(xbV1f|pI*=%;d-%F*E*X#ZH`4I%6SS+$EJDE&ct=8po
+  ziN#{?_j|kD%Cd|oiqds`xm@;oJ-^?NG3Gdqrs?5u*zI;{nogxsx~^|Fn^Y?Gdc6<;
+  zfMJ+iF1J`LMx&A2?dEwNW8ClebzPTbIh{@$hS6*`kH@1d%Lo7fA#}N1)oN7`gm$~V
+  z+wDx#)OFqMcE{s!JN0-xhG8ItAjVkJwEcb`3WWlJfU2r?;Pd%dmR+q@mSri5q9_W-
+  zaR2~ECX?B2w+zELozC0s*6Z~|QG^f{3I#<`?)Q7U-JZ|q5W;9Q8i_=pBuSzunx=U;
+  z9C)5jBoYw9^?EHyQl(M}1OlQcCX>lXB*ODN003Z&P17_@)3Pi=i0wb04<W?v-u}7K
+  zXmmQA+wDgE!qR9o8jr`%=ab_&uh(l?R=r;Tjiqon91I2-hIu?57~@*4h7h9uORK#=
+  gQItJW-{SoTm)8|5##k|m00000NkvXXu0mjf3JwksH2?qr
+  
+
+
   $ hg diff --git -r 2 -r 3
   diff --git a/binfile.bin b/nonbinfile
   copy from binfile.bin
--- a/tests/test-double-merge.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-double-merge.t	Thu Dec 17 17:27:32 2015 -0600
@@ -38,12 +38,12 @@
    preserving foo for resolve of bar
    preserving foo for resolve of foo
    bar: remote copied from foo -> m (premerge)
-  picked tool ':merge' for bar (binary False symlink False)
+  picked tool ':merge' for bar (binary False symlink False changedelete False)
   merging foo and bar to bar
   my bar@6a0df1dad128+ other bar@484bf6903104 ancestor foo@e6dc8efe11cc
    premerge successful
    foo: versions differ -> m (premerge)
-  picked tool ':merge' for foo (binary False symlink False)
+  picked tool ':merge' for foo (binary False symlink False changedelete False)
   merging foo
   my foo@6a0df1dad128+ other foo@484bf6903104 ancestor foo@e6dc8efe11cc
    premerge successful
--- a/tests/test-encoding.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-encoding.t	Thu Dec 17 17:27:32 2015 -0600
@@ -272,3 +272,14 @@
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 
   $ cd ..
+
+Test roundtrip encoding/decoding of utf8b for generated data
+
+#if hypothesis
+
+  >>> from hypothesishelpers import *
+  >>> from mercurial import encoding
+  >>> roundtrips(st.binary(), encoding.fromutf8b, encoding.toutf8b)
+  Round trip OK
+
+#endif
--- a/tests/test-extdiff.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-extdiff.t	Thu Dec 17 17:27:32 2015 -0600
@@ -126,6 +126,25 @@
   diff-like tools yield a non-zero exit code
 #endif
 
+issue3153: ensure using extdiff with removed subrepos doesn't crash:
+
+  $ hg init suba
+  $ cd suba
+  $ echo suba > suba
+  $ hg add
+  adding suba
+  $ hg ci -m "adding suba file"
+  $ cd ..
+  $ echo suba=suba > .hgsub
+  $ hg add
+  adding .hgsub
+  $ hg ci -Sm "adding subrepo"
+  $ echo > .hgsub
+  $ hg ci -m "removing subrepo"
+  $ hg falabala -r 4 -r 5 -S
+  diffing a.398e36faf9c6 a.5ab95fb166c4
+  [1]
+
 issue4463: usage of command line configuration without additional quoting
 
   $ cat <<EOF >> $HGRCPATH
--- a/tests/test-extension.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-extension.t	Thu Dec 17 17:27:32 2015 -0600
@@ -367,6 +367,15 @@
 
   $ echo 'debugextension = !' >> $HGRCPATH
 
+Asking for help about a deprecated extension should do something useful:
+
+  $ hg help glog
+  'glog' is provided by the following extension:
+  
+      graphlog      command to view revision graphs from a shell (DEPRECATED)
+  
+  (use "hg help extensions" for information on enabling extensions)
+
 Extension module help vs command help:
 
   $ echo 'extdiff =' >> $HGRCPATH
@@ -1009,6 +1018,50 @@
   
     throw  1.twentythree
 
+Refuse to load extensions with minimum version requirements
+
+  $ cat > minversion1.py << EOF
+  > from mercurial import util
+  > util.version = lambda: '3.5.2'
+  > minimumhgversion = '3.6'
+  > EOF
+  $ hg --config extensions.minversion=minversion1.py version
+  (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
+  Mercurial Distributed SCM (version 3.5.2)
+  (see https://mercurial-scm.org for more information)
+  
+  Copyright (C) 2005-* Matt Mackall and others (glob)
+  This is free software; see the source for copying conditions. There is NO
+  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+  $ cat > minversion2.py << EOF
+  > from mercurial import util
+  > util.version = lambda: '3.6'
+  > minimumhgversion = '3.7'
+  > EOF
+  $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
+  (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
+
+Can load version that is only off by point release
+
+  $ cat > minversion2.py << EOF
+  > from mercurial import util
+  > util.version = lambda: '3.6.1'
+  > minimumhgversion = '3.6'
+  > EOF
+  $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
+  [1]
+
+Can load minimum version identical to current
+
+  $ cat > minversion3.py << EOF
+  > from mercurial import util
+  > util.version = lambda: '3.5'
+  > minimumhgversion = '3.5'
+  > EOF
+  $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
+  [1]
+
 Restore HGRCPATH
 
   $ HGRCPATH=$ORGHGRCPATH
--- a/tests/test-fileset-generated.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-fileset-generated.t	Thu Dec 17 17:27:32 2015 -0600
@@ -47,6 +47,13 @@
   ! missing_content2_missing-tracked
   ! missing_missing_missing-tracked
 
+  $ hg st -A 'set:missing()'
+  ! content1_content1_missing-tracked
+  ! content1_content2_missing-tracked
+  ! content1_missing_missing-tracked
+  ! missing_content2_missing-tracked
+  ! missing_missing_missing-tracked
+
   $ hg st -A 'set:unknown()'
   ? content1_missing_content1-untracked
   ? content1_missing_content3-untracked
--- a/tests/test-fileset.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-fileset.t	Thu Dec 17 17:27:32 2015 -0600
@@ -73,6 +73,8 @@
   a2
   $ fileset 'deleted()'
   a1
+  $ fileset 'missing()'
+  a1
   $ fileset 'unknown()'
   c3
   $ fileset 'ignored()'
@@ -163,7 +165,7 @@
   $ hg merge
   merging b2
   warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
-  6 files updated, 0 files merged, 1 files removed, 1 files unresolved
+  * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
   use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
   [1]
   $ fileset 'resolved()'
--- a/tests/test-fncache.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-fncache.t	Thu Dec 17 17:27:32 2015 -0600
@@ -96,6 +96,7 @@
   .hg/phaseroots
   .hg/requires
   .hg/undo
+  .hg/undo.backup.dirstate
   .hg/undo.backupfiles
   .hg/undo.bookmarks
   .hg/undo.branch
@@ -132,6 +133,7 @@
   .hg/store/undo
   .hg/store/undo.backupfiles
   .hg/store/undo.phaseroots
+  .hg/undo.backup.dirstate
   .hg/undo.bookmarks
   .hg/undo.branch
   .hg/undo.desc
@@ -203,18 +205,36 @@
   $ cat > exceptionext.py <<EOF
   > import os
   > from mercurial import commands, error
-  > from mercurial.extensions import wrapfunction
+  > from mercurial.extensions import wrapcommand, wrapfunction
   > 
   > def lockexception(orig, vfs, lockname, wait, releasefn, *args, **kwargs):
   >     def releasewrap():
+  >         l.held = False # ensure __del__ is a noop
   >         raise error.Abort("forced lock failure")
-  >     return orig(vfs, lockname, wait, releasewrap, *args, **kwargs)
+  >     l = orig(vfs, lockname, wait, releasewrap, *args, **kwargs)
+  >     return l
   > 
   > def reposetup(ui, repo):
   >     wrapfunction(repo, '_lock', lockexception)
   > 
   > cmdtable = {}
   > 
+  > # wrap "commit" command to prevent wlock from being '__del__()'-ed
+  > # at the end of dispatching (for intentional "forced lcok failure")
+  > def commitwrap(orig, ui, repo, *pats, **opts):
+  >     repo = repo.unfiltered() # to use replaced repo._lock certainly
+  >     wlock = repo.wlock()
+  >     try:
+  >         return orig(ui, repo, *pats, **opts)
+  >     finally:
+  >         # multiple 'relase()' is needed for complete releasing wlock,
+  >         # because "forced" abort at last releasing store lock
+  >         # prevents wlock from being released at same 'lockmod.release()'
+  >         for i in range(wlock.held):
+  >             wlock.release()
+  > 
+  > def extsetup(ui):
+  >     wrapcommand(commands.table, "commit", commitwrap)
   > EOF
   $ extpath=`pwd`/exceptionext.py
   $ hg init fncachetxn
--- a/tests/test-generaldelta.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-generaldelta.t	Thu Dec 17 17:27:32 2015 -0600
@@ -3,10 +3,11 @@
 implementation of parentdelta: third manifest revision would be fully
 inserted due to big distance from its paren revision (zero).
 
-  $ hg init repo
+  $ hg init repo --config format.generaldelta=no --config format.usegeneraldelta=no
   $ cd repo
   $ echo foo > foo
   $ echo bar > bar
+  $ echo baz > baz
   $ hg commit -q -Am boo
   $ hg clone --pull . ../gdrepo -q --config format.generaldelta=yes
   $ for r in 1 2 3; do
@@ -62,7 +63,7 @@
   o  0 3903 a
   
   $ cd ..
-  $ hg init client
+  $ hg init client --config format.generaldelta=false --config format.usegeneraldelta=false
   $ cd client
   $ hg pull -q ../server -r 4
   $ hg debugindex x
@@ -71,10 +72,55 @@
 
   $ cd ..
 
+Test "usegeneraldelta" config
+(repo are general delta, but incoming bundle are not re-deltified)
+
+delta coming from the server base delta server are not recompressed.
+(also include the aggressive version for comparison)
+
+  $ hg clone repo --pull --config format.usegeneraldelta=1 usegd
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 6 changes to 3 files (+2 heads)
+  updating to branch default
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg clone repo --pull --config format.generaldelta=1 full
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 6 changes to 3 files (+2 heads)
+  updating to branch default
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg -R repo debugindex -m
+     rev    offset  length   base linkrev nodeid       p1           p2
+       0         0     104      0       0 cef96823c800 000000000000 000000000000
+       1       104      57      0       1 58ab9a8d541d cef96823c800 000000000000
+       2       161      57      0       2 134fdc6fd680 cef96823c800 000000000000
+       3       218     104      3       3 723508934dad cef96823c800 000000000000
+  $ hg -R usegd debugindex -m
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0     104     -1       0 cef96823c800 000000000000 000000000000
+       1       104      57      0       1 58ab9a8d541d cef96823c800 000000000000
+       2       161      57      1       2 134fdc6fd680 cef96823c800 000000000000
+       3       218      57      0       3 723508934dad cef96823c800 000000000000
+  $ hg -R full debugindex -m
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0     104     -1       0 cef96823c800 000000000000 000000000000
+       1       104      57      0       1 58ab9a8d541d cef96823c800 000000000000
+       2       161      57      0       2 134fdc6fd680 cef96823c800 000000000000
+       3       218      57      0       3 723508934dad cef96823c800 000000000000
+
 Test format.aggressivemergedeltas
 
   $ hg init --config format.generaldelta=1 aggressive
   $ cd aggressive
+  $ cat << EOF >> .hg/hgrc
+  > [format]
+  > generaldelta = 1
+  > EOF
   $ touch a b c d e
   $ hg commit -Aqm side1
   $ hg up -q null
@@ -87,8 +133,8 @@
   $ hg debugindex -m
      rev    offset  length  delta linkrev nodeid       p1           p2
        0         0      59     -1       0 8dde941edb6e 000000000000 000000000000
-       1        59      59     -1       1 315c023f341d 000000000000 000000000000
-       2       118      65      1       2 2ab389a983eb 315c023f341d 8dde941edb6e
+       1        59      61      0       1 315c023f341d 000000000000 000000000000
+       2       120      65      1       2 2ab389a983eb 315c023f341d 8dde941edb6e
 
   $ hg strip -q -r . --config extensions.strip=
 
@@ -99,8 +145,8 @@
   $ hg debugindex -m
      rev    offset  length  delta linkrev nodeid       p1           p2
        0         0      59     -1       0 8dde941edb6e 000000000000 000000000000
-       1        59      59     -1       1 315c023f341d 000000000000 000000000000
-       2       118      62      0       2 2ab389a983eb 315c023f341d 8dde941edb6e
+       1        59      61      0       1 315c023f341d 000000000000 000000000000
+       2       120      62      0       2 2ab389a983eb 315c023f341d 8dde941edb6e
 
 Test that strip bundle use bundle2
   $ hg --config extensions.strip= strip .
--- a/tests/test-globalopts.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-globalopts.t	Thu Dec 17 17:27:32 2015 -0600
@@ -349,6 +349,7 @@
    glossary      Glossary
    hgignore      Syntax for Mercurial Ignore Files
    hgweb         Configuring hgweb
+   internals     Technical implementation topics
    merge-tools   Merge Tools
    multirevs     Specifying Multiple Revisions
    patterns      File Name Patterns
@@ -431,6 +432,7 @@
    glossary      Glossary
    hgignore      Syntax for Mercurial Ignore Files
    hgweb         Configuring hgweb
+   internals     Technical implementation topics
    merge-tools   Merge Tools
    multirevs     Specifying Multiple Revisions
    patterns      File Name Patterns
--- a/tests/test-glog.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-glog.t	Thu Dec 17 17:27:32 2015 -0600
@@ -2400,4 +2400,25 @@
   @  3:5918b8d165d1
   |
 
+node template with changeset_printer:
+
+  $ hg log -Gqr 5:7 --config ui.graphnodetemplate='{rev}'
+  7  7:02dbb8e276b8
+  |
+  6    6:fc281d8ff18d
+  |\
+  5 |  5:99b31f1c2782
+  | |
+
+node template with changeset_templater (shared cache variable):
+
+  $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
+  > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
+  o  7 foo-bar+1
+  |
+  #    6 foo-bar+0
+  |\
+  o |  5 null+5
+  | |
+
   $ cd ..
--- a/tests/test-graft.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-graft.t	Thu Dec 17 17:27:32 2015 -0600
@@ -138,7 +138,7 @@
   grafting 4:9c233e8e184d "4"
   grafting 3:4c60f11aa304 "3"
 
-  $ HGEDITOR=cat hg graft 1 5 4 3 'merge()' 2 --debug
+  $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
   skipping ungraftable merge revision 6
   scanning for duplicate grafts
   skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
@@ -154,7 +154,7 @@
    ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
    preserving b for resolve of b
    b: local copied/moved from a -> m (premerge)
-  picked tool ':merge' for b (binary False symlink False)
+  picked tool ':merge' for b (binary False symlink False changedelete False)
   merging b and a to b
   my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
    premerge successful
@@ -174,6 +174,8 @@
   e
   committing manifest
   committing changelog
+  $ HGEDITOR=cat hg graft 4 3 --log --debug
+  scanning for duplicate grafts
   grafting 4:9c233e8e184d "4"
     searching for copies back to rev 1
   resolving manifests
@@ -184,17 +186,22 @@
   getting d
    b: remote unchanged -> k
    e: versions differ -> m (premerge)
-  picked tool ':merge' for e (binary False symlink False)
+  picked tool ':merge' for e (binary False symlink False changedelete False)
   merging e
   my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
    e: versions differ -> m (merge)
-  picked tool ':merge' for e (binary False symlink False)
+  picked tool ':merge' for e (binary False symlink False changedelete False)
   my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
   warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
   abort: unresolved conflicts, can't continue
-  (use hg resolve and hg graft --continue)
+  (use hg resolve and hg graft --continue --log)
   [255]
 
+Summary should mention graft:
+
+  $ hg summary |grep graft
+  commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
+
 Commit while interrupted should fail:
 
   $ hg ci -m 'commit interrupted graft'
--- a/tests/test-hardlinks.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-hardlinks.t	Thu Dec 17 17:27:32 2015 -0600
@@ -229,6 +229,7 @@
   2 r4/.hg/store/undo.backup.phaseroots
   2 r4/.hg/store/undo.backupfiles
   2 r4/.hg/store/undo.phaseroots
+  2 r4/.hg/undo.backup.dirstate
   2 r4/.hg/undo.bookmarks
   2 r4/.hg/undo.branch
   2 r4/.hg/undo.desc
@@ -264,6 +265,7 @@
   2 r4/.hg/store/undo.backup.phaseroots
   2 r4/.hg/store/undo.backupfiles
   2 r4/.hg/store/undo.phaseroots
+  2 r4/.hg/undo.backup.dirstate
   2 r4/.hg/undo.bookmarks
   2 r4/.hg/undo.branch
   2 r4/.hg/undo.desc
--- a/tests/test-help.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-help.t	Thu Dec 17 17:27:32 2015 -0600
@@ -111,6 +111,7 @@
    glossary      Glossary
    hgignore      Syntax for Mercurial Ignore Files
    hgweb         Configuring hgweb
+   internals     Technical implementation topics
    merge-tools   Merge Tools
    multirevs     Specifying Multiple Revisions
    patterns      File Name Patterns
@@ -187,6 +188,7 @@
    glossary      Glossary
    hgignore      Syntax for Mercurial Ignore Files
    hgweb         Configuring hgweb
+   internals     Technical implementation topics
    merge-tools   Merge Tools
    multirevs     Specifying Multiple Revisions
    patterns      File Name Patterns
@@ -372,17 +374,30 @@
   
       If no names are given, add all files to the repository.
   
-      An example showing how new (unknown) files are added automatically by "hg
-      add":
+      Examples:
+  
+        - New (unknown) files are added automatically by "hg add":
   
-        $ ls
-        foo.c
-        $ hg status
-        ? foo.c
-        $ hg add
-        adding foo.c
-        $ hg status
-        A foo.c
+            $ ls
+            foo.c
+            $ hg status
+            ? foo.c
+            $ hg add
+            adding foo.c
+            $ hg status
+            A foo.c
+  
+        - Specific files to be added can be specified:
+  
+            $ ls
+            bar.c  foo.c
+            $ hg status
+            ? bar.c
+            ? foo.c
+            $ hg add bar.c
+            $ hg status
+            A bar.c
+            ? foo.c
   
       Returns 0 if all files are successfully added.
   
@@ -760,6 +775,7 @@
    glossary      Glossary
    hgignore      Syntax for Mercurial Ignore Files
    hgweb         Configuring hgweb
+   internals     Technical implementation topics
    merge-tools   Merge Tools
    multirevs     Specifying Multiple Revisions
    patterns      File Name Patterns
@@ -799,6 +815,8 @@
                  description
    debugdata     dump the contents of a data file revision
    debugdate     parse and display a date
+   debugdeltachain
+                 dump information about delta chains in a revlog
    debugdirstate
                  show the contents of the current dirstate
    debugdiscovery
@@ -847,6 +865,159 @@
   
   (use "hg help -v debug" to show built-in aliases and global options)
 
+internals topic renders index of available sub-topics
+
+  $ hg help internals
+  Technical implementation topics
+  """""""""""""""""""""""""""""""
+  
+       bundles       container for exchange of repository data
+       changegroups  representation of revlog data
+
+sub-topics can be accessed
+
+  $ hg help internals.changegroups
+      Changegroups
+      ============
+  
+      Changegroups are representations of repository revlog data, specifically
+      the changelog, manifest, and filelogs.
+  
+      There are 2 versions of changegroups: "1" and "2". From a high-level, they
+      are almost exactly the same, with the only difference being a header on
+      entries in the changeset segment.
+  
+      Changegroups consists of 3 logical segments:
+  
+        +---------------------------------+
+        |           |          |          |
+        | changeset | manifest | filelogs |
+        |           |          |          |
+        +---------------------------------+
+  
+      The principle building block of each segment is a *chunk*. A *chunk* is a
+      framed piece of data:
+  
+        +---------------------------------------+
+        |           |                           |
+        |  length   |           data            |
+        | (32 bits) |       <length> bytes      |
+        |           |                           |
+        +---------------------------------------+
+  
+      Each chunk starts with a 32-bit big-endian signed integer indicating the
+      length of the raw data that follows.
+  
+      There is a special case chunk that has 0 length ("0x00000000"). We call
+      this an *empty chunk*.
+  
+      Delta Groups
+      ------------
+  
+      A *delta group* expresses the content of a revlog as a series of deltas,
+      or patches against previous revisions.
+  
+      Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
+      to signal the end of the delta group:
+  
+        +------------------------------------------------------------------------+
+        |                |             |               |             |           |
+        | chunk0 length  | chunk0 data | chunk1 length | chunk1 data |    0x0    |
+        |   (32 bits)    |  (various)  |   (32 bits)   |  (various)  | (32 bits) |
+        |                |             |               |             |           |
+        +------------------------------------------------------------+-----------+
+  
+      Each *chunk*'s data consists of the following:
+  
+        +-----------------------------------------+
+        |              |              |           |
+        | delta header | mdiff header |   delta   |
+        |  (various)   |  (12 bytes)  | (various) |
+        |              |              |           |
+        +-----------------------------------------+
+  
+      The *length* field is the byte length of the remaining 3 logical pieces of
+      data. The *delta* is a diff from an existing entry in the changelog.
+  
+      The *delta header* is different between versions "1" and "2" of the
+      changegroup format.
+  
+      Version 1:
+  
+        +------------------------------------------------------+
+        |            |             |             |             |
+        |    node    |   p1 node   |   p2 node   |  link node  |
+        | (20 bytes) |  (20 bytes) |  (20 bytes) |  (20 bytes) |
+        |            |             |             |             |
+        +------------------------------------------------------+
+  
+      Version 2:
+  
+        +------------------------------------------------------------------+
+        |            |             |             |            |            |
+        |    node    |   p1 node   |   p2 node   | base node  | link node  |
+        | (20 bytes) |  (20 bytes) |  (20 bytes) | (20 bytes) | (20 bytes) |
+        |            |             |             |            |            |
+        +------------------------------------------------------------------+
+  
+      The *mdiff header* consists of 3 32-bit big-endian signed integers
+      describing offsets at which to apply the following delta content:
+  
+        +-------------------------------------+
+        |           |            |            |
+        |  offset   | old length | new length |
+        | (32 bits) |  (32 bits) |  (32 bits) |
+        |           |            |            |
+        +-------------------------------------+
+  
+      In version 1, the delta is always applied against the previous node from
+      the changegroup or the first parent if this is the first entry in the
+      changegroup.
+  
+      In version 2, the delta base node is encoded in the entry in the
+      changegroup. This allows the delta to be expressed against any parent,
+      which can result in smaller deltas and more efficient encoding of data.
+  
+      Changeset Segment
+      -----------------
+  
+      The *changeset segment* consists of a single *delta group* holding
+      changelog data. It is followed by an *empty chunk* to denote the boundary
+      to the *manifests segment*.
+  
+      Manifest Segment
+      ----------------
+  
+      The *manifest segment* consists of a single *delta group* holding manifest
+      data. It is followed by an *empty chunk* to denote the boundary to the
+      *filelogs segment*.
+  
+      Filelogs Segment
+      ----------------
+  
+      The *filelogs* segment consists of multiple sub-segments, each
+      corresponding to an individual file whose data is being described:
+  
+        +--------------------------------------+
+        |          |          |          |     |
+        | filelog0 | filelog1 | filelog2 | ... |
+        |          |          |          |     |
+        +--------------------------------------+
+  
+      The final filelog sub-segment is followed by an *empty chunk* to denote
+      the end of the segment and the overall changegroup.
+  
+      Each filelog sub-segment consists of the following:
+  
+        +------------------------------------------+
+        |               |            |             |
+        | filename size |  filename  | delta group |
+        |   (32 bits)   |  (various) |  (various)  |
+        |               |            |             |
+        +------------------------------------------+
+  
+      That is, a *chunk* consisting of the filename (not terminated or padded)
+      followed by N chunks constituting the *delta group* for this file.
 
 Test list of commands with command with no help text
 
@@ -1026,14 +1197,33 @@
       helphook1
       helphook2
 
+help -c should only show debug --debug
+
+  $ hg help -c --debug|egrep debug|wc -l|egrep '^\s*0\s*$'
+  [1]
+
+help -c should only show deprecated for -v
+
+  $ hg help -c -v|egrep DEPRECATED|wc -l|egrep '^\s*0\s*$'
+  [1]
+
 Test -e / -c / -k combinations
 
-  $ hg help -c progress
-  abort: no such help topic: progress
-  (try "hg help --keyword progress")
+  $ hg help -c|egrep '^[A-Z].*:|^ debug'
+  Commands:
+  $ hg help -e|egrep '^[A-Z].*:|^ debug'
+  Extensions:
+  $ hg help -k|egrep '^[A-Z].*:|^ debug'
+  Topics:
+  Commands:
+  Extensions:
+  Extension Commands:
+  $ hg help -c schemes
+  abort: no such help topic: schemes
+  (try "hg help --keyword schemes")
   [255]
-  $ hg help -e progress |head -1
-  progress extension - show progress bars for some actions (DEPRECATED)
+  $ hg help -e schemes |head -1
+  schemes extension - extend schemes with shortcuts to repository swarms
   $ hg help -c -k dates |egrep '^(Topics|Extensions|Commands):'
   Commands:
   $ hg help -e -k a |egrep '^(Topics|Extensions|Commands):'
@@ -1068,12 +1258,10 @@
   
   Commands:
   
-   bookmarks                    create a new bookmark or list existing bookmarks
-   clone                        make a copy of an existing repository
-   debugapplystreamclonebundle  apply a stream clone bundle file
-   debugcreatestreamclonebundle create a stream clone bundle file
-   paths                        show aliases for remote repositories
-   update                       update working directory (or switch revisions)
+   bookmarks create a new bookmark or list existing bookmarks
+   clone     make a copy of an existing repository
+   paths     show aliases for remote repositories
+   update    update working directory (or switch revisions)
   
   Extensions:
   
@@ -1192,28 +1380,43 @@
       "paths"
       -------
   
-      Assigns symbolic names to repositories. The left side is the symbolic
-      name, and the right gives the directory or URL that is the location of the
-      repository. Default paths can be declared by setting the following
-      entries.
+      Assigns symbolic names and behavior to repositories.
+  
+      Options are symbolic names defining the URL or directory that is the
+      location of the repository. Example:
+  
+        [paths]
+        my_server = https://example.com/my_repo
+        local_path = /home/me/repo
+  
+      These symbolic names can be used from the command line. To pull from
+      "my_server": "hg pull my_server". To push to "local_path": "hg push
+      local_path".
+  
+      Options containing colons (":") denote sub-options that can influence
+      behavior for that specific path. Example:
+  
+        [paths]
+        my_server = https://example.com/my_path
+        my_server:pushurl = ssh://example.com/my_path
+  
+      The following sub-options can be defined:
+  
+      "pushurl"
+         The URL to use for push operations. If not defined, the location
+         defined by the path's main entry is used.
+  
+      The following special named paths exist:
   
       "default"
-          Directory or URL to use when pulling if no source is specified.
-          (default: repository from which the current repository was cloned)
+         The URL or directory to use when no source or remote is specified.
+  
+         "hg clone" will automatically define this path to the location the
+         repository was cloned from.
   
       "default-push"
-          Optional. Directory or URL to use when pushing if no destination is
-          specified.
-  
-      Custom paths can be defined by assigning the path to a name that later can
-      be used from the command line. Example:
-  
-        [paths]
-        my_path = http://example.com/path
-  
-      To push to the path defined in "my_path" run the command:
-  
-        hg push my_path
+         (deprecated) The URL or directory for the default "hg push" location.
+         "default:pushurl" should be used instead.
   
   $ hg help glossary.mcguffin
   abort: help section not found
@@ -1483,6 +1686,13 @@
   Configuring hgweb
   </td></tr>
   <tr><td>
+  <a href="/help/internals">
+  internals
+  </a>
+  </td><td>
+  Technical implementation topics
+  </td></tr>
+  <tr><td>
   <a href="/help/merge-tools">
   merge-tools
   </a>
@@ -1995,9 +2205,10 @@
   If no names are given, add all files to the repository.
   </p>
   <p>
-  An example showing how new (unknown) files are added
-  automatically by &quot;hg add&quot;:
+  Examples:
   </p>
+  <ul>
+   <li> New (unknown) files are added   automatically by &quot;hg add&quot;:
   <pre>
   \$ ls (re)
   foo.c
@@ -2008,6 +2219,19 @@
   \$ hg status (re)
   A foo.c
   </pre>
+   <li> Specific files to be added can be specified:
+  <pre>
+  \$ ls (re)
+  bar.c  foo.c
+  \$ hg status (re)
+  ? bar.c
+  ? foo.c
+  \$ hg add bar.c (re)
+  \$ hg status (re)
+  A bar.c
+  ? foo.c
+  </pre>
+  </ul>
   <p>
   Returns 0 if all files are successfully added.
   </p>
--- a/tests/test-hgignore.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-hgignore.t	Thu Dec 17 17:27:32 2015 -0600
@@ -55,6 +55,29 @@
   ? a.c
   ? syntax
 
+Ensure that comments work:
+
+  $ touch 'foo#bar' 'quux#'
+#if no-windows
+  $ touch 'baz\#wat'
+#endif
+  $ cat <<'EOF' >> .hgignore
+  > # full-line comment
+  >   # whitespace-only comment line
+  > syntax# pattern, no whitespace, then comment
+  > a.c  # pattern, then whitespace, then comment
+  > baz\\# # escaped comment character
+  > foo\#b # escaped comment character
+  > quux\## escaped comment character at end of name
+  > EOF
+  $ hg status
+  A dir/b.o
+  ? .hgignore
+  $ rm 'foo#bar' 'quux#'
+#if no-windows
+  $ rm 'baz\#wat'
+#endif
+
 Check it does not ignore the current directory '.':
 
   $ echo "^\." > .hgignore
--- a/tests/test-hgweb-commands.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-hgweb-commands.t	Thu Dec 17 17:27:32 2015 -0600
@@ -6,6 +6,11 @@
 - unbundle, tested in test-push-http
 - changegroupsubset, tested in test-pull
 
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > usegeneraldelta=yes
+  > EOF
+
 Set up the repo
 
   $ hg init test
@@ -2098,10 +2103,34 @@
 
 capabilities
 
-  $ get-with-headers.py 127.0.0.1:$HGPORT '?cmd=capabilities'; echo
+(plain version to check the format)
+
+  $ get-with-headers.py 127.0.0.1:$HGPORT '?cmd=capabilities' | dd ibs=75 count=1 2> /dev/null; echo
   200 Script output follows
   
-  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream-preferred stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1*%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 (glob)
+  lookup changegroupsubset branchmap pushkey known
+
+(spread version to check the content)
+
+  $ get-with-headers.py 127.0.0.1:$HGPORT '?cmd=capabilities' | tr ' ' '\n'; echo
+  200
+  Script
+  output
+  follows
+  
+  lookup
+  changegroupsubset
+  branchmap
+  pushkey
+  known
+  getbundle
+  unbundlehash
+  batch
+  stream-preferred
+  streamreqs=generaldelta,revlogv1
+  bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps
+  unbundle=HG10GZ,HG10BZ,HG10UN
+  httpheader=1024
 
 heads
 
--- a/tests/test-hgweb-diffs.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-hgweb-diffs.t	Thu Dec 17 17:27:32 2015 -0600
@@ -927,7 +927,7 @@
   </tr>
   <tr>
    <th>parents</th>
-   <td><a href="/file/0cd96de13884/a">0cd96de13884</a> </td>
+   <td><a href="/file/d73db4d812ff/a">d73db4d812ff</a> </td>
   </tr>
   <tr>
    <th>children</th>
--- a/tests/test-hgweb-filelog.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-hgweb-filelog.t	Thu Dec 17 17:27:32 2015 -0600
@@ -187,7 +187,10 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>log a</h3>
+  <h3>
+   log a @ 4:<a href="/rev/3f41bc784e7e">3f41bc784e7e</a>
+   <span class="branchname">a-branch</span> 
+  </h3>
   
   <form class="search" action="/log">
   
@@ -304,7 +307,10 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>log a</h3>
+  <h3>
+   log a @ 4:<a href="/rev/3f41bc784e7e">3f41bc784e7e</a>
+   <span class="branchname">a-branch</span> 
+  </h3>
   
   <form class="search" action="/log">
   
@@ -421,7 +427,10 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>log a</h3>
+  <h3>
+   log a @ 1:<a href="/rev/5ed941583260">5ed941583260</a>
+   <span class="tag">a-tag</span> <span class="tag">a-bookmark</span> 
+  </h3>
   
   <form class="search" action="/log">
   
@@ -530,7 +539,10 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>log a</h3>
+  <h3>
+   log a @ 1:<a href="/rev/5ed941583260">5ed941583260</a>
+   <span class="tag">a-tag</span> <span class="tag">a-bookmark</span> 
+  </h3>
   
   <form class="search" action="/log">
   
--- a/tests/test-hgweb-json.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-hgweb-json.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1062,6 +1062,10 @@
         "topic": "hgweb"
       },
       {
+        "summary": "Technical implementation topics",
+        "topic": "internals"
+      },
+      {
         "summary": "Merge Tools",
         "topic": "merge-tools"
       },
--- a/tests/test-hgweb-symrev.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-hgweb-symrev.t	Thu Dec 17 17:27:32 2015 -0600
@@ -166,6 +166,7 @@
   <li><a href="/annotate/xyzzy/foo?style=paper">annotate</a></li>
   <li><a href="/raw-file/xyzzy/foo">raw</a></li>
   <a href="/atom-log/a7c1559b7bba/foo" title="subscribe to atom feed">
+   log foo @ 1:<a href="/rev/a7c1559b7bba?style=paper">a7c1559b7bba</a>
   <a href="/log/xyzzy/foo?revcount=30&style=paper">less</a>
   <a href="/log/xyzzy/foo?revcount=120&style=paper">more</a>
   | <a href="/log/43c799df6e75/foo?style=paper">(0)</a> <a href="/log/tip/foo?style=paper">tip</a> </div>
@@ -353,6 +354,7 @@
   <li><a href="/annotate/xyzzy/foo?style=coal">annotate</a></li>
   <li><a href="/raw-file/xyzzy/foo">raw</a></li>
   <a href="/atom-log/a7c1559b7bba/foo" title="subscribe to atom feed">
+   log foo @ 1:<a href="/rev/a7c1559b7bba?style=coal">a7c1559b7bba</a>
   <a href="/log/xyzzy/foo?revcount=30&style=coal">less</a>
   <a href="/log/xyzzy/foo?revcount=120&style=coal">more</a>
   | <a href="/log/43c799df6e75/foo?style=coal">(0)</a> <a href="/log/tip/foo?style=coal">tip</a> </div>
@@ -816,7 +818,7 @@
   <a href="/file/43c799df6e75/foo?style=monoblue">file</a> |
   <a href="/diff/43c799df6e75/foo?style=monoblue">diff</a> |
   <a href="/annotate/43c799df6e75/foo?style=monoblue">annotate</a>
-      <a href="/log/43c799df6e75/foo?style=monoblue">(0)</a><a href="/log/tip/foo?style=monoblue">tip</a>
+      <a href="/log/43c799df6e75/foo?style=monoblue">(0)</a> <a href="/log/tip/foo?style=monoblue">tip</a> 
 
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'annotate/xyzzy/foo?style=monoblue' | egrep $REVLINKS
               <li><a href="/graph/xyzzy?style=monoblue">graph</a></li>
--- a/tests/test-hgwebdir.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-hgwebdir.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1181,6 +1181,38 @@
   </body>
   </html>
   
+
+test listening address/port specified by web-conf (issue4699):
+
+  $ killdaemons.py
+  $ cat >> paths.conf <<EOF
+  > [web]
+  > address = localhost
+  > port = $HGPORT1
+  > EOF
+  $ hg serve -d --pid-file=hg.pid --web-conf paths.conf \
+  >     -A access-paths.log -E error-paths-9.log
+  listening at http://*:$HGPORT1/ (bound to 127.0.0.1:$HGPORT1) (glob)
+  $ cat hg.pid >> $DAEMON_PIDS
+  $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
+  200 Script output follows
+  
+  
+  
+test --port option overrides web.port:
+
+  $ killdaemons.py
+  $ hg serve -p $HGPORT2 -d -v --pid-file=hg.pid --web-conf paths.conf \
+  >     -A access-paths.log -E error-paths-10.log
+  listening at http://*:$HGPORT2/ (bound to 127.0.0.1:$HGPORT2) (glob)
+  $ cat hg.pid >> $DAEMON_PIDS
+  $ get-with-headers.py localhost:$HGPORT2 '?style=raw'
+  200 Script output follows
+  
+  
+  
+
+  $ killdaemons.py
   $ cat > collections.conf <<EOF
   > [collections]
   > $root=$root
@@ -1338,6 +1370,14 @@
 
   $ cat error-paths-8.log
 
+paths errors 9
+
+  $ cat error-paths-9.log
+
+paths errors 10
+
+  $ cat error-paths-10.log
+
 collections errors
 
   $ cat error-collections.log
--- a/tests/test-histedit-arguments.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-arguments.t	Thu Dec 17 17:27:32 2015 -0600
@@ -71,7 +71,6 @@
   #  d, drop = remove commit from history
   #  m, mess = edit commit message without changing commit content
   #
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Run on a revision not ancestors of the current working directory.
 --------------------------------------------------------------------
@@ -92,7 +91,6 @@
   > pick c8e68270e35a 3 four
   > pick 08d98a8350f3 4 five
   > EOF
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg up --quiet
 
   $ HGEDITOR=cat hg histedit 'tip:2' --commands - << EOF
@@ -100,7 +98,6 @@
   > pick c8e68270e35a 3 four
   > pick 08d98a8350f3 4 five
   > EOF
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg up --quiet
 
 Test config specified default
@@ -110,7 +107,6 @@
   > pick c8e68270e35a 3 four
   > pick 08d98a8350f3 4 five
   > EOF
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Run on a revision not descendants of the initial parent
 --------------------------------------------------------------------
@@ -142,7 +138,6 @@
   $ mv .hg/histedit-state.back .hg/histedit-state
 
   $ hg histedit --continue
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-backup.hg (glob)
   $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
   @  4 f5ed five
@@ -164,7 +159,7 @@
   > pick 08d98a8350f3 4 five
   > EOF
   abort: missing rules for changeset c8e68270e35a
-  (do you want to use the drop action?)
+  (use "drop c8e68270e35a" to discard the change)
   [255]
 
 Test that extra revisions are detected
@@ -175,7 +170,7 @@
   > pick c8e68270e35a 3 four
   > pick 08d98a8350f3 4 five
   > EOF
-  abort: may not use changesets other than the ones listed
+  abort: may not use "pick" with changesets other than the ones listed
   [255]
 
 Test malformed line
@@ -251,9 +246,8 @@
   HG: branch 'default'
   HG: changed alpha
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/foo/.hg/strip-backup/*-backup.hg (glob)
-  saved backup bundle to $TESTTMP/foo/.hg/strip-backup/c8e68270e35a-23a13bf9-backup.hg (glob)
+  saved backup bundle to $TESTTMP/foo/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg update -q 2
   $ echo x > x
@@ -295,7 +289,6 @@
   #  d, drop = remove commit from history
   #  m, mess = edit commit message without changing commit content
   #
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Test --continue with --keep
 
@@ -339,7 +332,7 @@
   $ mv ../corrupt-histedit .hg/histedit-state
   $ hg histedit --abort
   warning: encountered an exception during histedit --abort; the repository may not have been completely cleaned up
-  abort: No such file or directory: * (glob)
+  abort: .*(No such file or directory:|The system cannot find the file specified).* (re)
   [255]
 Histedit state has been exited
   $ hg summary -q
@@ -347,3 +340,98 @@
   commit: 1 added, 1 unknown (new branch head)
   update: 4 new changesets (update)
 
+  $ cd ..
+
+Set up default base revision tests
+
+  $ hg init defaultbase
+  $ cd defaultbase
+  $ touch foo
+  $ hg -q commit -A -m root
+  $ echo 1 > foo
+  $ hg commit -m 'public 1'
+  $ hg phase --force --public -r .
+  $ echo 2 > foo
+  $ hg commit -m 'draft after public'
+  $ hg -q up -r 1
+  $ echo 3 > foo
+  $ hg commit -m 'head 1 public'
+  created new head
+  $ hg phase --force --public -r .
+  $ echo 4 > foo
+  $ hg commit -m 'head 1 draft 1'
+  $ echo 5 > foo
+  $ hg commit -m 'head 1 draft 2'
+  $ hg -q up -r 2
+  $ echo 6 > foo
+  $ hg commit -m 'head 2 commit 1'
+  $ echo 7 > foo
+  $ hg commit -m 'head 2 commit 2'
+  $ hg -q up -r 2
+  $ echo 8 > foo
+  $ hg commit -m 'head 3'
+  created new head
+  $ hg -q up -r 2
+  $ echo 9 > foo
+  $ hg commit -m 'head 4'
+  created new head
+  $ hg merge --tool :local -r 8
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg commit -m 'merge head 3 into head 4'
+  $ echo 11 > foo
+  $ hg commit -m 'commit 1 after merge'
+  $ echo 12 > foo
+  $ hg commit -m 'commit 2 after merge'
+
+  $ hg log -G -T '{rev}:{node|short} {phase} {desc}\n'
+  @  12:8cde254db839 draft commit 2 after merge
+  |
+  o  11:6f2f0241f119 draft commit 1 after merge
+  |
+  o    10:90506cc76b00 draft merge head 3 into head 4
+  |\
+  | o  9:f8607a373a97 draft head 4
+  | |
+  o |  8:0da92be05148 draft head 3
+  |/
+  | o  7:4c35cdf97d5e draft head 2 commit 2
+  | |
+  | o  6:931820154288 draft head 2 commit 1
+  |/
+  | o  5:8cdc02b9bc63 draft head 1 draft 2
+  | |
+  | o  4:463b8c0d2973 draft head 1 draft 1
+  | |
+  | o  3:23a0c4eefcbf public head 1 public
+  | |
+  o |  2:4117331c3abb draft draft after public
+  |/
+  o  1:4426d359ea59 public public 1
+  |
+  o  0:54136a8ddf32 public root
+  
+
+Default base revision should stop at public changesets
+
+  $ hg -q up 8cdc02b9bc63
+  $ hg histedit --commands - <<EOF
+  > pick 463b8c0d2973
+  > pick 8cdc02b9bc63
+  > EOF
+
+Default base revision should stop at branchpoint
+
+  $ hg -q up 4c35cdf97d5e
+  $ hg histedit --commands - <<EOF
+  > pick 931820154288
+  > pick 4c35cdf97d5e
+  > EOF
+
+Default base revision should stop at merge commit
+
+  $ hg -q up 8cde254db839
+  $ hg histedit --commands - <<EOF
+  > pick 6f2f0241f119
+  > pick 8cde254db839
+  > EOF
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-histedit-base.t	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,261 @@
+  $ . "$TESTDIR/histedit-helpers.sh"
+
+  $ cat >> $HGRCPATH <<EOF
+  > [alias]
+  > tglog = log -G --template "{rev}:{node}:{phase} '{desc}'\n"
+  > [extensions]
+  > histedit=
+  > [experimental]
+  > histeditng=True
+  > EOF
+
+Create repo a:
+
+  $ hg init a
+  $ cd a
+  $ hg unbundle "$TESTDIR/bundles/rebase.hg"
+  adding changesets
+  adding manifests
+  adding file changes
+  added 8 changesets with 7 changes to 7 files (+2 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg up tip
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg tglog
+  @  7:02de42196ebee42ef284b6780a87cdc96e8eaab6:draft 'H'
+  |
+  | o  6:eea13746799a9e0bfd88f29d3c2e9dc9389f524f:draft 'G'
+  |/|
+  o |  5:24b6387c8c8cae37178880f3fa95ded3cb1cf785:draft 'F'
+  | |
+  | o  4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba:draft 'E'
+  |/
+  | o  3:32af7686d403cf45b5d95f2d70cebea587ac806a:draft 'D'
+  | |
+  | o  2:5fddd98957c8a54a4d436dfe1da9d87f21a1b97b:draft 'C'
+  | |
+  | o  1:42ccdea3bb16d28e1848c95fe2e44c000f3f21b1:draft 'B'
+  |/
+  o  0:cd010b8cd998f3981a5a8115f94f8da4ab506089:draft 'A'
+  
+
+
+Go to D
+  $ hg update 3
+  3 files updated, 0 files merged, 2 files removed, 0 files unresolved
+edit the history to rebase B onto H
+
+
+Rebase B onto H
+  $ hg histedit 1 --commands - 2>&1 << EOF | fixbundle
+  > base 02de42196ebe
+  > pick 42ccdea3bb16 B
+  > pick 5fddd98957c8 C
+  > pick 32af7686d403 D
+  > EOF
+
+  $ hg tglog
+  @  7:0937e82309df47d14176ee15e45dbec5fbdef340:draft 'D'
+  |
+  o  6:f778d1cbddac4ab679d9983c9bb92e4c5e09e7fa:draft 'C'
+  |
+  o  5:3d41b7cc708545206213a842f96d812d2e73d818:draft 'B'
+  |
+  o  4:02de42196ebee42ef284b6780a87cdc96e8eaab6:draft 'H'
+  |
+  | o  3:eea13746799a9e0bfd88f29d3c2e9dc9389f524f:draft 'G'
+  |/|
+  o |  2:24b6387c8c8cae37178880f3fa95ded3cb1cf785:draft 'F'
+  | |
+  | o  1:9520eea781bcca16c1e15acc0ba14335a0e8e5ba:draft 'E'
+  |/
+  o  0:cd010b8cd998f3981a5a8115f94f8da4ab506089:draft 'A'
+  
+Rebase back and drop something
+  $ hg histedit 5 --commands - 2>&1 << EOF | fixbundle
+  > base cd010b8cd998
+  > pick 3d41b7cc7085 B
+  > drop f778d1cbddac C
+  > pick 0937e82309df D
+  > EOF
+
+  $ hg tglog
+  @  6:476cc3e4168da2d036b141f7f7dcff7f8e3fe846:draft 'D'
+  |
+  o  5:d273e35dcdf21a7eb305192ef2e362887cd0a6f8:draft 'B'
+  |
+  | o  4:02de42196ebee42ef284b6780a87cdc96e8eaab6:draft 'H'
+  | |
+  | | o  3:eea13746799a9e0bfd88f29d3c2e9dc9389f524f:draft 'G'
+  | |/|
+  | o |  2:24b6387c8c8cae37178880f3fa95ded3cb1cf785:draft 'F'
+  |/ /
+  | o  1:9520eea781bcca16c1e15acc0ba14335a0e8e5ba:draft 'E'
+  |/
+  o  0:cd010b8cd998f3981a5a8115f94f8da4ab506089:draft 'A'
+  
+Split stack
+  $ hg histedit 5 --commands - 2>&1 << EOF | fixbundle
+  > base cd010b8cd998
+  > pick d273e35dcdf2 B
+  > base cd010b8cd998
+  > pick 476cc3e4168d D
+  > EOF
+
+  $ hg tglog
+  @  6:d7a6f907a822c4ce6f15662ae45a42aa46d3818a:draft 'D'
+  |
+  | o  5:d273e35dcdf21a7eb305192ef2e362887cd0a6f8:draft 'B'
+  |/
+  | o  4:02de42196ebee42ef284b6780a87cdc96e8eaab6:draft 'H'
+  | |
+  | | o  3:eea13746799a9e0bfd88f29d3c2e9dc9389f524f:draft 'G'
+  | |/|
+  | o |  2:24b6387c8c8cae37178880f3fa95ded3cb1cf785:draft 'F'
+  |/ /
+  | o  1:9520eea781bcca16c1e15acc0ba14335a0e8e5ba:draft 'E'
+  |/
+  o  0:cd010b8cd998f3981a5a8115f94f8da4ab506089:draft 'A'
+  
+Abort
+  $ echo x > B
+  $ hg add B
+  $ hg commit -m "X"
+  $ hg tglog
+  @  7:591369deedfdcbf57471e894999a70d7f676186d:draft 'X'
+  |
+  o  6:d7a6f907a822c4ce6f15662ae45a42aa46d3818a:draft 'D'
+  |
+  | o  5:d273e35dcdf21a7eb305192ef2e362887cd0a6f8:draft 'B'
+  |/
+  | o  4:02de42196ebee42ef284b6780a87cdc96e8eaab6:draft 'H'
+  | |
+  | | o  3:eea13746799a9e0bfd88f29d3c2e9dc9389f524f:draft 'G'
+  | |/|
+  | o |  2:24b6387c8c8cae37178880f3fa95ded3cb1cf785:draft 'F'
+  |/ /
+  | o  1:9520eea781bcca16c1e15acc0ba14335a0e8e5ba:draft 'E'
+  |/
+  o  0:cd010b8cd998f3981a5a8115f94f8da4ab506089:draft 'A'
+  
+  $ hg histedit 6 --commands - 2>&1 << EOF | fixbundle
+  > base d273e35dcdf2 B
+  > drop d7a6f907a822 D
+  > pick 591369deedfd X
+  > EOF
+  merging B
+  warning: conflicts while merging B! (edit, then use 'hg resolve --mark')
+  Fix up the change and run hg histedit --continue
+  $ hg histedit --abort | fixbundle
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg tglog
+  @  7:591369deedfdcbf57471e894999a70d7f676186d:draft 'X'
+  |
+  o  6:d7a6f907a822c4ce6f15662ae45a42aa46d3818a:draft 'D'
+  |
+  | o  5:d273e35dcdf21a7eb305192ef2e362887cd0a6f8:draft 'B'
+  |/
+  | o  4:02de42196ebee42ef284b6780a87cdc96e8eaab6:draft 'H'
+  | |
+  | | o  3:eea13746799a9e0bfd88f29d3c2e9dc9389f524f:draft 'G'
+  | |/|
+  | o |  2:24b6387c8c8cae37178880f3fa95ded3cb1cf785:draft 'F'
+  |/ /
+  | o  1:9520eea781bcca16c1e15acc0ba14335a0e8e5ba:draft 'E'
+  |/
+  o  0:cd010b8cd998f3981a5a8115f94f8da4ab506089:draft 'A'
+  
+Continue
+  $ hg histedit 6 --commands - 2>&1 << EOF | fixbundle
+  > base d273e35dcdf2 B
+  > drop d7a6f907a822 D
+  > pick 591369deedfd X
+  > EOF
+  merging B
+  warning: conflicts while merging B! (edit, then use 'hg resolve --mark')
+  Fix up the change and run hg histedit --continue
+  $ echo b2 > B
+  $ hg resolve --mark B
+  (no more unresolved files)
+  $ hg histedit --continue | fixbundle
+  $ hg tglog
+  @  6:03772da75548bb42a8f1eacd8c91d0717a147fcd:draft 'X'
+  |
+  o  5:d273e35dcdf21a7eb305192ef2e362887cd0a6f8:draft 'B'
+  |
+  | o  4:02de42196ebee42ef284b6780a87cdc96e8eaab6:draft 'H'
+  | |
+  | | o  3:eea13746799a9e0bfd88f29d3c2e9dc9389f524f:draft 'G'
+  | |/|
+  | o |  2:24b6387c8c8cae37178880f3fa95ded3cb1cf785:draft 'F'
+  |/ /
+  | o  1:9520eea781bcca16c1e15acc0ba14335a0e8e5ba:draft 'E'
+  |/
+  o  0:cd010b8cd998f3981a5a8115f94f8da4ab506089:draft 'A'
+  
+
+base on a previously picked changeset
+  $ echo i > i
+  $ hg add i
+  $ hg commit -m "I"
+  $ echo j > j
+  $ hg add j
+  $ hg commit -m "J"
+  $ hg tglog
+  @  8:e8c55b19d366b335626e805484110d1d5f6f2ea3:draft 'J'
+  |
+  o  7:b2f90fd8aa85db5569e3cfc30cd1d7739546368e:draft 'I'
+  |
+  o  6:03772da75548bb42a8f1eacd8c91d0717a147fcd:draft 'X'
+  |
+  o  5:d273e35dcdf21a7eb305192ef2e362887cd0a6f8:draft 'B'
+  |
+  | o  4:02de42196ebee42ef284b6780a87cdc96e8eaab6:draft 'H'
+  | |
+  | | o  3:eea13746799a9e0bfd88f29d3c2e9dc9389f524f:draft 'G'
+  | |/|
+  | o |  2:24b6387c8c8cae37178880f3fa95ded3cb1cf785:draft 'F'
+  |/ /
+  | o  1:9520eea781bcca16c1e15acc0ba14335a0e8e5ba:draft 'E'
+  |/
+  o  0:cd010b8cd998f3981a5a8115f94f8da4ab506089:draft 'A'
+  
+  $ hg histedit 5 --commands - 2>&1 << EOF | fixbundle
+  > pick d273e35dcdf2 B
+  > pick 03772da75548 X
+  > base d273e35dcdf2 B
+  > pick e8c55b19d366 J
+  > base d273e35dcdf2 B
+  > pick b2f90fd8aa85 I
+  > EOF
+  abort: may not use "base" with changesets within the edited list
+
+  $ hg --config experimental.histeditng=False histedit 5 --commands - 2>&1 << EOF | fixbundle
+  > base cd010b8cd998 A
+  > pick d273e35dcdf2 B
+  > pick 03772da75548 X
+  > pick b2f90fd8aa85 I
+  > pick e8c55b19d366 J
+  > EOF
+  abort: unknown action "base"
+
+  $ hg tglog
+  @  8:e8c55b19d366b335626e805484110d1d5f6f2ea3:draft 'J'
+  |
+  o  7:b2f90fd8aa85db5569e3cfc30cd1d7739546368e:draft 'I'
+  |
+  o  6:03772da75548bb42a8f1eacd8c91d0717a147fcd:draft 'X'
+  |
+  o  5:d273e35dcdf21a7eb305192ef2e362887cd0a6f8:draft 'B'
+  |
+  | o  4:02de42196ebee42ef284b6780a87cdc96e8eaab6:draft 'H'
+  | |
+  | | o  3:eea13746799a9e0bfd88f29d3c2e9dc9389f524f:draft 'G'
+  | |/|
+  | o |  2:24b6387c8c8cae37178880f3fa95ded3cb1cf785:draft 'F'
+  |/ /
+  | o  1:9520eea781bcca16c1e15acc0ba14335a0e8e5ba:draft 'E'
+  |/
+  o  0:cd010b8cd998f3981a5a8115f94f8da4ab506089:draft 'A'
+  
--- a/tests/test-histedit-bookmark-motion.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-bookmark-motion.t	Thu Dec 17 17:27:32 2015 -0600
@@ -77,7 +77,6 @@
   #  d, drop = remove commit from history
   #  m, mess = edit commit message without changing commit content
   #
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg histedit 1 --commands - --verbose << EOF | grep histedit
   > pick 177f92b77385 2 c
   > drop d2ae7f538514 1 b
@@ -138,7 +137,6 @@
   #  d, drop = remove commit from history
   #  m, mess = edit commit message without changing commit content
   #
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg histedit 1 --commands - --verbose << EOF | grep histedit
   > pick b346ab9a313d 1 c
   > pick cacdfd884a93 3 f
--- a/tests/test-histedit-commute.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-commute.t	Thu Dec 17 17:27:32 2015 -0600
@@ -71,7 +71,6 @@
   #  d, drop = remove commit from history
   #  m, mess = edit commit message without changing commit content
   #
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 edit the history
 (use a hacky editor to check histedit-last-edit.txt backup)
@@ -85,9 +84,6 @@
   > EOF
   $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
   0 files updated, 0 files merged, 3 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 rules should end up in .hg/histedit-last-edit.txt:
   $ cat .hg/histedit-last-edit.txt
@@ -139,9 +135,6 @@
   > pick 8ade9693061e f
   > EOF
   0 files updated, 0 files merged, 3 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ hg log --graph
   @  changeset:   5:7eca9b5b1148
@@ -185,10 +178,6 @@
   > pick 177f92b77385 c
   > EOF
   0 files updated, 0 files merged, 4 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg log --graph
   @  changeset:   5:38b92f448761
   |  tag:         tip
@@ -230,8 +219,6 @@
   > pick de71b079d9ce e
   > EOF
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg log --graph
   @  changeset:   7:803ef1c6fcfd
   |  tag:         tip
@@ -281,7 +268,7 @@
   > pick de71b079d9ce e
   > pick 38b92f448761 c
   > EOF
-  abort: may not use changesets other than the ones listed
+  abort: may not use "pick" with changesets other than the ones listed
   $ hg log --graph
   @  changeset:   7:803ef1c6fcfd
   |  tag:         tip
@@ -349,7 +336,6 @@
   #  d, drop = remove commit from history
   #  m, mess = edit commit message without changing commit content
   #
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 should also work if a commit message is missing
   $ BUNDLE="$TESTDIR/missing-comment.hg"
@@ -380,7 +366,6 @@
      summary:     Checked in text file
   
   $ hg histedit 0
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd ..
 
   $ cd ..
@@ -421,10 +406,8 @@
   removing initial-dir/initial-file (glob)
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/issue4251/.hg/strip-backup/*-backup.hg (glob)
-  saved backup bundle to $TESTTMP/issue4251/.hg/strip-backup/b0f4233702ca-d99e7186-backup.hg (glob)
+  saved backup bundle to $TESTTMP/issue4251/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg --config diff.git=yes export 0
   # HG changeset patch
--- a/tests/test-histedit-drop.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-drop.t	Thu Dec 17 17:27:32 2015 -0600
@@ -60,9 +60,6 @@
   > pick 055a42cdd887 d
   > EOF
   0 files updated, 0 files merged, 4 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 log after edit
   $ hg log --graph
--- a/tests/test-histedit-edit.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-edit.t	Thu Dec 17 17:27:32 2015 -0600
@@ -141,8 +141,6 @@
   (use 'hg histedit --continue' or 'hg histedit --abort')
   [255]
   $ HGEDITOR='echo foobaz > ' hg histedit --continue 2>&1 | fixbundle
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ hg log --graph
   @  changeset:   6:b5f70786f9b0
@@ -262,7 +260,6 @@
   HG: user: test
   HG: branch 'default'
   HG: added f
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/r/.hg/strip-backup/b5f70786f9b0-c28d9c86-backup.hg (glob)
 
   $ hg status
@@ -283,7 +280,6 @@
   > EOF
   $ HGEDITOR="sh ../edit.sh" hg histedit tip 2>&1 | fixbundle
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg status
   $ hg log --limit 1
   changeset:   6:1fd3b2fe7754
@@ -364,9 +360,9 @@
   HG: branch 'default'
   HG: added f
   ====
+  note: commit message saved in .hg/last-message.txt
   transaction abort!
   rollback completed
-  note: commit message saved in .hg/last-message.txt
   abort: pretxncommit.unexpectedabort hook exited with status 1
   [255]
   $ cat .hg/last-message.txt
@@ -388,9 +384,9 @@
   HG: user: test
   HG: branch 'default'
   HG: added f
+  note: commit message saved in .hg/last-message.txt
   transaction abort!
   rollback completed
-  note: commit message saved in .hg/last-message.txt
   abort: pretxncommit.unexpectedabort hook exited with status 1
   [255]
 
@@ -406,7 +402,6 @@
   > mess 1fd3b2fe7754 f
   > EOF
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg status
   $ hg log --limit 1
   changeset:   6:62feedb1200e
@@ -434,7 +429,6 @@
   When you are finished, run hg histedit --continue to resume.
   [1]
   $ HGEDITOR=true hg histedit --continue
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/r0/.hg/strip-backup/cb9a9f314b8b-cc5ccb0b-backup.hg (glob)
 
   $ hg log -G
@@ -460,8 +454,24 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   reverting a
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  warning: histedit rules saved to: .hg/histedit-last-edit.txt
   abort: cannot fold into public change 18aa70c8ad22
   [255]
+  $ cat .hg/histedit-last-edit.txt
+  fold 0012be4a27ea 2 extend a
+  
+  # Edit history between 0012be4a27ea and 0012be4a27ea
+  #
+  # Commits are listed from least to most recent
+  #
+  # Commands:
+  #  p, fold = use commit
+  #  e, edit = use commit, but stop for amending
+  #  f, fold = use commit, but combine it with the one above
+  #  r, roll = like fold, but discard this commit's description
+  #  d, drop = remove commit from history
+  #  m, mess = edit commit message without changing commit content
+  #
 TODO: this abort shouldn't be required, but it is for now to leave the repo in
 a clean state.
   $ hg histedit --abort
--- a/tests/test-histedit-fold-non-commute.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-fold-non-commute.t	Thu Dec 17 17:27:32 2015 -0600
@@ -120,7 +120,6 @@
   
   
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   merging e
   warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
   Fix up the change and run hg histedit --continue
@@ -131,8 +130,6 @@
   (no more unresolved files)
   $ hg histedit --continue 2>&1 | fixbundle
   7b4e2f4b7bcd: empty changeset
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 log after edit
   $ hg log --graph
@@ -261,7 +258,6 @@
   $ hg histedit --continue 2>&1 | fixbundle | grep -v '2 files removed'
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   merging e
   warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
   Fix up the change and run hg histedit --continue
@@ -272,8 +268,6 @@
   (no more unresolved files)
   $ hg histedit --continue 2>&1 | fixbundle
   7b4e2f4b7bcd: empty changeset
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 log after edit
   $ hg log --graph
--- a/tests/test-histedit-fold.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-fold.t	Thu Dec 17 17:27:32 2015 -0600
@@ -55,12 +55,8 @@
   > pick 055a42cdd887 d
   > EOF
   0 files updated, 0 files merged, 4 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 log after edit
   $ hg logt --graph
@@ -118,9 +114,6 @@
   0 files updated, 0 files merged, 4 files removed, 0 files unresolved
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ HGEDITOR=$OLDHGEDITOR
 
@@ -248,7 +241,6 @@
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 tip after edit
   $ hg log --rev .
@@ -311,7 +303,6 @@
   (no more unresolved files)
   $ hg histedit --continue
   251d831eeec5: empty changeset
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/*-backup.hg (glob)
   $ hg logt --graph
   @  1:617f94f13c0f +4
@@ -389,7 +380,6 @@
   HG: branch 'default'
   HG: changed file
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/55c8d8dc79ce-4066cd98-backup.hg (glob)
   saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-a35700fc-backup.hg (glob)
   $ hg logt -G
@@ -449,7 +439,6 @@
   reverting b.txt
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ hg logt --follow b.txt
   1:cf858d235c76 rename
@@ -493,11 +482,8 @@
   > pick 6c795aa153cb a
   > EOF
   0 files updated, 0 files merged, 3 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   commit 9599899f62c05f4377548c32bf1c9f1a39634b0c
 
   $ hg logt
@@ -524,13 +510,13 @@
   2:0e01aeef5fa8 foo1
   1:578c7455730c a
   0:79b99e9c8e49 b
-  $ cat > $TESTTMP/editor.sh <<EOF
-  > echo ran editor >> $TESTTMP/editorlog.txt
-  > cat \$1 >> $TESTTMP/editorlog.txt
-  > echo END >> $TESTTMP/editorlog.txt
+  $ cat > "$TESTTMP/editor.sh" <<EOF
+  > echo ran editor >> "$TESTTMP/editorlog.txt"
+  > cat \$1 >> "$TESTTMP/editorlog.txt"
+  > echo END >> "$TESTTMP/editorlog.txt"
   > echo merged foos > \$1
   > EOF
-  $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit 1 --commands - 2>&1 <<EOF | fixbundle
+  $ HGEDITOR="sh \"$TESTTMP/editor.sh\"" hg histedit 1 --commands - 2>&1 <<EOF | fixbundle
   > pick 578c7455730c 1 a
   > pick 0e01aeef5fa8 2 foo1
   > fold b7389cc4d66e 3 foo2
@@ -540,11 +526,9 @@
   reverting foo
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   merging foo
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg logt
   2:e8bedbda72c1 merged foos
   1:578c7455730c a
--- a/tests/test-histedit-no-change.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-no-change.t	Thu Dec 17 17:27:32 2015 -0600
@@ -95,8 +95,6 @@
   When you are finished, run hg histedit --continue to resume.
   $ continueediting true "(leaving commit message unaltered)"
   % finalize changeset editing (leaving commit message unaltered)
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 
 check state of working copy
@@ -148,7 +146,6 @@
   When you are finished, run hg histedit --continue to resume.
   $ continueediting true "(leaving commit message unaltered)"
   % finalize changeset editing (leaving commit message unaltered)
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   Make changes as needed, you may commit or record as needed now.
   When you are finished, run hg histedit --continue to resume.
   $ graphlog "log after first edit"
@@ -187,7 +184,6 @@
   hist:   2 remaining (histedit --continue)
 
   $ hg histedit --abort 2>&1 | fixbundle
-  [1]
 
 modified files should survive the abort when we've moved away already
   $ hg st
@@ -208,5 +204,15 @@
   |
   @  0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
   
+aborting and not changing files can skip mentioning updating (no) files
+  $ hg up
+  5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg commit --close-branch -m 'closebranch'
+  $ startediting 1 1 "(not changing anything)" # edit the 3rd of 3 changesets
+  % start editing the history (not changing anything)
+  | edit 292aec348d9e 6 closebranch
+  Make changes as needed, you may commit or record as needed now.
+  When you are finished, run hg histedit --continue to resume.
+  $ hg histedit --abort
 
   $ cd ..
--- a/tests/test-histedit-non-commute-abort.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-non-commute-abort.t	Thu Dec 17 17:27:32 2015 -0600
@@ -70,15 +70,49 @@
   > pick 652413bf663e f
   > EOF
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   merging e
   warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
   Fix up the change and run hg histedit --continue
 
+insert unsupported advisory merge record
+  $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
+  $ hg debugmergestate
+  * version 2 records
+  local: 8f7551c7e4a2f2efe0bc8c741baf7f227d65d758
+  other: e860deea161a2f77de56603b340ebbb4536308ae
+  unrecognized entry: x	advisory record
+  file: e (record type "F", state "u", hash 58e6b3a414a1e090dfc6029add0f3555ccba127f)
+    local path: e (flags "")
+    ancestor path: e (node null)
+    other path: e (node 6b67ccefd5ce6de77e7ead4f5292843a0255329f)
+  $ hg resolve -l
+  U e
 
-abort the edit
+insert unsupported mandatory merge record
+  $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
+  $ hg debugmergestate
+  * version 2 records
+  local: 8f7551c7e4a2f2efe0bc8c741baf7f227d65d758
+  other: e860deea161a2f77de56603b340ebbb4536308ae
+  file: e (record type "F", state "u", hash 58e6b3a414a1e090dfc6029add0f3555ccba127f)
+    local path: e (flags "")
+    ancestor path: e (node null)
+    other path: e (node 6b67ccefd5ce6de77e7ead4f5292843a0255329f)
+  unrecognized entry: X	mandatory record
+  $ hg resolve -l
+  abort: unsupported merge state records: X
+  (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
+  [255]
+  $ hg resolve -ma
+  abort: unsupported merge state records: X
+  (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
+  [255]
+
+abort the edit (should clear out merge state)
   $ hg histedit --abort 2>&1 | fixbundle
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg debugmergestate
+  no merge state found
 
 log after abort
   $ hg resolve -l
--- a/tests/test-histedit-non-commute.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-non-commute.t	Thu Dec 17 17:27:32 2015 -0600
@@ -154,7 +154,6 @@
   $ hg resolve --mark e
   (no more unresolved files)
   $ hg histedit --continue 2>&1 | fixbundle
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   merging e
   warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
   Fix up the change and run hg histedit --continue
@@ -168,8 +167,6 @@
   (no more unresolved files)
   $ hg histedit --continue 2>&1 | fixbundle
   7b4e2f4b7bcd: empty changeset
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 log after edit
   $ hg log --graph
@@ -240,7 +237,6 @@
   $ hg resolve --mark e
   (no more unresolved files)
   $ hg histedit --continue 2>&1 | fixbundle
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   merging e
   warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
   Fix up the change and run hg histedit --continue
@@ -250,8 +246,6 @@
   (no more unresolved files)
   $ hg histedit --continue 2>&1 | fixbundle
   7b4e2f4b7bcd: empty changeset
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 post message fix
   $ hg log --graph
--- a/tests/test-histedit-obsolete.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-obsolete.t	Thu Dec 17 17:27:32 2015 -0600
@@ -56,7 +56,6 @@
   #  d, drop = remove commit from history
   #  m, mess = edit commit message without changing commit content
   #
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
   > pick 177f92b77385 2 c
   > drop d2ae7f538514 1 b
@@ -109,7 +108,6 @@
   > pick cacdfd884a93 8 f
   > EOF
   0 files updated, 0 files merged, 3 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg log --graph
   @  11:c13eb81022ca f
   |
@@ -123,7 +121,6 @@
   > pick b346ab9a313d 6 c
   > pick c13eb81022ca 8 f
   > EOF
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 
 
@@ -144,7 +141,6 @@
   [1]
   $ echo c >> c
   $ hg histedit --continue
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ hg log -r 'unstable()'
   11:c13eb81022ca f (no-eol)
@@ -155,6 +151,14 @@
   rebasing 11:c13eb81022ca "f"
   $ hg up tip -q
 
+check that extra has accumulated from histedit and rebase
+
+  $ hg log -T '{extras % "{key}={value}\n"}\n' -r tip
+  branch=default
+  histedit_source=cacdfd884a9321ec4e1de275ef3949fa953a1f83
+  rebase_source=c13eb81022caa686a369223fe7f926bc4f7db576
+  
+
 Test dropping of changeset on the top of the stack
 -------------------------------------------------------
 
@@ -168,7 +172,7 @@
   $ cd droplast
   $ hg histedit -r '40db8afa467b' --commands - << EOF
   > pick 40db8afa467b 10 c
-  > drop b449568bf7fc 11 f
+  > drop 947ece25170f 11 f
   > EOF
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg log -G
@@ -191,8 +195,6 @@
   > drop 1b3b05f35ff0 13 h
   > EOF
   0 files updated, 0 files merged, 3 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg log -G
   @  17:ee6544123ab8 c
   |
@@ -218,7 +220,7 @@
   $ hg ph -pv '.^'
   phase changed for 2 changesets
   $ hg log -G
-  @  13:b449568bf7fc (draft) f
+  @  13:947ece25170f (draft) f
   |
   o  12:40db8afa467b (public) c
   |
@@ -240,17 +242,17 @@
   > done
   $ hg phase --force --secret .~2
   $ hg log -G
-  @  18:ee118ab9fa44 (secret) k
+  @  18:14bda137d5b3 (secret) k
   |
-  o  17:3a6c53ee7f3d (secret) j
+  o  17:c62e7241a4f2 (secret) j
   |
-  o  16:b605fb7503f2 (secret) i
+  o  16:9cd3934e05af (secret) i
   |
-  o  15:7395e1ff83bd (draft) h
+  o  15:ee4a24fc4dfa (draft) h
   |
-  o  14:6b70183d2492 (draft) g
+  o  14:d22905de3528 (draft) g
   |
-  o  13:b449568bf7fc (draft) f
+  o  13:947ece25170f (draft) f
   |
   o  12:40db8afa467b (public) c
   |
@@ -268,13 +270,13 @@
 
   $ cp -r base simple-draft
   $ cd simple-draft
-  $ hg histedit -r 'b449568bf7fc' --commands - << EOF
-  > edit b449568bf7fc 11 f
-  > pick 6b70183d2492 12 g
-  > pick 7395e1ff83bd 13 h
-  > pick b605fb7503f2 14 i
-  > pick 3a6c53ee7f3d 15 j
-  > pick ee118ab9fa44 16 k
+  $ hg histedit -r '947ece25170f' --commands - << EOF
+  > edit 947ece25170f 11 f
+  > pick d22905de3528 12 g
+  > pick ee4a24fc4dfa 13 h
+  > pick 9cd3934e05af 14 i
+  > pick c62e7241a4f2 15 j
+  > pick 14bda137d5b3 16 k
   > EOF
   0 files updated, 0 files merged, 6 files removed, 0 files unresolved
   adding f
@@ -283,24 +285,18 @@
   [1]
   $ echo f >> f
   $ hg histedit --continue
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg log -G
-  @  24:12e89af74238 (secret) k
+  @  24:12925f763c90 (secret) k
+  |
+  o  23:4545a6e77442 (secret) j
   |
-  o  23:636a8687b22e (secret) j
+  o  22:d947a0798e76 (secret) i
   |
-  o  22:ccaf0a38653f (secret) i
+  o  21:28fb35ae4ebb (draft) h
   |
-  o  21:11a89d1c2613 (draft) h
+  o  20:10b22a5a9645 (draft) g
   |
-  o  20:c1dec7ca82ea (draft) g
-  |
-  o  19:087281e68428 (draft) f
+  o  19:c5a1db4a69f5 (draft) f
   |
   o  12:40db8afa467b (public) c
   |
@@ -317,13 +313,13 @@
   > [phases]
   > new-commit=secret
   > EOF
-  $ hg histedit -r 'b449568bf7fc' --commands - << EOF
-  > edit b449568bf7fc 11 f
-  > pick 6b70183d2492 12 g
-  > pick 7395e1ff83bd 13 h
-  > pick b605fb7503f2 14 i
-  > pick 3a6c53ee7f3d 15 j
-  > pick ee118ab9fa44 16 k
+  $ hg histedit -r '947ece25170f' --commands - << EOF
+  > edit 947ece25170f 11 f
+  > pick d22905de3528 12 g
+  > pick ee4a24fc4dfa 13 h
+  > pick 9cd3934e05af 14 i
+  > pick c62e7241a4f2 15 j
+  > pick 14bda137d5b3 16 k
   > EOF
   0 files updated, 0 files merged, 6 files removed, 0 files unresolved
   adding f
@@ -332,24 +328,18 @@
   [1]
   $ echo f >> f
   $ hg histedit --continue
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg log -G
-  @  24:12e89af74238 (secret) k
+  @  24:12925f763c90 (secret) k
+  |
+  o  23:4545a6e77442 (secret) j
   |
-  o  23:636a8687b22e (secret) j
+  o  22:d947a0798e76 (secret) i
   |
-  o  22:ccaf0a38653f (secret) i
+  o  21:28fb35ae4ebb (draft) h
   |
-  o  21:11a89d1c2613 (draft) h
+  o  20:10b22a5a9645 (draft) g
   |
-  o  20:c1dec7ca82ea (draft) g
-  |
-  o  19:087281e68428 (draft) f
+  o  19:c5a1db4a69f5 (draft) f
   |
   o  12:40db8afa467b (public) c
   |
@@ -366,32 +356,27 @@
 
   $ cp -r base reorder
   $ cd reorder
-  $ hg histedit -r 'b449568bf7fc' --commands - << EOF
-  > pick b449568bf7fc 11 f
-  > pick 3a6c53ee7f3d 15 j
-  > pick 6b70183d2492 12 g
-  > pick b605fb7503f2 14 i
-  > pick 7395e1ff83bd 13 h
-  > pick ee118ab9fa44 16 k
+  $ hg histedit -r '947ece25170f' --commands - << EOF
+  > pick 947ece25170f 11 f
+  > pick c62e7241a4f2 15 j
+  > pick d22905de3528 12 g
+  > pick 9cd3934e05af 14 i
+  > pick ee4a24fc4dfa 13 h
+  > pick 14bda137d5b3 16 k
   > EOF
   0 files updated, 0 files merged, 5 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg log -G
-  @  23:558246857888 (secret) k
+  @  23:9e712162b2c1 (secret) k
+  |
+  o  22:490861543602 (secret) h
   |
-  o  22:28bd44768535 (secret) h
+  o  21:86aeda50b70d (secret) i
   |
-  o  21:d5395202aeb9 (secret) i
+  o  20:b2fa360bc090 (secret) g
   |
-  o  20:21edda8e341b (secret) g
+  o  19:e10fb4e3eb8e (secret) j
   |
-  o  19:5ab64f3a4832 (secret) j
-  |
-  o  13:b449568bf7fc (draft) f
+  o  13:947ece25170f (draft) f
   |
   o  12:40db8afa467b (public) c
   |
@@ -413,51 +398,45 @@
   > [phases]
   > new-commit=secret
   > EOF
-  $ hg histedit -r 'b449568bf7fc' --commands - << EOF
-  > pick 7395e1ff83bd 13 h
-  > fold b449568bf7fc 11 f
-  > pick 6b70183d2492 12 g
-  > fold 3a6c53ee7f3d 15 j
-  > pick b605fb7503f2 14 i
-  > fold ee118ab9fa44 16 k
+  $ hg histedit -r '947ece25170f' --commands - << EOF
+  > pick ee4a24fc4dfa 13 h
+  > fold 947ece25170f 11 f
+  > pick d22905de3528 12 g
+  > fold c62e7241a4f2 15 j
+  > pick 9cd3934e05af 14 i
+  > fold 14bda137d5b3 16 k
   > EOF
   0 files updated, 0 files merged, 6 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg log -G
-  @  27:f9daec13fb98 (secret) i
+  @  27:769e8ee8708e (secret) i
   |
-  o  24:49807617f46a (secret) g
+  o  24:3de6dbab1b62 (secret) g
   |
-  o  21:050280826e04 (draft) h
+  o  21:1d51647632b2 (draft) h
   |
   o  12:40db8afa467b (public) c
   |
   o  0:cb9a9f314b8b (public) a
   
-  $ hg co 49807617f46a
+  $ hg co 3de6dbab1b62
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   $ echo wat >> wat
   $ hg add wat
   $ hg ci -m 'add wat'
   created new head
-  $ hg merge f9daec13fb98
+  $ hg merge 769e8ee8708e
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   $ hg ci -m 'merge'
   $ echo not wat > wat
   $ hg ci -m 'modify wat'
-  $ hg histedit 050280826e04
+  $ hg histedit 1d51647632b2
   abort: cannot edit history that contains merges
   [255]
   $ cd ..
--- a/tests/test-histedit-outgoing.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-histedit-outgoing.t	Thu Dec 17 17:27:32 2015 -0600
@@ -53,7 +53,6 @@
   #  d, drop = remove commit from history
   #  m, mess = edit commit message without changing commit content
   #
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd ..
 
 show the error from unrelated repos
@@ -85,7 +84,6 @@
   #  d, drop = remove commit from history
   #  m, mess = edit commit message without changing commit content
   #
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd ..
 
 test sensitivity to branch in URL:
@@ -109,7 +107,6 @@
   #  d, drop = remove commit from history
   #  m, mess = edit commit message without changing commit content
   #
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 test to check number of roots in outgoing revisions
 
--- a/tests/test-hook.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-hook.t	Thu Dec 17 17:27:32 2015 -0600
@@ -81,10 +81,10 @@
   pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
   2:ee9deb46ab31
   pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
+  created new head
   txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
   commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
   commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
-  created new head
   $ hg merge 1
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
@@ -563,9 +563,9 @@
   foo
   committing manifest
   committing changelog
+  committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
   calling hook commit.auto: hgext_hookext.autohook
   Automatically installed hook
-  committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
 
   $ hg showconfig hooks
   hooks.commit.auto=<function autohook at *> (glob)
--- a/tests/test-import.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-import.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1735,7 +1735,10 @@
   > mercurial.cmdutil.extrapostimport.append('foo')
   > mercurial.cmdutil.extrapostimportmap['foo'] = postimport
   > EOF
-  $ printf "[extensions]\nparseextra=$TESTTMP/parseextra.py" >> $HGRCPATH
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > parseextra=$TESTTMP/parseextra.py
+  > EOF
   $ hg up -C tip
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat > $TESTTMP/foo.patch <<EOF
--- a/tests/test-inherit-mode.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-inherit-mode.t	Thu Dec 17 17:27:32 2015 -0600
@@ -83,6 +83,7 @@
   00660 ./.hg/store/undo
   00660 ./.hg/store/undo.backupfiles
   00660 ./.hg/store/undo.phaseroots
+  00660 ./.hg/undo.backup.dirstate
   00660 ./.hg/undo.bookmarks
   00660 ./.hg/undo.branch
   00660 ./.hg/undo.desc
--- a/tests/test-init.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-init.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,5 +1,12 @@
 This test tries to exercise the ssh functionality with a dummy script
 
+(enable general delta early)
+
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > usegeneraldelta=yes
+  > EOF
+
   $ checknewrepo()
   > {
   >    name=$1
@@ -20,6 +27,7 @@
   00changelog.i created
   dotencode
   fncache
+  generaldelta
   revlogv1
   store
   $ echo this > local/foo
@@ -55,6 +63,7 @@
 
   $ hg --config format.usestore=false init old
   $ checknewrepo old
+  generaldelta
   revlogv1
 
 creating repo with format.usefncache=false
@@ -63,6 +72,7 @@
   $ checknewrepo old2
   store created
   00changelog.i created
+  generaldelta
   revlogv1
   store
 
@@ -73,6 +83,18 @@
   store created
   00changelog.i created
   fncache
+  generaldelta
+  revlogv1
+  store
+
+creating repo with format.dotencode=false
+
+  $ hg --config format.generaldelta=false --config format.usegeneraldelta=false init old4
+  $ checknewrepo old4
+  store created
+  00changelog.i created
+  dotencode
+  fncache
   revlogv1
   store
 
@@ -186,6 +208,7 @@
   00changelog.i created
   dotencode
   fncache
+  generaldelta
   revlogv1
   store
 
@@ -203,6 +226,7 @@
   00changelog.i created
   dotencode
   fncache
+  generaldelta
   revlogv1
   store
 
@@ -216,6 +240,7 @@
   00changelog.i created
   dotencode
   fncache
+  generaldelta
   revlogv1
   store
 
--- a/tests/test-install.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-install.t	Thu Dec 17 17:27:32 2015 -0600
@@ -41,3 +41,70 @@
   checking commit editor...
   checking username...
   no problems detected
+
+  $ cat >> wixxml.py << EOF
+  > import os, subprocess, sys
+  > import xml.etree.ElementTree as ET
+  > 
+  > # MSYS mangles the path if it expands $TESTDIR
+  > testdir = os.environ['TESTDIR']
+  > ns = {'wix' : 'http://schemas.microsoft.com/wix/2006/wi'}
+  > 
+  > def directory(node, relpath):
+  >     '''generator of files in the xml node, rooted at relpath'''
+  >     dirs = node.findall('./wix:Directory', ns)
+  > 
+  >     for d in dirs:
+  >         for subfile in directory(d, relpath + d.attrib['Name'] + '/'):
+  >             yield subfile
+  > 
+  >     files = node.findall('./wix:Component/wix:File', ns)
+  > 
+  >     for f in files:
+  >         yield relpath + f.attrib['Name']
+  > 
+  > def hgdirectory(relpath):
+  >     '''generator of tracked files, rooted at relpath'''
+  >     hgdir = "%s/../mercurial" % (testdir)
+  >     args = ['hg', '--cwd', hgdir, 'files', '--rev', '.', relpath]
+  >     proc = subprocess.Popen(args, stdout=subprocess.PIPE,
+  >                             stderr=subprocess.PIPE)
+  >     output = proc.communicate()[0]
+  > 
+  >     slash = '/'
+  >     for line in output.splitlines():
+  >         if os.name == 'nt':
+  >             yield line.replace(os.sep, slash)
+  >         else:
+  >             yield line
+  > 
+  > tracked = [f for f in hgdirectory(sys.argv[1])]
+  > 
+  > xml = ET.parse("%s/../contrib/wix/%s.wxs" % (testdir, sys.argv[1]))
+  > root = xml.getroot()
+  > dir = root.find('.//wix:DirectoryRef', ns)
+  > 
+  > installed = [f for f in directory(dir, '')]
+  > 
+  > print('Not installed:')
+  > for f in sorted(set(tracked) - set(installed)):
+  >     print('  %s' % f)
+  > 
+  > print('Not tracked:')
+  > for f in sorted(set(installed) - set(tracked)):
+  >     print('  %s' % f)
+  > EOF
+
+  $ python wixxml.py help
+  Not installed:
+    help/common.txt
+    help/hg.1.txt
+    help/hgignore.5.txt
+    help/hgrc.5.txt
+    help/internals/bundles.txt
+    help/internals/changegroups.txt
+  Not tracked:
+
+  $ python wixxml.py templates
+  Not installed:
+  Not tracked:
--- a/tests/test-issue1502.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-issue1502.t	Thu Dec 17 17:27:32 2015 -0600
@@ -19,8 +19,9 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  not updating: not a linear update
+  abort: not updating: not a linear update
   (merge or update --check to force update)
+  [255]
 
   $ hg -R foo1 book branchy
   $ hg -R foo1 book
--- a/tests/test-issue672.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-issue672.t	Thu Dec 17 17:27:32 2015 -0600
@@ -66,7 +66,7 @@
    ancestor: c64f439569a9, local: e327dca35ac8+, remote: 746e9549ea96
    preserving 1a for resolve of 1a
    1a: local copied/moved from 1 -> m (premerge)
-  picked tool ':merge' for 1a (binary False symlink False)
+  picked tool ':merge' for 1a (binary False symlink False changedelete False)
   merging 1a and 1 to 1a
   my 1a@e327dca35ac8+ other 1@746e9549ea96 ancestor 1@81f4b099af3d
    premerge successful
@@ -89,7 +89,7 @@
    preserving 1 for resolve of 1a
   removing 1
    1a: remote moved from 1 -> m (premerge)
-  picked tool ':merge' for 1a (binary False symlink False)
+  picked tool ':merge' for 1a (binary False symlink False changedelete False)
   merging 1 and 1a to 1a
   my 1a@746e9549ea96+ other 1a@e327dca35ac8 ancestor 1@81f4b099af3d
    premerge successful
--- a/tests/test-keyword.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-keyword.t	Thu Dec 17 17:27:32 2015 -0600
@@ -141,8 +141,8 @@
   committing manifest
   committing changelog
   overwriting a expanding keywords
+  committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
   running hook commit.test: cp a hooktest
-  committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
   $ hg status
   ? hooktest
   $ hg debugrebuildstate
@@ -1204,7 +1204,7 @@
   +xxxx
   $ hg shelve -q --name tmp
   $ hg shelve --list --patch
-  tmp             (*)    changes to 'localresolve' (glob)
+  tmp             (*)* changes to: localresolve (glob)
   
   diff --git a/a b/a
   --- a/a
--- a/tests/test-largefiles-cache.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-largefiles-cache.t	Thu Dec 17 17:27:32 2015 -0600
@@ -193,7 +193,7 @@
   $ echo corruption > .hg/largefiles/e2fb5f2139d086ded2cb600d5a91a196e76bf020
   $ hg up -C
   getting changed largefiles
-  large: data corruption in $TESTTMP/src/.hg/largefiles/e2fb5f2139d086ded2cb600d5a91a196e76bf020 with hash 6a7bb2556144babe3899b25e5428123735bb1e27
+  large: data corruption in $TESTTMP/src/.hg/largefiles/e2fb5f2139d086ded2cb600d5a91a196e76bf020 with hash 6a7bb2556144babe3899b25e5428123735bb1e27 (glob)
   0 largefiles updated, 0 removed
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg st
--- a/tests/test-largefiles-misc.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-largefiles-misc.t	Thu Dec 17 17:27:32 2015 -0600
@@ -509,6 +509,18 @@
   $ hg revert anotherlarge
   $ hg st
   ? sub/anotherlarge.orig
+
+Test orig files go where we want them
+  $ echo moremore >> anotherlarge
+  $ hg revert anotherlarge -v --config 'ui.origbackuppath=.hg/origbackups'
+  creating directory: $TESTTMP/addrm2/.hg/origbackups/.hglf/sub (glob)
+  saving current version of ../.hglf/sub/anotherlarge as $TESTTMP/addrm2/.hg/origbackups/.hglf/sub/anotherlarge.orig (glob)
+  reverting ../.hglf/sub/anotherlarge (glob)
+  creating directory: $TESTTMP/addrm2/.hg/origbackups/sub (glob)
+  found 90c622cf65cebe75c5842f9136c459333faf392e in store
+  found 90c622cf65cebe75c5842f9136c459333faf392e in store
+  $ ls ../.hg/origbackups/sub
+  anotherlarge.orig
   $ cd ..
 
 Test glob logging from the root dir
--- a/tests/test-largefiles-update.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-largefiles-update.t	Thu Dec 17 17:27:32 2015 -0600
@@ -596,7 +596,7 @@
   rebasing 1:72518492caa6 "#1"
   rebasing 4:07d6153b5c04 "#4"
   local changed .hglf/large1 which remote deleted
-  use (c)hanged version or (d)elete? c
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
 
   $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
   -e5bb990443d6a92aaf7223813720f7566c9dd05b
--- a/tests/test-lfconvert.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-lfconvert.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,6 +1,8 @@
   $ USERCACHE="$TESTTMP/cache"; export USERCACHE
   $ mkdir "${USERCACHE}"
   $ cat >> $HGRCPATH <<EOF
+  > [format]
+  > usegeneraldelta=yes
   > [extensions]
   > largefiles =
   > share =
@@ -97,6 +99,7 @@
   $ cat .hg/requires
   dotencode
   fncache
+  generaldelta
   largefiles
   revlogv1
   store
--- a/tests/test-lrucachedict.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-lrucachedict.py	Thu Dec 17 17:27:32 2015 -0600
@@ -34,5 +34,13 @@
     d.clear()
     printifpresent(d, ['b', 'c', 'd', 'e', 'f'])
 
+    # Now test dicts that aren't full.
+    d = util.lrucachedict(4)
+    d['a'] = 1
+    d['b'] = 2
+    d['a']
+    d['b']
+    printifpresent(d, ['a', 'b'])
+
 if __name__ == '__main__':
     test_lrucachedict()
--- a/tests/test-lrucachedict.py.out	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-lrucachedict.py.out	Thu Dec 17 17:27:32 2015 -0600
@@ -29,3 +29,7 @@
 'd' in d: False
 'e' in d: False
 'f' in d: False
+'a' in d: True
+d['a']: 1
+'b' in d: True
+d['b']: 2
--- a/tests/test-manifestv2.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-manifestv2.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,5 +1,10 @@
 Create repo with old manifest
 
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > usegeneraldelta=yes
+  > EOF
+
   $ hg init existing
   $ cd existing
   $ echo footext > foo
@@ -91,6 +96,6 @@
 Check that manifest revlog is smaller than for v1
 
   $ hg debugindex -m
-     rev    offset  length   base linkrev nodeid       p1           p2
-       0         0      81      0       0 57361477c778 000000000000 000000000000
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0      81     -1       0 57361477c778 000000000000 000000000000
        1        81      33      0       1 aeaab5a2ef74 57361477c778 000000000000
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-merge-changedelete.t	Thu Dec 17 17:27:32 2015 -0600
@@ -0,0 +1,988 @@
+Tests for change/delete conflicts, including:
+b5605d88dc27: Make ui.prompt repeat on "unrecognized response" again
+ (issue897)
+
+840e2b315c1f: Fix misleading error and prompts during update/merge
+ (issue556)
+
+Make sure HGMERGE doesn't interfere with the test
+  $ unset HGMERGE
+
+  $ status() {
+  >     echo "--- status ---"
+  >     hg st -A file1 file2 file3
+  >     echo "--- resolve --list ---"
+  >     hg resolve --list file1 file2 file3
+  >     echo "--- debugmergestate ---"
+  >     hg debugmergestate
+  >     for file in file1 file2 file3; do
+  >         if [ -f $file ]; then
+  >             echo "--- $file ---"
+  >             cat $file
+  >         else
+  >             echo "*** $file does not exist"
+  >         fi
+  >     done
+  > }
+
+  $ hg init repo
+  $ cd repo
+
+  $ echo 1 > file1
+  $ echo 2 > file2
+  $ echo 3 > file3
+  $ hg ci -Am 'added files'
+  adding file1
+  adding file2
+  adding file3
+
+  $ hg rm file1
+  $ echo changed >> file2
+  $ echo changed1 >> file3
+  $ hg ci -m 'removed file1, changed file2, changed file3'
+
+  $ hg co 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ echo changed >> file1
+  $ hg rm file2
+  $ echo changed2 >> file3
+  $ hg ci -m 'changed file1, removed file2, changed file3'
+  created new head
+
+
+Non-interactive merge:
+
+  $ hg merge -y
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  merging file3
+  warning: conflicts while merging file3! (edit, then use 'hg resolve --mark')
+  0 files updated, 2 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+
+  $ status
+  --- status ---
+  M file2
+  M file3
+  C file1
+  --- resolve --list ---
+  R file1
+  R file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3
+  changed2
+  =======
+  changed1
+  >>>>>>> other: 10f9a0a634e8  - test: removed file1, changed file2, changed file3
+
+
+Interactive merge:
+
+  $ hg co -C
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --config ui.interactive=true <<EOF
+  > c
+  > d
+  > EOF
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? d
+  merging file3
+  warning: conflicts while merging file3! (edit, then use 'hg resolve --mark')
+  0 files updated, 2 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+
+  $ status
+  --- status ---
+  file2: * (glob)
+  M file3
+  C file1
+  --- resolve --list ---
+  R file1
+  R file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  --- file1 ---
+  1
+  changed
+  *** file2 does not exist
+  --- file3 ---
+  3
+  <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3
+  changed2
+  =======
+  changed1
+  >>>>>>> other: 10f9a0a634e8  - test: removed file1, changed file2, changed file3
+
+
+Interactive merge with bad input:
+
+  $ hg co -C
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg merge --config ui.interactive=true <<EOF
+  > foo
+  > bar
+  > d
+  > baz
+  > c
+  > EOF
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? foo
+  unrecognized response
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? bar
+  unrecognized response
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? d
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? baz
+  unrecognized response
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  merging file3
+  warning: conflicts while merging file3! (edit, then use 'hg resolve --mark')
+  0 files updated, 1 files merged, 1 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+
+  $ status
+  --- status ---
+  M file2
+  M file3
+  R file1
+  --- resolve --list ---
+  R file1
+  R file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  *** file1 does not exist
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3
+  changed2
+  =======
+  changed1
+  >>>>>>> other: 10f9a0a634e8  - test: removed file1, changed file2, changed file3
+
+
+Interactive merge with not enough input:
+
+  $ hg co -C
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --config ui.interactive=true <<EOF
+  > d
+  > EOF
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? d
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
+  merging file3
+  warning: conflicts while merging file3! (edit, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 1 files removed, 2 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+
+  $ status
+  --- status ---
+  M file2
+  M file3
+  R file1
+  --- resolve --list ---
+  R file1
+  U file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "u", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  *** file1 does not exist
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3
+  changed2
+  =======
+  changed1
+  >>>>>>> other: 10f9a0a634e8  - test: removed file1, changed file2, changed file3
+
+Choose local versions of files
+
+  $ hg co -C
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --tool :local
+  0 files updated, 3 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ status 2>&1 | tee $TESTTMP/local.status
+  --- status ---
+  file2: * (glob)
+  M file3
+  C file1
+  --- resolve --list ---
+  R file1
+  R file2
+  R file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "r", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  --- file1 ---
+  1
+  changed
+  *** file2 does not exist
+  --- file3 ---
+  3
+  changed2
+
+Choose other versions of files
+
+  $ hg co -C
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg merge --tool :other
+  0 files updated, 2 files merged, 1 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ status 2>&1 | tee $TESTTMP/other.status
+  --- status ---
+  M file2
+  M file3
+  R file1
+  --- resolve --list ---
+  R file1
+  R file2
+  R file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "r", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  *** file1 does not exist
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed1
+
+Fail
+
+  $ hg co -C
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --tool :fail
+  0 files updated, 0 files merged, 0 files removed, 3 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ status 2>&1 | tee $TESTTMP/fail.status
+  --- status ---
+  M file2
+  M file3
+  C file1
+  --- resolve --list ---
+  U file1
+  U file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "u", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed2
+
+Force prompts with no input (should be similar to :fail)
+
+  $ hg co -C
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --config ui.interactive=True --tool :prompt
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? 
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
+  no tool found to merge file3
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? 
+  0 files updated, 0 files merged, 0 files removed, 3 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ status 2>&1 | tee $TESTTMP/prompt.status
+  --- status ---
+  M file2
+  M file3
+  C file1
+  --- resolve --list ---
+  U file1
+  U file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "u", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed2
+  $ diff -U8 $TESTTMP/fail.status $TESTTMP/prompt.status
+
+
+Force prompts
+
+  $ hg co -C
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --tool :prompt
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  no tool found to merge file3
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? u
+  0 files updated, 2 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ status
+  --- status ---
+  M file2
+  M file3
+  C file1
+  --- resolve --list ---
+  R file1
+  R file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed2
+
+Choose to merge all files
+
+  $ hg co -C
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --tool :merge3
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  merging file3
+  warning: conflicts while merging file3! (edit, then use 'hg resolve --mark')
+  0 files updated, 2 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ status
+  --- status ---
+  M file2
+  M file3
+  C file1
+  --- resolve --list ---
+  R file1
+  R file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3
+  changed2
+  ||||||| base
+  =======
+  changed1
+  >>>>>>> other: 10f9a0a634e8  - test: removed file1, changed file2, changed file3
+
+Exercise transitions between local, other, fail and prompt, and make sure the
+dirstate stays consistent. (Compare with each other and to the above
+invocations.)
+
+  $ testtransitions() {
+  >     # this traversal order covers every transition
+  >     tools="local other prompt local fail other local prompt other fail prompt fail local"
+  >     lasttool="merge3"
+  >     for tool in $tools; do
+  >         echo "=== :$lasttool -> :$tool ==="
+  >         ref="$TESTTMP/$tool.status"
+  >         hg resolve --unmark --all
+  >         hg resolve --tool ":$tool" --all --config ui.interactive=True
+  >         status > "$TESTTMP/compare.status" 2>&1
+  >         echo '--- diff of status ---'
+  >         if diff -U8 "$TESTTMP/$tool.status" "$TESTTMP/compare.status"; then
+  >             echo '(status identical)'
+  >         fi
+  >         lasttool="$tool"
+  >         echo
+  >     done
+  > }
+
+  $ testtransitions
+  === :merge3 -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? 
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
+  no tool found to merge file3
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? 
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
+  no tool found to merge file3
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? 
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
+  no tool found to merge file3
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+
+
+Non-interactive linear update
+
+  $ hg co -C 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo changed >> file1
+  $ hg rm file2
+  $ hg update 1 -y
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  1 files updated, 2 files merged, 0 files removed, 0 files unresolved
+  $ status
+  --- status ---
+  A file1
+  C file2
+  C file3
+  --- resolve --list ---
+  R file1
+  R file2
+  --- debugmergestate ---
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed1
+
+Choose local versions of files
+
+  $ hg co -C 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo changed >> file1
+  $ hg rm file2
+  $ hg update 1 --tool :local
+  1 files updated, 2 files merged, 0 files removed, 0 files unresolved
+  $ status 2>&1 | tee $TESTTMP/local.status
+  --- status ---
+  file2: * (glob)
+  A file1
+  C file3
+  --- resolve --list ---
+  R file1
+  R file2
+  --- debugmergestate ---
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  --- file1 ---
+  1
+  changed
+  *** file2 does not exist
+  --- file3 ---
+  3
+  changed1
+
+Choose other versions of files
+
+  $ hg co -C 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo changed >> file1
+  $ hg rm file2
+  $ hg update 1 --tool :other
+  1 files updated, 1 files merged, 1 files removed, 0 files unresolved
+  $ status 2>&1 | tee $TESTTMP/other.status
+  --- status ---
+  file1: * (glob)
+  C file2
+  C file3
+  --- resolve --list ---
+  R file1
+  R file2
+  --- debugmergestate ---
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  *** file1 does not exist
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed1
+
+Fail
+
+  $ hg co -C 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo changed >> file1
+  $ hg rm file2
+  $ hg update 1 --tool :fail
+  1 files updated, 0 files merged, 0 files removed, 2 files unresolved
+  use 'hg resolve' to retry unresolved file merges
+  [1]
+  $ status 2>&1 | tee $TESTTMP/fail.status
+  --- status ---
+  A file1
+  C file2
+  C file3
+  --- resolve --list ---
+  U file1
+  U file2
+  --- debugmergestate ---
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "u", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed1
+
+Force prompts with no input
+
+  $ hg co -C 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo changed >> file1
+  $ hg rm file2
+  $ hg update 1 --config ui.interactive=True --tool :prompt
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? 
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
+  1 files updated, 0 files merged, 0 files removed, 2 files unresolved
+  use 'hg resolve' to retry unresolved file merges
+  [1]
+  $ status 2>&1 | tee $TESTTMP/prompt.status
+  --- status ---
+  A file1
+  C file2
+  C file3
+  --- resolve --list ---
+  U file1
+  U file2
+  --- debugmergestate ---
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "u", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed1
+  $ diff -U8 $TESTTMP/fail.status $TESTTMP/prompt.status
+
+Choose to merge all files
+
+  $ hg co -C 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo changed >> file1
+  $ hg rm file2
+  $ hg update 1 --tool :merge3
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  1 files updated, 2 files merged, 0 files removed, 0 files unresolved
+  $ status
+  --- status ---
+  A file1
+  C file2
+  C file3
+  --- resolve --list ---
+  R file1
+  R file2
+  --- debugmergestate ---
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed1
+
+Test transitions between different merge tools
+
+  $ testtransitions
+  === :merge3 -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? 
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? 
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? 
+  remote changed file2 which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
--- a/tests/test-merge-commit.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-merge-commit.t	Thu Dec 17 17:27:32 2015 -0600
@@ -73,7 +73,7 @@
    ancestor: 0f2ff26688b9, local: 2263c1be0967+, remote: 0555950ead28
    preserving bar for resolve of bar
    bar: versions differ -> m (premerge)
-  picked tool ':merge' for bar (binary False symlink False)
+  picked tool ':merge' for bar (binary False symlink False changedelete False)
   merging bar
   my bar@2263c1be0967+ other bar@0555950ead28 ancestor bar@0f2ff26688b9
    premerge successful
@@ -159,7 +159,7 @@
    ancestor: 0f2ff26688b9, local: 2263c1be0967+, remote: 3ffa6b9e35f0
    preserving bar for resolve of bar
    bar: versions differ -> m (premerge)
-  picked tool ':merge' for bar (binary False symlink False)
+  picked tool ':merge' for bar (binary False symlink False changedelete False)
   merging bar
   my bar@2263c1be0967+ other bar@3ffa6b9e35f0 ancestor bar@0f2ff26688b9
    premerge successful
--- a/tests/test-merge-criss-cross.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-merge-criss-cross.t	Thu Dec 17 17:27:32 2015 -0600
@@ -83,11 +83,11 @@
    f1: remote is newer -> g
   getting f1
    f2: versions differ -> m (premerge)
-  picked tool ':dump' for f2 (binary False symlink False)
+  picked tool ':dump' for f2 (binary False symlink False changedelete False)
   merging f2
   my f2@3b08d01b0ab5+ other f2@adfe50279922 ancestor f2@40494bf2444c
    f2: versions differ -> m (merge)
-  picked tool ':dump' for f2 (binary False symlink False)
+  picked tool ':dump' for f2 (binary False symlink False changedelete False)
   my f2@3b08d01b0ab5+ other f2@adfe50279922 ancestor f2@40494bf2444c
   1 files updated, 0 files merged, 0 files removed, 1 files unresolved
   use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
--- a/tests/test-merge-force.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-merge-force.t	Thu Dec 17 17:27:32 2015 -0600
@@ -5,7 +5,8 @@
 local changeset, and then modified in the working copy to match the
 remote content, then finally forgotten.
 
-  $ hg init
+  $ hg init repo
+  $ cd repo
 
 Create base changeset
 
@@ -142,55 +143,55 @@
 
   $ hg merge -f --tool internal:merge3 'desc("remote")'
   local changed content1_missing_content1_content4-tracked which remote deleted
-  use (c)hanged version or (d)elete? c
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
   local changed content1_missing_content3_content3-tracked which remote deleted
-  use (c)hanged version or (d)elete? c
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
   local changed content1_missing_content3_content4-tracked which remote deleted
-  use (c)hanged version or (d)elete? c
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
   local changed content1_missing_missing_content4-tracked which remote deleted
-  use (c)hanged version or (d)elete? c
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
   remote changed content1_content2_content1_content1-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content1_content2-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content1_content4-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content1_missing-tracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content1_missing-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content2_content1-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content2_content2-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content2_content4-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content2_missing-tracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content2_missing-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content3_content1-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content3_content2-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content3_content3-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content3_content4-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content3_missing-tracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_content3_missing-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_missing_content1-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_missing_content2-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_missing_content4-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_missing_missing-tracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   remote changed content1_content2_missing_missing-untracked which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   merging content1_content2_content1_content4-tracked
   merging content1_content2_content2_content1-tracked
   merging content1_content2_content2_content4-tracked
@@ -214,7 +215,7 @@
   warning: conflicts while merging missing_content2_content3_content4-tracked! (edit, then use 'hg resolve --mark')
   warning: conflicts while merging missing_content2_missing_content4-tracked! (edit, then use 'hg resolve --mark')
   warning: conflicts while merging missing_content2_missing_content4-untracked! (edit, then use 'hg resolve --mark')
-  39 files updated, 3 files merged, 8 files removed, 10 files unresolved
+  18 files updated, 28 files merged, 8 files removed, 10 files unresolved
   use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
   [1]
 
@@ -226,14 +227,39 @@
 odd 'if force and branchmerge and different' case in manifestmerge().
 
   $ hg resolve -l
+  R content1_content2_content1_content1-untracked
+  R content1_content2_content1_content2-untracked
   U content1_content2_content1_content4-tracked
+  R content1_content2_content1_content4-untracked
+  R content1_content2_content1_missing-tracked
+  R content1_content2_content1_missing-untracked
   R content1_content2_content2_content1-tracked
+  R content1_content2_content2_content1-untracked
+  R content1_content2_content2_content2-untracked
   U content1_content2_content2_content4-tracked
+  R content1_content2_content2_content4-untracked
+  R content1_content2_content2_missing-tracked
+  R content1_content2_content2_missing-untracked
   R content1_content2_content3_content1-tracked
+  R content1_content2_content3_content1-untracked
+  R content1_content2_content3_content2-untracked
   U content1_content2_content3_content3-tracked
+  R content1_content2_content3_content3-untracked
   U content1_content2_content3_content4-tracked
+  R content1_content2_content3_content4-untracked
+  R content1_content2_content3_missing-tracked
+  R content1_content2_content3_missing-untracked
   R content1_content2_missing_content1-tracked
+  R content1_content2_missing_content1-untracked
+  R content1_content2_missing_content2-untracked
   U content1_content2_missing_content4-tracked
+  R content1_content2_missing_content4-untracked
+  R content1_content2_missing_missing-tracked
+  R content1_content2_missing_missing-untracked
+  R content1_missing_content1_content4-tracked
+  R content1_missing_content3_content3-tracked
+  R content1_missing_content3_content4-tracked
+  R content1_missing_missing_content4-tracked
   U missing_content2_content2_content4-tracked
   U missing_content2_content3_content3-tracked
   U missing_content2_content3_content4-tracked
@@ -260,22 +286,20 @@
 missing_missing_content3_missing-tracked becomes removed ('R'), even though
 the remote side did not touch the file
 
-  $ for f in `python $TESTDIR/generate-working-copy-states.py filelist 3`
-  > do
-  >   echo
-  >   hg status -A $f
-  >   if test -f $f
-  >   then
-  >     cat $f
-  >   else
-  >     echo '<missing>'
-  >   fi
-  >   if test -f ${f}.orig
-  >   then
-  >     echo ${f}.orig:
-  >     cat ${f}.orig
-  >   fi
-  > done
+  $ checkstatus() {
+  >   for f in `python $TESTDIR/generate-working-copy-states.py filelist 3`
+  >   do
+  >     echo
+  >     hg status -A $f
+  >     if test -f $f
+  >     then
+  >       cat $f
+  >     else
+  >       echo '<missing>'
+  >     fi
+  >   done
+  > }
+  $ checkstatus 2>&1 | tee $TESTTMP/status1
   
   C content1_content1_content1_content1-tracked
   content1
@@ -357,8 +381,6 @@
   =======
   content2
   >>>>>>> other: 85100b8c675b  - test: remote
-  content1_content2_content1_content4-tracked.orig:
-  content4
   
   M content1_content2_content1_content4-untracked
   content2
@@ -389,8 +411,6 @@
   =======
   content2
   >>>>>>> other: 85100b8c675b  - test: remote
-  content1_content2_content2_content4-tracked.orig:
-  content4
   
   M content1_content2_content2_content4-untracked
   content2
@@ -421,8 +441,6 @@
   =======
   content2
   >>>>>>> other: 85100b8c675b  - test: remote
-  content1_content2_content3_content3-tracked.orig:
-  content3
   
   M content1_content2_content3_content3-untracked
   content2
@@ -435,8 +453,6 @@
   =======
   content2
   >>>>>>> other: 85100b8c675b  - test: remote
-  content1_content2_content3_content4-tracked.orig:
-  content4
   
   M content1_content2_content3_content4-untracked
   content2
@@ -467,8 +483,6 @@
   =======
   content2
   >>>>>>> other: 85100b8c675b  - test: remote
-  content1_content2_missing_content4-tracked.orig:
-  content4
   
   M content1_content2_missing_content4-untracked
   content2
@@ -552,8 +566,6 @@
   =======
   content2
   >>>>>>> other: 85100b8c675b  - test: remote
-  missing_content2_content2_content4-tracked.orig:
-  content4
   
   M missing_content2_content2_content4-untracked
   content2
@@ -577,8 +589,6 @@
   =======
   content2
   >>>>>>> other: 85100b8c675b  - test: remote
-  missing_content2_content3_content3-tracked.orig:
-  content3
   
   M missing_content2_content3_content3-untracked
   content2
@@ -590,8 +600,6 @@
   =======
   content2
   >>>>>>> other: 85100b8c675b  - test: remote
-  missing_content2_content3_content4-tracked.orig:
-  content4
   
   M missing_content2_content3_content4-untracked
   content2
@@ -615,8 +623,6 @@
   =======
   content2
   >>>>>>> other: 85100b8c675b  - test: remote
-  missing_content2_missing_content4-tracked.orig:
-  content4
   
   M missing_content2_missing_content4-untracked
   <<<<<<< local: 0447570f1af6 - test: local
@@ -625,8 +631,6 @@
   =======
   content2
   >>>>>>> other: 85100b8c675b  - test: remote
-  missing_content2_missing_content4-untracked.orig:
-  content4
   
   M missing_content2_missing_missing-tracked
   content2
@@ -663,3 +667,116 @@
   
   missing_missing_missing_missing-untracked: * (glob)
   <missing>
+
+  $ for f in `python $TESTDIR/generate-working-copy-states.py filelist 3`
+  > do
+  >   if test -f ${f}.orig
+  >   then
+  >     echo ${f}.orig:
+  >     cat ${f}.orig
+  >   fi
+  > done
+  content1_content2_content1_content4-tracked.orig:
+  content4
+  content1_content2_content2_content4-tracked.orig:
+  content4
+  content1_content2_content3_content3-tracked.orig:
+  content3
+  content1_content2_content3_content4-tracked.orig:
+  content4
+  content1_content2_missing_content4-tracked.orig:
+  content4
+  missing_content2_content2_content4-tracked.orig:
+  content4
+  missing_content2_content3_content3-tracked.orig:
+  content3
+  missing_content2_content3_content4-tracked.orig:
+  content4
+  missing_content2_missing_content4-tracked.orig:
+  content4
+  missing_content2_missing_content4-untracked.orig:
+  content4
+
+Re-resolve and check status
+
+  $ hg resolve --unmark --all
+  $ hg resolve --all --tool :local
+  (no more unresolved files)
+  $ hg resolve --unmark --all
+  $ hg resolve --all --tool internal:merge3
+  remote changed content1_content2_content1_content1-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_content1_content2-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  merging content1_content2_content1_content4-tracked
+  remote changed content1_content2_content1_content4-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_content1_missing-tracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_content1_missing-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  merging content1_content2_content2_content1-tracked
+  remote changed content1_content2_content2_content1-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_content2_content2-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  merging content1_content2_content2_content4-tracked
+  remote changed content1_content2_content2_content4-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_content2_missing-tracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_content2_missing-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  merging content1_content2_content3_content1-tracked
+  remote changed content1_content2_content3_content1-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_content3_content2-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  merging content1_content2_content3_content3-tracked
+  remote changed content1_content2_content3_content3-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  merging content1_content2_content3_content4-tracked
+  remote changed content1_content2_content3_content4-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_content3_missing-tracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_content3_missing-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  merging content1_content2_missing_content1-tracked
+  remote changed content1_content2_missing_content1-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_missing_content2-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  merging content1_content2_missing_content4-tracked
+  remote changed content1_content2_missing_content4-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_missing_missing-tracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  remote changed content1_content2_missing_missing-untracked which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
+  local changed content1_missing_content1_content4-tracked which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
+  local changed content1_missing_content3_content3-tracked which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
+  local changed content1_missing_content3_content4-tracked which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
+  local changed content1_missing_missing_content4-tracked which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
+  merging missing_content2_content2_content4-tracked
+  merging missing_content2_content3_content3-tracked
+  merging missing_content2_content3_content4-tracked
+  merging missing_content2_missing_content4-tracked
+  merging missing_content2_missing_content4-untracked
+  warning: conflicts while merging content1_content2_content1_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging content1_content2_content2_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging content1_content2_content3_content3-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging content1_content2_content3_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging content1_content2_missing_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging missing_content2_content2_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging missing_content2_content3_content3-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging missing_content2_content3_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging missing_content2_missing_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging missing_content2_missing_content4-untracked! (edit, then use 'hg resolve --mark')
+  [1]
+  $ checkstatus > $TESTTMP/status2 2>&1
+  $ diff -U8 $TESTTMP/status1 $TESTTMP/status2
--- a/tests/test-merge-local.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-merge-local.t	Thu Dec 17 17:27:32 2015 -0600
@@ -59,6 +59,9 @@
   use 'hg resolve' to retry unresolved file merges
   [1]
 
+  $ hg resolve -m
+  (no more unresolved files)
+
   $ hg co 0
   merging zzz1_merge_ok
   merging zzz2_merge_bad
@@ -83,6 +86,9 @@
 
 Local merge with conflicts:
 
+  $ hg resolve -m
+  (no more unresolved files)
+
   $ hg co
   merging zzz1_merge_ok
   merging zzz2_merge_bad
@@ -91,7 +97,10 @@
   use 'hg resolve' to retry unresolved file merges
   [1]
 
-  $ hg co 0
+  $ hg resolve -m
+  (no more unresolved files)
+
+  $ hg co 0 --config 'ui.origbackuppath=.hg/origbackups'
   merging zzz1_merge_ok
   merging zzz2_merge_bad
   warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark')
@@ -99,6 +108,10 @@
   use 'hg resolve' to retry unresolved file merges
   [1]
 
+Are orig files from the last commit where we want them?
+  $ ls .hg/origbackups
+  zzz2_merge_bad.orig
+
   $ hg diff --nodates | grep "^[+-][^<>]"
   --- a/zzz1_merge_ok
   +++ b/zzz1_merge_ok
@@ -120,6 +133,9 @@
 
   $ hg revert zzz2_merge_bad
 
+  $ hg resolve -m
+  (no more unresolved files)
+
   $ hg co
   merging zzz1_merge_ok
   4 files updated, 1 files merged, 2 files removed, 0 files unresolved
--- a/tests/test-merge-prompt.t	Mon Nov 30 13:47:29 2015 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +0,0 @@
-Test for
-b5605d88dc27: Make ui.prompt repeat on "unrecognized response" again
- (issue897)
-
-840e2b315c1f: Fix misleading error and prompts during update/merge
- (issue556)
-
-  $ status() {
-  >     echo "--- status ---"
-  >     hg st -A file1 file2
-  >     for file in file1 file2; do
-  >         if [ -f $file ]; then
-  >             echo "--- $file ---"
-  >             cat $file
-  >         else
-  >             echo "*** $file does not exist"
-  >         fi
-  >     done
-  > }
-
-  $ hg init
-
-  $ echo 1 > file1
-  $ echo 2 > file2
-  $ hg ci -Am 'added file1 and file2'
-  adding file1
-  adding file2
-
-  $ hg rm file1
-  $ echo changed >> file2
-  $ hg ci -m 'removed file1, changed file2'
-
-  $ hg co 0
-  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-
-  $ echo changed >> file1
-  $ hg rm file2
-  $ hg ci -m 'changed file1, removed file2'
-  created new head
-
-
-Non-interactive merge:
-
-  $ hg merge -y
-  local changed file1 which remote deleted
-  use (c)hanged version or (d)elete? c
-  remote changed file2 which local deleted
-  use (c)hanged version or leave (d)eleted? c
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
-
-  $ status
-  --- status ---
-  M file2
-  C file1
-  --- file1 ---
-  1
-  changed
-  --- file2 ---
-  2
-  changed
-
-
-Interactive merge:
-
-  $ hg co -C
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-
-  $ hg merge --config ui.interactive=true <<EOF
-  > c
-  > d
-  > EOF
-  local changed file1 which remote deleted
-  use (c)hanged version or (d)elete? c
-  remote changed file2 which local deleted
-  use (c)hanged version or leave (d)eleted? d
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
-
-  $ status
-  --- status ---
-  file2: * (glob)
-  C file1
-  --- file1 ---
-  1
-  changed
-  *** file2 does not exist
-
-
-Interactive merge with bad input:
-
-  $ hg co -C
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-
-  $ hg merge --config ui.interactive=true <<EOF
-  > foo
-  > bar
-  > d
-  > baz
-  > c
-  > EOF
-  local changed file1 which remote deleted
-  use (c)hanged version or (d)elete? foo
-  unrecognized response
-  local changed file1 which remote deleted
-  use (c)hanged version or (d)elete? bar
-  unrecognized response
-  local changed file1 which remote deleted
-  use (c)hanged version or (d)elete? d
-  remote changed file2 which local deleted
-  use (c)hanged version or leave (d)eleted? baz
-  unrecognized response
-  remote changed file2 which local deleted
-  use (c)hanged version or leave (d)eleted? c
-  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
-
-  $ status
-  --- status ---
-  M file2
-  R file1
-  *** file1 does not exist
-  --- file2 ---
-  2
-  changed
-
-
-Interactive merge with not enough input:
-
-  $ hg co -C
-  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
-
-  $ hg merge --config ui.interactive=true <<EOF
-  > d
-  > EOF
-  local changed file1 which remote deleted
-  use (c)hanged version or (d)elete? d
-  remote changed file2 which local deleted
-  use (c)hanged version or leave (d)eleted? abort: response expected
-  [255]
-
-  $ status
-  --- status ---
-  file2: * (glob)
-  C file1
-  --- file1 ---
-  1
-  changed
-  *** file2 does not exist
-
--- a/tests/test-merge-remove.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-merge-remove.t	Thu Dec 17 17:27:32 2015 -0600
@@ -103,9 +103,9 @@
 
   $ hg merge -f
   remote changed bar which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   merging foo1 and foo to foo1
-  1 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 2 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   $ cat bar
   bleh
--- a/tests/test-merge-tools.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-merge-tools.t	Thu Dec 17 17:27:32 2015 -0600
@@ -50,6 +50,8 @@
   >   cat f
   >   echo "# hg stat"
   >   hg stat
+  >   echo "# hg resolve --list"
+  >   hg resolve --list
   >   rm -f f.orig
   > }
 
@@ -82,6 +84,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 simplest hgrc using false for merge:
 
@@ -103,6 +107,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 #if unix-permissions
 
@@ -150,6 +156,8 @@
   space
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 unless lowered on command line:
 
@@ -171,6 +179,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 or false set higher on command line:
 
@@ -192,6 +202,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 or true set to disabled:
   $ beforemerge
@@ -212,6 +224,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 or true.executable not found in PATH:
 
@@ -233,6 +247,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 or true.executable with bogus path:
 
@@ -254,6 +270,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 but true.executable set to cat found in PATH works:
 
@@ -280,6 +298,8 @@
   space
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 and true.executable set to cat with path works:
 
@@ -305,6 +325,8 @@
   space
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 #if unix-permissions
 
@@ -330,6 +352,8 @@
   space
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 #endif
 
@@ -356,6 +380,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 merge-patterns specifies executable not found in PATH and gets warning:
 
@@ -380,6 +406,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 merge-patterns specifies executable with bogus path and gets warning:
 
@@ -404,6 +432,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 ui.merge overrules priority
 
@@ -428,6 +458,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 ui.merge specifies internal:fail:
 
@@ -447,6 +479,8 @@
   space
   # hg stat
   M f
+  # hg resolve --list
+  U f
 
 ui.merge specifies :local (without internal prefix):
 
@@ -465,6 +499,8 @@
   space
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 ui.merge specifies internal:other:
 
@@ -483,6 +519,8 @@
   space
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 ui.merge specifies internal:prompt:
 
@@ -493,16 +531,106 @@
   true.executable=cat
   # hg update -C 1
   $ hg merge -r 2 --config ui.merge=internal:prompt
-   no tool found to merge f
-  keep (l)ocal or take (o)ther? l
-  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
+  no tool found to merge f
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? u
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ aftermerge
+  # cat f
+  revision 1
+  space
+  # hg stat
+  M f
+  # hg resolve --list
+  U f
+
+ui.merge specifies :prompt, with 'leave unresolved' chosen
+
+  $ beforemerge
+  [merge-tools]
+  false.whatever=
+  true.priority=1
+  true.executable=cat
+  # hg update -C 1
+  $ hg merge -r 2 --config ui.merge=:prompt --config ui.interactive=True << EOF
+  > u
+  > EOF
+  no tool found to merge f
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? u
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
   $ aftermerge
   # cat f
   revision 1
   space
   # hg stat
   M f
+  # hg resolve --list
+  U f
+
+prompt with EOF
+
+  $ beforemerge
+  [merge-tools]
+  false.whatever=
+  true.priority=1
+  true.executable=cat
+  # hg update -C 1
+  $ hg merge -r 2 --config ui.merge=internal:prompt --config ui.interactive=true
+  no tool found to merge f
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? 
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ aftermerge
+  # cat f
+  revision 1
+  space
+  # hg stat
+  M f
+  # hg resolve --list
+  U f
+  $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
+  no tool found to merge f
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? 
+  [1]
+  $ aftermerge
+  # cat f
+  revision 1
+  space
+  # hg stat
+  M f
+  ? f.orig
+  # hg resolve --list
+  U f
+  $ rm f
+  $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
+  no tool found to merge f
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? 
+  [1]
+  $ aftermerge
+  # cat f
+  revision 1
+  space
+  # hg stat
+  M f
+  # hg resolve --list
+  U f
+  $ hg resolve --all --config ui.merge=internal:prompt
+  no tool found to merge f
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? u
+  [1]
+  $ aftermerge
+  # cat f
+  revision 1
+  space
+  # hg stat
+  M f
+  ? f.orig
+  # hg resolve --list
+  U f
 
 ui.merge specifies internal:dump:
 
@@ -527,6 +655,8 @@
   ? f.local
   ? f.orig
   ? f.other
+  # hg resolve --list
+  U f
 
 f.base:
 
@@ -568,6 +698,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 Premerge
 
@@ -592,6 +724,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 HGMERGE specifies internal:other but is overruled by --tool=false
 
@@ -615,6 +749,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
   $ unset HGMERGE # make sure HGMERGE doesn't interfere with remaining tests
 
@@ -671,6 +807,8 @@
   space
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 update should also have --tool
 
@@ -712,6 +850,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 Default is silent simplemerge:
 
@@ -732,6 +872,8 @@
   revision 3
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 .premerge=True is same:
 
@@ -752,6 +894,8 @@
   revision 3
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 .premerge=False executes merge-tool:
 
@@ -778,6 +922,8 @@
   space
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 premerge=keep keeps conflict markers in:
 
@@ -810,6 +956,8 @@
   >>>>>>> other: 81448d39c9a0 - test: revision 4
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 premerge=keep-merge3 keeps conflict markers with base content:
 
@@ -848,6 +996,8 @@
   >>>>>>> other: 81448d39c9a0 - test: revision 4
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 
 Tool execution
@@ -886,6 +1036,8 @@
   space
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 Merge with "echo mergeresult > $local":
 
@@ -904,6 +1056,8 @@
   mergeresult
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 - and $local is the file f:
 
@@ -922,6 +1076,8 @@
   mergeresult
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 Merge with "echo mergeresult > $output" - the variable is a bit magic:
 
@@ -940,6 +1096,8 @@
   mergeresult
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 Merge using tool with a path that must be quoted:
 
@@ -969,6 +1127,8 @@
   space
   # hg stat
   M f
+  # hg resolve --list
+  R f
 
 Issue3581: Merging a filename that needs to be quoted
 (This test doesn't work on Windows filesystems even on Linux, so check
@@ -1029,6 +1189,8 @@
   # hg stat
   M f
   ? f.orig
+  # hg resolve --list
+  U f
 
 #if symlink
 
--- a/tests/test-merge-types.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-merge-types.t	Thu Dec 17 17:27:32 2015 -0600
@@ -36,7 +36,7 @@
    ancestor: c334dc3be0da, local: 521a1e40188f+, remote: 3574f3e69b1c
    preserving a for resolve of a
    a: versions differ -> m (premerge)
-  picked tool ':merge' for a (binary False symlink True)
+  picked tool ':merge' for a (binary False symlink True changedelete False)
   merging a
   my a@521a1e40188f+ other a@3574f3e69b1c ancestor a@c334dc3be0da
   warning: internal :merge cannot merge symlinks for a
@@ -69,7 +69,7 @@
    ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
    preserving a for resolve of a
    a: versions differ -> m (premerge)
-  picked tool ':union' for a (binary False symlink True)
+  picked tool ':union' for a (binary False symlink True changedelete False)
   merging a
   my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
   warning: internal :union cannot merge symlinks for a
@@ -92,7 +92,7 @@
    ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
    preserving a for resolve of a
    a: versions differ -> m (premerge)
-  picked tool ':merge3' for a (binary False symlink True)
+  picked tool ':merge3' for a (binary False symlink True changedelete False)
   merging a
   my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
   warning: internal :merge3 cannot merge symlinks for a
@@ -105,6 +105,50 @@
   a is an executable file with content:
   a
 
+  $ hg update -C 1
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg merge --debug --tool :merge-local
+    searching for copies back to rev 1
+  resolving manifests
+   branchmerge: True, force: False, partial: False
+   ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
+   preserving a for resolve of a
+   a: versions differ -> m (premerge)
+  picked tool ':merge-local' for a (binary False symlink True changedelete False)
+  merging a
+  my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
+  warning: internal :merge-local cannot merge symlinks for a
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+
+  $ tellmeabout a
+  a is an executable file with content:
+  a
+
+  $ hg update -C 1
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg merge --debug --tool :merge-other
+    searching for copies back to rev 1
+  resolving manifests
+   branchmerge: True, force: False, partial: False
+   ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
+   preserving a for resolve of a
+   a: versions differ -> m (premerge)
+  picked tool ':merge-other' for a (binary False symlink True changedelete False)
+  merging a
+  my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
+  warning: internal :merge-other cannot merge symlinks for a
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+
+  $ tellmeabout a
+  a is an executable file with content:
+  a
+
 Update to link without local change should get us a symlink (issue3316):
 
   $ hg up -C 0
@@ -126,10 +170,12 @@
    preserving a for resolve of a
    a: versions differ -> m (premerge)
   (couldn't find merge tool hgmerge|tool hgmerge can't handle symlinks) (re)
-  picked tool ':prompt' for a (binary False symlink True)
-   no tool found to merge a
-  keep (l)ocal or take (o)ther? l
-  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  picked tool ':prompt' for a (binary False symlink True changedelete False)
+  no tool found to merge a
+  keep (l)ocal, take (o)ther, or leave (u)nresolved? u
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges
+  [1]
   $ hg diff --git
   diff --git a/a b/a
   old mode 120000
--- a/tests/test-merge7.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-merge7.t	Thu Dec 17 17:27:32 2015 -0600
@@ -85,11 +85,11 @@
    ancestor: 96b70246a118, local: 50c3a7e29886+, remote: 40d11a4173a8
    preserving test.txt for resolve of test.txt
    test.txt: versions differ -> m (premerge)
-  picked tool ':merge' for test.txt (binary False symlink False)
+  picked tool ':merge' for test.txt (binary False symlink False changedelete False)
   merging test.txt
   my test.txt@50c3a7e29886+ other test.txt@40d11a4173a8 ancestor test.txt@96b70246a118
    test.txt: versions differ -> m (merge)
-  picked tool ':merge' for test.txt (binary False symlink False)
+  picked tool ':merge' for test.txt (binary False symlink False changedelete False)
   my test.txt@50c3a7e29886+ other test.txt@40d11a4173a8 ancestor test.txt@96b70246a118
   warning: conflicts while merging test.txt! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
--- a/tests/test-module-imports.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-module-imports.t	Thu Dec 17 17:27:32 2015 -0600
@@ -68,6 +68,33 @@
   > from .. import parent
   > EOF
 
+  $ touch testpackage/subpackage/foo.py
+  $ cat > testpackage/subpackage/__init__.py << EOF
+  > from __future__ import absolute_import
+  > from . import levelpriority  # should not cause cycle
+  > EOF
+
+  $ cat > testpackage/subpackage/localimport.py << EOF
+  > from __future__ import absolute_import
+  > from . import foo
+  > def bar():
+  >     # should not cause "higher-level import should come first"
+  >     from .. import unsorted
+  >     # but other errors should be detected
+  >     from .. import more
+  >     import testpackage.subpackage.levelpriority
+  > EOF
+
+  $ cat > testpackage/importmodulefromsub.py << EOF
+  > from __future__ import absolute_import
+  > from .subpackage import foo  # not a "direct symbol import"
+  > EOF
+
+  $ cat > testpackage/importsymbolfromsub.py << EOF
+  > from __future__ import absolute_import
+  > from .subpackage import foo, nonmodule
+  > EOF
+
   $ cat > testpackage/sortedentries.py << EOF
   > from __future__ import absolute_import
   > from . import (
@@ -87,20 +114,23 @@
   > EOF
 
   $ python "$import_checker" testpackage/*.py testpackage/subpackage/*.py
-  testpackage/importalias.py ui module must be "as" aliased to uimod
-  testpackage/importfromalias.py ui from testpackage must be "as" aliased to uimod
-  testpackage/importfromrelative.py import should be relative: testpackage.unsorted
-  testpackage/importfromrelative.py direct symbol import from testpackage.unsorted
-  testpackage/latesymbolimport.py symbol import follows non-symbol import: mercurial.node
-  testpackage/multiple.py multiple imported names: os, sys
-  testpackage/multiplegroups.py multiple "from . import" statements
-  testpackage/relativestdlib.py relative import of stdlib module
-  testpackage/requirerelative.py import should be relative: testpackage.unsorted
-  testpackage/sortedentries.py imports from testpackage not lexically sorted: bar < foo
-  testpackage/stdafterlocal.py stdlib import follows local import: os
-  testpackage/subpackage/levelpriority.py higher-level import should come first: testpackage
-  testpackage/symbolimport.py direct symbol import from testpackage.unsorted
-  testpackage/unsorted.py imports not lexically sorted: os < sys
+  testpackage/importalias.py:2: ui module must be "as" aliased to uimod
+  testpackage/importfromalias.py:2: ui from testpackage must be "as" aliased to uimod
+  testpackage/importfromrelative.py:2: import should be relative: testpackage.unsorted
+  testpackage/importfromrelative.py:2: direct symbol import foo from testpackage.unsorted
+  testpackage/importsymbolfromsub.py:2: direct symbol import nonmodule from testpackage.subpackage
+  testpackage/latesymbolimport.py:3: symbol import follows non-symbol import: mercurial.node
+  testpackage/multiple.py:2: multiple imported names: os, sys
+  testpackage/multiplegroups.py:3: multiple "from . import" statements
+  testpackage/relativestdlib.py:2: relative import of stdlib module
+  testpackage/requirerelative.py:2: import should be relative: testpackage.unsorted
+  testpackage/sortedentries.py:2: imports from testpackage not lexically sorted: bar < foo
+  testpackage/stdafterlocal.py:3: stdlib import follows local import: os
+  testpackage/subpackage/levelpriority.py:3: higher-level import should come first: testpackage
+  testpackage/subpackage/localimport.py:7: multiple "from .. import" statements
+  testpackage/subpackage/localimport.py:8: import should be relative: testpackage.subpackage.levelpriority
+  testpackage/symbolimport.py:2: direct symbol import foo from testpackage.unsorted
+  testpackage/unsorted.py:3: imports not lexically sorted: os < sys
   [1]
 
   $ cd "$TESTDIR"/..
--- a/tests/test-mq-qfold.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-mq-qfold.t	Thu Dec 17 17:27:32 2015 -0600
@@ -229,9 +229,9 @@
   HG: added aa
   HG: changed a
   ====
+  note: commit message saved in .hg/last-message.txt
   transaction abort!
   rollback completed
-  note: commit message saved in .hg/last-message.txt
   qrefresh interrupted while patch was popped! (revert --all, qpush to recover)
   abort: pretxncommit.unexpectedabort hook exited with status 1
   [255]
--- a/tests/test-mq-qnew.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-mq-qnew.t	Thu Dec 17 17:27:32 2015 -0600
@@ -299,9 +299,9 @@
   HG: branch 'default'
   HG: no files changed
   ====
+  note: commit message saved in .hg/last-message.txt
   transaction abort!
   rollback completed
-  note: commit message saved in .hg/last-message.txt
   abort: pretxncommit.unexpectedabort hook exited with status 1
   [255]
   $ cat .hg/last-message.txt
--- a/tests/test-mq-qpush-fail.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-mq-qpush-fail.t	Thu Dec 17 17:27:32 2015 -0600
@@ -444,7 +444,7 @@
   $ hg st a
   M a
   $ echo b >> b
-  $ hg --config mq.keepchanges=1 qpop --force
+  $ hg --config mq.keepchanges=1 qpop --force --config 'ui.origbackuppath=.hg/origbackups'
   popping p3
   now at: p2
   $ hg st b
@@ -461,4 +461,10 @@
   now at: p2
   $ hg st a
 
+test previous qpop (with --force and --config) saved .orig files to where user
+wants them
+  $ ls .hg/origbackups
+  b.orig
+  $ rm -rf .hg/origbackups
+
   $ cd ..
--- a/tests/test-mq-qrefresh-replace-log-message.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-mq-qrefresh-replace-log-message.t	Thu Dec 17 17:27:32 2015 -0600
@@ -185,9 +185,9 @@
   HG: branch 'default'
   HG: added file2
   ====
+  note: commit message saved in .hg/last-message.txt
   transaction abort!
   rollback completed
-  note: commit message saved in .hg/last-message.txt
   qrefresh interrupted while patch was popped! (revert --all, qpush to recover)
   abort: pretxncommit.unexpectedabort hook exited with status 1
   [255]
@@ -223,14 +223,14 @@
   M file2
   ====
 
-  $ HGEDITOR='sh "$TESTTMP/checkvisibility.sh"' hg qrefresh -e
+  $ HGEDITOR="sh \"$TESTTMP/checkvisibility.sh\"" hg qrefresh -e
   ====
   0:25e397dabed2
   A file2
   ====
+  note: commit message saved in .hg/last-message.txt
   transaction abort!
   rollback completed
-  note: commit message saved in .hg/last-message.txt
   qrefresh interrupted while patch was popped! (revert --all, qpush to recover)
   abort: pretxncommit.unexpectedabort hook exited with status 1
   [255]
--- a/tests/test-mq.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-mq.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1394,9 +1394,10 @@
 
 apply force, should discard changes in hello, but not bye
 
-  $ hg qpush -f --verbose
+  $ hg qpush -f --verbose --config 'ui.origbackuppath=.hg/origbackups'
   applying empty
-  saving current version of hello.txt as hello.txt.orig
+  creating directory: $TESTTMP/forcepush/.hg/origbackups (glob)
+  saving current version of hello.txt as $TESTTMP/forcepush/.hg/origbackups/hello.txt.orig (glob)
   patching file hello.txt
   committing files:
   hello.txt
@@ -1405,7 +1406,6 @@
   now at: empty
   $ hg st
   M bye.txt
-  ? hello.txt.orig
   $ hg diff --config diff.nodates=True
   diff -r ba252371dbc1 bye.txt
   --- a/bye.txt
@@ -1428,6 +1428,10 @@
   +world
   +universe
 
+test that the previous call to qpush with -f (--force) and --config actually put
+the orig files out of the working copy
+  $ ls .hg/origbackups
+  hello.txt.orig
 
 test popping revisions not in working dir ancestry
 
--- a/tests/test-obsolete-changeset-exchange.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-obsolete-changeset-exchange.t	Thu Dec 17 17:27:32 2015 -0600
@@ -118,19 +118,17 @@
   $ cd ..
 
 client only pulls down 1 changeset
-("all local heads known remotely" may change if the wire protocol discovery
-commands ever stop saying they have hidden changesets)
 
   $ cd pull-hidden-common-client
   $ hg pull --debug
   pulling from $TESTTMP/pull-hidden-common (glob)
   query 1; heads
   searching for changes
-  all local heads known remotely
-  3 changesets found
+  taking quick initial sample
+  query 2; still undecided: 2, sample size is: 2
+  2 total queries
+  1 changesets found
   list of changesets:
-  96ee1d7354c4ad7372047672c36a1f561e3a6a4c
-  a33779fdfc23063680fc31e9ff637dff6876d3d2
   bec0734cd68e84477ba7fc1d13e6cff53ab70129
   listing keys for "phase"
   listing keys for "bookmarks"
@@ -141,14 +139,12 @@
   bundle2-input-bundle: with-transaction
   bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
   adding changesets
-  add changeset 96ee1d7354c4
-  add changeset a33779fdfc23
   add changeset bec0734cd68e
   adding manifests
   adding file changes
   adding foo revisions
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  bundle2-input-part: total payload size 1378
+  bundle2-input-part: total payload size 474
   bundle2-input-part: "listkeys" (params: 1 mandatory) supported
   bundle2-input-part: "listkeys" (params: 1 mandatory) supported
   bundle2-input-bundle: 2 parts total
--- a/tests/test-obsolete.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-obsolete.t	Thu Dec 17 17:27:32 2015 -0600
@@ -238,6 +238,14 @@
 Fixing "bumped" situation
 We need to create a clone of 5 and add a special marker with a flag
 
+  $ hg summary
+  parent: 5:5601fb93a350 tip
+   add new_3_c
+  branch: default
+  commit: (clean)
+  update: 1 new changesets, 2 branch heads (merge)
+  phases: 1 draft
+  bumped: 1 changeset
   $ hg up '5^'
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg revert -ar 5
@@ -465,6 +473,14 @@
   94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   $ hg log -r 'obsolete()'
   4:94b33453f93b (draft) [ ] add original_d
+  $ hg summary
+  parent: 5:cda648ca50f5 tip
+   add original_e
+  branch: default
+  commit: (clean)
+  update: 1 new changesets, 2 branch heads (merge)
+  phases: 3 draft
+  unstable: 1 changeset
   $ hg log -G -r '::unstable()'
   @  5:cda648ca50f5 (draft) [tip ] add original_e
   |
@@ -712,34 +728,40 @@
 
   $ hg init repo-issue3805
   $ cd repo-issue3805
+  $ echo "base" > base
+  $ hg ci -Am "base"
+  adding base
   $ echo "foo" > foo
   $ hg ci -Am "A"
   adding foo
   $ hg clone . ../other-issue3805
   updating to branch default
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo "bar" >> foo
   $ hg ci --amend
   $ cd ../other-issue3805
   $ hg log -G
-  @  0:193e9254ce7e (draft) [tip ] A
+  @  1:29f0c6921ddd (draft) [tip ] A
+  |
+  o  0:d20a80d4def3 (draft) [ ] base
   
   $ hg log -G -R ../repo-issue3805
-  @  2:3816541e5485 (draft) [tip ] A
+  @  3:323a9c3ddd91 (draft) [tip ] A
+  |
+  o  0:d20a80d4def3 (draft) [ ] base
   
   $ hg incoming
   comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
   searching for changes
-  2:3816541e5485 (draft) [tip ] A
+  3:323a9c3ddd91 (draft) [tip ] A
   $ hg incoming --bundle ../issue3805.hg
   comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
   searching for changes
-  2:3816541e5485 (draft) [tip ] A
+  3:323a9c3ddd91 (draft) [tip ] A
   $ hg outgoing
   comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
   searching for changes
-  no changes found
-  [1]
+  1:29f0c6921ddd (draft) [tip ] A
 
 #if serve
 
@@ -749,12 +771,11 @@
   $ hg incoming http://localhost:$HGPORT
   comparing with http://localhost:$HGPORT/
   searching for changes
-  1:3816541e5485 (draft) [tip ] A
+  2:323a9c3ddd91 (draft) [tip ] A
   $ hg outgoing http://localhost:$HGPORT
   comparing with http://localhost:$HGPORT/
   searching for changes
-  no changes found
-  [1]
+  1:29f0c6921ddd (draft) [tip ] A
 
   $ killdaemons.py
 
@@ -767,13 +788,13 @@
   $ cd ..
   $ hg init repo-issue3814
   $ cd repo-issue3805
-  $ hg push -r 3816541e5485 ../repo-issue3814
+  $ hg push -r 323a9c3ddd91 ../repo-issue3814
   pushing to ../repo-issue3814
   searching for changes
   adding changesets
   adding manifests
   adding file changes
-  added 1 changesets with 1 changes to 1 files
+  added 2 changesets with 2 changes to 2 files
   2 new obsolescence markers
   $ hg out ../repo-issue3814
   comparing with ../repo-issue3814
@@ -783,24 +804,26 @@
 
 Test that a local tag blocks a changeset from being hidden
 
-  $ hg tag -l visible -r 0 --hidden
+  $ hg tag -l visible -r 1 --hidden
   $ hg log -G
-  @  2:3816541e5485 (draft) [tip ] A
-  
-  x  0:193e9254ce7e (draft) [visible ] A
+  @  3:323a9c3ddd91 (draft) [tip ] A
+  |
+  | x  1:29f0c6921ddd (draft) [visible ] A
+  |/
+  o  0:d20a80d4def3 (draft) [ ] base
   
 Test that removing a local tag does not cause some commands to fail
 
   $ hg tag -l -r tip tiptag
   $ hg tags
-  tiptag                             2:3816541e5485
-  tip                                2:3816541e5485
-  visible                            0:193e9254ce7e
+  tiptag                             3:323a9c3ddd91
+  tip                                3:323a9c3ddd91
+  visible                            1:29f0c6921ddd
   $ hg --config extensions.strip= strip -r tip --no-backup
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg tags
-  visible                            0:193e9254ce7e
-  tip                                0:193e9254ce7e
+  visible                            1:29f0c6921ddd
+  tip                                1:29f0c6921ddd
 
 Test bundle overlay onto hidden revision
 
--- a/tests/test-patchbomb.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-patchbomb.t	Thu Dec 17 17:27:32 2015 -0600
@@ -23,6 +23,8 @@
   >     print l,
   > EOF
   $ FILTERBOUNDARY="python `pwd`/prune-blank-after-boundary.py"
+  $ echo "[format]" >> $HGRCPATH
+  $ echo "usegeneraldelta=yes" >> $HGRCPATH
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "patchbomb=" >> $HGRCPATH
 
@@ -347,21 +349,22 @@
   Content-Disposition: attachment; filename="bundle.hg"
   Content-Transfer-Encoding: base64
   
-  SEcxMEJaaDkxQVkmU1nvR7I3AAAN////lFYQWj1/4HwRkdC/AywIAk0E4pfoSIIIgQCgGEQOcLAA
-  2tA1VPyp4mkeoG0EaaPU0GTT1GjRiNPIg9CZGBqZ6UbU9J+KFU09DNUaGgAAAAAANAGgAAAAA1U8
-  oGgAADQGgAANNANAAAAAAZipFLz3XoakCEQB3PVPyHJVi1iYkAAKQAZQGpQGZESInRnCFMqLDla2
-  Bx3qfRQeA2N4lnzKkAmP8kR2asievLLXXebVU8Vg4iEBqcJNJAxIapSU6SM4888ZAciRG6MYAIEE
-  SlIBpFisgGkyRjX//TMtfcUAEsGu56+YnE1OlTZmzKm8BSu2rvo4rHAYYaadIFFuTy0LYgIkgLVD
-  sgVa2F19D1tx9+hgbAygLgQwaIqcDdgA4BjQgIiz/AEP72++llgDKhKducqodGE4B0ETqF3JFOFC
-  Q70eyNw=
-  --===*=-- (glob)
+  SEcyMAAAAA5Db21wcmVzc2lvbj1CWkJaaDkxQVkmU1lCZFwPAAAKf//7nFYSWD1/4H7R09C/I70I
+  Ak0E4peoSIYIgQCgGUQOcLABGY2hqoAAAaBMTTAAAahgTCZoAAAAAMQaqn5GmapojQ00DEGI/VGJ
+  kDAJoGTDUAAyM0QaAEqalPTUaMhoyDIDR6IxAGEGmgAehMRhDRsoyB6TYTC8JyLN+jTGqitRAgRJ
+  b3SRlhd8/+VxlAUqAilLoKPEEyxFQkaEGo+DzItFeNiFAo8NMMweVtvXJFIMhjoKC18DeYwjLKBz
+  wrMcs86qJrctDNJorwBMuLcqvTVWHh1IlsIaaaYSUIP2IZsogT1+pSSZS+bSTJrgfKsO9go/f0HF
+  uW4Yr2vXpxDreOgSIAdK/xC8Yay48SLpxIuqc/BZ6rVZCgG21rr0zhCaEgXOTqNaYEvANvg0B0Qo
+  dgtqAs1FDcZgzYitwJh6ZAG0C4mA7FPrp9b7h0h/A44Xgd+0it1gvF0mFE/CCPwymXS+OisOOCAF
+  mDUDAC1pBvsXckU4UJBCZFwP
+  --===============*==-- (glob)
 
 with a specific bundle type
 (binary part must be different)
 
   $ hg email --date '1970-1-1 0:3' -n -f quux -t foo \
   >  -c bar -s test -r tip -b --desc description \
-  > --config patchbomb.bundletype=gzip | $FILTERBOUNDARY
+  > --config patchbomb.bundletype=gzip-v1 | $FILTERBOUNDARY
   searching for changes
   1 changesets found
   
--- a/tests/test-pathencode.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-pathencode.py	Thu Dec 17 17:27:32 2015 -0600
@@ -9,9 +9,6 @@
 import binascii, itertools, math, os, random, sys, time
 import collections
 
-if sys.version_info[:2] < (2, 6):
-    sys.exit(0)
-
 validchars = set(map(chr, range(0, 256)))
 alphanum = range(ord('A'), ord('Z'))
 
--- a/tests/test-paths.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-paths.t	Thu Dec 17 17:27:32 2015 -0600
@@ -44,6 +44,59 @@
   [1]
   $ cd ..
 
+sub-options for an undeclared path are ignored
+
+  $ hg init suboptions
+  $ cd suboptions
+
+  $ cat > .hg/hgrc << EOF
+  > [paths]
+  > path0 = https://example.com/path0
+  > path1:pushurl = https://example.com/path1
+  > EOF
+  $ hg paths
+  path0 = https://example.com/path0
+
+unknown sub-options aren't displayed
+
+  $ cat > .hg/hgrc << EOF
+  > [paths]
+  > path0 = https://example.com/path0
+  > path0:foo = https://example.com/path1
+  > EOF
+
+  $ hg paths
+  path0 = https://example.com/path0
+
+:pushurl must be a URL
+
+  $ cat > .hg/hgrc << EOF
+  > [paths]
+  > default = /path/to/nothing
+  > default:pushurl = /not/a/url
+  > EOF
+
+  $ hg paths
+  (paths.default:pushurl not a URL; ignoring)
+  default = /path/to/nothing
+
+#fragment is not allowed in :pushurl
+
+  $ cat > .hg/hgrc << EOF
+  > [paths]
+  > default = https://example.com/repo
+  > invalid = https://example.com/repo
+  > invalid:pushurl = https://example.com/repo#branch
+  > EOF
+
+  $ hg paths
+  ("#fragment" in paths.invalid:pushurl not supported; ignoring)
+  default = https://example.com/repo
+  invalid = https://example.com/repo
+  invalid:pushurl = https://example.com/repo
+
+  $ cd ..
+
 'file:' disables [paths] entries for clone destination
 
   $ cat >> $HGRCPATH <<EOF
--- a/tests/test-pull-update.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-pull-update.t	Thu Dec 17 17:27:32 2015 -0600
@@ -25,8 +25,9 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  not updating: not a linear update
+  abort: not updating: not a linear update
   (merge or update --check to force update)
+  [255]
 
   $ cd ../tt
 
@@ -39,8 +40,9 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  not updating: not a linear update
+  abort: not updating: not a linear update
   (merge or update --check to force update)
+  [255]
 
   $ HGMERGE=true hg merge
   merging foo
--- a/tests/test-push-cgi.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-push-cgi.t	Thu Dec 17 17:27:32 2015 -0600
@@ -31,7 +31,7 @@
   $ . "$TESTDIR/cgienv"
   $ REQUEST_METHOD="POST"; export REQUEST_METHOD
   $ CONTENT_TYPE="application/octet-stream"; export CONTENT_TYPE
-  $ hg bundle --all bundle.hg
+  $ hg bundle --type v1 --all bundle.hg
   1 changesets found
   $ CONTENT_LENGTH=279; export CONTENT_LENGTH;
 
--- a/tests/test-rebase-abort.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-rebase-abort.t	Thu Dec 17 17:27:32 2015 -0600
@@ -68,11 +68,49 @@
   unresolved conflicts (see hg resolve, then hg rebase --continue)
   [1]
 
-Abort:
+Insert unsupported advisory merge record:
+
+  $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
+  $ hg debugmergestate
+  * version 2 records
+  local: 3e046f2ecedb793b97ed32108086edd1a162f8bc
+  other: 46f0b057b5c061d276b91491c22151f78698abd2
+  unrecognized entry: x	advisory record
+  file: common (record type "F", state "u", hash 94c8c21d08740f5da9eaa38d1f175c592692f0d1)
+    local path: common (flags "")
+    ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)
+    other path: common (node 2f6411de53677f6f1048fef5bf888d67a342e0a5)
+  $ hg resolve -l
+  U common
+
+Insert unsupported mandatory merge record:
+
+  $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
+  $ hg debugmergestate
+  * version 2 records
+  local: 3e046f2ecedb793b97ed32108086edd1a162f8bc
+  other: 46f0b057b5c061d276b91491c22151f78698abd2
+  file: common (record type "F", state "u", hash 94c8c21d08740f5da9eaa38d1f175c592692f0d1)
+    local path: common (flags "")
+    ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)
+    other path: common (node 2f6411de53677f6f1048fef5bf888d67a342e0a5)
+  unrecognized entry: X	mandatory record
+  $ hg resolve -l
+  abort: unsupported merge state records: X
+  (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
+  [255]
+  $ hg resolve -ma
+  abort: unsupported merge state records: X
+  (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
+  [255]
+
+Abort (should clear out unsupported merge state):
 
   $ hg rebase --abort
   saved backup bundle to $TESTTMP/a/.hg/strip-backup/3e046f2ecedb-6beef7d5-backup.hg (glob)
   rebase aborted
+  $ hg debugmergestate
+  no merge state found
 
   $ hg tglog
   @  4:draft 'L2'
--- a/tests/test-rebase-conflicts.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-rebase-conflicts.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,4 +1,6 @@
   $ cat >> $HGRCPATH <<EOF
+  > [format]
+  > usegeneraldelta=yes
   > [extensions]
   > rebase=
   > 
@@ -275,13 +277,19 @@
   list of changesets:
   e31216eec445e44352c5f01588856059466a24c9
   2f2496ddf49d69b5ef23ad8cf9fb2e0e4faf0ac2
+  bundle2-output-bundle: "HG20", (1 params) 1 parts total
+  bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
   saved backup bundle to $TESTTMP/issue4041/.hg/strip-backup/e31216eec445-15f7a814-backup.hg (glob)
   3 changesets found
   list of changesets:
   4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
   19c888675e133ab5dff84516926a65672eaf04d9
   2a7f09cac94c7f4b73ebd5cd1a62d3b2e8e336bf
+  bundle2-output-bundle: "HG20", 1 parts total
+  bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
   adding branch
+  bundle2-input-bundle: with-transaction
+  bundle2-input-part: "changegroup" (params: 1 mandatory) supported
   adding changesets
   add changeset 4c9fbe56a16f
   add changeset 19c888675e13
@@ -290,6 +298,8 @@
   adding file changes
   adding f1.txt revisions
   added 2 changesets with 2 changes to 1 files
+  bundle2-input-part: total payload size 1713
+  bundle2-input-bundle: 0 parts total
   invalid branchheads cache (served): tip differs
   rebase completed
   updating the branch cache
--- a/tests/test-rebase-mq-skip.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-rebase-mq-skip.t	Thu Dec 17 17:27:32 2015 -0600
@@ -2,6 +2,8 @@
 already has one local mq patch
 
   $ cat >> $HGRCPATH <<EOF
+  > [format]
+  > usegeneraldelta=yes
   > [extensions]
   > rebase=
   > mq=
@@ -68,17 +70,17 @@
   $TESTTMP/a/.hg/patches/p0.patch (glob)
   2 changesets found
   uncompressed size of bundle content:
-       344 (changelog)
-       284 (manifests)
-       109  p0
-       109  p1
+       384 (changelog)
+       324 (manifests)
+       129  p0
+       129  p1
   saved backup bundle to $TESTTMP/a/.hg/strip-backup/13a46ce44f60-5da6ecfb-backup.hg (glob)
   2 changesets found
   uncompressed size of bundle content:
-       399 (changelog)
-       284 (manifests)
-       109  p0
-       109  p1
+       439 (changelog)
+       324 (manifests)
+       129  p0
+       129  p1
   adding branch
   adding changesets
   adding manifests
--- a/tests/test-rebase-newancestor.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-rebase-newancestor.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,4 +1,6 @@
   $ cat >> $HGRCPATH <<EOF
+  > [format]
+  > usegeneraldelta=yes
   > [extensions]
   > rebase=
   > 
@@ -132,7 +134,7 @@
   rebasing 2:ec2c14fb2984 "dev: f-dev stuff"
   rebasing 4:4b019212aaf6 "dev: merge default"
   remote changed f-default which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   rebasing 6:9455ee510502 "dev: merge default"
   saved backup bundle to $TESTTMP/ancestor-merge/.hg/strip-backup/1d1a643d390e-43e9e04b-backup.hg (glob)
   $ hg tglog
@@ -159,7 +161,7 @@
   rebasing 2:ec2c14fb2984 "dev: f-dev stuff"
   rebasing 4:4b019212aaf6 "dev: merge default"
   remote changed f-default which local deleted
-  use (c)hanged version or leave (d)eleted? c
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   rebasing 6:9455ee510502 "dev: merge default"
   saved backup bundle to $TESTTMP/ancestor-merge-2/.hg/strip-backup/ec2c14fb2984-62d0b222-backup.hg (glob)
   $ hg tglog
@@ -298,15 +300,15 @@
   rebase merging completed
   1 changesets found
   uncompressed size of bundle content:
-       193 (changelog)
-       196 (manifests)
-       162  other
+       213 (changelog)
+       216 (manifests)
+       182  other
   saved backup bundle to $TESTTMP/parentorder/.hg/strip-backup/4c5f12f25ebe-f46990e5-backup.hg (glob)
   1 changesets found
   uncompressed size of bundle content:
-       252 (changelog)
-       147 (manifests)
-       162  other
+       272 (changelog)
+       167 (manifests)
+       182  other
   adding branch
   adding changesets
   adding manifests
--- a/tests/test-rebase-obsolete.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-rebase-obsolete.t	Thu Dec 17 17:27:32 2015 -0600
@@ -11,7 +11,7 @@
   > evolution=createmarkers,allowunstable
   > [phases]
   > publish=False
-  > [extensions]'
+  > [extensions]
   > rebase=
   > EOF
 
@@ -248,6 +248,30 @@
   D
   
   
+  $ hg up -qr 'desc(G)'
+  $ hg graft 4596109a6a4328c398bde3a4a3b6737cfade3003
+  grafting 11:4596109a6a43 "D"
+  $ hg up -qr 'desc(E)'
+  $ hg rebase -s tip -d .
+  rebasing 14:0f4c66d0b70f "D" (tip)
+  $ hg log --style default --debug -r tip
+  changeset:   15:884f358981b4d32069bb539e0e95d49a35eb81d0
+  tag:         tip
+  phase:       draft
+  parent:      4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    15:648e8ede73ae3e497d093d3a4c8fcc2daa864f42
+  user:        Nicolas Dumazet <nicdumz.commits@gmail.com>
+  date:        Sat Apr 30 15:24:48 2011 +0200
+  files+:      D
+  extra:       branch=default
+  extra:       intermediate-source=4596109a6a4328c398bde3a4a3b6737cfade3003
+  extra:       rebase_source=0f4c66d0b70f8e1ce4aec01f8e95cf24ee923afa
+  extra:       source=32af7686d403cf45b5d95f2d70cebea587ac806a
+  description:
+  D
+  
+  
   $ cd ..
 
 collapse rebase
@@ -635,3 +659,29 @@
   $ hg rebase -d 'desc(B2)'
   note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
   rebasing 5:1a79b7535141 "D" (tip)
+  $ hg up 4
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo "O" > O
+  $ hg add O
+  $ hg commit -m O
+  $ echo "P" > P
+  $ hg add P
+  $ hg commit -m P
+  $ hg log -G
+  @  8:8d47583e023f P
+  |
+  o  7:360bbaa7d3ce O
+  |
+  | o  6:9c48361117de D
+  | |
+  o |  4:ff2c4d47b71d C
+  |/
+  o  2:261e70097290 B2
+  |
+  o  0:4a2df7238c3b A
+  
+  $ hg debugobsolete `hg log -r 7 -T '{node}\n'` --config experimental.evolution=all
+  $ hg rebase -d 6 -r "4::"
+  rebasing 4:ff2c4d47b71d "C"
+  note: not rebasing 7:360bbaa7d3ce "O", it has no successor
+  rebasing 8:8d47583e023f "P" (tip)
--- a/tests/test-rebuildstate.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-rebuildstate.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,3 +1,34 @@
+
+  $ cat > adddrop.py <<EOF
+  > from mercurial import cmdutil
+  > cmdtable = {}
+  > command = cmdutil.command(cmdtable)
+  > @command('debugadddrop',
+  >   [('', 'drop', False, 'drop file from dirstate', 'FILE'),
+  >    ('', 'normal-lookup', False, 'add file to dirstate', 'FILE')],
+  >     'hg debugadddrop')
+  > def debugadddrop(ui, repo, *pats, **opts):
+  >   '''Add or drop unnamed arguments to or from the dirstate'''
+  >   drop = opts.get('drop')
+  >   nl = opts.get('normal_lookup')
+  >   if nl and drop:
+  >       raise error.Abort('drop and normal-lookup are mutually exclusive')
+  >   wlock = repo.wlock()
+  >   try:
+  >     for file in pats:
+  >       if opts.get('normal_lookup'):
+  >         repo.dirstate.normallookup(file)
+  >       else:
+  >         repo.dirstate.drop(file)
+  > 
+  >     repo.dirstate.write(repo.currenttransaction())
+  >   finally:
+  >     wlock.release()
+  > EOF
+
+  $ echo "[extensions]" >> $HGRCPATH
+  $ echo "debugadddrop=`pwd`/adddrop.py" >> $HGRCPATH
+
 basic test for hg debugrebuildstate
 
   $ hg init repo
@@ -20,6 +51,15 @@
   n 644         -1 set                 bar
   n 644         -1 set                 foo
 
+  $ hg debugadddrop --normal-lookup file1 file2
+  $ hg debugadddrop --drop bar
+  $ hg debugadddrop --drop
+  $ hg debugstate --nodates
+  n   0         -1 unset               file1
+  n   0         -1 unset               file2
+  n 644         -1 set                 foo
+  $ hg debugrebuildstate
+
 status
 
   $ hg st -A
@@ -27,4 +67,59 @@
   ? baz
   C foo
 
-  $ cd ..
+Test debugdirstate --minimal where a file is not in parent manifest
+but in the dirstate
+  $ touch foo bar qux
+  $ hg add qux
+  $ hg remove bar
+  $ hg status -A
+  A qux
+  R bar
+  ? baz
+  C foo
+  $ hg debugadddrop --normal-lookup baz
+  $ hg debugdirstate --nodates
+  r   0          0 * bar (glob)
+  n   0         -1 * baz (glob)
+  n 644          0 * foo (glob)
+  a   0         -1 * qux (glob)
+  $ hg debugrebuilddirstate --minimal
+  $ hg debugdirstate --nodates
+  r   0          0 * bar (glob)
+  n 644          0 * foo (glob)
+  a   0         -1 * qux (glob)
+  $ hg status -A
+  A qux
+  R bar
+  ? baz
+  C foo
+
+Test debugdirstate --minimal where file is in the parent manifest but not the
+dirstate
+  $ hg manifest
+  bar
+  foo
+  $ hg status -A
+  A qux
+  R bar
+  ? baz
+  C foo
+  $ hg debugdirstate --nodates
+  r   0          0 * bar (glob)
+  n 644          0 * foo (glob)
+  a   0         -1 * qux (glob)
+  $ hg debugadddrop --drop foo
+  $ hg debugdirstate --nodates
+  r   0          0 * bar (glob)
+  a   0         -1 * qux (glob)
+  $ hg debugrebuilddirstate --minimal
+  $ hg debugdirstate --nodates
+  r   0          0 * bar (glob)
+  n 644         -1 * foo (glob)
+  a   0         -1 * qux (glob)
+  $ hg status -A
+  A qux
+  R bar
+  ? baz
+  C foo
+
--- a/tests/test-rename-merge1.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-rename-merge1.t	Thu Dec 17 17:27:32 2015 -0600
@@ -36,18 +36,18 @@
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: af1939970a1c, local: 044f8520aeeb+, remote: 85c198ef2f6c
+  note: possible conflict - a2 was renamed multiple times to:
+   c2
+   b2
    preserving a for resolve of b
   removing a
    b2: remote created -> g
   getting b2
    b: remote moved from a -> m (premerge)
-  picked tool ':merge' for b (binary False symlink False)
+  picked tool ':merge' for b (binary False symlink False changedelete False)
   merging a and b to b
   my b@044f8520aeeb+ other b@85c198ef2f6c ancestor a@af1939970a1c
    premerge successful
-  note: possible conflict - a2 was renamed multiple times to:
-   c2
-   b2
   1 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
@@ -177,10 +177,10 @@
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 19d7f95df299, local: 0084274f6b67+, remote: 5d32493049f0
+  note: possible conflict - file was deleted and renamed to:
+   newfile
    newfile: remote created -> g
   getting newfile
-  note: possible conflict - file was deleted and renamed to:
-   newfile
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   $ hg status
--- a/tests/test-rename-merge2.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-rename-merge2.t	Thu Dec 17 17:27:32 2015 -0600
@@ -90,16 +90,16 @@
    preserving rev for resolve of rev
    a: remote unchanged -> k
    b: remote copied from a -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging a and b to b
   my b@e300d1c794ec+ other b@4ce40f5aca24 ancestor a@924404dff337
    premerge successful
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -130,16 +130,16 @@
    a: remote is newer -> g
   getting a
    b: local copied/moved from a -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b and a to b
   my b@86a2aa42fc76+ other a@f4db7e329e71 ancestor a@924404dff337
    premerge successful
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -169,16 +169,16 @@
    preserving rev for resolve of rev
   removing a
    b: remote moved from a -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging a and b to b
   my b@e300d1c794ec+ other b@bdb19105162a ancestor a@924404dff337
    premerge successful
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -206,16 +206,16 @@
    preserving b for resolve of b
    preserving rev for resolve of rev
    b: local copied/moved from a -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b and a to b
   my b@02963e448370+ other a@f4db7e329e71 ancestor a@924404dff337
    premerge successful
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -244,11 +244,11 @@
    b: remote created -> g
   getting b
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -275,11 +275,11 @@
    ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 97c705ade336
    preserving rev for resolve of rev
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -310,11 +310,11 @@
    b: remote created -> g
   getting b
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -340,11 +340,11 @@
    ancestor: 924404dff337, local: 02963e448370+, remote: 97c705ade336
    preserving rev for resolve of rev
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -368,20 +368,20 @@
    preserving b for resolve of b
    preserving rev for resolve of rev
    b: both renamed from a -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b
   my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
    b: both renamed from a -> m (merge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
   merge tool returned: 0
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -411,21 +411,21 @@
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 02963e448370+, remote: fe905ef2c33e
+  note: possible conflict - a was renamed multiple times to:
+   b
+   c
    preserving rev for resolve of rev
    c: remote created -> g
   getting c
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
-  note: possible conflict - a was renamed multiple times to:
-   b
-   c
   1 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   --------------
@@ -447,20 +447,20 @@
    preserving b for resolve of b
    preserving rev for resolve of rev
    b: both created -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b
   my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
    b: both created -> m (merge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
   launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
   merge tool returned: 0
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -487,20 +487,20 @@
    a: other deleted -> r
   removing a
    b: both created -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b
   my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
    b: both created -> m (merge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
   launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
   merge tool returned: 0
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -526,20 +526,20 @@
    a: remote is newer -> g
   getting a
    b: both created -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b
   my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
    b: both created -> m (merge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
   launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
   merge tool returned: 0
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -566,20 +566,20 @@
    a: other deleted -> r
   removing a
    b: both created -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b
   my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
    b: both created -> m (merge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
   launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
   merge tool returned: 0
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -605,20 +605,20 @@
    a: remote is newer -> g
   getting a
    b: both created -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b
   my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
    b: both created -> m (merge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
   launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
   merge tool returned: 0
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -644,20 +644,20 @@
    preserving rev for resolve of rev
    a: remote unchanged -> k
    b: both created -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b
   my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor b@000000000000
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
    b: both created -> m (merge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor b@000000000000
   launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
   merge tool returned: 0
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -679,31 +679,31 @@
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a
-  remote changed a which local deleted
-  use (c)hanged version or leave (d)eleted? c
    preserving b for resolve of b
    preserving rev for resolve of rev
-   a: prompt recreating -> g
-  getting a
+   a: prompt deleted/changed -> m (premerge)
+  picked tool ':prompt' for a (binary False symlink False changedelete True)
+  remote changed a which local deleted
+  use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
    b: both created -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b
   my b@02963e448370+ other b@8dbce441892a ancestor b@000000000000
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
    b: both created -> m (merge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   my b@02963e448370+ other b@8dbce441892a ancestor b@000000000000
   launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
   merge tool returned: 0
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
-  1 files updated, 2 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 3 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   --------------
   M a
@@ -721,30 +721,32 @@
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a
-  local changed a which remote deleted
-  use (c)hanged version or (d)elete? c
+   preserving a for resolve of a
    preserving b for resolve of b
    preserving rev for resolve of rev
-   a: prompt keep -> a
+   a: prompt changed/deleted -> m (premerge)
+  picked tool ':prompt' for a (binary False symlink False changedelete True)
+  local changed a which remote deleted
+  use (c)hanged version, (d)elete, or leave (u)nresolved? c
    b: both created -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b
   my b@0b76e65c8289+ other b@bdb19105162a ancestor b@000000000000
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
    b: both created -> m (merge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   my b@0b76e65c8289+ other b@bdb19105162a ancestor b@000000000000
   launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
   merge tool returned: 0
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
-  0 files updated, 2 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 3 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   --------------
   M b
@@ -769,20 +771,20 @@
    preserving rev for resolve of rev
   removing a
    b: remote moved from a -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging a and b to b
   my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
    b: remote moved from a -> m (merge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
   merge tool returned: 0
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -810,20 +812,20 @@
    preserving b for resolve of b
    preserving rev for resolve of rev
    b: local copied/moved from a -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b and a to b
   my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
    b: local copied/moved from a -> m (merge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
   merge tool returned: 0
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -858,16 +860,16 @@
    c: remote created -> g
   getting c
    b: local copied/moved from a -> m (premerge)
-  picked tool 'python ../merge' for b (binary False symlink False)
+  picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b and a to b
   my b@02963e448370+ other a@2b958612230f ancestor a@924404dff337
    premerge successful
    rev: versions differ -> m (premerge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   merging rev
   my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
    rev: versions differ -> m (merge)
-  picked tool 'python ../merge' for rev (binary False symlink False)
+  picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
   my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
@@ -922,7 +924,7 @@
   $ mkdir 7 8
   $ echo m > 7/f
   $ echo m > 8/f
-  $ hg merge -f --tool internal:dump -v --debug -r2 | sed '/^ 0\/f: both created -> m/,$d' 2> /dev/null
+  $ hg merge -f --tool internal:dump -v --debug -r2 | sed '/^resolving manifests/,$d' 2> /dev/null
     searching for copies back to rev 1
     unmatched files in local:
      5/g
@@ -940,24 +942,6 @@
      src: '5/f' -> dst: '5/g' *
      src: '6/f' -> dst: '6/g' *
     checking for directory renames
-  resolving manifests
-   branchmerge: True, force: True, partial: False
-   ancestor: e6cb3cf11019, local: ec44bf929ab5+, remote: c62e34d0b898
-  remote changed 8/f which local deleted
-  use (c)hanged version or leave (d)eleted? c
-   preserving 0/f for resolve of 0/f
-   preserving 1/g for resolve of 1/g
-   preserving 2/f for resolve of 2/f
-   preserving 3/f for resolve of 3/f
-   preserving 3/f for resolve of 3/g
-   preserving 4/f for resolve of 4/g
-   preserving 5/f for resolve of 5/f
-   preserving 5/g for resolve of 5/g
-   preserving 6/g for resolve of 6/g
-   preserving 7/f for resolve of 7/f
-  removing 4/f
-   8/f: prompt recreating -> g
-  getting 8/f
   $ hg mani
   0/f
   1/g
--- a/tests/test-resolve.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-resolve.t	Thu Dec 17 17:27:32 2015 -0600
@@ -66,7 +66,7 @@
   > def markdriver(ui, repo, *pats, **opts):
   >     wlock = repo.wlock()
   >     try:
-  >         ms = merge.mergestate(repo)
+  >         ms = merge.mergestate.read(repo)
   >         m = scmutil.match(repo[None], pats, opts)
   >         for f in ms:
   >             if not m(f):
@@ -154,6 +154,26 @@
   abort: resolve command not applicable when not merging
   [255]
 
+can not update or merge when there are unresolved conflicts
+
+  $ hg up -qC 0
+  $ echo quux >> file1
+  $ hg up 1
+  merging file1
+  warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
+  1 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges
+  [1]
+  $ hg up 0
+  abort: outstanding merge conflicts
+  [255]
+  $ hg merge 2
+  abort: outstanding merge conflicts
+  [255]
+  $ hg merge --force 2
+  abort: outstanding merge conflicts
+  [255]
+
 set up conflict-free merge
 
   $ hg up -qC 3
@@ -197,6 +217,18 @@
   $ cat file2.orig
   foo
   baz
+
+.orig files should exists where specified
+  $ hg resolve --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
+  merging file1
+  creating directory: $TESTTMP/repo/.hg/origbackups (glob)
+  merging file2
+  warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
+  [1]
+  $ ls .hg/origbackups
+  file1.orig
+  file2.orig
   $ grep '<<<' file1 > /dev/null
   $ grep '<<<' file2 > /dev/null
 
@@ -209,22 +241,7 @@
 
 test .orig behavior with resolve
 
-  $ echo resolve > file
-  $ hg resolve -q file1 --tool 'f --dump $TESTTMP/repo/file1.orig'
-  */file1~base*: (glob)
-  >>>
-  foo
-  <<<
-  */file1~other*: (glob)
-  >>>
-  foo
-  bar
-  <<<
-  $TESTTMP/repo/file1: (glob)
-  >>>
-  foo
-  baz
-  <<<
+  $ hg resolve -q file1 --tool "sh -c 'f --dump \"$TESTTMP/repo/file1.orig\"'"
   $TESTTMP/repo/file1.orig: (glob)
   >>>
   foo
@@ -238,9 +255,69 @@
   $ cat file1
   resolved
 
+insert unsupported advisory merge record
+
+  $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
+  $ hg debugmergestate
+  * version 2 records
+  local: 57653b9f834a4493f7240b0681efcb9ae7cab745
+  other: dc77451844e37f03f5c559e3b8529b2b48d381d1
+  unrecognized entry: x	advisory record
+  file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
+    other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
+  file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
+    other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
+  $ hg resolve -l
+  R file1
+  U file2
+
+insert unsupported mandatory merge record
+
+  $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
+  $ hg debugmergestate
+  * version 2 records
+  local: 57653b9f834a4493f7240b0681efcb9ae7cab745
+  other: dc77451844e37f03f5c559e3b8529b2b48d381d1
+  file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
+    other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
+  file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
+    other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
+  unrecognized entry: X	mandatory record
+  $ hg resolve -l
+  abort: unsupported merge state records: X
+  (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
+  [255]
+  $ hg resolve -ma
+  abort: unsupported merge state records: X
+  (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
+  [255]
+  $ hg summary
+  parent: 2:57653b9f834a 
+   append baz to files
+  parent: 1:dc77451844e3 
+   append bar to files
+  branch: default
+  warning: merge state has unsupported record types: X
+  commit: 2 modified, 2 unknown (merge)
+  update: 2 new changesets (update)
+  phases: 5 draft
+
+update --clean shouldn't abort on unsupported records
+
+  $ hg up -qC 1
+  $ hg debugmergestate
+  no merge state found
+
 test crashed merge with empty mergestate
 
-  $ hg up -qC 1
   $ mkdir .hg/merge
   $ touch .hg/merge/state
 
--- a/tests/test-revert.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-revert.t	Thu Dec 17 17:27:32 2015 -0600
@@ -86,6 +86,16 @@
   saving current version of e as e.orig
   reverting e
 
+Test creation of backup (.orig) file in configured file location
+----------------------------------------------------------------
+
+  $ echo z > e
+  $ hg revert --all -v --config 'ui.origbackuppath=.hg/origbackups'
+  creating directory: $TESTTMP/repo/.hg/origbackups (glob)
+  saving current version of e as $TESTTMP/repo/.hg/origbackups/e.orig (glob)
+  reverting e
+  $ rm -rf .hg/origbackups
+
 revert on clean file (no change)
 --------------------------------
 
--- a/tests/test-revlog-ancestry.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-revlog-ancestry.py	Thu Dec 17 17:27:32 2015 -0600
@@ -17,10 +17,10 @@
     commit(name, time)
 
 def update(rev):
-    merge.update(repo, rev, False, True, False)
+    merge.update(repo, rev, False, True)
 
 def merge_(rev):
-    merge.update(repo, rev, True, False, False)
+    merge.update(repo, rev, True, False)
 
 if __name__ == '__main__':
     addcommit("A", 0)
@@ -82,4 +82,3 @@
     print '\nDescendants of 5 and 4'
     for r in repo.changelog.descendants([5, 4]):
         print r,
-
--- a/tests/test-rollback.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-rollback.t	Thu Dec 17 17:27:32 2015 -0600
@@ -113,9 +113,9 @@
   > echo "another precious commit message" > "$1"
   > __EOF__
   $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg --config hooks.pretxncommit=false commit 2>&1
+  note: commit message saved in .hg/last-message.txt
   transaction abort!
   rollback completed
-  note: commit message saved in .hg/last-message.txt
   abort: pretxncommit hook exited with status * (glob)
   [255]
   $ cat .hg/last-message.txt
--- a/tests/test-run-tests.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-run-tests.t	Thu Dec 17 17:27:32 2015 -0600
@@ -7,13 +7,20 @@
   $ unset HGTEST_PORT
   $ unset HGTEST_SHELL
 
-Smoke test
+Smoke test with install
 ============
 
-  $ run-tests.py $HGTEST_RUN_TESTS_PURE
+  $ run-tests.py $HGTEST_RUN_TESTS_PURE -l
   
   # Ran 0 tests, 0 skipped, 0 warned, 0 failed.
 
+Define a helper to avoid the install step
+=============
+  $ rt()
+  > {
+  >     run-tests.py --with-hg=`which hg` "$@"
+  > }
+
 a succesful test
 =======================
 
@@ -26,7 +33,7 @@
   >   nor this (?)
   > EOF
 
-  $ run-tests.py --with-hg=`which hg`
+  $ rt
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
@@ -44,7 +51,7 @@
   >>> fh.write(u'  $ echo babar\u03b1\n'.encode('utf-8')) and None
   >>> fh.write(u'  l\u03b5\u03b5t\n'.encode('utf-8')) and None
 
-  $ run-tests.py --with-hg=`which hg`
+  $ rt
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
@@ -73,7 +80,7 @@
   [1]
 
 test --xunit support
-  $ run-tests.py --with-hg=`which hg` --xunit=xunit.xml
+  $ rt --xunit=xunit.xml
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
@@ -129,7 +136,7 @@
 test for --retest
 ====================
 
-  $ run-tests.py --with-hg=`which hg` --retest
+  $ rt --retest
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
@@ -152,18 +159,18 @@
 
 successful
 
-  $ run-tests.py --with-hg=`which hg` test-success.t
+  $ rt test-success.t
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
 success w/ keyword
-  $ run-tests.py --with-hg=`which hg` -k xyzzy
+  $ rt -k xyzzy
   .
   # Ran 2 tests, 1 skipped, 0 warned, 0 failed.
 
 failed
 
-  $ run-tests.py --with-hg=`which hg` test-failure.t
+  $ rt test-failure.t
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
@@ -182,7 +189,7 @@
   [1]
 
 failure w/ keyword
-  $ run-tests.py --with-hg=`which hg` -k rataxes
+  $ rt -k rataxes
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
@@ -208,7 +215,7 @@
   $ cat > test-serve-fail.t <<EOF
   >   $ echo 'abort: child process failed to start blah'
   > EOF
-  $ run-tests.py --with-hg=`which hg` test-serve-fail.t
+  $ rt test-serve-fail.t
   
   ERROR: test-serve-fail.t output changed
   !
@@ -224,7 +231,7 @@
 Running In Debug Mode
 ======================
 
-  $ run-tests.py --with-hg=`which hg` --debug 2>&1 | grep -v pwd
+  $ rt --debug 2>&1 | grep -v pwd
   + echo *SALT* 0 0 (glob)
   *SALT* 0 0 (glob)
   + echo babar
@@ -250,7 +257,7 @@
 (duplicate the failing test to get predictable output)
   $ cp test-failure.t test-failure-copy.t
 
-  $ run-tests.py --with-hg=`which hg` --jobs 2 test-failure*.t -n
+  $ rt --jobs 2 test-failure*.t -n
   !!
   Failed test-failure*.t: output changed (glob)
   Failed test-failure*.t: output changed (glob)
@@ -262,7 +269,7 @@
   >>> f = open('test-nothing.t', 'w')
   >>> f.write('foo\n' * 1024) and None
   >>> f.write('  $ sleep 1') and None
-  $ run-tests.py --with-hg=`which hg` --jobs 2 --first
+  $ rt --jobs 2 --first
   
   --- $TESTTMP/test-failure*.t (glob)
   +++ $TESTTMP/test-failure*.t.err (glob)
@@ -292,7 +299,7 @@
 
 Refuse the fix
 
-  $ echo 'n' | run-tests.py --with-hg=`which hg` -i
+  $ echo 'n' | rt -i
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
@@ -318,7 +325,7 @@
 
 Interactive with custom view
 
-  $ echo 'n' | run-tests.py --with-hg=`which hg` -i --view echo
+  $ echo 'n' | rt -i --view echo
   $TESTTMP/test-failure.t $TESTTMP/test-failure.t.err (glob)
   Accept this change? [n]* (glob)
   ERROR: test-failure.t output changed
@@ -330,7 +337,7 @@
 
 View the fix
 
-  $ echo 'y' | run-tests.py --with-hg=`which hg` --view echo
+  $ echo 'y' | rt --view echo
   $TESTTMP/test-failure.t $TESTTMP/test-failure.t.err (glob)
   
   ERROR: test-failure.t output changed
@@ -348,8 +355,7 @@
   $ echo "  saved backup bundle to \$TESTTMP/foo.hg (glob)" >> test-failure.t
   $ echo "  $ echo 'saved backup bundle to \$TESTTMP/foo.hg'" >> test-failure.t
   $ echo "  saved backup bundle to \$TESTTMP/*.hg (glob)" >> test-failure.t
-  $ echo 'y' | run-tests.py --with-hg=`which hg` -i 2>&1 | \
-  >   sed -e 's,(glob)$,&<,g'
+  $ echo 'y' | rt -i 2>&1
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
@@ -361,9 +367,9 @@
    this test is still more bytes than success.
      $ echo 'saved backup bundle to $TESTTMP/foo.hg'
   -  saved backup bundle to $TESTTMP/foo.hg
-  +  saved backup bundle to $TESTTMP/foo.hg (glob)<
+  +  saved backup bundle to $TESTTMP/foo.hg* (glob)
      $ echo 'saved backup bundle to $TESTTMP/foo.hg'
-     saved backup bundle to $TESTTMP/foo.hg (glob)<
+     saved backup bundle to $TESTTMP/foo.hg* (glob)
      $ echo 'saved backup bundle to $TESTTMP/foo.hg'
   Accept this change? [n] ..
   # Ran 2 tests, 0 skipped, 0 warned, 0 failed.
@@ -386,7 +392,7 @@
 No Diff
 ===============
 
-  $ run-tests.py --with-hg=`which hg` --nodiff
+  $ rt --nodiff
   !.
   Failed test-failure.t: output changed
   # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
@@ -394,17 +400,42 @@
   [1]
 
 test --tmpdir support
-  $ run-tests.py --with-hg=`which hg` --tmpdir=$TESTTMP/keep test-success.t
+  $ rt --tmpdir=$TESTTMP/keep test-success.t
   
   Keeping testtmp dir: $TESTTMP/keep/child1/test-success.t (glob)
   Keeping threadtmp dir: $TESTTMP/keep/child1  (glob)
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
+timeouts
+========
+  $ cat > test-timeout.t <<EOF
+  >   $ sleep 2
+  >   $ echo pass
+  >   pass
+  > EOF
+  > echo '#require slow' > test-slow-timeout.t
+  > cat test-timeout.t >> test-slow-timeout.t
+  $ rt --timeout=1 --slowtimeout=3 test-timeout.t test-slow-timeout.t
+  st
+  Skipped test-slow-timeout.t: skipped
+  Failed test-timeout.t: timed out
+  # Ran 1 tests, 1 skipped, 0 warned, 1 failed.
+  python hash seed: * (glob)
+  [1]
+  $ rt --timeout=1 --slowtimeout=3 \
+  > test-timeout.t test-slow-timeout.t --allow-slow-tests
+  .t
+  Failed test-timeout.t: timed out
+  # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
+  python hash seed: * (glob)
+  [1]
+  $ rm test-timeout.t test-slow-timeout.t
+
 test for --time
 ==================
 
-  $ run-tests.py --with-hg=`which hg` test-success.t --time
+  $ rt test-success.t --time
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
   # Producing time report
@@ -414,7 +445,7 @@
 test for --time with --job enabled
 ====================================
 
-  $ run-tests.py --with-hg=`which hg` test-success.t --time --jobs 2
+  $ rt test-success.t --time --jobs 2
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
   # Producing time report
@@ -427,7 +458,7 @@
   >   $ echo xyzzy
   > #require false
   > EOF
-  $ run-tests.py --with-hg=`which hg` --nodiff
+  $ rt --nodiff
   !.s
   Skipped test-skip.t: skipped
   Failed test-failure.t: output changed
@@ -435,13 +466,13 @@
   python hash seed: * (glob)
   [1]
 
-  $ run-tests.py --with-hg=`which hg` --keyword xyzzy
+  $ rt --keyword xyzzy
   .s
   Skipped test-skip.t: skipped
   # Ran 2 tests, 2 skipped, 0 warned, 0 failed.
 
 Skips with xml
-  $ run-tests.py --with-hg=`which hg` --keyword xyzzy \
+  $ rt --keyword xyzzy \
   >  --xunit=xunit.xml
   .s
   Skipped test-skip.t: skipped
@@ -454,7 +485,7 @@
 
 Missing skips or blacklisted skips don't count as executed:
   $ echo test-failure.t > blacklist
-  $ run-tests.py --with-hg=`which hg` --blacklist=blacklist \
+  $ rt --blacklist=blacklist \
   >   test-failure.t test-bogus.t
   ss
   Skipped test-bogus.t: Doesn't exist
@@ -466,7 +497,7 @@
 test for --json
 ==================
 
-  $ run-tests.py --with-hg=`which hg` --json
+  $ rt --json
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
@@ -516,7 +547,7 @@
 Test that failed test accepted through interactive are properly reported:
 
   $ cp test-failure.t backup
-  $ echo y | run-tests.py --with-hg=`which hg` --json -i
+  $ echo y | rt --json -i
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
@@ -568,7 +599,7 @@
   >   foo * \ (glob)
   > EOF
 
-  $ run-tests.py --with-hg=`which hg` test-glob-backslash.t
+  $ rt test-glob-backslash.t
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
@@ -588,7 +619,7 @@
   >   $ echo foo
   >   foo
   > EOF
-  $ run-tests.py $HGTEST_RUN_TESTS_PURE test-hghave.t
+  $ rt $HGTEST_RUN_TESTS_PURE test-hghave.t
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
@@ -600,14 +631,18 @@
   > - \$TESTDIR, in which test-runtestdir.t is placed (expanded at runtime)
   > - \$RUNTESTDIR, in which run-tests.py is placed (expanded at runtime)
   > 
+  > #if windows
+  >   $ test "\$TESTDIR" = "$TESTTMP\anothertests"
+  > #else
   >   $ test "\$TESTDIR" = "$TESTTMP"/anothertests
+  > #endif
   >   $ test "\$RUNTESTDIR" = "$TESTDIR"
   >   $ head -n 3 "\$RUNTESTDIR"/../contrib/check-code.py
   >   #!/usr/bin/env python
   >   #
   >   # check-code - a style and portability checker for Mercurial
   > EOF
-  $ run-tests.py $HGTEST_RUN_TESTS_PURE test-runtestdir.t
+  $ rt $HGTEST_RUN_TESTS_PURE test-runtestdir.t
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
@@ -624,7 +659,7 @@
   >   $ custom-command.sh
   >   hello world
   > EOF
-  $ run-tests.py $HGTEST_RUN_TESTS_PURE test-testdir-path.t
+  $ rt $HGTEST_RUN_TESTS_PURE test-testdir-path.t
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
@@ -636,10 +671,10 @@
   >   $ echo pass
   >   pass
   > EOF
-  $ run-tests.py $HGTEST_RUN_TESTS_PURE test-very-slow-test.t
+  $ rt $HGTEST_RUN_TESTS_PURE test-very-slow-test.t
   s
   Skipped test-very-slow-test.t: skipped
   # Ran 0 tests, 1 skipped, 0 warned, 0 failed.
-  $ run-tests.py $HGTEST_RUN_TESTS_PURE --allow-slow-tests test-very-slow-test.t
+  $ rt $HGTEST_RUN_TESTS_PURE --allow-slow-tests test-very-slow-test.t
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
--- a/tests/test-shelve.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-shelve.t	Thu Dec 17 17:27:32 2015 -0600
@@ -140,11 +140,11 @@
 ensure that our shelved changes exist
 
   $ hg shelve -l
-  default-01      (*)* changes to '[mq]: second.patch' (glob)
-  default         (*)* changes to '[mq]: second.patch' (glob)
+  default-01      (*)* changes to: [mq]: second.patch (glob)
+  default         (*)* changes to: [mq]: second.patch (glob)
 
   $ hg shelve -l -p default
-  default         (*)* changes to '[mq]: second.patch' (glob)
+  default         (*)* changes to: [mq]: second.patch (glob)
   
   diff --git a/a/a b/a/a
   --- a/a/a
@@ -177,7 +177,7 @@
   unshelving change 'default-01'
   temporarily committing pending changes (restore with 'hg unshelve --abort')
   rebasing shelved changes
-  rebasing 4:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
+  rebasing 4:32c69314e062 "changes to: [mq]: second.patch" (tip)
   merging a/a
 
   $ hg revert --all -q
@@ -299,7 +299,7 @@
   unshelving change 'default'
   temporarily committing pending changes (restore with 'hg unshelve --abort')
   rebasing shelved changes
-  rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
+  rebasing 5:32c69314e062 "changes to: [mq]: second.patch" (tip)
   merging a/a
   warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
   unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
@@ -329,7 +329,7 @@
    c
   +=======
   +a
-  +>>>>>>> source: 4702e8911fe0 - shelve: changes to '[mq]: second.patch'
+  +>>>>>>> source: 32c69314e062 - shelve: changes to: [mq]: second.patch
   diff --git a/b/b b/b.rename/b
   rename from b/b
   rename to b.rename/b
@@ -402,7 +402,7 @@
   [255]
 
   $ hg unshelve -c
-  rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
+  rebasing 5:32c69314e062 "changes to: [mq]: second.patch" (tip)
   unshelve of 'default' complete
 
 ensure the repo is as we hope
@@ -469,13 +469,36 @@
 
 if we resolve a conflict while unshelving, the unshelve should succeed
 
+  $ hg unshelve --tool :merge-other --keep
+  unshelving change 'default'
+  temporarily committing pending changes (restore with 'hg unshelve --abort')
+  rebasing shelved changes
+  rebasing 6:2f694dd83a13 "changes to: second" (tip)
+  merging a/a
+  $ hg parents -q
+  4:33f7f61e6c5e
+  $ hg shelve -l
+  default         (*)* changes to: second (glob)
+  $ hg status
+  M a/a
+  A foo/foo
+  $ cat a/a
+  a
+  c
+  a
+  $ cat > a/a << EOF
+  > a
+  > c
+  > x
+  > EOF
+
   $ HGMERGE=true hg unshelve
   unshelving change 'default'
   temporarily committing pending changes (restore with 'hg unshelve --abort')
   rebasing shelved changes
-  rebasing 6:c5e6910e7601 "changes to 'second'" (tip)
+  rebasing 6:2f694dd83a13 "changes to: second" (tip)
   merging a/a
-  note: rebase of 6:c5e6910e7601 created no changes to commit
+  note: rebase of 6:2f694dd83a13 created no changes to commit
   $ hg parents -q
   4:33f7f61e6c5e
   $ hg shelve -l
@@ -492,11 +515,11 @@
   shelved as default
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg shelve --list
-  default         (*)    changes to 'create conflict' (glob)
-  $ hg unshelve --keep
+  default         (*)* changes to: create conflict (glob)
+  $ hg unshelve -k
   unshelving change 'default'
   $ hg shelve --list
-  default         (*)    changes to 'create conflict' (glob)
+  default         (*)* changes to: create conflict (glob)
   $ hg shelve --cleanup
   $ hg shelve --list
 
@@ -531,7 +554,7 @@
   shelved as test
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg --config extensions.mq=! shelve --list
-  test            (*)    changes to 'create conflict' (glob)
+  test            (*)* changes to: create conflict (glob)
   $ hg bookmark
    * test                      4:33f7f61e6c5e
   $ hg --config extensions.mq=! unshelve
@@ -564,7 +587,7 @@
   $ hg unshelve
   unshelving change 'default'
   rebasing shelved changes
-  rebasing 4:b8fefe789ed0 "changes to 'xyz'" (tip)
+  rebasing 4:82a0d7d6ba61 "changes to: xyz" (tip)
   $ hg status
   M z
 
@@ -591,7 +614,7 @@
   $ hg unshelve
   unshelving change 'default'
   rebasing shelved changes
-  rebasing 3:0cae6656c016 "changes to 'c'" (tip)
+  rebasing 3:958bcbd1776e "changes to: c" (tip)
   $ hg status
   A d
 
@@ -605,7 +628,7 @@
   $ hg unshelve
   unshelving change 'default'
   rebasing shelved changes
-  rebasing 3:be58f65f55fb "changes to 'b'" (tip)
+  rebasing 3:013284d9655e "changes to: b" (tip)
   $ hg status
   A d
 
@@ -688,7 +711,7 @@
   shelved as default
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg log -G --template '{rev}  {desc|firstline}  {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()'
-  o  4  changes to 'commit stuff'  shelve@localhost
+  o  4  changes to: commit stuff  shelve@localhost
   |
   $ hg log -G --template '{rev}  {desc|firstline}  {author}'
   @  3  commit stuff  test
@@ -703,13 +726,13 @@
   unshelving change 'default'
   temporarily committing pending changes (restore with 'hg unshelve --abort')
   rebasing shelved changes
-  rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
+  rebasing 5:81152db69da7 "changes to: commit stuff" (tip)
   merging f
   warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
   unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
   [1]
   $ hg log -G --template '{rev}  {desc|firstline}  {author}  {date|isodate}'
-  @  5  changes to 'commit stuff'  shelve@localhost  1970-01-01 00:00 +0000
+  @  5  changes to: commit stuff  shelve@localhost  1970-01-01 00:00 +0000
   |
   | @  4  pending changes temporary commit  shelve@localhost  2004-01-10 13:37 +0000
   |/
@@ -727,10 +750,11 @@
   g
   =======
   f
-  >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
+  >>>>>>> source: 81152db69da7 - shelve: changes to: commit stuff
   $ cat f.orig
   g
-  $ hg unshelve --abort
+  $ hg unshelve --abort -t false
+  tool option will be ignored
   rebase aborted
   unshelve of 'default' aborted
   $ hg st
@@ -742,7 +766,7 @@
   unshelving change 'default'
   temporarily committing pending changes (restore with 'hg unshelve --abort')
   rebasing shelved changes
-  rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
+  rebasing 5:81152db69da7 "changes to: commit stuff" (tip)
   $ hg st
   M a
   A f
@@ -758,7 +782,7 @@
   $ hg unshelve
   unshelving change 'default'
   rebasing shelved changes
-  rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
+  rebasing 5:81152db69da7 "changes to: commit stuff" (tip)
   merging f
   warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
   unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
@@ -771,7 +795,7 @@
   g
   =======
   f
-  >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
+  >>>>>>> source: 81152db69da7 - shelve: changes to: commit stuff
   $ cat f.orig
   g
   $ hg unshelve --abort
@@ -801,7 +825,7 @@
   $ hg unshelve
   unshelving change 'default'
   rebasing shelved changes
-  rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
+  rebasing 5:e42a7da90865 "changes to: second" (tip)
   merging a/a
   warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
   unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
@@ -817,8 +841,8 @@
   $ hg resolve -m a/a
   (no more unresolved files)
   $ hg unshelve -c
-  rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
-  note: rebase of 5:4b555fdb4e96 created no changes to commit
+  rebasing 5:e42a7da90865 "changes to: second" (tip)
+  note: rebase of 5:e42a7da90865 created no changes to commit
   unshelve of 'default' complete
   $ hg bookmark
    * test                      4:33f7f61e6c5e
@@ -911,7 +935,7 @@
   unshelving change 'test'
   temporarily committing pending changes (restore with 'hg unshelve --abort')
   rebasing shelved changes
-  rebasing 6:65b5d1c34c34 "changes to 'create conflict'" (tip)
+  rebasing 6:96a1354f65f6 "changes to: create conflict" (tip)
   merging a/a
   $ hg bookmark
    * test                      4:33f7f61e6c5e
@@ -945,7 +969,7 @@
   abort: --stat expects a single shelf
   [255]
   $ hg shelve --patch default
-  default         (* ago)    changes to 'create conflict' (glob)
+  default         (*)* changes to: create conflict (glob)
   
   diff --git a/shelf-patch-a b/shelf-patch-a
   new file mode 100644
@@ -954,7 +978,7 @@
   @@ -0,0 +1,1 @@
   +patch a
   $ hg shelve --stat default
-  default         (* ago)    changes to 'create conflict' (glob)
+  default         (*)* changes to: create conflict (glob)
    shelf-patch-a |  1 +
    1 files changed, 1 insertions(+), 0 deletions(-)
   $ hg shelve --patch nonexistentshelf
@@ -971,7 +995,7 @@
 
 no general delta
 
-  $ hg clone --pull repo bundle1 --config format.generaldelta=0
+  $ hg clone --pull repo bundle1 --config format.usegeneraldelta=0
   requesting all changes
   adding changesets
   adding manifests
@@ -986,12 +1010,12 @@
   shelved as default
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg debugbundle .hg/shelved/*.hg
-  7e30d8ac6f23cfc84330fd7e698730374615d21a
+  45993d65fe9dc3c6d8764b9c3b07fa831ee7d92d
   $ cd ..
 
 with general delta
 
-  $ hg clone --pull repo bundle2 --config format.generaldelta=1
+  $ hg clone --pull repo bundle2 --config format.usegeneraldelta=1
   requesting all changes
   adding changesets
   adding manifests
@@ -1008,7 +1032,7 @@
   $ hg debugbundle .hg/shelved/*.hg
   Stream params: {'Compression': 'BZ'}
   changegroup -- "{'version': '02'}"
-      7e30d8ac6f23cfc84330fd7e698730374615d21a
+      45993d65fe9dc3c6d8764b9c3b07fa831ee7d92d
   $ cd ..
 
 Test visibility of in-memory changes inside transaction to external hook
@@ -1062,13 +1086,13 @@
   $ hg unshelve --keep default
   temporarily committing pending changes (restore with 'hg unshelve --abort')
   rebasing shelved changes
-  rebasing 7:fcbb97608399 "changes to 'create conflict'" (tip)
+  rebasing 7:206bf5d4f922 "changes to: create conflict" (tip)
   ==== preupdate:
   VISIBLE 6:66b86db80ee4
   ACTUAL  5:703117a2acfb
   ====
   ==== preupdate:
-  VISIBLE 8:cb2a4e59c2d5
+  VISIBLE 8:a0e04704317e
   ACTUAL  5:703117a2acfb
   ====
   ==== preupdate:
@@ -1107,10 +1131,10 @@
   $ hg unshelve --keep default
   temporarily committing pending changes (restore with 'hg unshelve --abort')
   rebasing shelved changes
-  rebasing 7:fcbb97608399 "changes to 'create conflict'" (tip)
+  rebasing 7:206bf5d4f922 "changes to: create conflict" (tip)
   ==== update:
   VISIBLE 6:66b86db80ee4
-  VISIBLE 7:fcbb97608399
+  VISIBLE 7:206bf5d4f922
   ACTUAL  5:703117a2acfb
   ====
   ==== update:
@@ -1135,7 +1159,7 @@
 
   $ cd ..
 
-test Abort unshelve always gets user out of the unshelved state
+test .orig files go where the user wants them to
 ---------------------------------------------------------------
   $ hg init salvage
   $ cd salvage
@@ -1144,15 +1168,21 @@
   $ echo '' > root
   $ hg shelve -q
   $ echo 'contADDent' > root
-  $ hg unshelve -q
+  $ hg unshelve -q --config 'ui.origbackuppath=.hg/origbackups'
   warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
   unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
   [1]
+  $ ls .hg/origbackups
+  root.orig
+  $ rm -rf .hg/origbackups
+
+test Abort unshelve always gets user out of the unshelved state
+---------------------------------------------------------------
 Wreak havoc on the unshelve process
   $ rm .hg/unshelverebasestate
   $ hg unshelve --abort
   unshelve of 'default' aborted
-  abort: No such file or directory
+  abort: (No such file or directory|The system cannot find the file specified) (re)
   [255]
 Can the user leave the current state?
   $ hg up -C .
--- a/tests/test-ssh-bundle1.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-ssh-bundle1.t	Thu Dec 17 17:27:32 2015 -0600
@@ -5,6 +5,8 @@
   > [experimental]
   > # This test is dedicated to interaction through old bundle
   > bundle2-exp = False
+  > [format] # temporary settings
+  > usegeneraldelta=yes
   > EOF
 
 
@@ -460,8 +462,8 @@
   running python ".*/dummyssh" user@dummy ('|")hg -R remote serve --stdio('|") (re)
   sending hello command
   sending between command
-  remote: 345
-  remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
+  remote: 371
+  remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
   remote: 1
   preparing listkeys for "bookmarks"
   sending listkeys command
@@ -522,11 +524,11 @@
 
   $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
 
-  $ hg -q --config ui.ssh="python '$TESTDIR/dummyssh'" clone ssh://user@dummy/remote hookout
+  $ hg -q --config ui.ssh="python $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
   $ cd hookout
   $ touch hookfailure
   $ hg -q commit -A -m 'remote hook failure'
-  $ hg --config ui.ssh="python '$TESTDIR/dummyssh'" push
+  $ hg --config ui.ssh="python $TESTDIR/dummyssh" push
   pushing to ssh://user@dummy/remote
   searching for changes
   remote: adding changesets
--- a/tests/test-ssh.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-ssh.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,6 +1,11 @@
 
 This test tries to exercise the ssh functionality with a dummy script
 
+  $ cat <<EOF >> $HGRCPATH
+  > [format]
+  > usegeneraldelta=yes
+  > EOF
+
 creating 'remote' repo
 
   $ hg init remote
@@ -449,8 +454,8 @@
   running python ".*/dummyssh" user@dummy ('|")hg -R remote serve --stdio('|") (re)
   sending hello command
   sending between command
-  remote: 345
-  remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
+  remote: 371
+  remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
   remote: 1
   query 1; heads
   sending batch command
@@ -514,11 +519,11 @@
 
   $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
 
-  $ hg -q --config ui.ssh="python '$TESTDIR/dummyssh'" clone ssh://user@dummy/remote hookout
+  $ hg -q --config ui.ssh="python $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
   $ cd hookout
   $ touch hookfailure
   $ hg -q commit -A -m 'remote hook failure'
-  $ hg --config ui.ssh="python '$TESTDIR/dummyssh'" push
+  $ hg --config ui.ssh="python $TESTDIR/dummyssh" push
   pushing to ssh://user@dummy/remote
   searching for changes
   remote: adding changesets
--- a/tests/test-strip.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-strip.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,3 +1,5 @@
+  $ echo "[format]" >> $HGRCPATH
+  $ echo "usegeneraldelta=yes" >> $HGRCPATH
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "strip=" >> $HGRCPATH
 
@@ -208,7 +210,9 @@
   summary:     b
   
   $ hg debugbundle .hg/strip-backup/*
-  264128213d290d868c54642d13aeaa3675551a78
+  Stream params: {'Compression': 'BZ'}
+  changegroup -- "{'version': '02'}"
+      264128213d290d868c54642d13aeaa3675551a78
   $ hg pull .hg/strip-backup/*
   pulling from .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
   searching for changes
@@ -548,12 +552,19 @@
   $ echo b > b
   $ echo d > d
   $ hg strip --keep tip
-  saved backup bundle to $TESTTMP/test/.hg/strip-backup/57e364c8a475-4cfed93c-backup.hg (glob)
+  saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
   $ hg status
   M b
   ! bar
   ? c
   ? d
+
+... after updating the dirstate
+  $ hg add c
+  $ hg commit -mc
+  $ hg rm c
+  $ hg commit -mc
+  $ hg strip --keep '.^' -q
   $ cd ..
 
 stripping many nodes on a complex graph (issue3299)
@@ -569,11 +580,15 @@
   $ cd ..
   $ hg init bookmarks
   $ cd bookmarks
-  $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b'
+  $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b<m+2:d<2.:e<m+1:f'
   $ hg bookmark -r 'a' 'todelete'
   $ hg bookmark -r 'b' 'B'
   $ hg bookmark -r 'b' 'nostrip'
   $ hg bookmark -r 'c' 'delete'
+  $ hg bookmark -r 'd' 'multipledelete1'
+  $ hg bookmark -r 'e' 'multipledelete2'
+  $ hg bookmark -r 'f' 'singlenode1'
+  $ hg bookmark -r 'f' 'singlenode2'
   $ hg up -C todelete
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (activating bookmark todelete)
@@ -593,6 +608,36 @@
   $ hg bookmarks
      B                         9:ff43616e5d0f
      delete                    6:2702dd0c91e7
+     multipledelete1           11:e46a4836065c
+     multipledelete2           12:b4594d867745
+     singlenode1               13:43227190fef8
+     singlenode2               13:43227190fef8
+  $ hg strip -B multipledelete1 -B multipledelete2
+  saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/e46a4836065c-89ec65c2-backup.hg (glob)
+  bookmark 'multipledelete1' deleted
+  bookmark 'multipledelete2' deleted
+  $ hg id -ir e46a4836065c
+  abort: unknown revision 'e46a4836065c'!
+  [255]
+  $ hg id -ir b4594d867745
+  abort: unknown revision 'b4594d867745'!
+  [255]
+  $ hg strip -B singlenode1 -B singlenode2
+  saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/43227190fef8-8da858f2-backup.hg (glob)
+  bookmark 'singlenode1' deleted
+  bookmark 'singlenode2' deleted
+  $ hg id -ir 43227190fef8
+  abort: unknown revision '43227190fef8'!
+  [255]
+  $ hg strip -B unknownbookmark
+  abort: bookmark 'unknownbookmark' not found
+  [255]
+  $ hg strip -B unknownbookmark1 -B unknownbookmark2
+  abort: bookmark 'unknownbookmark1,unknownbookmark2' not found
+  [255]
+  $ hg strip -B delete -B unknownbookmark
+  abort: bookmark 'unknownbookmark' not found
+  [255]
   $ hg strip -B delete
   saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
   bookmark 'delete' deleted
@@ -622,14 +667,14 @@
   
   options ([+] can be repeated):
   
-   -r --rev REV [+]    strip specified revision (optional, can specify revisions
-                       without this option)
-   -f --force          force removal of changesets, discard uncommitted changes
-                       (no backup)
-      --no-backup      no backups
-   -k --keep           do not modify working directory during strip
-   -B --bookmark VALUE remove revs only reachable from given bookmark
-      --mq             operate on patch repository
+   -r --rev REV [+]        strip specified revision (optional, can specify
+                           revisions without this option)
+   -f --force              force removal of changesets, discard uncommitted
+                           changes (no backup)
+      --no-backup          no backups
+   -k --keep               do not modify working directory during strip
+   -B --bookmark VALUE [+] remove revs only reachable from given bookmark
+      --mq                 operate on patch repository
   
   (use "hg strip -h" to show more help)
   [255]
@@ -751,6 +796,8 @@
   list of changesets:
   6625a516847449b6f0fa3737b9ba56e9f0f3032c
   d8db9d1372214336d2b5570f20ee468d2c72fa8b
+  bundle2-output-bundle: "HG20", (1 params) 1 parts total
+  bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
   saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/6625a5168474-345bb43d-backup.hg (glob)
   invalid branchheads cache (served): tip differs
   truncating cache/rbc-revs-v1 to 24
--- a/tests/test-subrepo-git.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-subrepo-git.t	Thu Dec 17 17:27:32 2015 -0600
@@ -875,6 +875,16 @@
   $ hg status --subrepos
   ? s/barfoo
 
+revert moves orig files to the right place
+  $ echo 'bloop' > s/foobar
+  $ hg revert --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
+  reverting subrepo ../gitroot
+  creating directory: $TESTTMP/tc/.hg/origbackups (glob)
+  saving current version of foobar as $TESTTMP/tc/.hg/origbackups/foobar.orig (glob)
+  $ ls .hg/origbackups
+  foobar.orig
+  $ rm -rf .hg/origbackups
+
 show file at specific revision
   $ cat > s/foobar << EOF
   > woop    woop
--- a/tests/test-subrepo.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-subrepo.t	Thu Dec 17 17:27:32 2015 -0600
@@ -297,11 +297,11 @@
    ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
    preserving t for resolve of t
    t: versions differ -> m (premerge)
-  picked tool ':merge' for t (binary False symlink False)
+  picked tool ':merge' for t (binary False symlink False changedelete False)
   merging t
   my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
    t: versions differ -> m (merge)
-  picked tool ':merge' for t (binary False symlink False)
+  picked tool ':merge' for t (binary False symlink False changedelete False)
   my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
   warning: conflicts while merging t! (edit, then use 'hg resolve --mark')
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
--- a/tests/test-tag.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-tag.t	Thu Dec 17 17:27:32 2015 -0600
@@ -272,9 +272,9 @@
   HG: branch 'tag-and-branch-same-name'
   HG: changed .hgtags
   ====
+  note: commit message saved in .hg/last-message.txt
   transaction abort!
   rollback completed
-  note: commit message saved in .hg/last-message.txt
   abort: pretxncommit.unexpectedabort hook exited with status 1
   [255]
   $ cat .hg/last-message.txt
--- a/tests/test-template-engine.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-template-engine.t	Thu Dec 17 17:27:32 2015 -0600
@@ -44,4 +44,17 @@
   0 97e5f848f0936960273bbf75be6388cd0350a32b -1 0000000000000000000000000000000000000000
   -1 0000000000000000000000000000000000000000 -1 0000000000000000000000000000000000000000
 
+Fuzzing the unicode escaper to ensure it produces valid data
+
+#if hypothesis
+
+  >>> from hypothesishelpers import *
+  >>> import mercurial.templatefilters as tf
+  >>> import json
+  >>> @check(st.text().map(lambda s: s.encode('utf-8')))
+  ... def testtfescapeproducesvalidjson(text):
+  ...     json.loads('"' + tf.jsonescape(text) + '"')
+
+#endif
+
   $ cd ..
--- a/tests/test-tools.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-tools.t	Thu Dec 17 17:27:32 2015 -0600
@@ -43,7 +43,13 @@
   foo: mode=644
 #endif
 
+#if no-windows
   $ python $TESTDIR/seq.py 10 > bar
+#else
+Convert CRLF -> LF for consistency
+  $ python $TESTDIR/seq.py 10 | sed "s/$//" > bar
+#endif
+
 #if unix-permissions symlink
   $ chmod +x bar
   $ f bar --newer foo --mode --type --size --dump --links --bytes 7
--- a/tests/test-treemanifest.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-treemanifest.t	Thu Dec 17 17:27:32 2015 -0600
@@ -1,3 +1,7 @@
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > usegeneraldelta=yes
+  > EOF
 
 Set up repo
 
@@ -28,7 +32,7 @@
   $ hg debugdata -m 1
   a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
   b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
-  dir1\x008b3ffd73f901e83304c83d33132c8e774ceac44ed (esc)
+  dir1\x008b3ffd73f901e83304c83d33132c8e774ceac44et (esc)
   e\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
   $ hg debugdata --dir dir1 0
   a\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
@@ -118,13 +122,13 @@
   $ cat dir1/b
   6
   $ hg debugindex --dir dir1
-     rev    offset  length   base linkrev nodeid       p1           p2
-       0         0      54      0       1 8b3ffd73f901 000000000000 000000000000
-       1        54      68      0       2 b66d046c644f 8b3ffd73f901 000000000000
-       2       122      12      0       4 b87265673c8a b66d046c644f 000000000000
-       3       134      95      0       5 aa5d3adcec72 b66d046c644f 000000000000
-       4       229      81      0       6 e29b066b91ad b66d046c644f 000000000000
-       5       310     107      5       7 a120ce2b83f5 e29b066b91ad aa5d3adcec72
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0      54     -1       1 8b3ffd73f901 000000000000 000000000000
+       1        54      68      0       2 68e9d057c5a8 8b3ffd73f901 000000000000
+       2       122      12      1       4 4698198d2624 68e9d057c5a8 000000000000
+       3       134      55      1       5 44844058ccce 68e9d057c5a8 000000000000
+       4       189      55      1       6 bf3d9b744927 68e9d057c5a8 000000000000
+       5       244      55      4       7 dde7c0af2a03 bf3d9b744927 44844058ccce
 
 Merge keeping directory from parent 1 does not create revlog entry. (Note that
 dir1's manifest does change, but only because dir1/a's filelog changes.)
@@ -250,13 +254,13 @@
 Parent of tree root manifest should be flat manifest, and two for merge
 
   $ hg debugindex -m
-     rev    offset  length   base linkrev nodeid       p1           p2
-       0         0      80      0       0 40536115ed9e 000000000000 000000000000
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0      80     -1       0 40536115ed9e 000000000000 000000000000
        1        80      83      0       1 f3376063c255 40536115ed9e 000000000000
-       2       163     103      0       2 5d9b9da231a2 40536115ed9e 000000000000
-       3       266      83      0       3 d17d663cbd8a 5d9b9da231a2 f3376063c255
-       4       349     132      4       4 c05a51345f86 f3376063c255 000000000000
-       5       481     110      4       5 82594b1f557d 5d9b9da231a2 f3376063c255
+       2       163      89      0       2 5d9b9da231a2 40536115ed9e 000000000000
+       3       252      83      2       3 d17d663cbd8a 5d9b9da231a2 f3376063c255
+       4       335     124      1       4 51e32a8c60ee f3376063c255 000000000000
+       5       459     126      2       5 cc5baa78b230 5d9b9da231a2 f3376063c255
 
 
 Status across flat/tree boundary should work
@@ -270,16 +274,16 @@
 Turning off treemanifest config has no effect
 
   $ hg debugindex .hg/store/meta/dir1/00manifest.i
-     rev    offset  length   base linkrev nodeid       p1           p2
-       0         0     125      0       4 63c9c0557d24 000000000000 000000000000
-       1       125     109      0       5 23d12a1f6e0e 000000000000 000000000000
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0     127     -1       4 064927a0648a 000000000000 000000000000
+       1       127     111      0       5 25ecb8cb8618 000000000000 000000000000
   $ echo 2 > dir1/a
   $ hg --config experimental.treemanifest=False ci -qm 'modify dir1/a'
   $ hg debugindex .hg/store/meta/dir1/00manifest.i
-     rev    offset  length   base linkrev nodeid       p1           p2
-       0         0     125      0       4 63c9c0557d24 000000000000 000000000000
-       1       125     109      0       5 23d12a1f6e0e 000000000000 000000000000
-       2       234      55      0       6 3cb2d87b4250 23d12a1f6e0e 000000000000
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0     127     -1       4 064927a0648a 000000000000 000000000000
+       1       127     111      0       5 25ecb8cb8618 000000000000 000000000000
+       2       238      55      1       6 5b16163a30c6 25ecb8cb8618 000000000000
 
 Create deeper repo with tree manifests.
 
--- a/tests/test-up-local-change.t	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-up-local-change.t	Thu Dec 17 17:27:32 2015 -0600
@@ -50,11 +50,11 @@
    b: remote created -> g
   getting b
    a: versions differ -> m (premerge)
-  picked tool 'true' for a (binary False symlink False)
+  picked tool 'true' for a (binary False symlink False changedelete False)
   merging a
   my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
    a: versions differ -> m (merge)
-  picked tool 'true' for a (binary False symlink False)
+  picked tool 'true' for a (binary False symlink False changedelete False)
   my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
   launching merge tool: true *$TESTTMP/r2/a* * * (glob)
   merge tool returned: 0
@@ -74,11 +74,11 @@
    b: other deleted -> r
   removing b
    a: versions differ -> m (premerge)
-  picked tool 'true' for a (binary False symlink False)
+  picked tool 'true' for a (binary False symlink False changedelete False)
   merging a
   my a@1e71731e6fbb+ other a@c19d34741b0a ancestor a@1e71731e6fbb
    a: versions differ -> m (merge)
-  picked tool 'true' for a (binary False symlink False)
+  picked tool 'true' for a (binary False symlink False changedelete False)
   my a@1e71731e6fbb+ other a@c19d34741b0a ancestor a@1e71731e6fbb
   launching merge tool: true *$TESTTMP/r2/a* * * (glob)
   merge tool returned: 0
@@ -106,11 +106,11 @@
    b: remote created -> g
   getting b
    a: versions differ -> m (premerge)
-  picked tool 'true' for a (binary False symlink False)
+  picked tool 'true' for a (binary False symlink False changedelete False)
   merging a
   my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
    a: versions differ -> m (merge)
-  picked tool 'true' for a (binary False symlink False)
+  picked tool 'true' for a (binary False symlink False changedelete False)
   my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
   launching merge tool: true *$TESTTMP/r2/a* * * (glob)
   merge tool returned: 0
--- a/tests/test-walkrepo.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-walkrepo.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,9 +1,20 @@
+from __future__ import absolute_import
+
 import os
-from mercurial import hg, ui
-from mercurial.scmutil import walkrepos
-from mercurial.util import checklink
-from os import mkdir, chdir
-from os.path import join as pjoin
+
+from mercurial import (
+    hg,
+    scmutil,
+    ui,
+    util,
+)
+
+chdir = os.chdir
+mkdir = os.mkdir
+pjoin = os.path.join
+
+walkrepos = scmutil.walkrepos
+checklink = util.checklink
 
 u = ui.ui()
 sym = checklink('.')
--- a/tests/test-wireproto.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/test-wireproto.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+
 from mercurial import wireproto
 
 class proto(object):
--- a/tests/tinyproxy.py	Mon Nov 30 13:47:29 2015 -0600
+++ b/tests/tinyproxy.py	Thu Dec 17 17:27:32 2015 -0600
@@ -1,5 +1,7 @@
 #!/usr/bin/env python
 
+from __future__ import absolute_import
+
 __doc__ = """Tiny HTTP Proxy.
 
 This module implements GET, HEAD, POST, PUT and DELETE methods
@@ -12,7 +14,12 @@
 
 __version__ = "0.2.1"
 
-import BaseHTTPServer, select, socket, SocketServer, urlparse, os
+import BaseHTTPServer
+import os
+import select
+import socket
+import SocketServer
+import urlparse
 
 class ProxyHandler (BaseHTTPServer.BaseHTTPRequestHandler):
     __base = BaseHTTPServer.BaseHTTPRequestHandler