changeset 25195:472a685a4961

merge with stable
author Matt Mackall <mpm@selenic.com>
date Tue, 19 May 2015 07:17:57 -0500
parents 08d1ef09ed37 (diff) ef4538ba67ef (current diff)
children 7a1af58ab242
files mercurial/context.py mercurial/exchange.py mercurial/match.py tests/test-subrepo-deep-nested-change.t
diffstat 221 files changed, 4233 insertions(+), 2653 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sun May 17 22:09:37 2015 -0400
+++ b/Makefile	Tue May 19 07:17:57 2015 -0500
@@ -157,6 +157,16 @@
 	N=`cd dist && echo mercurial-*.mpkg | sed 's,\.mpkg$$,,'` && hdiutil create -srcfolder dist/$$N.mpkg/ -scrub -volname "$$N" -ov packages/osx/$$N.dmg
 	rm -rf dist/mercurial-*.mpkg
 
+debian-jessie:
+	mkdir -p packages/debian-jessie
+	contrib/builddeb
+	mv debbuild/*.deb packages/debian-jessie
+	rm -rf debbuild
+
+docker-debian-jessie:
+	mkdir -p packages/debian/jessie
+	contrib/dockerdeb jessie
+
 fedora20:
 	mkdir -p packages/fedora20
 	contrib/buildrpm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/builddeb	Tue May 19 07:17:57 2015 -0500
@@ -0,0 +1,62 @@
+#!/bin/sh -e
+#
+# Build a Mercurial debian package from the current repo
+#
+# Tested on Jessie (stable as of original script authoring.)
+
+. $(dirname $0)/packagelib.sh
+
+BUILD=1
+DEBBUILDDIR="$PWD/debbuild"
+while [ "$1" ]; do
+    case "$1" in
+    --prepare )
+        shift
+        BUILD=
+        ;;
+    --debbuilddir )
+        shift
+        DEBBUILDDIR="$1"
+        shift
+        ;;
+    * )
+        echo "Invalid parameter $1!" 1>&2
+        exit 1
+        ;;
+    esac
+done
+
+set -u
+
+rm -rf $DEBBUILDDIR
+mkdir -p $DEBBUILDDIR
+
+if [ ! -d .hg ]; then
+    echo 'You are not inside a Mercurial repository!' 1>&2
+    exit 1
+fi
+
+gethgversion
+
+cp -r $PWD/contrib/debian $DEBBUILDDIR/DEBIAN
+chmod -R 0755 $DEBBUILDDIR/DEBIAN
+
+control=$DEBBUILDDIR/DEBIAN/control
+
+# This looks like sed -i, but sed -i behaves just differently enough
+# between BSD and GNU sed that I gave up and did the dumb thing.
+sed "s/__VERSION__/$version/" < $control > $control.tmp
+mv $control.tmp $control
+
+if [ "$BUILD" ]; then
+    dpkg-deb --build $DEBBUILDDIR
+    mv $DEBBUILDDIR.deb $DEBBUILDDIR/mercurial-$version-$release.deb
+    if [ $? = 0 ]; then
+        echo
+        echo "Built packages for $version-$release:"
+        find $DEBBUILDDIR/ -type f -newer $control
+    fi
+else
+    echo "Prepared sources for $version-$release $control are in $DEBBUILDDIR - use like:"
+    echo "dpkg-deb --build $DEBBUILDDIR"
+fi
--- a/contrib/buildrpm	Sun May 17 22:09:37 2015 -0400
+++ b/contrib/buildrpm	Tue May 19 07:17:57 2015 -0500
@@ -7,6 +7,8 @@
 # - CentOS 5
 # - centOS 6
 
+. $(dirname $0)/packagelib.sh
+
 BUILD=1
 RPMBUILDDIR="$PWD/rpmbuild"
 while [ "$1" ]; do
@@ -45,25 +47,8 @@
     exit 1
 fi
 
-# build local hg and use it
-python setup.py build_py -c -d .
-HG="$PWD/hg"
-PYTHONPATH="$PWD/mercurial/pure"
-export PYTHONPATH
-
-mkdir -p $RPMBUILDDIR/SOURCES $RPMBUILDDIR/SPECS $RPMBUILDDIR/RPMS $RPMBUILDDIR/SRPMS $RPMBUILDDIR/BUILD
-
-hgversion=`$HG version | sed -ne 's/.*(version \(.*\))$/\1/p'`
+gethgversion
 
-if echo $hgversion | grep -- '-' > /dev/null 2>&1; then
-    # nightly build case, version is like 1.3.1+250-20b91f91f9ca
-    version=`echo $hgversion | cut -d- -f1`
-    release=`echo $hgversion | cut -d- -f2 | sed -e 's/+.*//'`
-else
-    # official tag, version is like 1.3.1
-    version=`echo $hgversion | sed -e 's/+.*//'`
-    release='0'
-fi
 if [ "$PYTHONVER" ]; then
     release=$release+$PYTHONVER
     RPMPYTHONVER=$PYTHONVER
--- a/contrib/check-code.py	Sun May 17 22:09:37 2015 -0400
+++ b/contrib/check-code.py	Tue May 19 07:17:57 2015 -0500
@@ -217,14 +217,6 @@
     (r'(\w|\)),\w', "missing whitespace after ,"),
     (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
     (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
-    (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)(\1except.*?:\n'
-     r'((?:\n|\1\s.*\n)+?))+\1finally:',
-     'no try/except/finally in Python 2.4'),
-    (r'(?<!def)(\s+|^|\()next\(.+\)',
-     'no next(foo) in Python 2.4 and 2.5, use foo.next() instead'),
-    (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
-     r'((?:\n|\1\s.*\n)+?)\1finally:',
-     'no yield inside try/finally in Python 2.4'),
     (r'.{81}', "line too long"),
     (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
     (r'[^\n]\Z', "no trailing newline"),
@@ -237,8 +229,9 @@
      "linebreak after :"),
     (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
     (r'class\s[^( \n]+\(\):',
-     "class foo() not available in Python 2.4, use class foo(object)"),
-    (r'\b(%s)\(' % '|'.join(keyword.kwlist),
+     "class foo() creates old style object, use class foo(object)"),
+    (r'\b(%s)\(' % '|'.join(k for k in keyword.kwlist
+                            if k not in ('print', 'exec')),
      "Python keyword is not a function"),
     (r',]', "unneeded trailing ',' in list"),
 #    (r'class\s[A-Z][^\(]*\((?!Exception)',
@@ -247,12 +240,8 @@
 #    (r'^\s*print\s+', "avoid using print in core and extensions"),
     (r'[\x80-\xff]', "non-ASCII character literal"),
     (r'("\')\.format\(', "str.format() not available in Python 2.4"),
-    (r'^\s*with\s+', "with not available in Python 2.4"),
-    (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
-    (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
-    (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
-    (r'(?<!def)\s+(any|all|format)\(',
-     "any/all/format not available in Python 2.4", 'no-py24'),
+    (r'(?<!def)\s+(format)\(',
+     "format not available in Python 2.4", 'no-py24'),
     (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
     (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
      "gratuitous whitespace after Python keyword"),
@@ -280,8 +269,6 @@
      'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
     (r'opener\([^)]*\).read\(',
      "use opener.read() instead"),
-    (r'BaseException', 'not in Python 2.4, use Exception'),
-    (r'os\.path\.relpath', 'os.path.relpath is not in Python 2.5'),
     (r'opener\([^)]*\).write\(',
      "use opener.write() instead"),
     (r'[\s\(](open|file)\([^)]*\)\.read\(',
--- a/contrib/check-commit	Sun May 17 22:09:37 2015 -0400
+++ b/contrib/check-commit	Tue May 19 07:17:57 2015 -0500
@@ -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"),
+    (r"^# .*\n.{78,}", "summary line too long (limit is 78)"),
     (r"^\+\n \n", "adds double empty line"),
     (r"\+\s+def [a-z]+_[a-z]", "adds a function with foo_bar naming"),
 ]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/debian/control	Tue May 19 07:17:57 2015 -0500
@@ -0,0 +1,9 @@
+Package: mercurial
+Version: __VERSION__
+Section: vcs
+Priority: optional
+Architecture: all
+Depends: python
+Conflicts: mercurial-common
+Maintainer: Mercurial Developers <mercurial-devel@selenic.com>
+Description: Mercurial (probably nightly) package built by upstream.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/docker/debian-jessie	Tue May 19 07:17:57 2015 -0500
@@ -0,0 +1,11 @@
+FROM debian:jessie
+RUN apt-get update && apt-get install -y \
+  build-essential \
+  debhelper \
+  dh-python \
+  devscripts \
+  python \
+  python-all-dev \
+  python-docutils \
+  zip \
+  unzip
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/dockerdeb	Tue May 19 07:17:57 2015 -0500
@@ -0,0 +1,39 @@
+#!/bin/bash -eu
+
+. $(dirname $0)/dockerlib.sh
+. $(dirname $0)/packagelib.sh
+
+BUILDDIR=$(dirname $0)
+export ROOTDIR=$(cd $BUILDDIR/..; pwd)
+
+checkdocker
+
+PLATFORM="debian-$1"
+shift # extra params are passed to build process
+
+initcontainer $PLATFORM
+
+DEBBUILDDIR=$ROOTDIR/packages/$PLATFORM
+contrib/builddeb --debbuilddir $DEBBUILDDIR/staged --prepare
+
+DSHARED=/mnt/shared/
+if [ $(uname) = "Darwin" ] ; then
+    $DOCKER run -u $DBUILDUSER --rm -v $DEBBUILDDIR:$DSHARED -v $PWD:/mnt/hg $CONTAINER \
+            sh -c "cd /mnt/hg && make clean && make local"
+fi
+$DOCKER run -u $DBUILDUSER --rm -v $DEBBUILDDIR:$DSHARED -v $PWD:/mnt/hg $CONTAINER \
+  sh -c "cd /mnt/hg && make PREFIX=$DSHARED/staged/usr install"
+$DOCKER run -u $DBUILDUSER --rm -v $DEBBUILDDIR:$DSHARED $CONTAINER \
+  dpkg-deb --build $DSHARED/staged
+if [ $(uname) = "Darwin" ] ; then
+    $DOCKER run -u $DBUILDUSER --rm -v $DEBBUILDDIR:$DSHARED -v $PWD:/mnt/hg $CONTAINER \
+            sh -c "cd /mnt/hg && make clean"
+fi
+
+gethgversion
+
+rm -r $DEBBUILDDIR/staged
+mv $DEBBUILDDIR/staged.deb $DEBBUILDDIR/mercurial-$version-$release.deb
+
+echo
+echo "Build complete - results can be found in $DEBBUILDDIR"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/dockerlib.sh	Tue May 19 07:17:57 2015 -0500
@@ -0,0 +1,42 @@
+#!/bin/sh -eu
+
+# This function exists to set up the DOCKER variable and verify that
+# it's the binary we expect. It also verifies that the docker service
+# is running on the system and we can talk to it.
+function checkdocker() {
+  if which docker.io >> /dev/null 2>&1 ; then
+    DOCKER=docker.io
+  elif which docker >> /dev/null 2>&1 ; then
+    DOCKER=docker
+  else
+    echo "Error: docker must be installed"
+    exit 1
+  fi
+
+  $DOCKER -h 2> /dev/null | grep -q Jansens && { echo "Error: $DOCKER is the Docking System Tray - install docker.io instead"; exit 1; }
+  $DOCKER version | grep -q "^Client version:" || { echo "Error: unexpected output from \"$DOCKER version\""; exit 1; }
+  $DOCKER version | grep -q "^Server version:" || { echo "Error: could not get docker server version - check it is running and your permissions"; exit 1; }
+}
+
+# Construct a container and leave its name in $CONTAINER for future use.
+function initcontainer() {
+  [ "$1" ] || { echo "Error: platform name must be specified"; exit 1; }
+
+  DFILE="$ROOTDIR/contrib/docker/$1"
+  [ -f "$DFILE" ] || { echo "Error: docker file $DFILE not found"; exit 1; }
+
+  CONTAINER="hg-dockerrpm-$1"
+  DBUILDUSER=build
+  (
+    cat $DFILE
+    if [ $(uname) = "Darwin" ] ; then
+        # The builder is using boot2docker on OS X, so we're going to
+        # *guess* the uid of the user inside the VM that is actually
+        # running docker. This is *very likely* to fail at some point.
+        echo RUN useradd $DBUILDUSER -u 1000
+    else
+        echo RUN groupadd $DBUILDUSER -g `id -g`
+        echo RUN useradd $DBUILDUSER -u `id -u` -g $DBUILDUSER
+    fi
+  ) | $DOCKER build --tag $CONTAINER -
+}
--- a/contrib/dockerrpm	Sun May 17 22:09:37 2015 -0400
+++ b/contrib/dockerrpm	Tue May 19 07:17:57 2015 -0500
@@ -1,36 +1,16 @@
 #!/bin/bash -e
 
+. $(dirname $0)/dockerlib.sh
+
 BUILDDIR=$(dirname $0)
-ROOTDIR=$(cd $BUILDDIR/..; pwd)
+export ROOTDIR=$(cd $BUILDDIR/..; pwd)
 
-if which docker.io >> /dev/null 2>&1 ; then
-  DOCKER=docker.io
-elif which docker >> /dev/null 2>&1 ; then
-  DOCKER=docker
-else
-  echo "Error: docker must be installed"
-  exit 1
-fi
-
-$DOCKER -h 2> /dev/null | grep -q Jansens && { echo "Error: $DOCKER is the Docking System Tray - install docker.io instead"; exit 1; }
-$DOCKER version | grep -q "^Client version:" || { echo "Error: unexpected output from \"$DOCKER version\""; exit 1; }
-$DOCKER version | grep -q "^Server version:" || { echo "Error: could not get docker server version - check it is running and your permissions"; exit 1; }
+checkdocker
 
 PLATFORM="$1"
-[ "$PLATFORM" ] || { echo "Error: platform name must be specified"; exit 1; }
 shift # extra params are passed to buildrpm
 
-DFILE="$ROOTDIR/contrib/docker/$PLATFORM"
-[ -f "$DFILE" ] || { echo "Error: docker file $DFILE not found"; exit 1; }
-
-CONTAINER="hg-dockerrpm-$PLATFORM"
-
-DBUILDUSER=build
-(
-cat $DFILE
-echo RUN groupadd $DBUILDUSER -g `id -g`
-echo RUN useradd $DBUILDUSER -u `id -u` -g $DBUILDUSER
-) | $DOCKER build --tag $CONTAINER -
+initcontainer $PLATFORM
 
 RPMBUILDDIR=$ROOTDIR/packages/$PLATFORM
 contrib/buildrpm --rpmbuilddir $RPMBUILDDIR --prepare $*
--- a/contrib/hg-ssh	Sun May 17 22:09:37 2015 -0400
+++ b/contrib/hg-ssh	Tue May 19 07:17:57 2015 -0500
@@ -64,7 +64,7 @@
             if readonly:
                 cmd += [
                     '--config',
-                    'hooks.prechangegroup.hg-ssh=python:__main__.rejectpush',
+                    'hooks.pretxnopen.hg-ssh=python:__main__.rejectpush',
                     '--config',
                     'hooks.prepushkey.hg-ssh=python:__main__.rejectpush'
                     ]
--- a/contrib/import-checker.py	Sun May 17 22:09:37 2015 -0400
+++ b/contrib/import-checker.py	Tue May 19 07:17:57 2015 -0500
@@ -26,6 +26,72 @@
         return '.'.join(p for p in parts if p != 'pure')
     return '.'.join(parts)
 
+def fromlocalfunc(modulename, localmods):
+    """Get a function to examine which locally defined module the
+    target source imports via a specified name.
+
+    `modulename` is an `dotted_name_of_path()`-ed source file path,
+    which may have `.__init__` at the end of it, of the target source.
+
+    `localmods` is a dict (or set), of which key is an absolute
+    `dotted_name_of_path()`-ed source file path of locally defined (=
+    Mercurial specific) modules.
+
+    This function assumes that module names not existing in
+    `localmods` are ones of Python standard libarary.
+
+    This function returns the function, which takes `name` argument,
+    and returns `(absname, dottedpath, hassubmod)` tuple if `name`
+    matches against locally defined module. Otherwise, it returns
+    False.
+
+    It is assumed that `name` doesn't have `.__init__`.
+
+    `absname` is an absolute module name of specified `name`
+    (e.g. "hgext.convert"). This can be used to compose prefix for sub
+    modules or so.
+
+    `dottedpath` is a `dotted_name_of_path()`-ed source file path
+    (e.g. "hgext.convert.__init__") of `name`. This is used to look
+    module up in `localmods` again.
+
+    `hassubmod` is whether it may have sub modules under it (for
+    convenient, even though this is also equivalent to "absname !=
+    dottednpath")
+
+    >>> localmods = {'foo.__init__': True, 'foo.foo1': True,
+    ...              'foo.bar.__init__': True, 'foo.bar.bar1': True,
+    ...              'baz.__init__': True, 'baz.baz1': True }
+    >>> fromlocal = fromlocalfunc('foo.xxx', localmods)
+    >>> # relative
+    >>> fromlocal('foo1')
+    ('foo.foo1', 'foo.foo1', False)
+    >>> fromlocal('bar')
+    ('foo.bar', 'foo.bar.__init__', True)
+    >>> fromlocal('bar.bar1')
+    ('foo.bar.bar1', 'foo.bar.bar1', False)
+    >>> # absolute
+    >>> fromlocal('baz')
+    ('baz', 'baz.__init__', True)
+    >>> fromlocal('baz.baz1')
+    ('baz.baz1', 'baz.baz1', False)
+    >>> # unknown = maybe standard library
+    >>> fromlocal('os')
+    False
+    """
+    prefix = '.'.join(modulename.split('.')[:-1])
+    if prefix:
+        prefix += '.'
+    def fromlocal(name):
+        # check relative name at first
+        for n in prefix + name, name:
+            if n in localmods:
+                return (n, n, False)
+            dottedpath = n + '.__init__'
+            if dottedpath in localmods:
+                return (n, dottedpath, True)
+        return False
+    return fromlocal
 
 def list_stdlib_modules():
     """List the modules present in the stdlib.
@@ -104,38 +170,94 @@
 
 stdlib_modules = set(list_stdlib_modules())
 
-def imported_modules(source, ignore_nested=False):
+def imported_modules(source, modulename, localmods, ignore_nested=False):
     """Given the source of a file as a string, yield the names
     imported by that file.
 
     Args:
       source: The python source to examine as a string.
+      modulename: of specified python source (may have `__init__`)
+      localmods: dict of locally defined module names (may have `__init__`)
       ignore_nested: If true, import statements that do not start in
                      column zero will be ignored.
 
     Returns:
-      A list of module names imported by the given source.
+      A list of absolute module names imported by the given source.
 
+    >>> modulename = 'foo.xxx'
+    >>> localmods = {'foo.__init__': True,
+    ...              'foo.foo1': True, 'foo.foo2': True,
+    ...              'foo.bar.__init__': True, 'foo.bar.bar1': True,
+    ...              'baz.__init__': True, 'baz.baz1': True }
+    >>> # standard library (= not locally defined ones)
+    >>> sorted(imported_modules(
+    ...        'from stdlib1 import foo, bar; import stdlib2',
+    ...        modulename, localmods))
+    []
+    >>> # relative importing
     >>> sorted(imported_modules(
-    ...         'import foo ; from baz import bar; import foo.qux'))
-    ['baz.bar', 'foo', 'foo.qux']
+    ...        'import foo1; from bar import bar1',
+    ...        modulename, localmods))
+    ['foo.bar.__init__', 'foo.bar.bar1', 'foo.foo1']
+    >>> sorted(imported_modules(
+    ...        'from bar.bar1 import name1, name2, name3',
+    ...        modulename, localmods))
+    ['foo.bar.bar1']
+    >>> # absolute importing
+    >>> sorted(imported_modules(
+    ...        'from baz import baz1, name1',
+    ...        modulename, localmods))
+    ['baz.__init__', 'baz.baz1']
+    >>> # mixed importing, even though it shouldn't be recommended
+    >>> sorted(imported_modules(
+    ...        'import stdlib, foo1, baz',
+    ...        modulename, localmods))
+    ['baz.__init__', 'foo.foo1']
+    >>> # ignore_nested
     >>> sorted(imported_modules(
     ... '''import foo
     ... def wat():
     ...     import bar
-    ... ''', ignore_nested=True))
-    ['foo']
+    ... ''', modulename, localmods))
+    ['foo.__init__', 'foo.bar.__init__']
+    >>> sorted(imported_modules(
+    ... '''import foo
+    ... def wat():
+    ...     import bar
+    ... ''', modulename, localmods, ignore_nested=True))
+    ['foo.__init__']
     """
+    fromlocal = fromlocalfunc(modulename, localmods)
     for node in ast.walk(ast.parse(source)):
         if ignore_nested and getattr(node, 'col_offset', 0) > 0:
             continue
         if isinstance(node, ast.Import):
             for n in node.names:
-                yield n.name
+                found = fromlocal(n.name)
+                if not found:
+                    # this should import standard library
+                    continue
+                yield found[1]
         elif isinstance(node, ast.ImportFrom):
-            prefix = node.module + '.'
+            found = fromlocal(node.module)
+            if not found:
+                # this should import standard library
+                continue
+
+            absname, dottedpath, hassubmod = found
+            yield dottedpath
+            if not hassubmod:
+                # examination of "node.names" should be redundant
+                # e.g.: from mercurial.node import nullid, nullrev
+                continue
+
+            prefix = absname + '.'
             for n in node.names:
-                yield prefix + n.name
+                found = fromlocal(prefix + n.name)
+                if not found:
+                    # this should be a function or a property of "node.module"
+                    continue
+                yield found[1]
 
 def verify_stdlib_on_own_line(source):
     """Given some python source, verify that stdlib imports are done
@@ -171,8 +293,6 @@
     while visit:
         path = visit.pop(0)
         for i in sorted(imports.get(path[-1], [])):
-            if i not in stdlib_modules and not i.startswith('mercurial.'):
-                i = mod.rsplit('.', 1)[0] + '.' + i
             if len(path) < shortest.get(i, 1000):
                 shortest[i] = len(path)
                 if i in path:
@@ -194,10 +314,12 @@
 def find_cycles(imports):
     """Find cycles in an already-loaded import graph.
 
-    >>> imports = {'top.foo': ['bar', 'os.path', 'qux'],
-    ...            'top.bar': ['baz', 'sys'],
-    ...            'top.baz': ['foo'],
-    ...            'top.qux': ['foo']}
+    All module names recorded in `imports` should be absolute one.
+
+    >>> imports = {'top.foo': ['top.bar', 'os.path', 'top.qux'],
+    ...            'top.bar': ['top.baz', 'sys'],
+    ...            'top.baz': ['top.foo'],
+    ...            'top.qux': ['top.foo']}
     >>> print '\\n'.join(sorted(find_cycles(imports)))
     top.bar -> top.baz -> top.foo -> top.bar
     top.foo -> top.qux -> top.foo
@@ -215,17 +337,23 @@
     return len(c), c
 
 def main(argv):
-    if len(argv) < 2:
-        print 'Usage: %s file [file] [file] ...'
+    if len(argv) < 2 or (argv[1] == '-' and len(argv) > 2):
+        print 'Usage: %s {-|file [file] [file] ...}'
         return 1
+    if argv[1] == '-':
+        argv = argv[:1]
+        argv.extend(l.rstrip() for l in sys.stdin.readlines())
+    localmods = {}
     used_imports = {}
     any_errors = False
     for source_path in argv[1:]:
+        modname = dotted_name_of_path(source_path, trimpure=True)
+        localmods[modname] = source_path
+    for modname, source_path in sorted(localmods.iteritems()):
         f = open(source_path)
-        modname = dotted_name_of_path(source_path, trimpure=True)
         src = f.read()
         used_imports[modname] = sorted(
-            imported_modules(src, ignore_nested=True))
+            imported_modules(src, modname, localmods, ignore_nested=True))
         for error in verify_stdlib_on_own_line(src):
             any_errors = True
             print source_path, error
--- a/contrib/mercurial.spec	Sun May 17 22:09:37 2015 -0400
+++ b/contrib/mercurial.spec	Tue May 19 07:17:57 2015 -0500
@@ -37,8 +37,8 @@
 %if "%{?withpython}"
 BuildRequires: readline-devel, openssl-devel, ncurses-devel, zlib-devel, bzip2-devel
 %else
-BuildRequires: python >= 2.4, python-devel, python-docutils >= 0.5
-Requires: python >= 2.4
+BuildRequires: python >= 2.6, python-devel, python-docutils >= 0.5
+Requires: python >= 2.6
 %endif
 # The hgk extension uses the wish tcl interpreter, but we don't enforce it
 #Requires: tk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/packagelib.sh	Tue May 19 07:17:57 2015 -0500
@@ -0,0 +1,19 @@
+gethgversion() {
+    make clean
+    make local || make local PURE=--pure
+    HG="$PWD/hg"
+
+    $HG version > /dev/null || { echo 'abort: hg version failed!'; exit 1 ; }
+
+    hgversion=`$HG version | sed -ne 's/.*(version \(.*\))$/\1/p'`
+
+    if echo $hgversion | grep -- '-' > /dev/null 2>&1; then
+        # nightly build case, version is like 1.3.1+250-20b91f91f9ca
+        version=`echo $hgversion | cut -d- -f1`
+        release=`echo $hgversion | cut -d- -f2 | sed -e 's/+.*//'`
+    else
+        # official tag, version is like 1.3.1
+        version=`echo $hgversion | sed -e 's/+.*//'`
+        release='0'
+    fi
+}
--- a/contrib/revsetbenchmarks.txt	Sun May 17 22:09:37 2015 -0400
+++ b/contrib/revsetbenchmarks.txt	Tue May 19 07:17:57 2015 -0500
@@ -17,6 +17,7 @@
 # those two `roots(...)` inputs are close to what phase movement use.
 roots((tip~100::) - (tip~100::tip))
 roots((0::) - (0::tip))
+42:68 and roots(42:tip)
 ::p1(p1(tip))::
 public()
 :10000 and public()
--- a/contrib/synthrepo.py	Sun May 17 22:09:37 2015 -0400
+++ b/contrib/synthrepo.py	Tue May 19 07:17:57 2015 -0500
@@ -41,6 +41,10 @@
 from mercurial.i18n import _
 from mercurial.node import nullrev, nullid, short
 
+# 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'
 
 cmdtable = {}
--- a/contrib/wix/guids.wxi	Sun May 17 22:09:37 2015 -0400
+++ b/contrib/wix/guids.wxi	Tue May 19 07:17:57 2015 -0500
@@ -28,6 +28,7 @@
   <?define templates.atom.guid = {D30E14A5-8AF0-4268-8B00-00BEE9E09E39} ?>
   <?define templates.coal.guid = {B63CCAAB-4EAF-43b4-901E-4BD13F5B78FC} ?>
   <?define templates.gitweb.guid = {827334AF-1EFD-421B-962C-5660A068F612} ?>
+  <?define templates.json.guid = {F535BE7A-EC34-46E0-B9BE-013F3DBAFB19} ?>
   <?define templates.monoblue.guid = {8060A1E4-BD4C-453E-92CB-9536DC44A9E3} ?>
   <?define templates.paper.guid = {61AB1DE9-645F-46ED-8AF8-0CF02267FFBB} ?>
   <?define templates.raw.guid = {834DF8D7-9784-43A6-851D-A96CE1B3575B} ?>
--- a/contrib/wix/templates.wxs	Sun May 17 22:09:37 2015 -0400
+++ b/contrib/wix/templates.wxs	Tue May 19 07:17:57 2015 -0500
@@ -12,6 +12,7 @@
       <ComponentRef Id="templates.atom" />
       <ComponentRef Id="templates.coal" />
       <ComponentRef Id="templates.gitweb" />
+      <ComponentRef Id="templates.json" />
       <ComponentRef Id="templates.monoblue" />
       <ComponentRef Id="templates.paper" />
       <ComponentRef Id="templates.raw" />
@@ -36,6 +37,13 @@
           <File Name="map-cmdline.phases" />
         </Component>
 
+        <Directory Id="templates.jsondir" Name="json">
+          <Component Id="templates.json" Guid="$(var.templates.json.guid)" Win64='$(var.IsX64)'>
+            <File Id="json.changelist.tmpl" Name="changelist.tmpl" KeyPath="yes" />
+            <File Id="json.map"             Name="map" />
+          </Component>
+        </Directory>
+
         <Directory Id="templates.atomdir" Name="atom">
           <Component Id="templates.atom" Guid="$(var.templates.atom.guid)" Win64='$(var.IsX64)'>
             <File Id="atom.changelog.tmpl"      Name="changelog.tmpl" KeyPath="yes" />
--- a/hgext/acl.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/acl.py	Tue May 19 07:17:57 2015 -0500
@@ -195,6 +195,10 @@
 from mercurial import util, match
 import getpass, urllib
 
+# 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'
 
 def _getusers(ui, group):
--- a/hgext/blackbox.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/blackbox.py	Tue May 19 07:17:57 2015 -0500
@@ -35,6 +35,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 lastblackbox = None
 
--- a/hgext/bugzilla.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/bugzilla.py	Tue May 19 07:17:57 2015 -0500
@@ -279,9 +279,13 @@
 
 from mercurial.i18n import _
 from mercurial.node import short
-from mercurial import cmdutil, mail, templater, util
+from mercurial import cmdutil, mail, util
 import re, time, urlparse, xmlrpclib
 
+# 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'
 
 class bzaccess(object):
@@ -876,8 +880,6 @@
         if not mapfile and not tmpl:
             tmpl = _('changeset {node|short} in repo {root} refers '
                      'to bug {bug}.\ndetails:\n\t{desc|tabindent}')
-        if tmpl:
-            tmpl = templater.parsestring(tmpl, quoted=False)
         t = cmdutil.changeset_templater(self.ui, self.repo,
                                         False, None, tmpl, mapfile, False)
         self.ui.pushbuffer()
--- a/hgext/censor.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/censor.py	Tue May 19 07:17:57 2015 -0500
@@ -31,6 +31,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 @command('censor',
--- a/hgext/children.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/children.py	Tue May 19 07:17:57 2015 -0500
@@ -20,6 +20,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 @command('children',
--- a/hgext/churn.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/churn.py	Tue May 19 07:17:57 2015 -0500
@@ -9,17 +9,20 @@
 '''command to display statistics about repository history'''
 
 from mercurial.i18n import _
-from mercurial import patch, cmdutil, scmutil, util, templater, commands
+from mercurial import patch, cmdutil, scmutil, util, commands
 from mercurial import encoding
 import os
 import time, datetime
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 def maketemplater(ui, repo, tmpl):
-    tmpl = templater.parsestring(tmpl, quoted=False)
     try:
         t = cmdutil.changeset_templater(ui, repo, False, None, tmpl,
                                         None, False)
--- a/hgext/color.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/color.py	Tue May 19 07:17:57 2015 -0500
@@ -162,6 +162,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 # start and stop parameters for effects
--- a/hgext/convert/__init__.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/convert/__init__.py	Tue May 19 07:17:57 2015 -0500
@@ -15,6 +15,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 # Commands definition was moved elsewhere to ease demandload job.
--- a/hgext/convert/filemap.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/convert/filemap.py	Tue May 19 07:17:57 2015 -0500
@@ -332,7 +332,7 @@
             mp1 = self.parentmap[p1]
             if mp1 == SKIPREV or mp1 in knownparents:
                 continue
-            isancestor = util.any(p2 for p2 in parents
+            isancestor = any(p2 for p2 in parents
                                   if p1 != p2 and mp1 != self.parentmap[p2]
                                   and mp1 in self.wantedancestors[p2])
             if not isancestor and not hasbranchparent and len(parents) > 1:
--- a/hgext/eol.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/eol.py	Tue May 19 07:17:57 2015 -0500
@@ -95,6 +95,10 @@
 from mercurial import util, config, extensions, match, error
 import re, os
 
+# 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'
 
 # Matches a lone LF, i.e., one that is not part of CRLF.
--- a/hgext/extdiff.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/extdiff.py	Tue May 19 07:17:57 2015 -0500
@@ -67,6 +67,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 def snapshot(ui, repo, files, node, tmproot):
--- a/hgext/factotum.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/factotum.py	Tue May 19 07:17:57 2015 -0500
@@ -67,21 +67,20 @@
     while True:
         fd = os.open('%s/rpc' % _mountpoint, os.O_RDWR)
         try:
-            try:
-                os.write(fd, 'start %s' % params)
-                l = os.read(fd, ERRMAX).split()
-                if l[0] == 'ok':
-                    os.write(fd, 'read')
-                    status, user, passwd = os.read(fd, ERRMAX).split(None, 2)
-                    if status == 'ok':
-                        if passwd.startswith("'"):
-                            if passwd.endswith("'"):
-                                passwd = passwd[1:-1].replace("''", "'")
-                            else:
-                                raise util.Abort(_('malformed password string'))
-                        return (user, passwd)
-            except (OSError, IOError):
-                raise util.Abort(_('factotum not responding'))
+            os.write(fd, 'start %s' % params)
+            l = os.read(fd, ERRMAX).split()
+            if l[0] == 'ok':
+                os.write(fd, 'read')
+                status, user, passwd = os.read(fd, ERRMAX).split(None, 2)
+                if status == 'ok':
+                    if passwd.startswith("'"):
+                        if passwd.endswith("'"):
+                            passwd = passwd[1:-1].replace("''", "'")
+                        else:
+                            raise util.Abort(_('malformed password string'))
+                    return (user, passwd)
+        except (OSError, IOError):
+            raise util.Abort(_('factotum not responding'))
         finally:
             os.close(fd)
         getkey(self, params)
--- a/hgext/fetch.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/fetch.py	Tue May 19 07:17:57 2015 -0500
@@ -15,6 +15,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 @command('fetch',
--- a/hgext/gpg.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/gpg.py	Tue May 19 07:17:57 2015 -0500
@@ -12,6 +12,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 class gpg(object):
@@ -255,7 +259,7 @@
 
     if not opts["force"]:
         msigs = match.exact(repo.root, '', ['.hgsigs'])
-        if util.any(repo.status(match=msigs, unknown=True, ignored=True)):
+        if any(repo.status(match=msigs, unknown=True, ignored=True)):
             raise util.Abort(_("working copy of .hgsigs is changed "),
                              hint=_("please commit .hgsigs manually"))
 
--- a/hgext/graphlog.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/graphlog.py	Tue May 19 07:17:57 2015 -0500
@@ -20,6 +20,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 @command('glog',
--- a/hgext/hgcia.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/hgcia.py	Tue May 19 07:17:57 2015 -0500
@@ -43,11 +43,15 @@
 
 from mercurial.i18n import _
 from mercurial.node import bin, short
-from mercurial import cmdutil, patch, templater, util, mail
+from mercurial import cmdutil, patch, util, mail
 import email.Parser
 
 import socket, xmlrpclib
 from xml.sax import saxutils
+# 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'
 
 socket_timeout = 30 # seconds
@@ -206,7 +210,6 @@
                 template = self.dstemplate
             else:
                 template = self.deftemplate
-        template = templater.parsestring(template, quoted=False)
         t = cmdutil.changeset_templater(self.ui, self.repo, False, None,
                                         template, style, False)
         self.templater = t
--- a/hgext/hgk.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/hgk.py	Tue May 19 07:17:57 2015 -0500
@@ -41,6 +41,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 @command('debug-diff-tree',
--- a/hgext/highlight/__init__.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/highlight/__init__.py	Tue May 19 07:17:57 2015 -0500
@@ -24,6 +24,10 @@
 import highlight
 from mercurial.hgweb import webcommands, webutil, common
 from mercurial import extensions, encoding
+# 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'
 
 def filerevision_highlight(orig, web, tmpl, fctx):
--- a/hgext/histedit.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/histedit.py	Tue May 19 07:17:57 2015 -0500
@@ -181,6 +181,10 @@
 cmdtable = {}
 command = cmdutil.command(cmdtable)
 
+# 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'
 
 # i18n: command names and abbreviations must remain untranslated
@@ -707,15 +711,15 @@
     if force and not outg:
         raise util.Abort(_('--force only allowed with --outgoing'))
     if cont:
-        if util.any((outg, abort, revs, freeargs, rules, editplan)):
+        if any((outg, abort, revs, freeargs, rules, editplan)):
             raise util.Abort(_('no arguments allowed with --continue'))
         goal = 'continue'
     elif abort:
-        if util.any((outg, revs, freeargs, rules, editplan)):
+        if any((outg, revs, freeargs, rules, editplan)):
             raise util.Abort(_('no arguments allowed with --abort'))
         goal = 'abort'
     elif editplan:
-        if util.any((outg, revs, freeargs)):
+        if any((outg, revs, freeargs)):
             raise util.Abort(_('only --commands argument allowed with '
                                '--edit-plan'))
         goal = 'edit-plan'
--- a/hgext/keyword.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/keyword.py	Tue May 19 07:17:57 2015 -0500
@@ -83,7 +83,7 @@
 '''
 
 from mercurial import commands, context, cmdutil, dispatch, filelog, extensions
-from mercurial import localrepo, match, patch, templatefilters, templater, util
+from mercurial import localrepo, match, patch, templatefilters, util
 from mercurial import scmutil, pathutil
 from mercurial.hgweb import webcommands
 from mercurial.i18n import _
@@ -91,6 +91,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 # hg commands that do not act on keywords
@@ -191,8 +195,7 @@
 
         kwmaps = self.ui.configitems('keywordmaps')
         if kwmaps: # override default templates
-            self.templates = dict((k, templater.parsestring(v, False))
-                                  for k, v in kwmaps)
+            self.templates = dict(kwmaps)
         else:
             self.templates = _defaultkwmaps(self.ui)
 
@@ -457,9 +460,7 @@
     repo.commit(text=msg)
     ui.status(_('\n\tkeywords expanded\n'))
     ui.write(repo.wread(fn))
-    for root, dirs, files in os.walk(tmpdir):
-        for f in files:
-            util.unlinkpath(repo.vfs.reljoin(root, f))
+    repo.wvfs.rmtree(repo.root)
 
 @command('kwexpand',
     commands.walkopts,
--- a/hgext/largefiles/__init__.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/largefiles/__init__.py	Tue May 19 07:17:57 2015 -0500
@@ -112,6 +112,10 @@
 import reposetup
 import uisetup as uisetupmod
 
+# 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'
 
 reposetup = reposetup.reposetup
--- a/hgext/largefiles/lfutil.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/largefiles/lfutil.py	Tue May 19 07:17:57 2015 -0500
@@ -364,10 +364,10 @@
 
 def islfilesrepo(repo):
     if ('largefiles' in repo.requirements and
-            util.any(shortnameslash in f[0] for f in repo.store.datafiles())):
+            any(shortnameslash in f[0] for f in repo.store.datafiles())):
         return True
 
-    return util.any(openlfdirstate(repo.ui, repo, False))
+    return any(openlfdirstate(repo.ui, repo, False))
 
 class storeprotonotcapable(Exception):
     def __init__(self, storetypes):
--- a/hgext/largefiles/overrides.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/largefiles/overrides.py	Tue May 19 07:17:57 2015 -0500
@@ -27,7 +27,7 @@
     m = copy.copy(match)
     lfile = lambda f: lfutil.standin(f) in manifest
     m._files = filter(lfile, m._files)
-    m._fmap = set(m._files)
+    m._fileroots = set(m._files)
     m._always = False
     origmatchfn = m.matchfn
     m.matchfn = lambda f: lfile(f) and origmatchfn(f)
@@ -42,7 +42,7 @@
     notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
             manifest or f in excluded)
     m._files = filter(notlfile, m._files)
-    m._fmap = set(m._files)
+    m._fileroots = set(m._files)
     m._always = False
     origmatchfn = m.matchfn
     m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
@@ -358,7 +358,7 @@
                     and repo.wvfs.isdir(standin):
                 m._files.append(standin)
 
-        m._fmap = set(m._files)
+        m._fileroots = set(m._files)
         m._always = False
         origmatchfn = m.matchfn
         def lfmatchfn(f):
@@ -578,14 +578,13 @@
     nolfiles = False
     installnormalfilesmatchfn(repo[None].manifest())
     try:
-        try:
-            result = orig(ui, repo, pats, opts, rename)
-        except util.Abort, e:
-            if str(e) != _('no files to copy'):
-                raise e
-            else:
-                nonormalfiles = True
-            result = 0
+        result = orig(ui, repo, pats, opts, rename)
+    except util.Abort, e:
+        if str(e) != _('no files to copy'):
+            raise e
+        else:
+            nonormalfiles = True
+        result = 0
     finally:
         restorematchfn()
 
@@ -608,86 +607,85 @@
             os.makedirs(makestandin(dest))
 
     try:
-        try:
-            # When we call orig below it creates the standins but we don't add
-            # them to the dir state until later so lock during that time.
-            wlock = repo.wlock()
+        # When we call orig below it creates the standins but we don't add
+        # them to the dir state until later so lock during that time.
+        wlock = repo.wlock()
 
-            manifest = repo[None].manifest()
-            def overridematch(ctx, pats=[], opts={}, globbed=False,
-                    default='relpath'):
-                newpats = []
-                # The patterns were previously mangled to add the standin
-                # directory; we need to remove that now
-                for pat in pats:
-                    if match_.patkind(pat) is None and lfutil.shortname in pat:
-                        newpats.append(pat.replace(lfutil.shortname, ''))
-                    else:
-                        newpats.append(pat)
-                match = oldmatch(ctx, newpats, opts, globbed, default)
-                m = copy.copy(match)
-                lfile = lambda f: lfutil.standin(f) in manifest
-                m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
-                m._fmap = set(m._files)
-                origmatchfn = m.matchfn
-                m.matchfn = lambda f: (lfutil.isstandin(f) and
-                                    (f in manifest) and
-                                    origmatchfn(lfutil.splitstandin(f)) or
-                                    None)
-                return m
-            oldmatch = installmatchfn(overridematch)
-            listpats = []
+        manifest = repo[None].manifest()
+        def overridematch(ctx, pats=[], opts={}, globbed=False,
+                default='relpath'):
+            newpats = []
+            # The patterns were previously mangled to add the standin
+            # directory; we need to remove that now
             for pat in pats:
-                if match_.patkind(pat) is not None:
-                    listpats.append(pat)
+                if match_.patkind(pat) is None and lfutil.shortname in pat:
+                    newpats.append(pat.replace(lfutil.shortname, ''))
                 else:
-                    listpats.append(makestandin(pat))
+                    newpats.append(pat)
+            match = oldmatch(ctx, newpats, opts, globbed, default)
+            m = copy.copy(match)
+            lfile = lambda f: lfutil.standin(f) in manifest
+            m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
+            m._fileroots = set(m._files)
+            origmatchfn = m.matchfn
+            m.matchfn = lambda f: (lfutil.isstandin(f) and
+                                (f in manifest) and
+                                origmatchfn(lfutil.splitstandin(f)) or
+                                None)
+            return m
+        oldmatch = installmatchfn(overridematch)
+        listpats = []
+        for pat in pats:
+            if match_.patkind(pat) is not None:
+                listpats.append(pat)
+            else:
+                listpats.append(makestandin(pat))
 
-            try:
-                origcopyfile = util.copyfile
-                copiedfiles = []
-                def overridecopyfile(src, dest):
-                    if (lfutil.shortname in src and
-                        dest.startswith(repo.wjoin(lfutil.shortname))):
-                        destlfile = dest.replace(lfutil.shortname, '')
-                        if not opts['force'] and os.path.exists(destlfile):
-                            raise IOError('',
-                                _('destination largefile already exists'))
-                    copiedfiles.append((src, dest))
-                    origcopyfile(src, dest)
-
-                util.copyfile = overridecopyfile
-                result += orig(ui, repo, listpats, opts, rename)
-            finally:
-                util.copyfile = origcopyfile
-
-            lfdirstate = lfutil.openlfdirstate(ui, repo)
-            for (src, dest) in copiedfiles:
+        try:
+            origcopyfile = util.copyfile
+            copiedfiles = []
+            def overridecopyfile(src, dest):
                 if (lfutil.shortname in src and
                     dest.startswith(repo.wjoin(lfutil.shortname))):
-                    srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
-                    destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
-                    destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
-                    if not os.path.isdir(destlfiledir):
-                        os.makedirs(destlfiledir)
-                    if rename:
-                        os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
+                    destlfile = dest.replace(lfutil.shortname, '')
+                    if not opts['force'] and os.path.exists(destlfile):
+                        raise IOError('',
+                            _('destination largefile already exists'))
+                copiedfiles.append((src, dest))
+                origcopyfile(src, dest)
+
+            util.copyfile = overridecopyfile
+            result += orig(ui, repo, listpats, opts, rename)
+        finally:
+            util.copyfile = origcopyfile
 
-                        # The file is gone, but this deletes any empty parent
-                        # directories as a side-effect.
-                        util.unlinkpath(repo.wjoin(srclfile), True)
-                        lfdirstate.remove(srclfile)
-                    else:
-                        util.copyfile(repo.wjoin(srclfile),
-                                      repo.wjoin(destlfile))
+        lfdirstate = lfutil.openlfdirstate(ui, repo)
+        for (src, dest) in copiedfiles:
+            if (lfutil.shortname in src and
+                dest.startswith(repo.wjoin(lfutil.shortname))):
+                srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
+                destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
+                destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
+                if not os.path.isdir(destlfiledir):
+                    os.makedirs(destlfiledir)
+                if rename:
+                    os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
 
-                    lfdirstate.add(destlfile)
-            lfdirstate.write()
-        except util.Abort, e:
-            if str(e) != _('no files to copy'):
-                raise e
-            else:
-                nolfiles = True
+                    # The file is gone, but this deletes any empty parent
+                    # directories as a side-effect.
+                    util.unlinkpath(repo.wjoin(srclfile), True)
+                    lfdirstate.remove(srclfile)
+                else:
+                    util.copyfile(repo.wjoin(srclfile),
+                                  repo.wjoin(destlfile))
+
+                lfdirstate.add(destlfile)
+        lfdirstate.write()
+    except util.Abort, e:
+        if str(e) != _('no files to copy'):
+            raise e
+        else:
+            nolfiles = True
     finally:
         restorematchfn()
         wlock.release()
@@ -744,7 +742,7 @@
                 return f
             m._files = [tostandin(f) for f in m._files]
             m._files = [f for f in m._files if f is not None]
-            m._fmap = set(m._files)
+            m._fileroots = set(m._files)
             origmatchfn = m.matchfn
             def matchfn(f):
                 if lfutil.isstandin(f):
@@ -985,7 +983,7 @@
     for subpath in sorted(ctx.substate):
         sub = ctx.sub(subpath)
         submatch = match_.narrowmatcher(subpath, match)
-        sub.archive(archiver, os.path.join(prefix, repo._path) + '/', submatch)
+        sub.archive(archiver, prefix + repo._path + '/', submatch)
 
 # If a largefile is modified, the change is not reflected in its
 # standin until a commit. cmdutil.bailifchanged() raises an exception
--- a/hgext/largefiles/proto.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/largefiles/proto.py	Tue May 19 07:17:57 2015 -0500
@@ -31,17 +31,16 @@
     tmpfp = util.atomictempfile(path, createmode=repo.store.createmode)
 
     try:
-        try:
-            proto.getfile(tmpfp)
-            tmpfp._fp.seek(0)
-            if sha != lfutil.hexsha1(tmpfp._fp):
-                raise IOError(0, _('largefile contents do not match hash'))
-            tmpfp.close()
-            lfutil.linktousercache(repo, sha)
-        except IOError, e:
-            repo.ui.warn(_('largefiles: failed to put %s into store: %s\n') %
-                         (sha, e.strerror))
-            return wireproto.pushres(1)
+        proto.getfile(tmpfp)
+        tmpfp._fp.seek(0)
+        if sha != lfutil.hexsha1(tmpfp._fp):
+            raise IOError(0, _('largefile contents do not match hash'))
+        tmpfp.close()
+        lfutil.linktousercache(repo, sha)
+    except IOError, e:
+        repo.ui.warn(_('largefiles: failed to put %s into store: %s\n') %
+                     (sha, e.strerror))
+        return wireproto.pushres(1)
     finally:
         tmpfp.discard()
 
--- a/hgext/largefiles/remotestore.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/largefiles/remotestore.py	Tue May 19 07:17:57 2015 -0500
@@ -36,13 +36,12 @@
         self.ui.debug('remotestore: sendfile(%s, %s)\n' % (filename, hash))
         fd = None
         try:
-            try:
-                fd = lfutil.httpsendfile(self.ui, filename)
-            except IOError, e:
-                raise util.Abort(
-                    _('remotestore: could not open file %s: %s')
-                    % (filename, str(e)))
+            fd = lfutil.httpsendfile(self.ui, filename)
             return self._put(hash, fd)
+        except IOError, e:
+            raise util.Abort(
+                _('remotestore: could not open file %s: %s')
+                % (filename, str(e)))
         finally:
             if fd:
                 fd.close()
--- a/hgext/largefiles/reposetup.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/largefiles/reposetup.py	Tue May 19 07:17:57 2015 -0500
@@ -365,7 +365,7 @@
     repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
 
     def checkrequireslfiles(ui, repo, **kwargs):
-        if 'largefiles' not in repo.requirements and util.any(
+        if 'largefiles' not in repo.requirements and any(
                 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
             repo.requirements.add('largefiles')
             repo._writerequirements()
--- a/hgext/mq.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/mq.py	Tue May 19 07:17:57 2015 -0500
@@ -76,6 +76,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 # force load strip extension formerly included in mq and import some utility
@@ -298,7 +302,7 @@
         self.haspatch = diffstart > 1
         self.plainmode = (plainmode or
                           '# HG changeset patch' not in self.comments and
-                          util.any(c.startswith('Date: ') or
+                          any(c.startswith('Date: ') or
                                    c.startswith('From: ')
                                    for c in self.comments))
 
@@ -376,14 +380,17 @@
         if repo.ui.configbool('mq', 'secret', False):
             phase = phases.secret
     if phase is not None:
-        backup = repo.ui.backupconfig('phases', 'new-commit')
+        phasebackup = repo.ui.backupconfig('phases', 'new-commit')
+    allowemptybackup = repo.ui.backupconfig('ui', 'allowemptycommit')
     try:
         if phase is not None:
             repo.ui.setconfig('phases', 'new-commit', phase, 'mq')
+        repo.ui.setconfig('ui', 'allowemptycommit', True)
         return repo.commit(*args, **kwargs)
     finally:
+        repo.ui.restoreconfig(allowemptybackup)
         if phase is not None:
-            repo.ui.restoreconfig(backup)
+            repo.ui.restoreconfig(phasebackup)
 
 class AbortNoCleanup(error.Abort):
     pass
@@ -807,9 +814,10 @@
     def apply(self, repo, series, list=False, update_status=True,
               strict=False, patchdir=None, merge=None, all_files=None,
               tobackup=None, keepchanges=False):
-        wlock = lock = tr = None
+        wlock = dsguard = lock = tr = None
         try:
             wlock = repo.wlock()
+            dsguard = cmdutil.dirstateguard(repo, 'mq.apply')
             lock = repo.lock()
             tr = repo.transaction("qpush")
             try:
@@ -818,21 +826,22 @@
                                   tobackup=tobackup, keepchanges=keepchanges)
                 tr.close()
                 self.savedirty()
+                dsguard.close()
                 return ret
             except AbortNoCleanup:
                 tr.close()
                 self.savedirty()
+                dsguard.close()
                 raise
             except: # re-raises
                 try:
                     tr.abort()
                 finally:
                     repo.invalidate()
-                    repo.dirstate.invalidate()
                     self.invalidate()
                 raise
         finally:
-            release(tr, lock, wlock)
+            release(tr, lock, dsguard, wlock)
             self.removeundo(repo)
 
     def _apply(self, repo, series, list=False, update_status=True,
@@ -1681,8 +1690,9 @@
 
             bmlist = repo[top].bookmarks()
 
+            dsguard = None
             try:
-                repo.dirstate.beginparentchange()
+                dsguard = cmdutil.dirstateguard(repo, 'mq.refresh')
                 if diffopts.git or diffopts.upgrade:
                     copies = {}
                     for dst in a:
@@ -1735,13 +1745,12 @@
 
                 # assumes strip can roll itself back if interrupted
                 repo.setparents(*cparents)
-                repo.dirstate.endparentchange()
                 self.applied.pop()
                 self.applieddirty = True
                 strip(self.ui, repo, [top], update=False, backup=False)
-            except: # re-raises
-                repo.dirstate.invalidate()
-                raise
+                dsguard.close()
+            finally:
+                release(dsguard)
 
             try:
                 # might be nice to attempt to roll back strip after this
--- a/hgext/notify.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/notify.py	Tue May 19 07:17:57 2015 -0500
@@ -138,9 +138,13 @@
 # load. This was not a problem on Python 2.7.
 import email.Parser
 from mercurial.i18n import _
-from mercurial import patch, cmdutil, templater, util, mail
+from mercurial import patch, cmdutil, util, mail
 import fnmatch
 
+# 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'
 
 # template for single changeset can include email headers.
@@ -190,8 +194,6 @@
                     self.ui.config('notify', 'template'))
         if not mapfile and not template:
             template = deftemplates.get(hooktype) or single_template
-        if template:
-            template = templater.parsestring(template, quoted=False)
         self.t = cmdutil.changeset_templater(self.ui, self.repo, False, None,
                                              template, mapfile, False)
 
--- a/hgext/pager.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/pager.py	Tue May 19 07:17:57 2015 -0500
@@ -59,6 +59,10 @@
 from mercurial import commands, dispatch, util, extensions, cmdutil
 from mercurial.i18n import _
 
+# 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'
 
 def _pagerfork(ui, p):
--- a/hgext/patchbomb.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/patchbomb.py	Tue May 19 07:17:57 2015 -0500
@@ -71,6 +71,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 def prompt(ui, prompt, default=None, rest=':'):
--- a/hgext/progress.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/progress.py	Tue May 19 07:17:57 2015 -0500
@@ -40,6 +40,10 @@
 import threading
 
 from mercurial.i18n import _
+# 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'
 
 from mercurial import encoding
--- a/hgext/purge.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/purge.py	Tue May 19 07:17:57 2015 -0500
@@ -30,6 +30,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 @command('purge|clean',
--- a/hgext/rebase.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/rebase.py	Tue May 19 07:17:57 2015 -0500
@@ -29,6 +29,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 def _savegraft(ctx, extra):
@@ -67,7 +71,7 @@
     ('e', 'edit', False, _('invoke editor on commit messages')),
     ('l', 'logfile', '',
      _('read collapse commit message from file'), _('FILE')),
-    ('', 'keep', False, _('keep original changesets')),
+    ('k', 'keep', False, _('keep original changesets')),
     ('', 'keepbranches', False, _('keep original branch names')),
     ('D', 'detach', False, _('(DEPRECATED)')),
     ('i', 'interactive', False, _('(DEPRECATED)')),
@@ -358,9 +362,9 @@
 
         # Keep track of the current bookmarks in order to reset them later
         currentbookmarks = repo._bookmarks.copy()
-        activebookmark = activebookmark or repo._bookmarkcurrent
+        activebookmark = activebookmark or repo._activebookmark
         if activebookmark:
-            bookmarks.unsetcurrent(repo)
+            bookmarks.deactivate(repo)
 
         extrafn = _makeextrafn(extrafns)
 
@@ -498,7 +502,7 @@
 
         if (activebookmark and
             repo['.'].node() == repo._bookmarks[activebookmark]):
-                bookmarks.setcurrent(repo, activebookmark)
+                bookmarks.activate(repo, activebookmark)
 
     finally:
         release(lock, wlock)
@@ -530,10 +534,9 @@
     '''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.'''
+    dsguard = cmdutil.dirstateguard(repo, 'rebase')
     try:
-        repo.dirstate.beginparentchange()
         repo.setparents(repo[p1].node(), repo[p2].node())
-        repo.dirstate.endparentchange()
         ctx = repo[rev]
         if commitmsg is None:
             commitmsg = ctx.description()
@@ -552,11 +555,10 @@
             repo.ui.restoreconfig(backup)
 
         repo.dirstate.setbranch(repo[newnode].branch())
+        dsguard.close()
         return newnode
-    except util.Abort:
-        # Invalidate the previous setparents
-        repo.dirstate.invalidate()
-        raise
+    finally:
+        release(dsguard)
 
 def rebasenode(repo, rev, p1, base, state, collapse, target):
     'Rebase a single revision rev on top of p1 using base as merge ancestor'
@@ -893,7 +895,7 @@
             repair.strip(repo.ui, repo, strippoints)
 
     if activebookmark and activebookmark in repo._bookmarks:
-        bookmarks.setcurrent(repo, activebookmark)
+        bookmarks.activate(repo, activebookmark)
 
     clearstatus(repo)
     repo.ui.warn(_('rebase aborted\n'))
@@ -1057,7 +1059,7 @@
                 hg.update(repo, dest)
                 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
                     ui.status(_("updating bookmark %s\n")
-                              % repo._bookmarkcurrent)
+                              % repo._activebookmark)
     else:
         if opts.get('tool'):
             raise util.Abort(_('--tool can only be used with --rebase'))
@@ -1118,4 +1120,3 @@
          _("use 'hg rebase --continue' or 'hg rebase --abort'")])
     # ensure rebased rev are not hidden
     extensions.wrapfunction(repoview, '_getdynamicblockers', _rebasedvisible)
-
--- a/hgext/record.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/record.py	Tue May 19 07:17:57 2015 -0500
@@ -13,6 +13,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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/relink.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/relink.py	Tue May 19 07:17:57 2015 -0500
@@ -13,6 +13,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 @command('relink', [], _('[ORIGIN]'))
--- a/hgext/schemes.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/schemes.py	Tue May 19 07:17:57 2015 -0500
@@ -44,6 +44,10 @@
 from mercurial import extensions, hg, templater, util
 from mercurial.i18n import _
 
+# 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/share.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/share.py	Tue May 19 07:17:57 2015 -0500
@@ -12,6 +12,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 @command('share',
--- a/hgext/shelve.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/shelve.py	Tue May 19 07:17:57 2015 -0500
@@ -21,6 +21,7 @@
 shelve".
 """
 
+import collections
 from mercurial.i18n import _
 from mercurial.node import nullid, nullrev, bin, hex
 from mercurial import changegroup, cmdutil, scmutil, phases, commands
@@ -32,6 +33,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 class shelvedfile(object):
@@ -143,7 +148,7 @@
 
         Much faster than the revset ancestors(ctx) & draft()"""
         seen = set([nullrev])
-        visit = util.deque()
+        visit = collections.deque()
         visit.append(ctx)
         while visit:
             ctx = visit.popleft()
@@ -163,7 +168,7 @@
 
     # we never need the user, so we use a generic user for all shelve operations
     user = 'shelve@localhost'
-    label = repo._bookmarkcurrent or parent.branch() or 'default'
+    label = repo._activebookmark or parent.branch() or 'default'
 
     # slashes aren't allowed in filenames, therefore we rename it
     label = label.replace('/', '_')
@@ -284,17 +289,15 @@
     """subcommand that deletes a specific shelve"""
     if not pats:
         raise util.Abort(_('no shelved changes specified!'))
-    wlock = None
+    wlock = repo.wlock()
     try:
-        wlock = repo.wlock()
-        try:
-            for name in pats:
-                for suffix in 'hg patch'.split():
-                    shelvedfile(repo, name, suffix).unlink()
-        except OSError, err:
-            if err.errno != errno.ENOENT:
-                raise
-            raise util.Abort(_("shelved change '%s' not found") % name)
+        for name in pats:
+            for suffix in 'hg patch'.split():
+                shelvedfile(repo, name, suffix).unlink()
+    except OSError, err:
+        if err.errno != errno.ENOENT:
+            raise
+        raise util.Abort(_("shelved change '%s' not found") % name)
     finally:
         lockmod.release(wlock)
 
@@ -363,6 +366,17 @@
         finally:
             fp.close()
 
+def singlepatchcmds(ui, repo, pats, opts, subcommand):
+    """subcommand that displays a single shelf"""
+    if len(pats) != 1:
+        raise util.Abort(_("--%s expects a single shelf") % subcommand)
+    shelfname = pats[0]
+
+    if not shelvedfile(repo, shelfname, 'patch').exists():
+        raise util.Abort(_("cannot find shelf %s") % shelfname)
+
+    listcmd(ui, repo, pats, opts)
+
 def checkparents(repo, state):
     """check parent while resuming an unshelve"""
     if state.parents != repo.dirstate.parents():
@@ -693,21 +707,21 @@
     cmdutil.checkunfinished(repo)
 
     allowables = [
-        ('addremove', 'create'), # 'create' is pseudo action
-        ('cleanup', 'cleanup'),
-#       ('date', 'create'), # ignored for passing '--date "0 0"' in tests
-        ('delete', 'delete'),
-        ('edit', 'create'),
-        ('list', 'list'),
-        ('message', 'create'),
-        ('name', 'create'),
-        ('patch', 'list'),
-        ('stat', 'list'),
+        ('addremove', set(['create'])), # 'create' is pseudo action
+        ('cleanup', set(['cleanup'])),
+#       ('date', set(['create'])), # ignored for passing '--date "0 0"' in tests
+        ('delete', set(['delete'])),
+        ('edit', set(['create'])),
+        ('list', set(['list'])),
+        ('message', set(['create'])),
+        ('name', set(['create'])),
+        ('patch', set(['patch', 'list'])),
+        ('stat', set(['stat', 'list'])),
     ]
     def checkopt(opt):
         if opts[opt]:
             for i, allowable in allowables:
-                if opts[i] and opt != allowable:
+                if opts[i] and opt not in allowable:
                     raise util.Abort(_("options '--%s' and '--%s' may not be "
                                        "used together") % (opt, i))
             return True
@@ -719,11 +733,11 @@
         return deletecmd(ui, repo, pats)
     elif checkopt('list'):
         return listcmd(ui, repo, pats, opts)
+    elif checkopt('patch'):
+        return singlepatchcmds(ui, repo, pats, opts, subcommand='patch')
+    elif checkopt('stat'):
+        return singlepatchcmds(ui, repo, pats, opts, subcommand='stat')
     else:
-        for i in ('patch', 'stat'):
-            if opts[i]:
-                raise util.Abort(_("option '--%s' may not be "
-                                   "used when shelving a change") % (i,))
         return createcmd(ui, repo, pats, opts)
 
 def extsetup(ui):
--- a/hgext/strip.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/strip.py	Tue May 19 07:17:57 2015 -0500
@@ -11,6 +11,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 def checksubstate(repo, baserev=None):
@@ -60,8 +64,8 @@
 
         marks = repo._bookmarks
         if bookmark:
-            if bookmark == repo._bookmarkcurrent:
-                bookmarks.unsetcurrent(repo)
+            if bookmark == repo._activebookmark:
+                bookmarks.deactivate(repo)
             del marks[bookmark]
             marks.write()
             ui.write(_("bookmark '%s' deleted\n") % bookmark)
--- a/hgext/transplant.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/transplant.py	Tue May 19 07:17:57 2015 -0500
@@ -26,6 +26,10 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+# 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'
 
 class transplantentry(object):
--- a/hgext/win32mbcs.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/win32mbcs.py	Tue May 19 07:17:57 2015 -0500
@@ -48,6 +48,10 @@
 import os, sys
 from mercurial.i18n import _
 from mercurial import util, encoding
+# 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'
 
 _encoding = None                                # see extsetup
--- a/hgext/win32text.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/win32text.py	Tue May 19 07:17:57 2015 -0500
@@ -46,6 +46,10 @@
 from mercurial import util
 import re
 
+# 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'
 
 # regexp for single LF without CR preceding.
--- a/hgext/zeroconf/__init__.py	Sun May 17 22:09:37 2015 -0400
+++ b/hgext/zeroconf/__init__.py	Tue May 19 07:17:57 2015 -0500
@@ -31,6 +31,10 @@
 from mercurial import extensions
 from mercurial.hgweb import server as servermod
 
+# 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'
 
 # publish
--- a/mercurial/ancestor.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/ancestor.py	Tue May 19 07:17:57 2015 -0500
@@ -5,8 +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.
 
+import collections
 import heapq
-import util
 from node import nullrev
 
 def commonancestorsheads(pfunc, *nodes):
@@ -314,7 +314,7 @@
 
         parentrevs = self._parentrevs
         stoprev = self._stoprev
-        visit = util.deque(revs)
+        visit = collections.deque(revs)
 
         while visit:
             for parent in parentrevs(visit.popleft()):
--- a/mercurial/archival.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/archival.py	Tue May 19 07:17:57 2015 -0500
@@ -37,6 +37,10 @@
     prefix = util.pconvert(lpfx)
     if not prefix.endswith('/'):
         prefix += '/'
+    # Drop the leading '.' path component if present, so Windows can read the
+    # zip files (issue4634)
+    if prefix.startswith('./'):
+        prefix = prefix[2:]
     if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
         raise util.Abort(_('archive prefix contains illegal components'))
     return prefix
@@ -50,7 +54,7 @@
 
 def guesskind(dest):
     for kind, extensions in exts.iteritems():
-        if util.any(dest.endswith(ext) for ext in extensions):
+        if any(dest.endswith(ext) for ext in extensions):
             return kind
     return None
 
--- a/mercurial/bookmarks.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/bookmarks.py	Tue May 19 07:17:57 2015 -0500
@@ -8,7 +8,7 @@
 import os
 from mercurial.i18n import _
 from mercurial.node import hex, bin
-from mercurial import encoding, error, util, obsolete, lock as lockmod
+from mercurial import encoding, util, obsolete, lock as lockmod
 import errno
 
 class bmstore(dict):
@@ -22,7 +22,7 @@
     {hash}\s{name}\n (the same format as localtags) in
     .hg/bookmarks. The mapping is stored as {name: nodeid}.
 
-    This class does NOT handle the "current" bookmark state at this
+    This class does NOT handle the "active" bookmark state at this
     time.
     """
 
@@ -83,8 +83,8 @@
 
     def _writerepo(self, repo):
         """Factored out for extensibility"""
-        if repo._bookmarkcurrent not in self:
-            unsetcurrent(repo)
+        if repo._activebookmark not in self:
+            deactivate(repo)
 
         wlock = repo.wlock()
         try:
@@ -106,13 +106,12 @@
         for name, node in self.iteritems():
             fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
 
-def readcurrent(repo):
-    '''Get the current bookmark
-
-    If we use gittish branches we have a current bookmark that
-    we are on. This function returns the name of the bookmark. It
-    is stored in .hg/bookmarks.current
-    '''
+def readactive(repo):
+    """
+    Get the active bookmark. We can have an active bookmark that updates
+    itself as we commit. This function returns the name of that bookmark.
+    It is stored in .hg/bookmarks.current
+    """
     mark = None
     try:
         file = repo.vfs('bookmarks.current')
@@ -129,17 +128,17 @@
         file.close()
     return mark
 
-def setcurrent(repo, mark):
-    '''Set the name of the bookmark that we are currently on
-
-    Set the name of the bookmark that we are on (hg update <bookmark>).
+def activate(repo, mark):
+    """
+    Set the given bookmark to be 'active', meaning that this bookmark will
+    follow new commits that are made.
     The name is recorded in .hg/bookmarks.current
-    '''
+    """
     if mark not in repo._bookmarks:
         raise AssertionError('bookmark %s does not exist!' % mark)
 
-    current = repo._bookmarkcurrent
-    if current == mark:
+    active = repo._activebookmark
+    if active == mark:
         return
 
     wlock = repo.wlock()
@@ -149,42 +148,36 @@
         file.close()
     finally:
         wlock.release()
-    repo._bookmarkcurrent = mark
+    repo._activebookmark = mark
 
-def unsetcurrent(repo):
+def deactivate(repo):
+    """
+    Unset the active bookmark in this reposiotry.
+    """
     wlock = repo.wlock()
     try:
-        try:
-            repo.vfs.unlink('bookmarks.current')
-            repo._bookmarkcurrent = None
-        except OSError, inst:
-            if inst.errno != errno.ENOENT:
-                raise
+        repo.vfs.unlink('bookmarks.current')
+        repo._activebookmark = None
+    except OSError, inst:
+        if inst.errno != errno.ENOENT:
+            raise
     finally:
         wlock.release()
 
-def iscurrent(repo, mark=None, parents=None):
-    '''Tell whether the current bookmark is also active
+def isactivewdirparent(repo):
+    """
+    Tell whether the 'active' bookmark (the one that follows new commits)
+    points to one of the parents of the current working directory (wdir).
 
-    I.e., the bookmark listed in .hg/bookmarks.current also points to a
-    parent of the working directory.
-    '''
-    if not mark:
-        mark = repo._bookmarkcurrent
-    if not parents:
-        parents = [p.node() for p in repo[None].parents()]
+    While this is normally the case, it can on occasion be false; for example,
+    immediately after a pull, the active bookmark can be moved to point
+    to a place different than the wdir. This is solved by running `hg update`.
+    """
+    mark = repo._activebookmark
     marks = repo._bookmarks
+    parents = [p.node() for p in repo[None].parents()]
     return (mark in marks and marks[mark] in parents)
 
-def updatecurrentbookmark(repo, oldnode, curbranch):
-    try:
-        return update(repo, oldnode, repo.branchtip(curbranch))
-    except error.RepoLookupError:
-        if curbranch == "default": # no default branch!
-            return update(repo, oldnode, repo.lookup("tip"))
-        else:
-            raise util.Abort(_("branch %s not found") % curbranch)
-
 def deletedivergent(repo, deletefrom, bm):
     '''Delete divergent versions of bm on nodes in deletefrom.
 
@@ -207,33 +200,33 @@
     check out and where to move the active bookmark from, if needed.'''
     movemarkfrom = None
     if checkout is None:
-        curmark = repo._bookmarkcurrent
-        if iscurrent(repo):
+        activemark = repo._activebookmark
+        if isactivewdirparent(repo):
             movemarkfrom = repo['.'].node()
-        elif curmark:
-            ui.status(_("updating to active bookmark %s\n") % curmark)
-            checkout = curmark
+        elif activemark:
+            ui.status(_("updating to active bookmark %s\n") % activemark)
+            checkout = activemark
     return (checkout, movemarkfrom)
 
 def update(repo, parents, node):
     deletefrom = parents
     marks = repo._bookmarks
     update = False
-    cur = repo._bookmarkcurrent
-    if not cur:
+    active = repo._activebookmark
+    if not active:
         return False
 
-    if marks[cur] in parents:
+    if marks[active] in parents:
         new = repo[node]
         divs = [repo[b] for b in marks
-                if b.split('@', 1)[0] == cur.split('@', 1)[0]]
+                if b.split('@', 1)[0] == active.split('@', 1)[0]]
         anc = repo.changelog.ancestors([new.rev()])
         deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
-        if validdest(repo, repo[marks[cur]], new):
-            marks[cur] = new.node()
+        if validdest(repo, repo[marks[active]], new):
+            marks[active] = new.node()
             update = True
 
-    if deletedivergent(repo, deletefrom, cur):
+    if deletedivergent(repo, deletefrom, active):
         update = True
 
     if update:
--- a/mercurial/bundle2.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/bundle2.py	Tue May 19 07:17:57 2015 -0500
@@ -315,7 +315,7 @@
     try:
         for part in iterparts:
             _processpart(op, part)
-    except Exception, exc:
+    except BaseException, exc:
         for part in iterparts:
             # consume the bundle content
             part.seek(0, 2)
@@ -762,7 +762,7 @@
             for chunk in self._payloadchunks():
                 yield _pack(_fpayloadsize, len(chunk))
                 yield chunk
-        except Exception, exc:
+        except BaseException, exc:
             # backup exception data for later
             exc_info = sys.exc_info()
             msg = 'unexpected error: %s' % exc
--- a/mercurial/bundlerepo.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/bundlerepo.py	Tue May 19 07:17:57 2015 -0500
@@ -177,11 +177,10 @@
         return manifest.manifest.revision(self, nodeorrev)
 
 class bundlefilelog(bundlerevlog, filelog.filelog):
-    def __init__(self, opener, path, bundle, linkmapper, repo):
+    def __init__(self, opener, path, bundle, linkmapper):
         filelog.filelog.__init__(self, opener, path)
         bundlerevlog.__init__(self, opener, self.indexfile, bundle,
                               linkmapper)
-        self._repo = repo
 
     def baserevision(self, nodeorrev):
         return filelog.filelog.revision(self, nodeorrev)
@@ -322,8 +321,7 @@
 
         if f in self.bundlefilespos:
             self.bundle.seek(self.bundlefilespos[f])
-            return bundlefilelog(self.svfs, f, self.bundle,
-                                 self.changelog.rev, self)
+            return bundlefilelog(self.svfs, f, self.bundle, self.changelog.rev)
         else:
             return filelog.filelog(self.svfs, f)
 
--- a/mercurial/changegroup.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/changegroup.py	Tue May 19 07:17:57 2015 -0500
@@ -283,8 +283,6 @@
         if bundlecaps is None:
             bundlecaps = set()
         self._bundlecaps = bundlecaps
-        self._changelog = repo.changelog
-        self._manifest = repo.manifest
         reorder = repo.ui.config('bundle', 'reorder', 'auto')
         if reorder == 'auto':
             reorder = None
@@ -304,7 +302,7 @@
     def fileheader(self, fname):
         return chunkheader(len(fname)) + fname
 
-    def group(self, nodelist, revlog, lookup, units=None, reorder=None):
+    def group(self, nodelist, revlog, lookup, units=None):
         """Calculate a delta group, yielding a sequence of changegroup chunks
         (strings).
 
@@ -325,7 +323,7 @@
 
         # for generaldelta revlogs, we linearize the revs; this will both be
         # much quicker and generate a much smaller bundle
-        if (revlog._generaldelta and reorder is not False) or reorder:
+        if (revlog._generaldelta and self._reorder is None) or self._reorder:
             dag = dagutil.revlogdag(revlog)
             revs = set(revlog.rev(n) for n in nodelist)
             revs = dag.linearize(revs)
@@ -347,23 +345,20 @@
             for c in self.revchunk(revlog, curr, prev, linknode):
                 yield c
 
+        if units is not None:
+            self._progress(msgbundling, None)
         yield self.close()
 
     # filter any nodes that claim to be part of the known set
-    def prune(self, revlog, missing, commonrevs, source):
+    def prune(self, revlog, missing, commonrevs):
         rr, rl = revlog.rev, revlog.linkrev
         return [n for n in missing if rl(rr(n)) not in commonrevs]
 
     def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
         '''yield a sequence of changegroup chunks (strings)'''
         repo = self._repo
-        cl = self._changelog
-        mf = self._manifest
-        reorder = self._reorder
-        progress = self._progress
-
-        # for progress output
-        msgbundling = _('bundling')
+        cl = repo.changelog
+        ml = repo.manifest
 
         clrevorder = {}
         mfs = {} # needed manifests
@@ -383,20 +378,34 @@
 
         self._verbosenote(_('uncompressed size of bundle content:\n'))
         size = 0
-        for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets'),
-                                reorder=reorder):
+        for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')):
             size += len(chunk)
             yield chunk
         self._verbosenote(_('%8.i (changelog)\n') % size)
-        progress(msgbundling, None)
 
+        # We need to make sure that the linkrev in the changegroup refers to
+        # the first changeset that introduced the manifest or file revision.
+        # The fastpath is usually safer than the slowpath, because the filelogs
+        # are walked in revlog order.
+        #
+        # When taking the slowpath with reorder=None and the manifest revlog
+        # uses generaldelta, the manifest may be walked in the "wrong" order.
+        # Without 'clrevorder', we would get an incorrect linkrev (see fix in
+        # cc0ff93d0c0c).
+        #
+        # When taking the fastpath, we are only vulnerable to reordering
+        # of the changelog itself. The changelog never uses generaldelta, so
+        # it is only reordered when reorder=True. To handle this case, we
+        # simply take the slowpath, which already has the 'clrevorder' logic.
+        # This was also fixed in cc0ff93d0c0c.
+        fastpathlinkrev = fastpathlinkrev and not self._reorder
         # Callback for the manifest, used to collect linkrevs for filelog
         # revisions.
         # Returns the linkrev node (collected in lookupcl).
         def lookupmf(x):
             clnode = mfs[x]
-            if not fastpathlinkrev or reorder:
-                mdata = mf.readfast(x)
+            if not fastpathlinkrev:
+                mdata = ml.readfast(x)
                 for f, n in mdata.iteritems():
                     if f in changedfiles:
                         # record the first changeset introducing this filelog
@@ -407,25 +416,23 @@
                             fclnodes[n] = clnode
             return clnode
 
-        mfnodes = self.prune(mf, mfs, commonrevs, source)
+        mfnodes = self.prune(ml, mfs, commonrevs)
         size = 0
-        for chunk in self.group(mfnodes, mf, lookupmf, units=_('manifests'),
-                                reorder=reorder):
+        for chunk in self.group(mfnodes, ml, lookupmf, units=_('manifests')):
             size += len(chunk)
             yield chunk
         self._verbosenote(_('%8.i (manifests)\n') % size)
-        progress(msgbundling, None)
 
         mfs.clear()
-        needed = set(cl.rev(x) for x in clnodes)
+        clrevs = set(cl.rev(x) for x in clnodes)
 
         def linknodes(filerevlog, fname):
-            if fastpathlinkrev and not reorder:
+            if fastpathlinkrev:
                 llr = filerevlog.linkrev
                 def genfilenodes():
                     for r in filerevlog:
                         linkrev = llr(r)
-                        if linkrev in needed:
+                        if linkrev in clrevs:
                             yield filerevlog.node(r), cl.node(linkrev)
                 return dict(genfilenodes())
             return fnodes.get(fname, {})
@@ -435,15 +442,14 @@
             yield chunk
 
         yield self.close()
-        progress(msgbundling, None)
 
         if clnodes:
             repo.hook('outgoing', node=hex(clnodes[0]), source=source)
 
+    # The 'source' parameter is useful for extensions
     def generatefiles(self, changedfiles, linknodes, commonrevs, source):
         repo = self._repo
         progress = self._progress
-        reorder = self._reorder
         msgbundling = _('bundling')
 
         total = len(changedfiles)
@@ -460,18 +466,18 @@
             def lookupfilelog(x):
                 return linkrevnodes[x]
 
-            filenodes = self.prune(filerevlog, linkrevnodes, commonrevs, source)
+            filenodes = self.prune(filerevlog, linkrevnodes, commonrevs)
             if filenodes:
                 progress(msgbundling, i + 1, item=fname, unit=msgfiles,
                          total=total)
                 h = self.fileheader(fname)
                 size = len(h)
                 yield h
-                for chunk in self.group(filenodes, filerevlog, lookupfilelog,
-                                        reorder=reorder):
+                for chunk in self.group(filenodes, filerevlog, lookupfilelog):
                     size += len(chunk)
                     yield chunk
                 self._verbosenote(_('%8.i  %s\n') % (size, fname))
+        progress(msgbundling, None)
 
     def deltaparent(self, revlog, rev, p1, p2, prev):
         return prev
@@ -513,11 +519,13 @@
     version = '02'
     deltaheader = _CHANGEGROUPV2_DELTA_HEADER
 
-    def group(self, nodelist, revlog, lookup, units=None, reorder=None):
-        if (revlog._generaldelta and reorder is not True):
-            reorder = False
-        return super(cg2packer, self).group(nodelist, revlog, lookup,
-                                            units=units, reorder=reorder)
+    def __init__(self, repo, bundlecaps=None):
+        super(cg2packer, self).__init__(repo, bundlecaps)
+        if self._reorder is None:
+            # Since generaldelta is directly supported by cg2, reordering
+            # generally doesn't help, so we disable it by default (treating
+            # bundle.reorder=auto just like bundle.reorder=False).
+            self._reorder = False
 
     def deltaparent(self, revlog, rev, p1, p2, prev):
         dp = revlog.deltaparent(rev)
@@ -788,8 +796,8 @@
         if repo.ui.configbool('server', 'validate', default=False):
             # validate incoming csets have their manifests
             for cset in xrange(clstart, clend):
-                mfest = repo.changelog.read(repo.changelog.node(cset))[0]
-                mfest = repo.manifest.readdelta(mfest)
+                mfnode = repo.changelog.read(repo.changelog.node(cset))[0]
+                mfest = repo.manifest.readdelta(mfnode)
                 # store file nodes we must see
                 for f, n in mfest.iteritems():
                     needfiles.setdefault(f, set()).add(n)
--- a/mercurial/changelog.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/changelog.py	Tue May 19 07:17:57 2015 -0500
@@ -172,14 +172,6 @@
         self.rev(self.node(0))
         return self._nodecache
 
-    def hasnode(self, node):
-        """filtered version of revlog.hasnode"""
-        try:
-            i = self.rev(node)
-            return i not in self.filteredrevs
-        except KeyError:
-            return False
-
     def headrevs(self):
         if self.filteredrevs:
             try:
--- a/mercurial/cmdutil.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/cmdutil.py	Tue May 19 07:17:57 2015 -0500
@@ -450,14 +450,17 @@
     """opens the changelog, manifest, a filelog or a given revlog"""
     cl = opts['changelog']
     mf = opts['manifest']
+    dir = opts['dir']
     msg = None
     if cl and mf:
         msg = _('cannot specify --changelog and --manifest at the same time')
+    elif cl and dir:
+        msg = _('cannot specify --changelog and --dir at the same time')
     elif cl or mf:
         if file_:
             msg = _('cannot specify filename with --changelog or --manifest')
         elif not repo:
-            msg = _('cannot specify --changelog or --manifest '
+            msg = _('cannot specify --changelog or --manifest or --dir '
                     'without a repository')
     if msg:
         raise util.Abort(msg)
@@ -466,6 +469,13 @@
     if repo:
         if cl:
             r = repo.unfiltered().changelog
+        elif dir:
+            if 'treemanifest' not in repo.requirements:
+                raise util.Abort(_("--dir can only be used on repos with "
+                                   "treemanifest enabled"))
+            dirlog = repo.dirlog(file_)
+            if len(dirlog):
+                r = dirlog
         elif mf:
             r = repo.manifest
         elif file_:
@@ -818,6 +828,7 @@
     msg = _('applied to working directory')
 
     rejects = False
+    dsguard = None
 
     try:
         cmdline_message = logmessage(ui, opts)
@@ -859,7 +870,7 @@
 
         n = None
         if update:
-            repo.dirstate.beginparentchange()
+            dsguard = dirstateguard(repo, 'tryimportone')
             if p1 != parents[0]:
                 updatefunc(repo, p1.node())
             if p2 != parents[1]:
@@ -896,10 +907,16 @@
                     editor = None
                 else:
                     editor = getcommiteditor(editform=editform, **opts)
-                n = repo.commit(message, opts.get('user') or user,
-                                opts.get('date') or date, match=m,
-                                editor=editor, force=partial)
-            repo.dirstate.endparentchange()
+                allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
+                try:
+                    if partial:
+                        repo.ui.setconfig('ui', 'allowemptycommit', True)
+                    n = repo.commit(message, opts.get('user') or user,
+                                    opts.get('date') or date, match=m,
+                                    editor=editor)
+                finally:
+                    repo.ui.restoreconfig(allowemptyback)
+            dsguard.close()
         else:
             if opts.get('exact') or opts.get('import_branch'):
                 branch = branch or 'default'
@@ -937,6 +954,7 @@
             msg = _('created %s') % short(n)
         return (msg, n, rejects)
     finally:
+        lockmod.release(dsguard)
         os.unlink(tmpname)
 
 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
@@ -1443,9 +1461,9 @@
         tmpl = ui.config('ui', 'logtemplate')
         if tmpl:
             try:
-                tmpl = templater.parsestring(tmpl)
+                tmpl = templater.unquotestring(tmpl)
             except SyntaxError:
-                tmpl = templater.parsestring(tmpl, quoted=False)
+                pass
             return tmpl, None
         else:
             style = util.expandpath(ui.config('ui', 'style', ''))
@@ -1477,9 +1495,9 @@
     t = ui.config('templates', tmpl)
     if t:
         try:
-            tmpl = templater.parsestring(t)
+            tmpl = templater.unquotestring(t)
         except SyntaxError:
-            tmpl = templater.parsestring(t, quoted=False)
+            tmpl = t
         return tmpl, None
 
     if tmpl == 'list':
@@ -1825,13 +1843,12 @@
         for windowsize in increasingwindows():
             nrevs = []
             for i in xrange(windowsize):
-                try:
-                    rev = it.next()
-                    if want(rev):
-                        nrevs.append(rev)
-                except (StopIteration):
+                rev = next(it, None)
+                if rev is None:
                     stopiteration = True
                     break
+                elif want(rev):
+                    nrevs.append(rev)
             for rev in sorted(nrevs):
                 fns = fncache.get(rev)
                 ctx = change(rev)
@@ -1916,10 +1933,7 @@
     # --follow with FILE behaviour depends on revs...
     it = iter(revs)
     startrev = it.next()
-    try:
-        followdescendants = startrev < it.next()
-    except (StopIteration):
-        followdescendants = False
+    followdescendants = startrev < next(it, startrev)
 
     # branch and only_branch are really aliases and must be handled at
     # the same time
@@ -2113,15 +2127,11 @@
         if not opts.get('rev'):
             revs.sort(reverse=True)
     if limit is not None:
-        count = 0
         limitedrevs = []
-        it = iter(revs)
-        while count < limit:
-            try:
-                limitedrevs.append(it.next())
-            except (StopIteration):
+        for idx, r in enumerate(revs):
+            if limit <= idx:
                 break
-            count += 1
+            limitedrevs.append(r)
         revs = revset.baseset(limitedrevs)
 
     return revs, expr, filematcher
@@ -2336,7 +2346,7 @@
                     return True
             return False
 
-        isdir = f in deleteddirs or f in wctx.dirs()
+        isdir = f in deleteddirs or wctx.hasdir(f)
         if f in repo.dirstate or isdir or f == '.' or insubrepo():
             continue
 
@@ -2464,9 +2474,10 @@
     ui.note(_('amending changeset %s\n') % old)
     base = old.p1()
 
-    wlock = lock = newid = None
+    wlock = dsguard = lock = newid = None
     try:
         wlock = repo.wlock()
+        dsguard = dirstateguard(repo, 'amend')
         lock = repo.lock()
         tr = repo.transaction('amend')
         try:
@@ -2480,13 +2491,13 @@
             # First, do a regular commit to record all changes in the working
             # directory (if there are any)
             ui.callhooks = False
-            currentbookmark = repo._bookmarkcurrent
+            activebookmark = repo._activebookmark
             try:
-                repo._bookmarkcurrent = None
+                repo._activebookmark = None
                 opts['message'] = 'temporary amend commit for %s' % old
                 node = commit(ui, repo, commitfunc, pats, opts)
             finally:
-                repo._bookmarkcurrent = currentbookmark
+                repo._activebookmark = activebookmark
                 ui.callhooks = True
             ctx = repo[node]
 
@@ -2637,6 +2648,7 @@
             tr.close()
         finally:
             tr.release()
+        dsguard.close()
         if not createmarkers and newid != old.node():
             # Strip the intermediate commit (if there was one) and the amended
             # commit
@@ -2645,9 +2657,7 @@
             ui.note(_('stripping amended changeset %s\n') % old)
             repair.strip(ui, repo, old.node(), topic='amend-backup')
     finally:
-        if newid is None:
-            repo.dirstate.invalidate()
-        lockmod.release(lock, wlock)
+        lockmod.release(lock, dsguard, wlock)
     return newid
 
 def commiteditor(repo, ctx, subs, editform=''):
@@ -2721,8 +2731,8 @@
         edittext.append(_("HG: branch merge"))
     if ctx.branch():
         edittext.append(_("HG: branch '%s'") % ctx.branch())
-    if bookmarks.iscurrent(repo):
-        edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
+    if bookmarks.isactivewdirparent(repo):
+        edittext.append(_("HG: bookmark '%s'") % repo._activebookmark)
     edittext.extend([_("HG: subrepo %s") % s for s in subs])
     edittext.extend([_("HG: added %s") % f for f in added])
     edittext.extend([_("HG: changed %s") % f for f in modified])
@@ -3259,3 +3269,59 @@
     for f, clearable, allowcommit, msg, hint in unfinishedstates:
         if clearable and repo.vfs.exists(f):
             util.unlink(repo.join(f))
+
+class dirstateguard(object):
+    '''Restore dirstate at unexpected failure.
+
+    At the construction, this class does:
+
+    - write current ``repo.dirstate`` out, and
+    - save ``.hg/dirstate`` into the backup file
+
+    This restores ``.hg/dirstate`` from backup file, if ``release()``
+    is invoked before ``close()``.
+
+    This just removes the backup file at ``close()`` before ``release()``.
+    '''
+
+    def __init__(self, repo, name):
+        repo.dirstate.write()
+        self._repo = repo
+        self._filename = 'dirstate.backup.%s.%d' % (name, id(self))
+        repo.vfs.write(self._filename, repo.vfs.tryread('dirstate'))
+        self._active = True
+        self._closed = False
+
+    def __del__(self):
+        if self._active: # still active
+            # this may occur, even if this class is used correctly:
+            # for example, releasing other resources like transaction
+            # may raise exception before ``dirstateguard.release`` in
+            # ``release(tr, ....)``.
+            self._abort()
+
+    def close(self):
+        if not self._active: # already inactivated
+            msg = (_("can't close already inactivated backup: %s")
+                   % self._filename)
+            raise util.Abort(msg)
+
+        self._repo.vfs.unlink(self._filename)
+        self._active = False
+        self._closed = True
+
+    def _abort(self):
+        # this "invalidate()" prevents "wlock.release()" from writing
+        # changes of dirstate out after restoring to original status
+        self._repo.dirstate.invalidate()
+
+        self._repo.vfs.rename(self._filename, 'dirstate')
+        self._active = False
+
+    def release(self):
+        if not self._closed:
+            if not self._active: # already inactivated
+                msg = (_("can't release already inactivated backup: %s")
+                       % self._filename)
+                raise util.Abort(msg)
+            self._abort()
--- a/mercurial/commands.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/commands.py	Tue May 19 07:17:57 2015 -0500
@@ -979,8 +979,8 @@
                     if mark not in marks:
                         raise util.Abort(_("bookmark '%s' does not exist") %
                                          mark)
-                    if mark == repo._bookmarkcurrent:
-                        bookmarks.unsetcurrent(repo)
+                    if mark == repo._activebookmark:
+                        bookmarks.deactivate(repo)
                     del marks[mark]
                 marks.write()
 
@@ -994,8 +994,8 @@
                     raise util.Abort(_("bookmark '%s' does not exist") % rename)
                 checkconflict(repo, mark, cur, force)
                 marks[mark] = marks[rename]
-                if repo._bookmarkcurrent == rename and not inactive:
-                    bookmarks.setcurrent(repo, mark)
+                if repo._activebookmark == rename and not inactive:
+                    bookmarks.activate(repo, mark)
                 del marks[rename]
                 marks.write()
 
@@ -1005,8 +1005,8 @@
                     mark = checkformat(mark)
                     if newact is None:
                         newact = mark
-                    if inactive and mark == repo._bookmarkcurrent:
-                        bookmarks.unsetcurrent(repo)
+                    if inactive and mark == repo._activebookmark:
+                        bookmarks.deactivate(repo)
                         return
                     tgt = cur
                     if rev:
@@ -1014,18 +1014,18 @@
                     checkconflict(repo, mark, cur, force, tgt)
                     marks[mark] = tgt
                 if not inactive and cur == marks[newact] and not rev:
-                    bookmarks.setcurrent(repo, newact)
-                elif cur != tgt and newact == repo._bookmarkcurrent:
-                    bookmarks.unsetcurrent(repo)
+                    bookmarks.activate(repo, newact)
+                elif cur != tgt and newact == repo._activebookmark:
+                    bookmarks.deactivate(repo)
                 marks.write()
 
             elif inactive:
                 if len(marks) == 0:
                     ui.status(_("no bookmarks set\n"))
-                elif not repo._bookmarkcurrent:
+                elif not repo._activebookmark:
                     ui.status(_("no active bookmark\n"))
                 else:
-                    bookmarks.unsetcurrent(repo)
+                    bookmarks.deactivate(repo)
         finally:
             wlock.release()
     else: # show bookmarks
@@ -1035,7 +1035,7 @@
         if len(marks) == 0 and not fm:
             ui.status(_("no bookmarks set\n"))
         for bmark, n in sorted(marks.iteritems()):
-            current = repo._bookmarkcurrent
+            current = repo._activebookmark
             if bmark == current:
                 prefix, label = '*', 'bookmarks.current'
             else:
@@ -1506,7 +1506,7 @@
                                match,
                                extra=extra)
 
-        current = repo._bookmarkcurrent
+        current = repo._activebookmark
         marks = old.bookmarks()
         node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
         if node == old.node():
@@ -1519,7 +1519,7 @@
             for bm in marks:
                 newmarks[bm] = node
                 if bm == current:
-                    bookmarks.setcurrent(repo, bm)
+                    bookmarks.activate(repo, bm)
             newmarks.write()
     else:
         def commitfunc(ui, repo, message, match, opts):
@@ -2056,7 +2056,8 @@
 
 @command('debugdata',
     [('c', 'changelog', False, _('open changelog')),
-     ('m', 'manifest', False, _('open manifest'))],
+     ('m', 'manifest', False, _('open manifest')),
+     ('', 'dir', False, _('open directory manifest'))],
     _('-c|-m|FILE REV'))
 def debugdata(ui, repo, file_, rev=None, **opts):
     """dump the contents of a data file revision"""
@@ -2227,6 +2228,7 @@
 @command('debugindex',
     [('c', 'changelog', False, _('open changelog')),
      ('m', 'manifest', False, _('open manifest')),
+     ('', 'dir', False, _('open directory manifest')),
      ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
     _('[-f FORMAT] -c|-m|FILE'),
     optionalrepo=True)
@@ -2545,26 +2547,25 @@
         try:
             tr = repo.transaction('debugobsolete')
             try:
-                try:
-                    date = opts.get('date')
-                    if date:
-                        date = util.parsedate(date)
-                    else:
-                        date = None
-                    prec = parsenodeid(precursor)
-                    parents = None
-                    if opts['record_parents']:
-                        if prec not in repo.unfiltered():
-                            raise util.Abort('cannot used --record-parents on '
-                                             'unknown changesets')
-                        parents = repo.unfiltered()[prec].parents()
-                        parents = tuple(p.node() for p in parents)
-                    repo.obsstore.create(tr, prec, succs, opts['flags'],
-                                         parents=parents, date=date,
-                                         metadata=metadata)
-                    tr.close()
-                except ValueError, exc:
-                    raise util.Abort(_('bad obsmarker input: %s') % exc)
+                date = opts.get('date')
+                if date:
+                    date = util.parsedate(date)
+                else:
+                    date = None
+                prec = parsenodeid(precursor)
+                parents = None
+                if opts['record_parents']:
+                    if prec not in repo.unfiltered():
+                        raise util.Abort('cannot used --record-parents on '
+                                         'unknown changesets')
+                    parents = repo.unfiltered()[prec].parents()
+                    parents = tuple(p.node() for p in parents)
+                repo.obsstore.create(tr, prec, succs, opts['flags'],
+                                     parents=parents, date=date,
+                                     metadata=metadata)
+                tr.close()
+            except ValueError, exc:
+                raise util.Abort(_('bad obsmarker input: %s') % exc)
             finally:
                 tr.release()
         finally:
@@ -2730,6 +2731,7 @@
 @command('debugrevlog',
     [('c', 'changelog', False, _('open changelog')),
      ('m', 'manifest', False, _('open manifest')),
+     ('', 'dir', False, _('open directory manifest')),
      ('d', 'dump', False, _('dump index data'))],
     _('-c|-m|FILE'),
     optionalrepo=True)
@@ -4045,8 +4047,8 @@
             parents = ctx.parents()
             changed = ""
             if default or id or num:
-                if (util.any(repo.status())
-                    or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
+                if (any(repo.status())
+                    or any(ctx.sub(s).dirty() for s in ctx.substate)):
                     changed = '+'
             if default or id:
                 output = ["%s%s" %
@@ -4213,7 +4215,7 @@
         cmdutil.bailifchanged(repo)
 
     base = opts["base"]
-    wlock = lock = tr = None
+    wlock = dsguard = lock = tr = None
     msgs = []
     ret = 0
 
@@ -4221,7 +4223,7 @@
     try:
         try:
             wlock = repo.wlock()
-            repo.dirstate.beginparentchange()
+            dsguard = cmdutil.dirstateguard(repo, 'import')
             if not opts.get('no_commit'):
                 lock = repo.lock()
                 tr = repo.transaction('import')
@@ -4262,18 +4264,16 @@
                 tr.close()
             if msgs:
                 repo.savecommitmessage('\n* * *\n'.join(msgs))
-            repo.dirstate.endparentchange()
+            dsguard.close()
             return ret
-        except: # re-raises
-            # wlock.release() indirectly calls dirstate.write(): since
-            # we're crashing, we do not want to change the working dir
-            # parent after all, so make sure it writes nothing
-            repo.dirstate.invalidate()
-            raise
+        finally:
+            # TODO: get rid of this meaningless try/finally enclosing.
+            # this is kept only to reduce changes in a patch.
+            pass
     finally:
         if tr:
             tr.release()
-        release(lock, wlock)
+        release(lock, dsguard, wlock)
 
 @command('incoming|in',
     [('f', 'force', None,
@@ -4702,9 +4702,9 @@
     if node:
         node = scmutil.revsingle(repo, node).node()
 
-    if not node and repo._bookmarkcurrent:
-        bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
-        curhead = repo[repo._bookmarkcurrent].node()
+    if not node and repo._activebookmark:
+        bmheads = repo.bookmarkheads(repo._activebookmark)
+        curhead = repo[repo._activebookmark].node()
         if len(bmheads) == 2:
             if curhead == bmheads[0]:
                 node = bmheads[1]
@@ -4719,7 +4719,7 @@
                 "please merge with an explicit rev or bookmark"),
                 hint=_("run 'hg heads' to see all heads"))
 
-    if not node and not repo._bookmarkcurrent:
+    if not node and not repo._activebookmark:
         branch = repo[None].branch()
         bheads = repo.branchheads(branch)
         nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
@@ -4952,11 +4952,11 @@
      ('f', 'force', False, _('allow to move boundary backward')),
      ('r', 'rev', [], _('target revision'), _('REV')),
     ],
-    _('[-p|-d|-s] [-f] [-r] REV...'))
+    _('[-p|-d|-s] [-f] [-r] [REV...]'))
 def phase(ui, repo, *revs, **opts):
     """set or show the current phase name
 
-    With no argument, show the phase name of specified revisions.
+    With no argument, show the phase name of the current revision(s).
 
     With one of -p/--public, -d/--draft or -s/--secret, change the
     phase value of the specified revisions.
@@ -4981,7 +4981,9 @@
     revs = list(revs)
     revs.extend(opts['rev'])
     if not revs:
-        raise util.Abort(_('no revisions specified'))
+        # display both parents as the second parent phase can influence
+        # the phase of a merge commit
+        revs = [c.rev() for c in repo[None].parents()]
 
     revs = scmutil.revrange(repo, revs)
 
@@ -5049,7 +5051,7 @@
             return 0
         if not ret and not checkout:
             if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
-                ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
+                ui.status(_("updating bookmark %s\n") % repo._activebookmark)
         return ret
     if modheads > 1:
         currentbranchheads = len(repo.branchheads())
@@ -5520,7 +5522,7 @@
             hint = _("uncommitted merge, use --all to discard all changes,"
                      " or 'hg update -C .' to abort the merge")
             raise util.Abort(msg, hint=hint)
-        dirty = util.any(repo.status())
+        dirty = any(repo.status())
         node = ctx.node()
         if node != parent:
             if dirty:
@@ -5872,7 +5874,7 @@
     """summarize working directory state
 
     This generates a brief summary of the working directory state,
-    including parents, branch, commit status, and available updates.
+    including parents, branch, commit status, phase and available updates.
 
     With the --remote option, this will check the default paths for
     incoming and outgoing changes. This can be time-consuming.
@@ -5914,7 +5916,7 @@
         ui.status(m, label='log.branch')
 
     if marks:
-        current = repo._bookmarkcurrent
+        current = repo._activebookmark
         # i18n: column positioning for "hg summary"
         ui.write(_('bookmarks:'), label='log.bookmark')
         if current is not None:
@@ -6000,6 +6002,25 @@
         ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
                  (new, len(bheads)))
 
+    t = []
+    draft = len(repo.revs('draft()'))
+    if draft:
+        t.append(_('%d draft') % draft)
+    secret = len(repo.revs('secret()'))
+    if secret:
+        t.append(_('%d secret') % secret)
+
+    if parents:
+        parentphase = max(p.phase() for p in parents)
+    else:
+        parentphase = phases.public
+
+    if draft or secret:
+        ui.status(_('phases: %s (%s)\n') % (', '.join(t),
+                                            phases.phasenames[parentphase]))
+    else:
+        ui.note(_('phases: (%s)\n') % phases.phasenames[parentphase])
+
     cmdutil.summaryhooks(ui, repo)
 
     if opts.get('remote'):
@@ -6405,15 +6426,15 @@
 
     if not ret and movemarkfrom:
         if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
-            ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
+            ui.status(_("updating bookmark %s\n") % repo._activebookmark)
     elif brev in repo._bookmarks:
-        bookmarks.setcurrent(repo, brev)
+        bookmarks.activate(repo, brev)
         ui.status(_("(activating bookmark %s)\n") % brev)
     elif brev:
-        if repo._bookmarkcurrent:
+        if repo._activebookmark:
             ui.status(_("(leaving bookmark %s)\n") %
-                      repo._bookmarkcurrent)
-        bookmarks.unsetcurrent(repo)
+                      repo._activebookmark)
+        bookmarks.deactivate(repo)
 
     return ret
 
--- a/mercurial/config.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/config.py	Tue May 19 07:17:57 2015 -0500
@@ -10,10 +10,11 @@
 import os, errno
 
 class config(object):
-    def __init__(self, data=None):
+    def __init__(self, data=None, includepaths=[]):
         self._data = {}
         self._source = {}
         self._unset = []
+        self._includepaths = includepaths
         if data:
             for k in data._data:
                 self._data[k] = data[k].copy()
@@ -110,13 +111,17 @@
                 item = None
                 cont = False
             m = includere.match(l)
-            if m:
-                inc = util.expandpath(m.group(1))
-                base = os.path.dirname(src)
-                inc = os.path.normpath(os.path.join(base, inc))
-                if include:
+
+            if m and include:
+                expanded = util.expandpath(m.group(1))
+                includepaths = [os.path.dirname(src)] + self._includepaths
+
+                for base in includepaths:
+                    inc = os.path.normpath(os.path.join(base, expanded))
+
                     try:
                         include(inc, remap=remap, sections=sections)
+                        break
                     except IOError, inst:
                         if inst.errno != errno.ENOENT:
                             raise error.ParseError(_("cannot include %s (%s)")
--- a/mercurial/context.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/context.py	Tue May 19 07:17:57 2015 -0500
@@ -251,11 +251,13 @@
     def sub(self, path):
         return subrepo.subrepo(self, path)
 
-    def match(self, pats=[], include=None, exclude=None, default='glob'):
+    def match(self, pats=[], include=None, exclude=None, default='glob',
+              listsubrepos=False):
         r = self._repo
         return matchmod.match(r.root, r.getcwd(), pats,
                               include, exclude, default,
-                              auditor=r.auditor, ctx=self)
+                              auditor=r.auditor, ctx=self,
+                              listsubrepos=listsubrepos)
 
     def diff(self, ctx2=None, match=None, **opts):
         """Returns a diff generator for the given contexts and matcher"""
@@ -459,7 +461,7 @@
                 pass
         except (error.FilteredIndexError, error.FilteredLookupError,
                 error.FilteredRepoLookupError):
-            if repo.filtername == 'visible':
+            if repo.filtername.startswith('visible'):
                 msg = _("hidden revision '%s'") % changeid
                 hint = _('use --hidden to access hidden revisions')
                 raise error.FilteredRepoLookupError(msg, hint=hint)
@@ -595,8 +597,8 @@
         def bad(fn, msg):
             # The manifest doesn't know about subrepos, so don't complain about
             # paths into valid subrepos.
-            if util.any(fn == s or fn.startswith(s + '/')
-                        for s in self.substate):
+            if any(fn == s or fn.startswith(s + '/')
+                   for s in self.substate):
                 return
             oldbad(fn, _('no such file in rev %s') % self)
         match.bad = bad
@@ -1443,17 +1445,20 @@
             finally:
                 wlock.release()
 
-    def match(self, pats=[], include=None, exclude=None, default='glob'):
+    def match(self, pats=[], include=None, exclude=None, default='glob',
+              listsubrepos=False):
         r = self._repo
 
         # Only a case insensitive filesystem needs magic to translate user input
         # to actual case in the filesystem.
         if not util.checkcase(r.root):
             return matchmod.icasefsmatcher(r.root, r.getcwd(), pats, include,
-                                           exclude, default, r.auditor, self)
+                                           exclude, default, r.auditor, self,
+                                           listsubrepos=listsubrepos)
         return matchmod.match(r.root, r.getcwd(), pats,
                               include, exclude, default,
-                              auditor=r.auditor, ctx=self)
+                              auditor=r.auditor, ctx=self,
+                              listsubrepos=listsubrepos)
 
     def _filtersuspectsymlink(self, files):
         if not files or self._repo.dirstate._checklink:
--- a/mercurial/crecord.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/crecord.py	Tue May 19 07:17:57 2015 -0500
@@ -20,7 +20,8 @@
 
 # os.name is one of: 'posix', 'nt', 'dos', 'os2', 'mac', or 'ce'
 if os.name == 'posix':
-    import curses, fcntl, termios
+    import curses
+    import fcntl, termios
 else:
     # I have no idea if wcurses works with crecord...
     try:
--- a/mercurial/dagparser.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/dagparser.py	Tue May 19 07:17:57 2015 -0500
@@ -176,10 +176,7 @@
     chiter = (c for c in desc)
 
     def nextch():
-        try:
-            return chiter.next()
-        except StopIteration:
-            return '\0'
+        return next(chiter, '\0')
 
     def nextrun(c, allow):
         s = ''
--- a/mercurial/dirs.c	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/dirs.c	Tue May 19 07:17:57 2015 -0500
@@ -9,7 +9,6 @@
 
 #define PY_SSIZE_T_CLEAN
 #include <Python.h>
-#include <string.h>
 #include "util.h"
 
 /*
@@ -29,23 +28,25 @@
 	PyObject *dict;
 } dirsObject;
 
-static inline Py_ssize_t _finddir(PyObject *path, Py_ssize_t pos)
+static inline Py_ssize_t _finddir(const char *path, Py_ssize_t pos)
 {
-	const char *s = PyString_AS_STRING(path);
+	while (pos != -1) {
+		if (path[pos] == '/')
+			break;
+		pos -= 1;
+	}
 
-	const char *ret = strchr(s + pos, '/');
-	return (ret != NULL) ? (ret - s) : -1;
+	return pos;
 }
 
 static int _addpath(PyObject *dirs, PyObject *path)
 {
-	char *cpath = PyString_AS_STRING(path);
-	Py_ssize_t len = PyString_GET_SIZE(path);
-	Py_ssize_t pos = -1;
+	const char *cpath = PyString_AS_STRING(path);
+	Py_ssize_t pos = PyString_GET_SIZE(path);
 	PyObject *key = NULL;
 	int ret = -1;
 
-	while ((pos = _finddir(path, pos + 1)) != -1) {
+	while ((pos = _finddir(cpath, pos - 1)) != -1) {
 		PyObject *val;
 
 		/* It's likely that every prefix already has an entry
@@ -53,18 +54,10 @@
 		   deallocating a string for each prefix we check. */
 		if (key != NULL)
 			((PyStringObject *)key)->ob_shash = -1;
-		else if (pos != 0) {
-			/* pos >= 1, which means that len >= 2. This is
-			   guaranteed to produce a non-interned string. */
-			key = PyString_FromStringAndSize(cpath, len);
-			if (key == NULL)
-				goto bail;
-		} else {
-			/* pos == 0, which means we need to increment the dir
-			   count for the empty string. We need to make sure we
-			   don't muck around with interned strings, so throw it
-			   away later. */
-			key = PyString_FromString("");
+		else {
+			/* Force Python to not reuse a small shared string. */
+			key = PyString_FromStringAndSize(cpath,
+							 pos < 2 ? 2 : pos);
 			if (key == NULL)
 				goto bail;
 		}
@@ -74,11 +67,7 @@
 		val = PyDict_GetItem(dirs, key);
 		if (val != NULL) {
 			PyInt_AS_LONG(val) += 1;
-			if (pos != 0)
-				PyString_AS_STRING(key)[pos] = '/';
-			else
-				key = NULL;
-			continue;
+			break;
 		}
 
 		/* Force Python to not reuse a small shared int. */
@@ -92,9 +81,6 @@
 		Py_DECREF(val);
 		if (ret == -1)
 			goto bail;
-
-		/* Clear the key out since we've already exposed it to Python
-		   and can't mutate it further. */
 		Py_CLEAR(key);
 	}
 	ret = 0;
@@ -107,14 +93,15 @@
 
 static int _delpath(PyObject *dirs, PyObject *path)
 {
-	Py_ssize_t pos = -1;
+	char *cpath = PyString_AS_STRING(path);
+	Py_ssize_t pos = PyString_GET_SIZE(path);
 	PyObject *key = NULL;
 	int ret = -1;
 
-	while ((pos = _finddir(path, pos + 1)) != -1) {
+	while ((pos = _finddir(cpath, pos - 1)) != -1) {
 		PyObject *val;
 
-		key = PyString_FromStringAndSize(PyString_AS_STRING(path), pos);
+		key = PyString_FromStringAndSize(cpath, pos);
 
 		if (key == NULL)
 			goto bail;
@@ -126,9 +113,11 @@
 			goto bail;
 		}
 
-		if (--PyInt_AS_LONG(val) <= 0 &&
-		    PyDict_DelItem(dirs, key) == -1)
-			goto bail;
+		if (--PyInt_AS_LONG(val) <= 0) {
+			if (PyDict_DelItem(dirs, key) == -1)
+				goto bail;
+		} else
+			break;
 		Py_CLEAR(key);
 	}
 	ret = 0;
--- a/mercurial/dirstate.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/dirstate.py	Tue May 19 07:17:57 2015 -0500
@@ -143,7 +143,9 @@
 
     @rootcache('.hgignore')
     def _ignore(self):
-        files = [self._join('.hgignore')]
+        files = []
+        if os.path.exists(self._join('.hgignore')):
+            files.append(self._join('.hgignore'))
         for name, path in self._ui.configitems("ui"):
             if name == 'ignore' or name.startswith('ignore.'):
                 # we need to use os.path.join here rather than self._join
@@ -972,7 +974,7 @@
             # fast path -- filter the other way around, since typically files is
             # much smaller than dmap
             return [f for f in files if f in dmap]
-        if not match.anypats() and util.all(fn in dmap for fn in files):
+        if not match.anypats() and all(fn in dmap for fn in files):
             # fast path -- all the values are known to be files, so just return
             # that
             return list(files)
--- a/mercurial/dispatch.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/dispatch.py	Tue May 19 07:17:57 2015 -0500
@@ -921,6 +921,30 @@
             stats.sort(field)
             stats.pprint(limit=limit, file=fp, climit=climit)
 
+def flameprofile(ui, func, fp):
+    try:
+        from flamegraph import flamegraph
+    except ImportError:
+        raise util.Abort(_(
+            'flamegraph not available - install from '
+            'https://github.com/evanhempel/python-flamegraph'))
+    freq = ui.configint('profiling', 'freq', default=1000)
+    filter_ = None
+    collapse_recursion = True
+    thread = flamegraph.ProfileThread(fp, 1.0 / freq,
+                                      filter_, collapse_recursion)
+    start_time = time.clock()
+    try:
+        thread.start()
+        func()
+    finally:
+        thread.stop()
+        thread.join()
+        print 'Collected %d stack frames (%d unique) in %2.2f seconds.' % (
+            time.clock() - start_time, thread.num_frames(),
+            thread.num_frames(unique=True))
+
+
 def statprofile(ui, func, fp):
     try:
         import statprof
@@ -952,7 +976,7 @@
         profiler = os.getenv('HGPROF')
         if profiler is None:
             profiler = ui.config('profiling', 'type', default='ls')
-        if profiler not in ('ls', 'stat'):
+        if profiler not in ('ls', 'stat', 'flame'):
             ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
             profiler = 'ls'
 
@@ -967,6 +991,8 @@
         try:
             if profiler == 'ls':
                 return lsprofile(ui, checkargs, fp)
+            elif profiler == 'flame':
+                return flameprofile(ui, checkargs, fp)
             else:
                 return statprofile(ui, checkargs, fp)
         finally:
--- a/mercurial/exchange.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/exchange.py	Tue May 19 07:17:57 2015 -0500
@@ -536,7 +536,8 @@
         return
     pushop.stepsdone.add('obsmarkers')
     if pushop.outobsmarkers:
-        buildobsmarkerspart(bundler, pushop.outobsmarkers)
+        markers = sorted(pushop.outobsmarkers)
+        buildobsmarkerspart(bundler, markers)
 
 @b2partsgenerator('bookmarks')
 def _pushb2bookmarks(pushop, bundler):
@@ -751,7 +752,7 @@
     pushop.stepsdone.add('obsmarkers')
     if pushop.outobsmarkers:
         rslts = []
-        remotedata = obsolete._pushkeyescape(pushop.outobsmarkers)
+        remotedata = obsolete._pushkeyescape(sorted(pushop.outobsmarkers))
         for key in sorted(remotedata, reverse=True):
             # reverse sort to ensure we end with dump0
             data = remotedata[key]
@@ -1180,7 +1181,7 @@
     # bundle10 case
     usebundle2 = False
     if bundlecaps is not None:
-        usebundle2 = util.any((cap.startswith('HG2') for cap in bundlecaps))
+        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'))
@@ -1257,6 +1258,7 @@
             heads = repo.heads()
         subset = [c.node() for c in repo.set('::%ln', heads)]
         markers = repo.obsstore.relevantmarkers(subset)
+        markers = sorted(markers)
         buildobsmarkerspart(bundler, markers)
 
 def check_heads(repo, their_heads, context):
@@ -1313,7 +1315,7 @@
                         def recordout(output):
                             r.newpart('output', data=output, mandatory=False)
                 tr.close()
-            except Exception, exc:
+            except BaseException, exc:
                 exc.duringunbundle2 = True
                 if captureoutput and r is not None:
                     parts = exc._bundle2salvagedoutput = r.salvageoutput()
--- a/mercurial/filemerge.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/filemerge.py	Tue May 19 07:17:57 2015 -0500
@@ -354,7 +354,6 @@
 
     ui = repo.ui
     template = ui.config('ui', 'mergemarkertemplate', _defaultconflictmarker)
-    template = templater.parsestring(template, quoted=False)
     tmpl = templater.templater(None, cache={'conflictmarker': template})
 
     pad = max(len(l) for l in labels)
--- a/mercurial/hbisect.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/hbisect.py	Tue May 19 07:17:57 2015 -0500
@@ -8,6 +8,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+import collections
 import os
 import error
 from i18n import _
@@ -71,7 +72,7 @@
 
     # build children dict
     children = {}
-    visit = util.deque([badrev])
+    visit = collections.deque([badrev])
     candidates = []
     while visit:
         rev = visit.popleft()
--- a/mercurial/help/config.txt	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/help/config.txt	Tue May 19 07:17:57 2015 -0500
@@ -376,8 +376,8 @@
         HG: --
         HG: user: {author}\n{ifeq(p2rev, "-1", "",
        "HG: branch merge\n")
-       }HG: branch '{branch}'\n{if(currentbookmark,
-       "HG: bookmark '{currentbookmark}'\n")  }{subrepos %
+       }HG: branch '{branch}'\n{if(activebookmark,
+       "HG: bookmark '{activebookmark}'\n")   }{subrepos %
        "HG: subrepo {subrepo}\n"              }{file_adds %
        "HG: added {file}\n"                   }{file_mods %
        "HG: changed {file}\n"                 }{file_dels %
--- a/mercurial/help/templates.txt	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/help/templates.txt	Tue May 19 07:17:57 2015 -0500
@@ -67,7 +67,7 @@
 
 - Output the description set to a fill-width of 30::
 
-   $ hg log -r 0 --template "{fill(desc, '30')}"
+   $ hg log -r 0 --template "{fill(desc, 30)}"
 
 - Use a conditional to test for the default branch::
 
@@ -104,4 +104,4 @@
 
 - Print the first word of each line of a commit message::
 
-   $ hg log --template "{word(\"0\", desc)}\n"
+   $ hg log --template "{word(0, desc)}\n"
--- a/mercurial/hg.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/hg.py	Tue May 19 07:17:57 2015 -0500
@@ -497,7 +497,7 @@
                 destrepo.ui.status(status)
                 _update(destrepo, uprev)
                 if update in destrepo._bookmarks:
-                    bookmarks.setcurrent(destrepo, update)
+                    bookmarks.activate(destrepo, update)
     finally:
         release(srclock, destlock)
         if cleandir is not None:
--- a/mercurial/hgweb/hgwebdir_mod.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/hgweb/hgwebdir_mod.py	Tue May 19 07:17:57 2015 -0500
@@ -176,71 +176,70 @@
 
     def run_wsgi(self, req):
         try:
-            try:
-                self.refresh()
+            self.refresh()
 
-                virtual = req.env.get("PATH_INFO", "").strip('/')
-                tmpl = self.templater(req)
-                ctype = tmpl('mimetype', encoding=encoding.encoding)
-                ctype = templater.stringify(ctype)
+            virtual = req.env.get("PATH_INFO", "").strip('/')
+            tmpl = self.templater(req)
+            ctype = tmpl('mimetype', encoding=encoding.encoding)
+            ctype = templater.stringify(ctype)
 
-                # a static file
-                if virtual.startswith('static/') or 'static' in req.form:
-                    if virtual.startswith('static/'):
-                        fname = virtual[7:]
-                    else:
-                        fname = req.form['static'][0]
-                    static = self.ui.config("web", "static", None,
-                                            untrusted=False)
-                    if not static:
-                        tp = self.templatepath or templater.templatepaths()
-                        if isinstance(tp, str):
-                            tp = [tp]
-                        static = [os.path.join(p, 'static') for p in tp]
-                    staticfile(static, fname, req)
-                    return []
+            # a static file
+            if virtual.startswith('static/') or 'static' in req.form:
+                if virtual.startswith('static/'):
+                    fname = virtual[7:]
+                else:
+                    fname = req.form['static'][0]
+                static = self.ui.config("web", "static", None,
+                                        untrusted=False)
+                if not static:
+                    tp = self.templatepath or templater.templatepaths()
+                    if isinstance(tp, str):
+                        tp = [tp]
+                    static = [os.path.join(p, 'static') for p in tp]
+                staticfile(static, fname, req)
+                return []
 
-                # top-level index
-                elif not virtual:
-                    req.respond(HTTP_OK, ctype)
-                    return self.makeindex(req, tmpl)
+            # top-level index
+            elif not virtual:
+                req.respond(HTTP_OK, ctype)
+                return self.makeindex(req, tmpl)
 
-                # nested indexes and hgwebs
+            # nested indexes and hgwebs
 
-                repos = dict(self.repos)
-                virtualrepo = virtual
-                while virtualrepo:
-                    real = repos.get(virtualrepo)
-                    if real:
-                        req.env['REPO_NAME'] = virtualrepo
-                        try:
-                            # ensure caller gets private copy of ui
-                            repo = hg.repository(self.ui.copy(), real)
-                            return hgweb(repo).run_wsgi(req)
-                        except IOError, inst:
-                            msg = inst.strerror
-                            raise ErrorResponse(HTTP_SERVER_ERROR, msg)
-                        except error.RepoError, inst:
-                            raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
+            repos = dict(self.repos)
+            virtualrepo = virtual
+            while virtualrepo:
+                real = repos.get(virtualrepo)
+                if real:
+                    req.env['REPO_NAME'] = virtualrepo
+                    try:
+                        # ensure caller gets private copy of ui
+                        repo = hg.repository(self.ui.copy(), real)
+                        return hgweb(repo).run_wsgi(req)
+                    except IOError, inst:
+                        msg = inst.strerror
+                        raise ErrorResponse(HTTP_SERVER_ERROR, msg)
+                    except error.RepoError, inst:
+                        raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
 
-                    up = virtualrepo.rfind('/')
-                    if up < 0:
-                        break
-                    virtualrepo = virtualrepo[:up]
+                up = virtualrepo.rfind('/')
+                if up < 0:
+                    break
+                virtualrepo = virtualrepo[:up]
 
-                # browse subdirectories
-                subdir = virtual + '/'
-                if [r for r in repos if r.startswith(subdir)]:
-                    req.respond(HTTP_OK, ctype)
-                    return self.makeindex(req, tmpl, subdir)
+            # browse subdirectories
+            subdir = virtual + '/'
+            if [r for r in repos if r.startswith(subdir)]:
+                req.respond(HTTP_OK, ctype)
+                return self.makeindex(req, tmpl, subdir)
 
-                # prefixes not found
-                req.respond(HTTP_NOT_FOUND, ctype)
-                return tmpl("notfound", repo=virtual)
+            # prefixes not found
+            req.respond(HTTP_NOT_FOUND, ctype)
+            return tmpl("notfound", repo=virtual)
 
-            except ErrorResponse, err:
-                req.respond(err, ctype)
-                return tmpl('error', error=err.message or '')
+        except ErrorResponse, err:
+            req.respond(err, ctype)
+            return tmpl('error', error=err.message or '')
         finally:
             tmpl = None
 
--- a/mercurial/hgweb/webcommands.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/hgweb/webcommands.py	Tue May 19 07:17:57 2015 -0500
@@ -130,6 +130,8 @@
                 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))
 
 @webcommand('file')
@@ -230,7 +232,7 @@
             # no revset syntax used
             return MODE_KEYWORD, query
 
-        if util.any((token, (value or '')[:3]) == ('string', 're:')
+        if any((token, (value or '')[:3]) == ('string', 're:')
                     for token, value, pos in revset.tokenize(revdef)):
             return MODE_KEYWORD, query
 
@@ -546,6 +548,7 @@
                 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))
 
@@ -808,6 +811,8 @@
                 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 = webcommand('diff')(filediff)
@@ -880,6 +885,8 @@
                 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,
@@ -946,6 +953,8 @@
                 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))
 
 @webcommand('filelog')
--- a/mercurial/hook.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/hook.py	Tue May 19 07:17:57 2015 -0500
@@ -39,26 +39,25 @@
         if demandimportenabled:
             demandimport.disable()
         try:
+            obj = __import__(modname)
+        except ImportError:
+            e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
             try:
-                obj = __import__(modname)
+                # extensions are loaded with hgext_ prefix
+                obj = __import__("hgext_%s" % modname)
             except ImportError:
-                e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
-                try:
-                    # extensions are loaded with hgext_ prefix
-                    obj = __import__("hgext_%s" % modname)
-                except ImportError:
-                    e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
-                    if ui.tracebackflag:
-                        ui.warn(_('exception from first failed import '
-                                  'attempt:\n'))
-                    ui.traceback(e1)
-                    if ui.tracebackflag:
-                        ui.warn(_('exception from second failed import '
-                                  'attempt:\n'))
-                    ui.traceback(e2)
-                    raise util.Abort(_('%s hook is invalid '
-                                       '(import of "%s" failed)') %
-                                     (hname, modname))
+                e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
+                if ui.tracebackflag:
+                    ui.warn(_('exception from first failed import '
+                              'attempt:\n'))
+                ui.traceback(e1)
+                if ui.tracebackflag:
+                    ui.warn(_('exception from second failed import '
+                              'attempt:\n'))
+                ui.traceback(e2)
+                raise util.Abort(_('%s hook is invalid '
+                                   '(import of "%s" failed)') %
+                                 (hname, modname))
         finally:
             if demandimportenabled:
                 demandimport.enable()
@@ -79,27 +78,24 @@
     starttime = time.time()
 
     try:
-        try:
-            # redirect IO descriptors to the ui descriptors so hooks
-            # that write directly to these don't mess up the command
-            # protocol when running through the command server
-            old = sys.stdout, sys.stderr, sys.stdin
-            sys.stdout, sys.stderr, sys.stdin = ui.fout, ui.ferr, ui.fin
+        # redirect IO descriptors to the ui descriptors so hooks
+        # that write directly to these don't mess up the command
+        # protocol when running through the command server
+        old = sys.stdout, sys.stderr, sys.stdin
+        sys.stdout, sys.stderr, sys.stdin = ui.fout, ui.ferr, ui.fin
 
-            r = obj(ui=ui, repo=repo, hooktype=name, **args)
-        except KeyboardInterrupt:
+        r = obj(ui=ui, repo=repo, hooktype=name, **args)
+    except Exception, exc:
+        if isinstance(exc, util.Abort):
+            ui.warn(_('error: %s hook failed: %s\n') %
+                         (hname, exc.args[0]))
+        else:
+            ui.warn(_('error: %s hook raised an exception: '
+                           '%s\n') % (hname, exc))
+        if throw:
             raise
-        except Exception, exc:
-            if isinstance(exc, util.Abort):
-                ui.warn(_('error: %s hook failed: %s\n') %
-                             (hname, exc.args[0]))
-            else:
-                ui.warn(_('error: %s hook raised an exception: '
-                               '%s\n') % (hname, exc))
-            if throw:
-                raise
-            ui.traceback()
-            return True
+        ui.traceback()
+        return True
     finally:
         sys.stdout, sys.stderr, sys.stdin = old
         duration = time.time() - starttime
--- a/mercurial/httppeer.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/httppeer.py	Tue May 19 07:17:57 2015 -0500
@@ -198,16 +198,15 @@
         headers = {'Content-Type': 'application/mercurial-0.1'}
 
         try:
-            try:
-                r = self._call(cmd, data=fp, headers=headers, **args)
-                vals = r.split('\n', 1)
-                if len(vals) < 2:
-                    raise error.ResponseError(_("unexpected response:"), r)
-                return vals
-            except socket.error, err:
-                if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
-                    raise util.Abort(_('push failed: %s') % err.args[1])
-                raise util.Abort(err.args[1])
+            r = self._call(cmd, data=fp, headers=headers, **args)
+            vals = r.split('\n', 1)
+            if len(vals) < 2:
+                raise error.ResponseError(_("unexpected response:"), r)
+            return vals
+        except socket.error, err:
+            if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
+                raise util.Abort(_('push failed: %s') % err.args[1])
+            raise util.Abort(err.args[1])
         finally:
             fp.close()
             os.unlink(tempname)
--- a/mercurial/ignore.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/ignore.py	Tue May 19 07:17:57 2015 -0500
@@ -7,50 +7,6 @@
 
 from i18n import _
 import util, match
-import re
-
-_commentre = None
-
-def ignorepats(lines):
-    '''parse lines (iterable) of .hgignore text, returning a tuple of
-    (patterns, parse errors). These patterns should be given to compile()
-    to be validated and converted into a match function.'''
-    syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
-    syntax = 'relre:'
-    patterns = []
-    warnings = []
-
-    for line in lines:
-        if "#" in line:
-            global _commentre
-            if not _commentre:
-                _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
-            # remove comments prefixed by an even number of escapes
-            line = _commentre.sub(r'\1', line)
-            # fixup properly escaped comments that survived the above
-            line = line.replace("\\#", "#")
-        line = line.rstrip()
-        if not line:
-            continue
-
-        if line.startswith('syntax:'):
-            s = line[7:].strip()
-            try:
-                syntax = syntaxes[s]
-            except KeyError:
-                warnings.append(_("ignoring invalid syntax '%s'") % s)
-            continue
-        pat = syntax + line
-        for s, rels in syntaxes.iteritems():
-            if line.startswith(rels):
-                pat = line
-                break
-            elif line.startswith(s+':'):
-                pat = rels + line[len(s) + 1:]
-                break
-        patterns.append(pat)
-
-    return patterns, warnings
 
 def readpats(root, files, warn):
     '''return a dict mapping ignore-file-name to list-of-patterns'''
@@ -60,16 +16,11 @@
         if f in pats:
             continue
         try:
-            pats[f] = []
-            fp = open(f)
-            pats[f], warnings = ignorepats(fp)
-            fp.close()
-            for warning in warnings:
-                warn("%s: %s\n" % (f, warning))
+            pats[f] = match.readpatternfile(f, warn)
         except IOError, inst:
-            if f != files[0]:
-                warn(_("skipping unreadable ignore file '%s': %s\n") %
-                     (f, inst.strerror))
+            warn(_("skipping unreadable ignore file '%s': %s\n") %
+                 (f, inst.strerror))
+
     return [(f, pats[f]) for f in files if f in pats]
 
 def ignore(root, files, warn):
--- a/mercurial/localrepo.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/localrepo.py	Tue May 19 07:17:57 2015 -0500
@@ -192,11 +192,11 @@
 
 class localrepository(object):
 
-    supportedformats = set(('revlogv1', 'generaldelta', 'manifestv2'))
+    supportedformats = set(('revlogv1', 'generaldelta', 'treemanifest',
+                            'manifestv2'))
     _basesupported = supportedformats | set(('store', 'fncache', 'shared',
                                              'dotencode'))
-    openerreqs = set(('revlogv1', 'generaldelta', 'manifestv2'))
-    requirements = ['revlogv1']
+    openerreqs = set(('revlogv1', 'generaldelta', 'treemanifest', 'manifestv2'))
     filtername = None
 
     # a list of (ui, featureset) functions.
@@ -204,9 +204,10 @@
     featuresetupfuncs = set()
 
     def _baserequirements(self, create):
-        return self.requirements[:]
+        return ['revlogv1']
 
     def __init__(self, baseui, path=None, create=False):
+        self.requirements = set()
         self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
         self.wopener = self.wvfs
         self.root = self.wvfs.base
@@ -243,14 +244,14 @@
                 if not self.wvfs.exists():
                     self.wvfs.makedirs()
                 self.vfs.makedir(notindexed=True)
-                requirements = self._baserequirements(create)
+                self.requirements.update(self._baserequirements(create))
                 if self.ui.configbool('format', 'usestore', True):
                     self.vfs.mkdir("store")
-                    requirements.append("store")
+                    self.requirements.add("store")
                     if self.ui.configbool('format', 'usefncache', True):
-                        requirements.append("fncache")
+                        self.requirements.add("fncache")
                         if self.ui.configbool('format', 'dotencode', True):
-                            requirements.append('dotencode')
+                            self.requirements.add('dotencode')
                     # create an invalid changelog
                     self.vfs.append(
                         "00changelog.i",
@@ -258,21 +259,22 @@
                         ' dummy changelog to prevent using the old repo layout'
                     )
                 if self.ui.configbool('format', 'generaldelta', False):
-                    requirements.append("generaldelta")
+                    self.requirements.add("generaldelta")
+                if self.ui.configbool('experimental', 'treemanifest', False):
+                    self.requirements.add("treemanifest")
                 if self.ui.configbool('experimental', 'manifestv2', False):
-                    requirements.append("manifestv2")
-                requirements = set(requirements)
+                    self.requirements.add("manifestv2")
             else:
                 raise error.RepoError(_("repository %s not found") % path)
         elif create:
             raise error.RepoError(_("repository %s already exists") % path)
         else:
             try:
-                requirements = scmutil.readrequires(self.vfs, self.supported)
+                self.requirements = scmutil.readrequires(
+                        self.vfs, self.supported)
             except IOError, inst:
                 if inst.errno != errno.ENOENT:
                     raise
-                requirements = set()
 
         self.sharedpath = self.path
         try:
@@ -287,13 +289,14 @@
             if inst.errno != errno.ENOENT:
                 raise
 
-        self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
+        self.store = store.store(
+                self.requirements, self.sharedpath, scmutil.vfs)
         self.spath = self.store.path
         self.svfs = self.store.vfs
         self.sopener = self.svfs
         self.sjoin = self.store.join
         self.vfs.createmode = self.store.createmode
-        self._applyrequirements(requirements)
+        self._applyopenerreqs()
         if create:
             self._writerequirements()
 
@@ -336,9 +339,8 @@
             caps.add('bundle2=' + urllib.quote(capsblob))
         return caps
 
-    def _applyrequirements(self, requirements):
-        self.requirements = requirements
-        self.svfs.options = dict((r, 1) for r in requirements
+    def _applyopenerreqs(self):
+        self.svfs.options = dict((r, 1) for r in self.requirements
                                            if r in self.openerreqs)
         chunkcachesize = self.ui.configint('format', 'chunkcachesize')
         if chunkcachesize is not None:
@@ -349,15 +351,9 @@
         manifestcachesize = self.ui.configint('format', 'manifestcachesize')
         if manifestcachesize is not None:
             self.svfs.options['manifestcachesize'] = manifestcachesize
-        usetreemanifest = self.ui.configbool('experimental', 'treemanifest')
-        if usetreemanifest is not None:
-            self.svfs.options['usetreemanifest'] = usetreemanifest
 
     def _writerequirements(self):
-        reqfile = self.vfs("requires", "w")
-        for r in sorted(self.requirements):
-            reqfile.write("%s\n" % r)
-        reqfile.close()
+        scmutil.writerequires(self.vfs, self.requirements)
 
     def _checknested(self, path):
         """Determine if path is a legal nested repository."""
@@ -419,8 +415,8 @@
         return bookmarks.bmstore(self)
 
     @repofilecache('bookmarks.current')
-    def _bookmarkcurrent(self):
-        return bookmarks.readcurrent(self)
+    def _activebookmark(self):
+        return bookmarks.readactive(self)
 
     def bookmarkheads(self, bookmark):
         name = bookmark.split('@', 1)[0]
@@ -464,6 +460,9 @@
     def manifest(self):
         return manifest.manifest(self.svfs)
 
+    def dirlog(self, dir):
+        return self.manifest.dirlog(dir)
+
     @repofilecache('dirstate')
     def dirstate(self):
         warned = [0]
@@ -628,7 +627,7 @@
 
         if not local:
             m = matchmod.exact(self.root, '', ['.hgtags'])
-            if util.any(self.status(match=m, unknown=True, ignored=True)):
+            if any(self.status(match=m, unknown=True, ignored=True)):
                 raise util.Abort(_('working copy of .hgtags is changed'),
                                  hint=_('please commit .hgtags manually'))
 
@@ -1381,7 +1380,7 @@
             wctx = self[None]
             merge = len(wctx.parents()) > 1
 
-            if not force and merge and not match.always():
+            if not force and merge and match.ispartial():
                 raise util.Abort(_('cannot partially commit a merge '
                                    '(do not specify files or patterns)'))
 
@@ -1466,9 +1465,10 @@
             cctx = context.workingcommitctx(self, status,
                                             text, user, date, extra)
 
-            if (not force and not extra.get("close") and not merge
-                and not cctx.files()
-                and wctx.branch() == wctx.p1().branch()):
+            allowemptycommit = (wctx.branch() != wctx.p1().branch()
+                                or extra.get('close') or merge or cctx.files()
+                                or self.ui.configbool('ui', 'allowemptycommit'))
+            if not allowemptycommit:
                 return None
 
             if merge and cctx.deleted():
@@ -1521,7 +1521,7 @@
         def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
             # hack for command that use a temporary commit (eg: histedit)
             # temporary commit got stripped before hook release
-            if node in self:
+            if self.changelog.hasnode(ret):
                 self.hook("commit", node=node, parent1=parent1,
                           parent2=parent2)
         self._afterlock(commithook)
@@ -1754,7 +1754,7 @@
         """
         return util.hooks()
 
-    def stream_in(self, remote, requirements):
+    def stream_in(self, remote, remotereqs):
         lock = self.lock()
         try:
             # Save remote branchmap. We will use it later
@@ -1827,10 +1827,11 @@
                             util.bytecount(total_bytes / elapsed)))
 
             # new requirements = old non-format requirements +
-            #                    new format-related
+            #                    new format-related remote requirements
             # requirements from the streamed-in repository
-            requirements.update(set(self.requirements) - self.supportedformats)
-            self._applyrequirements(requirements)
+            self.requirements = remotereqs | (
+                    self.requirements - self.supportedformats)
+            self._applyopenerreqs()
             self._writerequirements()
 
             if rbranchmap:
--- a/mercurial/manifest.c	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/manifest.c	Tue May 19 07:17:57 2015 -0500
@@ -235,7 +235,7 @@
 	PyObject *ret = NULL, *path = NULL, *hash = NULL, *flags = NULL;
 	l = lmiter_nextline((lmIter *)o);
 	if (!l) {
-		goto bail;
+		goto done;
 	}
 	pl = pathlen(l);
 	path = PyString_FromStringAndSize(l->start, pl);
@@ -244,10 +244,10 @@
 	flags = PyString_FromStringAndSize(l->start + consumed,
 									   l->len - consumed - 1);
 	if (!path || !hash || !flags) {
-		goto bail;
+		goto done;
 	}
 	ret = PyTuple_Pack(3, path, hash, flags);
- bail:
+done:
 	Py_XDECREF(path);
 	Py_XDECREF(hash);
 	Py_XDECREF(flags);
@@ -672,7 +672,7 @@
 	copy->pydata = self->pydata;
 	Py_INCREF(copy->pydata);
 	return copy;
- nomem:
+nomem:
 	PyErr_NoMemory();
 	Py_XDECREF(copy);
 	return NULL;
@@ -724,7 +724,7 @@
 	}
 	copy->livelines = copy->numlines;
 	return copy;
- nomem:
+nomem:
 	PyErr_NoMemory();
 	Py_XDECREF(copy);
 	return NULL;
@@ -845,7 +845,7 @@
 	}
 	Py_DECREF(emptyTup);
 	return ret;
- nomem:
+nomem:
 	PyErr_NoMemory();
 	Py_XDECREF(ret);
 	Py_XDECREF(emptyTup);
--- a/mercurial/manifest.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/manifest.py	Tue May 19 07:17:57 2015 -0500
@@ -219,7 +219,7 @@
         files instead of over manifest files.'''
         files = match.files()
         return (len(files) < 100 and (match.isexact() or
-            (not match.anypats() and util.all(fn in self for fn in files))))
+            (not match.anypats() and all(fn in self for fn in files))))
 
     def walk(self, match):
         '''Generates matching file names.
@@ -444,11 +444,15 @@
 class treemanifest(object):
     def __init__(self, dir='', text=''):
         self._dir = dir
+        self._node = revlog.nullid
         self._dirs = {}
         # Using _lazymanifest here is a little slower than plain old dicts
         self._files = {}
         self._flags = {}
-        self.parse(text)
+        def readsubtree(subdir, subm):
+            raise AssertionError('treemanifest constructor only accepts '
+                                 'flat manifests')
+        self.parse(text, readsubtree)
 
     def _subpath(self, path):
         return self._dir + path
@@ -461,10 +465,25 @@
 
     def _isempty(self):
         return (not self._files and (not self._dirs or
-                util.all(m._isempty() for m in self._dirs.values())))
+                all(m._isempty() for m in self._dirs.values())))
 
     def __str__(self):
-        return '<treemanifest dir=%s>' % self._dir
+        return ('<treemanifest dir=%s, node=%s>' %
+                (self._dir, revlog.hex(self._node)))
+
+    def dir(self):
+        '''The directory that this tree manifest represents, including a
+        trailing '/'. Empty string for the repo root directory.'''
+        return self._dir
+
+    def node(self):
+        '''This node of this instance. nullid for unsaved instances. Should
+        be updated when the instance is read or written from a revlog.
+        '''
+        return self._node
+
+    def setnode(self, node):
+        self._node = node
 
     def iteritems(self):
         for p, n in sorted(self._dirs.items() + self._files.items()):
@@ -557,6 +576,7 @@
 
     def setflag(self, f, flags):
         """Set the flags (symlink, executable) for path f."""
+        assert 'd' not in flags
         dir, subpath = _splittopdir(f)
         if dir:
             if dir not in self._dirs:
@@ -567,6 +587,7 @@
 
     def copy(self):
         copy = treemanifest(self._dir)
+        copy._node = self._node
         for d in self._dirs:
             copy._dirs[d] = self._dirs[d].copy()
         copy._files = dict.copy(self._files)
@@ -635,18 +656,10 @@
             if not self.hasdir(fn):
                 match.bad(fn, None)
 
-    def _walk(self, match, alldirs=False):
-        '''Recursively generates matching file names for walk().
-
-        Will visit all subdirectories if alldirs is True, otherwise it will
-        only visit subdirectories for which match.visitdir is True.'''
-
-        if not alldirs:
-            # substring to strip trailing slash
-            visit = match.visitdir(self._dir[:-1] or '.')
-            if not visit:
-                return
-            alldirs = (visit == 'all')
+    def _walk(self, match):
+        '''Recursively generates matching file names for walk().'''
+        if not match.visitdir(self._dir[:-1] or '.'):
+            return
 
         # yield this dir's files and walk its submanifests
         for p in sorted(self._dirs.keys() + self._files.keys()):
@@ -655,7 +668,7 @@
                 if match(fullp):
                     yield fullp
             else:
-                for f in self._dirs[p]._walk(match, alldirs):
+                for f in self._dirs[p]._walk(match):
                     yield f
 
     def matches(self, match):
@@ -665,19 +678,13 @@
 
         return self._matches(match)
 
-    def _matches(self, match, alldirs=False):
+    def _matches(self, match):
         '''recursively generate a new manifest filtered by the match argument.
-
-        Will visit all subdirectories if alldirs is True, otherwise it will
-        only visit subdirectories for which match.visitdir is True.'''
-
+        '''
         ret = treemanifest(self._dir)
-        if not alldirs:
-            # substring to strip trailing slash
-            visit = match.visitdir(self._dir[:-1] or '.')
-            if not visit:
-                return ret
-            alldirs = (visit == 'all')
+
+        if not match.visitdir(self._dir[:-1] or '.'):
+            return ret
 
         for fn in self._files:
             fullp = self._subpath(fn)
@@ -688,7 +695,7 @@
                 ret._flags[fn] = self._flags[fn]
 
         for dir, subm in self._dirs.iteritems():
-            m = subm._matches(match, alldirs)
+            m = subm._matches(match)
             if not m._isempty():
                 ret._dirs[dir] = m
 
@@ -737,11 +744,18 @@
         _diff(self, m2)
         return result
 
-    def parse(self, text):
+    def parse(self, text, readsubtree):
         for f, n, fl in _parse(text):
-            self[f] = n
-            if fl:
-                self.setflag(f, fl)
+            if fl == 'd':
+                f = f + '/'
+                self._dirs[f] = readsubtree(self._subpath(f), n)
+            else:
+                # Use __setitem__ and setflag rather than assigning directly
+                # to _files and _flags, thereby letting us parse flat manifests
+                # as well as tree manifests.
+                self[f] = n
+                if fl:
+                    self.setflag(f, fl)
 
     def text(self, usemanifestv2=False):
         """Get the full data of this manifest as a bytestring."""
@@ -749,8 +763,30 @@
         return _text(((f, self[f], flags(f)) for f in self.keys()),
                      usemanifestv2)
 
+    def dirtext(self, usemanifestv2=False):
+        """Get the full data of this directory as a bytestring. Make sure that
+        any submanifests have been written first, so their nodeids are correct.
+        """
+        flags = self.flags
+        dirs = [(d[:-1], self._dirs[d]._node, 'd') for d in self._dirs]
+        files = [(f, self._files[f], flags(f)) for f in self._files]
+        return _text(sorted(dirs + files), usemanifestv2)
+
+    def writesubtrees(self, m1, m2, writesubtree):
+        emptytree = treemanifest()
+        for d, subm in self._dirs.iteritems():
+            subp1 = m1._dirs.get(d, emptytree)._node
+            subp2 = m2._dirs.get(d, emptytree)._node
+            if subp1 == revlog.nullid:
+                subp1, subp2 = subp2, subp1
+            writesubtree(subm, subp1, subp2)
+
 class manifest(revlog.revlog):
-    def __init__(self, opener):
+    def __init__(self, opener, dir='', dirlogcache=None):
+        '''The 'dir' and 'dirlogcache' arguments are for internal use by
+        manifest.manifest only. External users should create a root manifest
+        log with manifest.manifest(opener) and call dirlog() on it.
+        '''
         # During normal operations, we expect to deal with not more than four
         # revs at a time (such as during commit --amend). When rebasing large
         # stacks of commits, the number can go up, hence the config knob below.
@@ -760,19 +796,38 @@
         opts = getattr(opener, 'options', None)
         if opts is not None:
             cachesize = opts.get('manifestcachesize', cachesize)
-            usetreemanifest = opts.get('usetreemanifest', usetreemanifest)
+            usetreemanifest = opts.get('treemanifest', usetreemanifest)
             usemanifestv2 = opts.get('manifestv2', usemanifestv2)
         self._mancache = util.lrucachedict(cachesize)
-        revlog.revlog.__init__(self, opener, "00manifest.i")
         self._treeinmem = usetreemanifest
         self._treeondisk = usetreemanifest
         self._usemanifestv2 = usemanifestv2
+        indexfile = "00manifest.i"
+        if dir:
+            assert self._treeondisk
+            if not dir.endswith('/'):
+                dir = dir + '/'
+            indexfile = "meta/" + dir + "00manifest.i"
+        revlog.revlog.__init__(self, opener, indexfile)
+        self._dir = dir
+        # The dirlogcache is kept on the root manifest log
+        if dir:
+            self._dirlogcache = dirlogcache
+        else:
+            self._dirlogcache = {'': self}
 
     def _newmanifest(self, data=''):
         if self._treeinmem:
-            return treemanifest('', data)
+            return treemanifest(self._dir, data)
         return manifestdict(data)
 
+    def dirlog(self, dir):
+        assert self._treeondisk
+        if dir not in self._dirlogcache:
+            self._dirlogcache[dir] = manifest(self.opener, dir,
+                                              self._dirlogcache)
+        return self._dirlogcache[dir]
+
     def _slowreaddelta(self, node):
         r0 = self.deltaparent(self.rev(node))
         m0 = self.read(self.node(r0))
@@ -793,7 +848,13 @@
         return self._newmanifest(d)
 
     def readfast(self, node):
-        '''use the faster of readdelta or read'''
+        '''use the faster of readdelta or read
+
+        This will return a manifest which is either only the files
+        added/modified relative to p1, or all files in the
+        manifest. Which one is returned depends on the codepath used
+        to retrieve the data.
+        '''
         r = self.rev(node)
         deltaparent = self.deltaparent(r)
         if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
@@ -806,8 +867,16 @@
         if node in self._mancache:
             return self._mancache[node][0]
         text = self.revision(node)
-        arraytext = array.array('c', text)
-        m = self._newmanifest(text)
+        if self._treeondisk:
+            def readsubtree(dir, subm):
+                return self.dirlog(dir).read(subm)
+            m = self._newmanifest()
+            m.parse(text, readsubtree)
+            m.setnode(node)
+            arraytext = None
+        else:
+            m = self._newmanifest(text)
+            arraytext = array.array('c', text)
         self._mancache[node] = (m, arraytext)
         return m
 
@@ -845,10 +914,34 @@
             # just encode a fulltext of the manifest and pass that
             # through to the revlog layer, and let it handle the delta
             # process.
-            text = m.text(self._usemanifestv2)
-            arraytext = array.array('c', text)
-            n = self.addrevision(text, transaction, link, p1, p2)
+            if self._treeondisk:
+                m1 = self.read(p1)
+                m2 = self.read(p2)
+                n = self._addtree(m, transaction, link, m1, m2)
+                arraytext = None
+            else:
+                text = m.text(self._usemanifestv2)
+                n = self.addrevision(text, transaction, link, p1, p2)
+                arraytext = array.array('c', text)
 
         self._mancache[n] = (m, arraytext)
 
         return n
+
+    def _addtree(self, m, transaction, link, m1, m2):
+        def writesubtree(subm, subp1, subp2):
+            sublog = self.dirlog(subm.dir())
+            sublog.add(subm, transaction, link, subp1, subp2, None, None)
+        m.writesubtrees(m1, m2, writesubtree)
+        text = m.dirtext(self._usemanifestv2)
+        # If the manifest is unchanged compared to one parent,
+        # don't write a new revision
+        if text == m1.dirtext(self._usemanifestv2):
+            n = m1.node()
+        elif text == m2.dirtext(self._usemanifestv2):
+            n = m2.node()
+        else:
+            n = self.addrevision(text, transaction, link, m1.node(), m2.node())
+        # Save nodeid so parent manifest can calculate its nodeid
+        m.setnode(n)
+        return n
--- a/mercurial/match.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/match.py	Tue May 19 07:17:57 2015 -0500
@@ -21,7 +21,7 @@
     except AttributeError:
         return m.match
 
-def _expandsets(kindpats, ctx):
+def _expandsets(kindpats, ctx, listsubrepos):
     '''Returns the kindpats list with the 'set' patterns expanded.'''
     fset = set()
     other = []
@@ -32,6 +32,12 @@
                 raise util.Abort("fileset expression with no context")
             s = ctx.getfileset(pat)
             fset.update(s)
+
+            if listsubrepos:
+                for subpath in ctx.substate:
+                    s = ctx.sub(subpath).getfileset(pat)
+                    fset.update(subpath + '/' + f for f in s)
+
             continue
         other.append((kind, pat))
     return fset, other
@@ -47,7 +53,8 @@
 
 class match(object):
     def __init__(self, root, cwd, patterns, include=[], exclude=[],
-                 default='glob', exact=False, auditor=None, ctx=None):
+                 default='glob', exact=False, auditor=None, ctx=None,
+                 listsubrepos=False):
         """build an object to match a set of file patterns
 
         arguments:
@@ -80,11 +87,13 @@
         matchfns = []
         if include:
             kindpats = self._normalize(include, 'glob', root, cwd, auditor)
-            self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)')
+            self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
+                                              listsubrepos)
             matchfns.append(im)
         if exclude:
             kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
-            self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)')
+            self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
+                                              listsubrepos)
             matchfns.append(lambda f: not em(f))
         if exact:
             if isinstance(patterns, list):
@@ -97,7 +106,8 @@
             if not _kindpatsalwaysmatch(kindpats):
                 self._files = _roots(kindpats)
                 self._anypats = self._anypats or _anypats(kindpats)
-                self.patternspat, pm = _buildmatch(ctx, kindpats, '$')
+                self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
+                                                   listsubrepos)
                 matchfns.append(pm)
 
         if not matchfns:
@@ -113,7 +123,7 @@
                 return True
 
         self.matchfn = m
-        self._fmap = set(self._files)
+        self._fileroots = set(self._files)
 
     def __call__(self, fn):
         return self.matchfn(fn)
@@ -161,21 +171,17 @@
 
     @propertycache
     def _dirs(self):
-        return set(util.dirs(self._fmap)) | set(['.'])
+        return set(util.dirs(self._fileroots)) | set(['.'])
 
     def visitdir(self, dir):
-        '''Helps while traversing a directory tree. 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. If 'all' is returned, calling this method on a subdirectory
-        gives an undefined result.'''
-        if not self._fmap or self.exact(dir):
-            return 'all'
-        return dir in self._dirs
+        return (not self._fileroots or '.' in self._fileroots or
+                dir in self._fileroots or dir in self._dirs or
+                any(parentdir in self._fileroots
+                    for parentdir in util.finddirs(dir)))
 
     def exact(self, f):
         '''Returns True if f is in .files().'''
-        return f in self._fmap
+        return f in self._fileroots
 
     def anypats(self):
         '''Matcher uses patterns or include/exclude.'''
@@ -186,6 +192,14 @@
         - optimization might be possible and necessary.'''
         return self._always
 
+    def ispartial(self):
+        '''True if the matcher won't always match.
+
+        Although it's just the inverse of _always in this implementation,
+        an extenion such as narrowhg might make it return something
+        slightly different.'''
+        return not self._always
+
     def isexact(self):
         return self.matchfn == self.exact
 
@@ -264,11 +278,11 @@
         # If the parent repo had a path to this subrepo and no patterns are
         # specified, this submatcher always matches.
         if not self._always and not matcher._anypats:
-            self._always = util.any(f == path for f in matcher._files)
+            self._always = any(f == path for f in matcher._files)
 
         self._anypats = matcher._anypats
         self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
-        self._fmap = set(self._files)
+        self._fileroots = set(self._files)
 
     def abs(self, f):
         return self._matcher.abs(self._path + "/" + f)
@@ -285,17 +299,17 @@
     """
 
     def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
-                 ctx):
+                 ctx, listsubrepos=False):
         init = super(icasefsmatcher, self).__init__
         self._dsnormalize = ctx.repo().dirstate.normalize
 
         init(root, cwd, patterns, include, exclude, default, auditor=auditor,
-             ctx=ctx)
+             ctx=ctx, listsubrepos=listsubrepos)
 
         # m.exact(file) must be based off of the actual user input, otherwise
         # inexact case matches are treated as exact, and not noted without -v.
         if self._files:
-            self._fmap = set(_roots(self._kp))
+            self._fileroots = set(_roots(self._kp))
 
     def _normalize(self, patterns, default, root, cwd, auditor):
         self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
@@ -418,10 +432,10 @@
         return '.*' + pat
     return _globre(pat) + globsuffix
 
-def _buildmatch(ctx, kindpats, globsuffix):
+def _buildmatch(ctx, kindpats, globsuffix, listsubrepos):
     '''Return regexp string and a matcher function for kindpats.
     globsuffix is appended to the regexp of globs.'''
-    fset, kindpats = _expandsets(kindpats, ctx)
+    fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
     if not kindpats:
         return "", fset.__contains__
 
@@ -486,3 +500,49 @@
     for kind, pat in kindpats:
         if kind in ('glob', 're', 'relglob', 'relre', 'set'):
             return True
+
+_commentre = None
+
+def readpatternfile(filepath, warn):
+    '''parse a pattern file, returning a list of
+    patterns. These patterns should be given to compile()
+    to be validated and converted into a match function.'''
+    syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
+    syntax = 'relre:'
+    patterns = []
+
+    fp = open(filepath)
+    for line in fp:
+        if "#" in line:
+            global _commentre
+            if not _commentre:
+                _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
+            # remove comments prefixed by an even number of escapes
+            line = _commentre.sub(r'\1', line)
+            # fixup properly escaped comments that survived the above
+            line = line.replace("\\#", "#")
+        line = line.rstrip()
+        if not line:
+            continue
+
+        if line.startswith('syntax:'):
+            s = line[7:].strip()
+            try:
+                syntax = syntaxes[s]
+            except KeyError:
+                warn(_("%s: ignoring invalid syntax '%s'\n") % (filepath, s))
+            continue
+
+        linesyntax = syntax
+        for s, rels in syntaxes.iteritems():
+            if line.startswith(rels):
+                linesyntax = rels
+                line = line[len(rels):]
+                break
+            elif line.startswith(s+':'):
+                linesyntax = rels
+                line = line[len(s) + 1:]
+                break
+        patterns.append(linesyntax + line)
+    fp.close()
+    return patterns
--- a/mercurial/merge.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/merge.py	Tue May 19 07:17:57 2015 -0500
@@ -605,7 +605,7 @@
             # Consensus?
             if len(bids) == 1: # all bids are the same kind of method
                 m, l = bids.items()[0]
-                if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
+                if all(a == l[0] for a in l[1:]): # len(bids) is > 1
                     repo.ui.note(" %s: consensus for %s\n" % (f, m))
                     actions[f] = l[0]
                     continue
@@ -617,7 +617,7 @@
             # If there are gets and they all agree [how could they not?], do it.
             if 'g' in bids:
                 ga0 = bids['g'][0]
-                if util.all(a == ga0 for a in bids['g'][1:]):
+                if all(a == ga0 for a in bids['g'][1:]):
                     repo.ui.note(" %s: picking 'get' action\n" % f)
                     actions[f] = ga0
                     continue
--- a/mercurial/obsolete.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/obsolete.py	Tue May 19 07:17:57 2015 -0500
@@ -718,7 +718,7 @@
     """List markers over pushkey"""
     if not repo.obsstore:
         return {}
-    return _pushkeyescape(repo.obsstore)
+    return _pushkeyescape(sorted(repo.obsstore))
 
 def pushmarker(repo, key, old, new):
     """Push markers over pushkey"""
@@ -1110,13 +1110,17 @@
 @cachefor('unstable')
 def _computeunstableset(repo):
     """the set of non obsolete revisions with obsolete parents"""
-    # revset is not efficient enough here
-    # we do (obsolete()::) - obsolete() by hand
-    obs = getrevs(repo, 'obsolete')
-    if not obs:
-        return set()
-    cl = repo.changelog
-    return set(r for r in cl.descendants(obs) if r not in obs)
+    revs = [(ctx.rev(), ctx) for ctx in
+            repo.set('(not public()) and (not obsolete())')]
+    revs.sort(key=lambda x:x[0])
+    unstable = set()
+    for rev, ctx in revs:
+        # A rev is unstable if one of its parent is obsolete or unstable
+        # this works since we traverse following growing rev order
+        if any((x.obsolete() or (x.rev() in unstable))
+                for x in ctx.parents()):
+            unstable.add(rev)
+    return unstable
 
 @cachefor('suspended')
 def _computesuspendedset(repo):
@@ -1139,19 +1143,18 @@
     public = phases.public
     cl = repo.changelog
     torev = cl.nodemap.get
-    obs = getrevs(repo, 'obsolete')
-    for rev in repo:
+    for ctx in repo.set('(not public()) and (not obsolete())'):
+        rev = ctx.rev()
         # We only evaluate mutable, non-obsolete revision
-        if (public < phase(repo, rev)) and (rev not in obs):
-            node = cl.node(rev)
-            # (future) A cache of precursors may worth if split is very common
-            for pnode in allprecursors(repo.obsstore, [node],
-                                       ignoreflags=bumpedfix):
-                prev = torev(pnode) # unfiltered! but so is phasecache
-                if (prev is not None) and (phase(repo, prev) <= public):
-                    # we have a public precursors
-                    bumped.add(rev)
-                    break # Next draft!
+        node = ctx.node()
+        # (future) A cache of precursors may worth if split is very common
+        for pnode in allprecursors(repo.obsstore, [node],
+                                   ignoreflags=bumpedfix):
+            prev = torev(pnode) # unfiltered! but so is phasecache
+            if (prev is not None) and (phase(repo, prev) <= public):
+                # we have a public precursors
+                bumped.add(rev)
+                break # Next draft!
     return bumped
 
 @cachefor('divergent')
--- a/mercurial/parser.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/parser.py	Tue May 19 07:17:57 2015 -0500
@@ -27,10 +27,7 @@
     def _advance(self):
         'advance the tokenizer'
         t = self.current
-        try:
-            self.current = self._iter.next()
-        except StopIteration:
-            pass
+        self.current = next(self._iter, None)
         return t
     def _match(self, m, pos):
         'make sure the tokenizer matches an end condition'
--- a/mercurial/parsers.c	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/parsers.c	Tue May 19 07:17:57 2015 -0500
@@ -1071,14 +1071,17 @@
 		phases[i] = phases[parent_2];
 }
 
-static PyObject *compute_phases(indexObject *self, PyObject *args)
+static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
 {
 	PyObject *roots = Py_None;
+	PyObject *ret = NULL;
 	PyObject *phaseslist = NULL;
 	PyObject *phaseroots = NULL;
 	PyObject *rev = NULL;
 	PyObject *p1 = NULL;
 	PyObject *p2 = NULL;
+	PyObject *phaseset = NULL;
+	PyObject *phasessetlist = NULL;
 	Py_ssize_t addlen = self->added ? PyList_GET_SIZE(self->added) : 0;
 	Py_ssize_t len = index_length(self) - 1;
 	Py_ssize_t numphase = 0;
@@ -1088,6 +1091,7 @@
 	int parent_1, parent_2;
 	char *phases = NULL;
 	const char *data;
+	long phase;
 
 	if (!PyArg_ParseTuple(args, "O", &roots))
 		goto release_none;
@@ -1100,13 +1104,24 @@
 	/* 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;
+
+	PyList_SET_ITEM(phasessetlist, 0, Py_None);
+	Py_INCREF(Py_None);
+
 	for (i = 0; i < numphase-1; i++) {
 		phaseroots = PyList_GET_ITEM(roots, i);
+		phaseset = PySet_New(NULL);
+		if (phaseset == NULL)
+			goto release_phasesetlist;
+		PyList_SET_ITEM(phasessetlist, i+1, phaseset);
 		if (!PyList_Check(phaseroots))
-			goto release_phases;
+			goto release_phasesetlist;
 		minrevphase = add_roots_get_min(self, phaseroots, i+1, phases);
 		if (minrevphase == -2) /* Error from add_roots_get_min */
-			goto release_phases;
+			goto release_phasesetlist;
 		minrevallphases = MIN(minrevallphases, minrevphase);
 	}
 	/* Propagate the phase information from the roots to the revs */
@@ -1121,7 +1136,7 @@
 			p2 = PyTuple_GET_ITEM(rev, 6);
 			if (!PyInt_Check(p1) || !PyInt_Check(p2)) {
 				PyErr_SetString(PyExc_TypeError, "revlog parents are invalid");
-				goto release_phases;
+				goto release_phasesetlist;
 			}
 			parent_1 = (int)PyInt_AS_LONG(p1);
 			parent_2 = (int)PyInt_AS_LONG(p2);
@@ -1131,14 +1146,35 @@
 	/* Transform phase list to a python list */
 	phaseslist = PyList_New(len);
 	if (phaseslist == NULL)
-		goto release_phases;
-	for (i = 0; i < len; i++)
-		PyList_SET_ITEM(phaseslist, i, PyInt_FromLong(phases[i]));
+		goto release_phasesetlist;
+	for (i = 0; i < len; i++) {
+		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);
+			PySet_Add(phaseset, PyInt_FromLong(i));
+		}
+		PyList_SET_ITEM(phaseslist, i, PyInt_FromLong(phase));
+	}
+	ret = PyList_New(2);
+	if (ret == NULL)
+		goto release_phaseslist;
 
+	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:
+	Py_XDECREF(phaseslist);
+release_phasesetlist:
+	Py_XDECREF(phasessetlist);
 release_phases:
 	free(phases);
 release_none:
-	return phaseslist;
+	return ret;
 }
 
 static PyObject *index_headrevs(indexObject *self, PyObject *args)
@@ -2278,8 +2314,8 @@
 	 "clear the index caches"},
 	{"get", (PyCFunction)index_m_get, METH_VARARGS,
 	 "get an index entry"},
-	{"computephases", (PyCFunction)compute_phases, METH_VARARGS,
-		"compute phases"},
+	{"computephasesmapsets", (PyCFunction)compute_phases_map_sets,
+			METH_VARARGS, "compute phases"},
 	{"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
 	 "get head revisions"}, /* Can do filtering since 3.2 */
 	{"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
--- a/mercurial/patch.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/patch.py	Tue May 19 07:17:57 2015 -0500
@@ -6,6 +6,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+import collections
 import cStringIO, email, os, errno, re, posixpath, copy
 import tempfile, zlib, shutil
 # On python2.4 you have to import these by name or they fail to
@@ -15,7 +16,6 @@
 
 from i18n import _
 from node import hex, short
-import cStringIO
 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, error
 import pathutil
 
@@ -830,7 +830,7 @@
         self.hunks = []
 
     def binary(self):
-        return util.any(h.startswith('index ') for h in self.header)
+        return any(h.startswith('index ') for h in self.header)
 
     def pretty(self, fp):
         for h in self.header:
@@ -853,7 +853,7 @@
         fp.write(''.join(self.header))
 
     def allhunks(self):
-        return util.any(self.allhunks_re.match(h) for h in self.header)
+        return any(self.allhunks_re.match(h) for h in self.header)
 
     def files(self):
         match = self.diffgit_re.match(self.header[0])
@@ -872,7 +872,7 @@
         return '<header %s>' % (' '.join(map(repr, self.files())))
 
     def isnewfile(self):
-        return util.any(self.newfile_re.match(h) for h in self.header)
+        return any(self.newfile_re.match(h) for h in self.header)
 
     def special(self):
         # Special files are shown only at the header level and not at the hunk
@@ -885,7 +885,7 @@
         nocontent = len(self.header) == 2
         emptynewfile = self.isnewfile() and nocontent
         return emptynewfile or \
-                util.any(self.special_re.match(h) for h in self.header)
+                any(self.special_re.match(h) for h in self.header)
 
 class recordhunk(object):
     """patch hunk
@@ -2102,7 +2102,7 @@
 
     def lrugetfilectx():
         cache = {}
-        order = util.deque()
+        order = collections.deque()
         def getfilectx(f, ctx):
             fctx = ctx.filectx(f, filelog=cache.get(f))
             if f not in cache:
--- a/mercurial/pathutil.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/pathutil.py	Tue May 19 07:17:57 2015 -0500
@@ -152,7 +152,19 @@
                 break
             name = dirname
 
-        raise util.Abort(_("%s not under root '%s'") % (myname, root))
+        # A common mistake is to use -R, but specify a file relative to the repo
+        # instead of cwd.  Detect that case, and provide a hint to the user.
+        hint = None
+        try:
+            if cwd != root:
+                canonpath(root, root, myname, auditor)
+                hint = (_("consider using '--cwd %s'")
+                        % os.path.relpath(root, cwd))
+        except util.Abort:
+            pass
+
+        raise util.Abort(_("%s not under root '%s'") % (myname, root),
+                         hint=hint)
 
 def normasprefix(path):
     '''normalize the specified path as path prefix
--- a/mercurial/phases.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/phases.py	Tue May 19 07:17:57 2015 -0500
@@ -155,6 +155,7 @@
             # Cheap trick to allow shallow-copy without copy module
             self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
             self._phaserevs = None
+            self._phasesets = None
             self.filterunknown(repo)
             self.opener = repo.svfs
 
@@ -177,7 +178,7 @@
         nativeroots = []
         for phase in trackedphases:
             nativeroots.append(map(repo.changelog.rev, self.phaseroots[phase]))
-        return repo.changelog.computephases(nativeroots)
+        return repo.changelog.computephasesmapsets(nativeroots)
 
     def _computephaserevspure(self, repo):
         repo = repo.unfiltered()
@@ -199,7 +200,8 @@
                                       'nativephaseskillswitch'):
                     self._computephaserevspure(repo)
                 else:
-                    self._phaserevs = self._getphaserevsnative(repo)
+                    res = self._getphaserevsnative(repo)
+                    self._phaserevs, self._phasesets = res
             except AttributeError:
                 self._computephaserevspure(repo)
         return self._phaserevs
--- a/mercurial/repoview.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/repoview.py	Tue May 19 07:17:57 2015 -0500
@@ -115,16 +115,15 @@
     """
     wlock = fh = None
     try:
-        try:
-            wlock = repo.wlock(wait=False)
-            # write cache to file
-            newhash = cachehash(repo, hideable)
-            fh = repo.vfs.open(cachefile, 'w+b', atomictemp=True)
-            _writehiddencache(fh, newhash, hidden)
-        except (IOError, OSError):
-            repo.ui.debug('error writing hidden changesets cache')
-        except error.LockHeld:
-            repo.ui.debug('cannot obtain lock to write hidden changesets cache')
+        wlock = repo.wlock(wait=False)
+        # write cache to file
+        newhash = cachehash(repo, hideable)
+        fh = repo.vfs.open(cachefile, 'w+b', atomictemp=True)
+        _writehiddencache(fh, newhash, hidden)
+    except (IOError, OSError):
+        repo.ui.debug('error writing hidden changesets cache')
+    except error.LockHeld:
+        repo.ui.debug('cannot obtain lock to write hidden changesets cache')
     finally:
         if fh:
             fh.close()
@@ -197,7 +196,7 @@
     Secret and hidden changeset should not pretend to be here."""
     assert not repo.changelog.filteredrevs
     # fast check to avoid revset call on huge repo
-    if util.any(repo._phasecache.phaseroots[1:]):
+    if any(repo._phasecache.phaseroots[1:]):
         getphase = repo._phasecache.phase
         maymutable = filterrevs(repo, 'base')
         return frozenset(r for r in maymutable if getphase(repo, r))
--- a/mercurial/revlog.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/revlog.py	Tue May 19 07:17:57 2015 -0500
@@ -12,6 +12,7 @@
 """
 
 # import stuff from node for others to import from revlog
+import collections
 from node import bin, hex, nullid, nullrev
 from i18n import _
 import ancestor, mdiff, parsers, error, util, templatefilters
@@ -485,7 +486,7 @@
 
         # take all ancestors from heads that aren't in has
         missing = set()
-        visit = util.deque(r for r in heads if r not in has)
+        visit = collections.deque(r for r in heads if r not in has)
         while visit:
             r = visit.popleft()
             if r in missing:
--- a/mercurial/revset.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/revset.py	Tue May 19 07:17:57 2015 -0500
@@ -25,23 +25,22 @@
     cl = repo.changelog
 
     def iterate():
-        revqueue, revsnode = None, None
+        revs.sort(reverse=True)
+        irevs = iter(revs)
         h = []
 
-        revs.sort(reverse=True)
-        revqueue = util.deque(revs)
-        if revqueue:
-            revsnode = revqueue.popleft()
-            heapq.heappush(h, -revsnode)
+        inputrev = next(irevs, None)
+        if inputrev is not None:
+            heapq.heappush(h, -inputrev)
 
         seen = set()
         while h:
             current = -heapq.heappop(h)
+            if current == inputrev:
+                inputrev = next(irevs, None)
+                if inputrev is not None:
+                    heapq.heappush(h, -inputrev)
             if current not in seen:
-                if revsnode and current == revsnode:
-                    if revqueue:
-                        revsnode = revqueue.popleft()
-                        heapq.heappush(h, -revsnode)
                 seen.add(current)
                 yield current
                 for parent in cl.parentrevs(current)[:cut]:
@@ -334,11 +333,6 @@
         return baseset([x])
     return baseset()
 
-def symbolset(repo, subset, x):
-    if x in symbols:
-        raise error.ParseError(_("can't use %s here") % x)
-    return stringset(repo, subset, x)
-
 def rangeset(repo, subset, x, y):
     m = getset(repo, fullreposet(repo), x)
     n = getset(repo, fullreposet(repo), y)
@@ -363,7 +357,7 @@
 
 def orset(repo, subset, x, y):
     xl = getset(repo, subset, x)
-    yl = getset(repo, subset - xl, y)
+    yl = getset(repo, subset, y)
     return xl + yl
 
 def notset(repo, subset, x):
@@ -1127,7 +1121,7 @@
 
     def matches(r):
         c = repo[r]
-        return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
+        return any(kw in encoding.lower(t) for t in c.files() + [c.user(),
             c.description()])
 
     return subset.filter(matches)
@@ -1151,12 +1145,11 @@
     result = []
     it = iter(os)
     for x in xrange(lim):
-        try:
-            y = it.next()
-            if y in ss:
-                result.append(y)
-        except (StopIteration):
+        y = next(it, None)
+        if y is None:
             break
+        elif y in ss:
+            result.append(y)
     return baseset(result)
 
 def last(repo, subset, x):
@@ -1179,12 +1172,11 @@
     result = []
     it = iter(os)
     for x in xrange(lim):
-        try:
-            y = it.next()
-            if y in ss:
-                result.append(y)
-        except (StopIteration):
+        y = next(it, None)
+        if y is None:
             break
+        elif y in ss:
+            result.append(y)
     return baseset(result)
 
 def maxrev(repo, subset, x):
@@ -1486,6 +1478,22 @@
     except error.RepoLookupError:
         return baseset()
 
+def _notpublic(repo, subset, x):
+    """``_notpublic()``
+    Changeset not in public phase."""
+    # i18n: "public" is a keyword
+    getargs(x, 0, 0, _("_notpublic takes no arguments"))
+    if repo._phasecache._phasesets:
+        s = set()
+        for u in repo._phasecache._phasesets[1:]:
+            s.update(u)
+        return subset & s
+    else:
+        phase = repo._phasecache.phase
+        target = phases.public
+        condition = lambda r: phase(repo, r) != target
+        return subset.filter(condition, cache=False)
+
 def public(repo, subset, x):
     """``public()``
     Changeset in public phase."""
@@ -1684,7 +1692,7 @@
     Changesets in set with no parent changeset in set.
     """
     s = getset(repo, fullreposet(repo), x)
-    subset = baseset([r for r in s if r in subset])
+    subset = subset & s# baseset([r for r in s if r in subset])
     cs = _children(repo, subset, s)
     return subset - cs
 
@@ -1787,7 +1795,7 @@
             return s.added or s.modified or s.removed
 
         if s.added:
-            return util.any(submatches(c.substate.keys()))
+            return any(submatches(c.substate.keys()))
 
         if s.modified:
             subs = set(c.p1().substate.keys())
@@ -1798,7 +1806,7 @@
                     return True
 
         if s.removed:
-            return util.any(submatches(c.p1().substate.keys()))
+            return any(submatches(c.p1().substate.keys()))
 
         return False
 
@@ -1992,6 +2000,7 @@
     "parents": parents,
     "present": present,
     "public": public,
+    "_notpublic": _notpublic,
     "remote": remote,
     "removes": removes,
     "rev": rev,
@@ -2066,6 +2075,7 @@
     "parents",
     "present",
     "public",
+    "_notpublic",
     "remote",
     "removes",
     "rev",
@@ -2088,7 +2098,7 @@
     "range": rangeset,
     "dagrange": dagrange,
     "string": stringset,
-    "symbol": symbolset,
+    "symbol": stringset,
     "and": andset,
     "or": orset,
     "not": notset,
@@ -2097,7 +2107,6 @@
     "ancestor": ancestorspec,
     "parent": parentspec,
     "parentpost": p1,
-    "only": only,
 }
 
 def optimize(x, small):
@@ -2158,8 +2167,14 @@
             wb, wa = wa, wb
         return max(wa, wb), (op, ta, tb)
     elif op == 'not':
-        o = optimize(x[1], not small)
-        return o[0], (op, o[1])
+        # Optimize not public() to _notpublic() because we have a fast version
+        if x[1] == ('func', ('symbol', 'public'), None):
+            newsym =  ('func', ('symbol', '_notpublic'), None)
+            o = optimize(newsym, not small)
+            return o[0], o[1]
+        else:
+            o = optimize(x[1], not small)
+            return o[0], (op, o[1])
     elif op == 'parentpost':
         o = optimize(x[1], small)
         return o[0], (op, o[1])
@@ -2939,6 +2954,42 @@
     def __repr__(self):
         return '<%s %r>' % (type(self).__name__, self._subset)
 
+def _iterordered(ascending, iter1, iter2):
+    """produce an ordered iteration from two iterators with the same order
+
+    The ascending is used to indicated the iteration direction.
+    """
+    choice = max
+    if ascending:
+        choice = min
+
+    val1 = None
+    val2 = None
+    try:
+        # Consume both iterators in an ordered way until one is empty
+        while True:
+            if val1 is None:
+                val1 = iter1.next()
+            if val2 is None:
+                val2 = iter2.next()
+            next = choice(val1, val2)
+            yield next
+            if val1 == next:
+                val1 = None
+            if val2 == next:
+                val2 = None
+    except StopIteration:
+        # Flush any remaining values and consume the other one
+        it = iter2
+        if val1 is not None:
+            yield val1
+            it = iter1
+        elif val2 is not None:
+            # might have been equality and both are empty
+            yield val2
+        for val in it:
+            yield val
+
 class addset(abstractsmartset):
     """Represent the addition of two sets
 
@@ -2948,6 +2999,64 @@
     If the ascending attribute is set, that means the two structures are
     ordered in either an ascending or descending way. Therefore, we can add
     them maintaining the order by iterating over both at the same time
+
+    >>> xs = baseset([0, 3, 2])
+    >>> ys = baseset([5, 2, 4])
+
+    >>> rs = addset(xs, ys)
+    >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
+    (True, True, False, True, 0, 4)
+    >>> rs = addset(xs, baseset([]))
+    >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
+    (True, True, False, 0, 2)
+    >>> rs = addset(baseset([]), baseset([]))
+    >>> bool(rs), 0 in rs, rs.first(), rs.last()
+    (False, False, None, None)
+
+    iterate unsorted:
+    >>> rs = addset(xs, ys)
+    >>> [x for x in rs]  # without _genlist
+    [0, 3, 2, 5, 4]
+    >>> assert not rs._genlist
+    >>> len(rs)
+    5
+    >>> [x for x in rs]  # with _genlist
+    [0, 3, 2, 5, 4]
+    >>> assert rs._genlist
+
+    iterate ascending:
+    >>> rs = addset(xs, ys, ascending=True)
+    >>> [x for x in rs], [x for x in rs.fastasc()]  # without _asclist
+    ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
+    >>> assert not rs._asclist
+    >>> len(rs)
+    5
+    >>> [x for x in rs], [x for x in rs.fastasc()]
+    ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
+    >>> assert rs._asclist
+
+    iterate descending:
+    >>> rs = addset(xs, ys, ascending=False)
+    >>> [x for x in rs], [x for x in rs.fastdesc()]  # without _asclist
+    ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
+    >>> assert not rs._asclist
+    >>> len(rs)
+    5
+    >>> [x for x in rs], [x for x in rs.fastdesc()]
+    ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
+    >>> assert rs._asclist
+
+    iterate ascending without fastasc:
+    >>> rs = addset(xs, generatorset(ys), ascending=True)
+    >>> assert rs.fastasc is None
+    >>> [x for x in rs]
+    [0, 2, 3, 4, 5]
+
+    iterate descending without fastdesc:
+    >>> rs = addset(generatorset(xs), ys, ascending=False)
+    >>> assert rs.fastdesc is None
+    >>> [x for x in rs]
+    [5, 4, 3, 2, 0]
     """
     def __init__(self, revs1, revs2, ascending=None):
         self._r1 = revs1
@@ -2966,10 +3075,10 @@
     @util.propertycache
     def _list(self):
         if not self._genlist:
-            self._genlist = baseset(self._iterator())
+            self._genlist = baseset(iter(self))
         return self._genlist
 
-    def _iterator(self):
+    def __iter__(self):
         """Iterate over both collections without repeating elements
 
         If the ascending attribute is not set, iterate over the first one and
@@ -2980,35 +3089,41 @@
         same time, yielding only one value at a time in the given order.
         """
         if self._ascending is None:
-            def gen():
+            if self._genlist:
+                return iter(self._genlist)
+            def arbitraryordergen():
                 for r in self._r1:
                     yield r
                 inr1 = self._r1.__contains__
                 for r in self._r2:
                     if not inr1(r):
                         yield r
-            gen = gen()
-        else:
-            iter1 = iter(self._r1)
-            iter2 = iter(self._r2)
-            gen = self._iterordered(self._ascending, iter1, iter2)
-        return gen
-
-    def __iter__(self):
-        if self._ascending is None:
-            if self._genlist:
-                return iter(self._genlist)
-            return iter(self._iterator())
+            return arbitraryordergen()
+        # try to use our own fast iterator if it exists
         self._trysetasclist()
         if self._ascending:
-            it = self.fastasc
+            attr = 'fastasc'
         else:
-            it = self.fastdesc
-        if it is None:
-            # consume the gen and try again
-            self._list
-            return iter(self)
-        return it()
+            attr = 'fastdesc'
+        it = getattr(self, attr)
+        if it is not None:
+            return it()
+        # maybe half of the component supports fast
+        # get iterator for _r1
+        iter1 = getattr(self._r1, attr)
+        if iter1 is None:
+            # let's avoid side effect (not sure it matters)
+            iter1 = iter(sorted(self._r1, reverse=not self._ascending))
+        else:
+            iter1 = iter1()
+        # get iterator for _r2
+        iter2 = getattr(self._r2, attr)
+        if iter2 is None:
+            # let's avoid side effect (not sure it matters)
+            iter2 = iter(sorted(self._r2, reverse=not self._ascending))
+        else:
+            iter2 = iter2()
+        return _iterordered(self._ascending, iter1, iter2)
 
     def _trysetasclist(self):
         """populate the _asclist attribute if possible and necessary"""
@@ -3024,7 +3139,7 @@
         iter2 = self._r2.fastasc
         if None in (iter1, iter2):
             return None
-        return lambda: self._iterordered(True, iter1(), iter2())
+        return lambda: _iterordered(True, iter1(), iter2())
 
     @property
     def fastdesc(self):
@@ -3035,48 +3150,7 @@
         iter2 = self._r2.fastdesc
         if None in (iter1, iter2):
             return None
-        return lambda: self._iterordered(False, iter1(), iter2())
-
-    def _iterordered(self, ascending, iter1, iter2):
-        """produce an ordered iteration from two iterators with the same order
-
-        The ascending is used to indicated the iteration direction.
-        """
-        choice = max
-        if ascending:
-            choice = min
-
-        val1 = None
-        val2 = None
-
-        choice = max
-        if ascending:
-            choice = min
-        try:
-            # Consume both iterators in an ordered way until one is
-            # empty
-            while True:
-                if val1 is None:
-                    val1 = iter1.next()
-                if val2 is None:
-                    val2 = iter2.next()
-                next = choice(val1, val2)
-                yield next
-                if val1 == next:
-                    val1 = None
-                if val2 == next:
-                    val2 = None
-        except StopIteration:
-            # Flush any remaining values and consume the other one
-            it = iter2
-            if val1 is not None:
-                yield val1
-                it = iter1
-            elif val2 is not None:
-                # might have been equality and both are empty
-                yield val2
-            for val in it:
-                yield val
+        return lambda: _iterordered(False, iter1(), iter2())
 
     def __contains__(self, x):
         return x in self._r1 or x in self._r2
@@ -3143,7 +3217,12 @@
                 self.__contains__ = self._desccontains
 
     def __nonzero__(self):
-        for r in self:
+        # Do not use 'for r in self' because it will enforce the iteration
+        # order (default ascending), possibly unrolling a whole descending
+        # iterator.
+        if self._genlist:
+            return True
+        for r in self._consumegen():
             return True
         return False
 
@@ -3267,9 +3346,7 @@
             for x in self._consumegen():
                 pass
             return self.first()
-        if self:
-            return it().next()
-        return None
+        return next(it(), None)
 
     def last(self):
         if self._ascending:
@@ -3281,9 +3358,7 @@
             for x in self._consumegen():
                 pass
             return self.first()
-        if self:
-            return it().next()
-        return None
+        return next(it(), None)
 
     def __repr__(self):
         d = {False: '-', True: '+'}[self._ascending]
--- a/mercurial/scmutil.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/scmutil.py	Tue May 19 07:17:57 2015 -0500
@@ -803,7 +803,7 @@
         pats = expandpats(pats or [])
 
     m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
-                         default)
+                         default, listsubrepos=opts.get('subrepos'))
     def badfn(f, msg):
         ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
     m.bad = badfn
@@ -1011,6 +1011,12 @@
                    " for more information"))
     return requirements
 
+def writerequires(opener, requirements):
+    reqfile = opener("requires", "w")
+    for r in sorted(requirements):
+        reqfile.write("%s\n" % r)
+    reqfile.close()
+
 class filecachesubentry(object):
     def __init__(self, path, stat):
         self.path = path
--- a/mercurial/setdiscovery.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/setdiscovery.py	Tue May 19 07:17:57 2015 -0500
@@ -40,6 +40,7 @@
 classified with it (since all ancestors or descendants will be marked as well).
 """
 
+import collections
 from node import nullid, nullrev
 from i18n import _
 import random
@@ -65,7 +66,7 @@
     else:
         heads = dag.heads()
     dist = {}
-    visit = util.deque(heads)
+    visit = collections.deque(heads)
     seen = set()
     factor = 1
     while visit:
@@ -168,7 +169,7 @@
         ui.debug("all remote heads known locally\n")
         return (srvheadhashes, False, srvheadhashes,)
 
-    if sample and len(ownheads) <= initialsamplesize and util.all(yesno):
+    if sample and len(ownheads) <= initialsamplesize and all(yesno):
         ui.note(_("all local heads known remotely\n"))
         ownheadhashes = dag.externalizeall(ownheads)
         return (ownheadhashes, True, srvheadhashes,)
--- a/mercurial/store.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/store.py	Tue May 19 07:17:57 2015 -0500
@@ -187,7 +187,7 @@
 
 def _hashencode(path, dotencode):
     digest = _sha(path).hexdigest()
-    le = lowerencode(path).split('/')[1:]
+    le = lowerencode(path[5:]).split('/') # skips prefix 'data/' or 'meta/'
     parts = _auxencode(le, dotencode)
     basename = parts[-1]
     _root, ext = os.path.splitext(basename)
--- a/mercurial/subrepo.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/subrepo.py	Tue May 19 07:17:57 2015 -0500
@@ -500,6 +500,10 @@
         """return file flags"""
         return ''
 
+    def getfileset(self, expr):
+        """Resolve the fileset expression for this repo"""
+        return set()
+
     def printfiles(self, ui, m, fm, fmt):
         """handle the files command for this subrepo"""
         return 1
@@ -592,21 +596,14 @@
     def _storeclean(self, path):
         clean = True
         itercache = self._calcstorehash(path)
-        try:
-            for filehash in self._readstorehashcache(path):
-                if filehash != itercache.next():
-                    clean = False
-                    break
-        except StopIteration:
+        for filehash in self._readstorehashcache(path):
+            if filehash != next(itercache, None):
+                clean = False
+                break
+        if clean:
+            # if not empty:
             # the cached and current pull states have a different size
-            clean = False
-        if clean:
-            try:
-                itercache.next()
-                # the cached and current pull states have a different size
-                clean = False
-            except StopIteration:
-                pass
+            clean = next(itercache, None) is None
         return clean
 
     def _calcstorehash(self, remotepath):
@@ -917,6 +914,26 @@
             ctx = self._repo[rev]
         return cmdutil.files(ui, ctx, m, fm, fmt, True)
 
+    @annotatesubrepoerror
+    def getfileset(self, expr):
+        if self._ctx.rev() is None:
+            ctx = self._repo[None]
+        else:
+            rev = self._state[1]
+            ctx = self._repo[rev]
+
+        files = ctx.getfileset(expr)
+
+        for subpath in ctx.substate:
+            sub = ctx.sub(subpath)
+
+            try:
+                files.extend(subpath + '/' + f for f in sub.getfileset(expr))
+            except error.LookupError:
+                self.ui.status(_("skipping missing subrepository: %s\n")
+                               % self.wvfs.reljoin(reporelpath(self), subpath))
+        return files
+
     def walk(self, match):
         ctx = self._repo[None]
         return ctx.walk(match)
@@ -1711,7 +1728,7 @@
         modified, added, removed = [], [], []
         self._gitupdatestat()
         if rev2:
-            command = ['diff-tree', rev1, rev2]
+            command = ['diff-tree', '-r', rev1, rev2]
         else:
             command = ['diff-index', rev1]
         out = self._gitcommand(command)
--- a/mercurial/tags.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/tags.py	Tue May 19 07:17:57 2015 -0500
@@ -509,26 +509,25 @@
             return
 
         try:
+            f = repo.vfs.open(_fnodescachefile, 'ab')
             try:
-                f = repo.vfs.open(_fnodescachefile, 'ab')
-                try:
-                    # if the file has been truncated
-                    actualoffset = f.tell()
-                    if actualoffset < self._dirtyoffset:
-                        self._dirtyoffset = actualoffset
-                        data = self._raw[self._dirtyoffset:]
-                    f.seek(self._dirtyoffset)
-                    f.truncate()
-                    repo.ui.log('tagscache',
-                                'writing %d bytes to %s\n' % (
-                                len(data), _fnodescachefile))
-                    f.write(data)
-                    self._dirtyoffset = None
-                finally:
-                    f.close()
-            except (IOError, OSError), inst:
+                # if the file has been truncated
+                actualoffset = f.tell()
+                if actualoffset < self._dirtyoffset:
+                    self._dirtyoffset = actualoffset
+                    data = self._raw[self._dirtyoffset:]
+                f.seek(self._dirtyoffset)
+                f.truncate()
                 repo.ui.log('tagscache',
-                            "couldn't write %s: %s\n" % (
-                            _fnodescachefile, inst))
+                            'writing %d bytes to %s\n' % (
+                            len(data), _fnodescachefile))
+                f.write(data)
+                self._dirtyoffset = None
+            finally:
+                f.close()
+        except (IOError, OSError), inst:
+            repo.ui.log('tagscache',
+                        "couldn't write %s: %s\n" % (
+                        _fnodescachefile, inst))
         finally:
             lock.release()
--- a/mercurial/templatefilters.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templatefilters.py	Tue May 19 07:17:57 2015 -0500
@@ -326,6 +326,8 @@
     """
     if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
         return "".join([stringify(t) for t in thing if t is not None])
+    if thing is None:
+        return ""
     return str(thing)
 
 def strip(text):
--- a/mercurial/templatekw.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templatekw.py	Tue May 19 07:17:57 2015 -0500
@@ -210,7 +210,7 @@
     """
     repo = args['ctx']._repo
     bookmarks = args['ctx'].bookmarks()
-    current = repo._bookmarkcurrent
+    current = repo._activebookmark
     makemap = lambda v: {'bookmark': v, 'current': current}
     f = _showlist('bookmark', bookmarks, **args)
     return _hybrid(f, bookmarks, makemap, lambda x: x['bookmark'])
@@ -221,15 +221,21 @@
     childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
     return showlist('children', childrevs, element='child', **args)
 
+# Deprecated, but kept alive for help generation a purpose.
 def showcurrentbookmark(**args):
     """:currentbookmark: String. The active bookmark, if it is
+    associated with the changeset (DEPRECATED)"""
+    return showactivebookmark(**args)
+
+def showactivebookmark(**args):
+    """:activetbookmark: String. The active bookmark, if it is
     associated with the changeset"""
     import bookmarks as bookmarks # to avoid circular import issues
     repo = args['repo']
-    if bookmarks.iscurrent(repo):
-        current = repo._bookmarkcurrent
-        if current in args['ctx'].bookmarks():
-            return current
+    if bookmarks.isactivewdirparent(repo):
+        active = repo._activebookmark
+        if active in args['ctx'].bookmarks():
+            return active
     return ''
 
 def showdate(repo, ctx, templ, **args):
@@ -418,12 +424,14 @@
 # cache - a cache dictionary for the whole templater run
 # revcache - a cache dictionary for the current revision
 keywords = {
+    'activebookmark': showactivebookmark,
     'author': showauthor,
     'bisect': showbisect,
     'branch': showbranch,
     'branches': showbranches,
     'bookmarks': showbookmarks,
     'children': showchildren,
+    # currentbookmark is deprecated
     'currentbookmark': showcurrentbookmark,
     'date': showdate,
     'desc': showdescription,
--- a/mercurial/templater.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templater.py	Tue May 19 07:17:57 2015 -0500
@@ -20,6 +20,7 @@
     "|": (5, None, ("|", 5)),
     "%": (6, None, ("%", 6)),
     ")": (0, None, None),
+    "integer": (0, ("integer",), None),
     "symbol": (0, ("symbol",), None),
     "string": (0, ("string",), None),
     "rawstring": (0, ("rawstring",), None),
@@ -59,6 +60,20 @@
                 pos += 1
             else:
                 raise error.ParseError(_("unterminated string"), s)
+        elif c.isdigit() or c == '-':
+            s = pos
+            if c == '-': # simply take negate operator as part of integer
+                pos += 1
+            if pos >= end or not program[pos].isdigit():
+                raise error.ParseError(_("integer literal without digits"), s)
+            pos += 1
+            while pos < end:
+                d = program[pos]
+                if not d.isdigit():
+                    break
+                pos += 1
+            yield ('integer', program[s:pos], s)
+            pos -= 1
         elif c.isalnum() or c in '_':
             s = pos
             pos += 1
@@ -100,12 +115,12 @@
         parseres, pos = p.parse(pd)
         parsed.append(parseres)
 
-    return [compileexp(e, context) for e in parsed]
+    return [compileexp(e, context, methods) for e in parsed]
 
-def compileexp(exp, context):
+def compileexp(exp, context, curmethods):
     t = exp[0]
-    if t in methods:
-        return methods[t](exp, context)
+    if t in curmethods:
+        return curmethods[t](exp, context)
     raise error.ParseError(_("unknown method '%s'") % t)
 
 # template evaluation
@@ -135,6 +150,9 @@
         return context._load(exp[1])
     raise error.ParseError(_("expected template specifier"))
 
+def runinteger(context, mapping, data):
+    return int(data)
+
 def runstring(context, mapping, data):
     return data.decode("string-escape")
 
@@ -157,7 +175,7 @@
     return v
 
 def buildfilter(exp, context):
-    func, data = compileexp(exp[1], context)
+    func, data = compileexp(exp[1], context, methods)
     filt = getfilter(exp[2], context)
     return (runfilter, (func, data, filt))
 
@@ -179,7 +197,7 @@
                            "keyword '%s'") % (filt.func_name, dt))
 
 def buildmap(exp, context):
-    func, data = compileexp(exp[1], context)
+    func, data = compileexp(exp[1], context, methods)
     ctmpl = gettemplate(exp[2], context)
     return (runmap, (func, data, ctmpl))
 
@@ -208,7 +226,7 @@
 
 def buildfunc(exp, context):
     n = getsymbol(exp[1])
-    args = [compileexp(x, context) for x in getlist(exp[2])]
+    args = [compileexp(x, context, exprmethods) for x in getlist(exp[2])]
     if n in funcs:
         f = funcs[n]
         return (f, args)
@@ -551,8 +569,7 @@
         num = int(stringify(args[0][0](context, mapping, args[0][1])))
     except ValueError:
         # i18n: "word" is a keyword
-        raise error.ParseError(
-                _("Use strings like '3' for numbers passed to word function"))
+        raise error.ParseError(_("word expects an integer index"))
     text = stringify(args[1][0](context, mapping, args[1][1]))
     if len(args) == 3:
         splitter = stringify(args[2][0](context, mapping, args[2][1]))
@@ -565,17 +582,23 @@
     else:
         return tokens[num]
 
-methods = {
+# methods to interpret function arguments or inner expressions (e.g. {_(x)})
+exprmethods = {
+    "integer": lambda e, c: (runinteger, e[1]),
     "string": lambda e, c: (runstring, e[1]),
     "rawstring": lambda e, c: (runrawstring, e[1]),
     "symbol": lambda e, c: (runsymbol, e[1]),
-    "group": lambda e, c: compileexp(e[1], c),
+    "group": lambda e, c: compileexp(e[1], c, exprmethods),
 #    ".": buildmember,
     "|": buildfilter,
     "%": buildmap,
     "func": buildfunc,
     }
 
+# methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
+methods = exprmethods.copy()
+methods["integer"] = exprmethods["symbol"]  # '{1}' as variable
+
 funcs = {
     "date": date,
     "diff": diff,
@@ -618,14 +641,25 @@
                 for j in _flatten(i):
                     yield j
 
-def parsestring(s, quoted=True):
-    '''unwrap quotes if quoted is True'''
-    if quoted:
-        if len(s) < 2 or s[0] != s[-1]:
-            raise SyntaxError(_('unmatched quotes'))
-        return s[1:-1]
-
-    return s
+def unquotestring(s):
+    '''unwrap quotes'''
+    if len(s) < 2 or s[0] != s[-1]:
+        raise SyntaxError(_('unmatched quotes'))
+    # de-backslash-ify only <\">. it is invalid syntax in non-string part of
+    # template, but we are likely to escape <"> in quoted string and it was
+    # accepted before, thanks to issue4290. <\\"> is unmodified because it
+    # is ambiguous and it was processed as such before 2.8.1.
+    #
+    #  template  result
+    #  --------- ------------------------
+    #  {\"\"}    parse error
+    #  "{""}"    {""} -> <>
+    #  "{\"\"}"  {""} -> <>
+    #  {"\""}    {"\""} -> <">
+    #  '{"\""}'  {"\""} -> <">
+    #  "{"\""}"  parse error (don't care)
+    q = s[0]
+    return s[1:-1].replace('\\\\' + q, '\\\\\\' + q).replace('\\' + q, q)
 
 class engine(object):
     '''template expansion engine.
@@ -709,7 +743,7 @@
             raise util.Abort(_("style '%s' not found") % mapfile,
                              hint=_("available styles: %s") % stylelist())
 
-        conf = config.config()
+        conf = config.config(includepaths=templatepaths())
         conf.read(mapfile)
 
         for key, val in conf[''].items():
@@ -717,7 +751,7 @@
                 raise SyntaxError(_('%s: missing value') % conf.source('', key))
             if val[0] in "'\"":
                 try:
-                    self.cache[key] = parsestring(val)
+                    self.cache[key] = unquotestring(val)
                 except SyntaxError, inst:
                     raise SyntaxError('%s: %s' %
                                       (conf.source('', key), inst.args[0]))
--- a/mercurial/templates/gitweb/fileannotate.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/gitweb/fileannotate.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -39,19 +39,23 @@
 <table cellspacing="0">
 <tr>
  <td>author</td>
- <td>{author|obfuscate}</td></tr>
+ <td>{author|obfuscate}</td>
+</tr>
 <tr>
  <td></td>
- <td class="date age">{date|rfc822date}</td></tr>
+ <td class="date age">{date|rfc822date}</td>
+</tr>
 {branch%filerevbranch}
 <tr>
  <td>changeset {rev}</td>
- <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
+ <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
+</tr>
 {parent%fileannotateparent}
 {child%fileannotatechild}
 <tr>
  <td>permissions</td>
- <td style="font-family:monospace">{permissions|permissions}</td></tr>
+ <td style="font-family:monospace">{permissions|permissions}</td>
+</tr>
 </table>
 </div>
 
--- a/mercurial/templates/gitweb/filecomparison.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/gitweb/filecomparison.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -39,7 +39,8 @@
 {branch%filerevbranch}
 <tr>
  <td>changeset {rev}</td>
- <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
+ <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
+</tr>
 {parent%filecompparent}
 {child%filecompchild}
 </table>
--- a/mercurial/templates/gitweb/filediff.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/gitweb/filediff.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -39,7 +39,8 @@
 {branch%filerevbranch}
 <tr>
  <td>changeset {rev}</td>
- <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
+ <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
+</tr>
 {parent%filediffparent}
 {child%filediffchild}
 </table>
--- a/mercurial/templates/gitweb/filerevision.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/gitweb/filerevision.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -39,19 +39,23 @@
 <table cellspacing="0">
 <tr>
  <td>author</td>
- <td>{author|obfuscate}</td></tr>
+ <td>{author|obfuscate}</td>
+</tr>
 <tr>
  <td></td>
- <td class="date age">{date|rfc822date}</td></tr>
+ <td class="date age">{date|rfc822date}</td>
+</tr>
 {branch%filerevbranch}
 <tr>
  <td>changeset {rev}</td>
- <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
+ <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
+</tr>
 {parent%filerevparent}
 {child%filerevchild}
 <tr>
  <td>permissions</td>
- <td style="font-family:monospace">{permissions|permissions}</td></tr>
+ <td style="font-family:monospace">{permissions|permissions}</td>
+</tr>
 </table>
 </div>
 
--- a/mercurial/templates/gitweb/map	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/gitweb/map	Tue May 19 07:17:57 2015 -0500
@@ -293,11 +293,16 @@
     <td>
       <a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
         <b>{desc|strip|firstline|escape|nonempty}</b>
+        <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span>
       </a>
     </td>
     <td class="link">
-      <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a>&nbsp;|&nbsp;<a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> {rename%filelogrename}</td>
-    </tr>'
+      <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
+      <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
+      <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
+      {rename%filelogrename}
+    </td>
+  </tr>'
 archiveentry = ' | <a href="{url|urlescape}archive/{node|short}{extension}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a> '
 indexentry = '
   <tr class="parity{parity}">
--- a/mercurial/templates/map-cmdline.bisect	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/map-cmdline.bisect	Tue May 19 07:17:57 2015 -0500
@@ -1,25 +1,14 @@
-changeset = 'changeset:   {rev}:{node|short}\nbisect:      {bisect}\n{branches}{bookmarks}{tags}{parents}user:        {author}\ndate:        {date|date}\nsummary:     {desc|firstline}\n\n'
-changeset_quiet = '{bisect|shortbisect} {rev}:{node|short}\n'
-changeset_verbose = 'changeset:   {rev}:{node|short}\nbisect:      {bisect}\n{branches}{bookmarks}{tags}{parents}user:        {author}\ndate:        {date|date}\n{files}{file_copies_switch}description:\n{desc|strip}\n\n\n'
-changeset_debug = 'changeset:   {rev}:{node}\nbisect:      {bisect}\n{branches}{bookmarks}{tags}{parents}{manifest}user:        {author}\ndate:        {date|date}\n{file_mods}{file_adds}{file_dels}{file_copies_switch}{extras}description:\n{desc|strip}\n\n\n'
-start_files = 'files:      '
-file = ' {file}'
-end_files = '\n'
-start_file_mods = 'files:      '
-file_mod = ' {file_mod}'
-end_file_mods = '\n'
-start_file_adds = 'files+:     '
-file_add = ' {file_add}'
-end_file_adds = '\n'
-start_file_dels = 'files-:     '
-file_del = ' {file_del}'
-end_file_dels = '\n'
-start_file_copies = 'copies:     '
-file_copy = ' {name} ({source})'
-end_file_copies = '\n'
-parent = 'parent:      {rev}:{node|formatnode}\n'
-manifest = 'manifest:    {rev}:{node}\n'
-branch = 'branch:      {branch}\n'
-tag = 'tag:         {tag}\n'
-bookmark = 'bookmark:    {bookmark}\n'
-extra = 'extra:       {key}={value|stringescape}\n'
+%include map-cmdline.default
+
+changeset = '{cset}{lbisect}{branches}{bookmarks}{tags}{parents}{user}{ldate}{summary}\n'
+changeset_quiet = '{lshortbisect} {rev}:{node|short}\n'
+changeset_verbose = '{cset}{lbisect}{branches}{bookmarks}{tags}{parents}{user}{ldate}{lfiles}{lfile_copies_switch}{description}\n'
+changeset_debug = '{fullcset}{lbisect}{branches}{bookmarks}{tags}{lphase}{parents}{manifest}{user}{ldate}{lfile_mods}{lfile_adds}{lfile_dels}{lfile_copies_switch}{extras}{description}\n'
+
+# We take the zeroth word in order to omit "(implicit)" in the label
+bisectlabel = ' bisect.{word('0', bisect)}'
+
+lbisect ='{label("log.bisect{if(bisect, bisectlabel)}",
+                                "bisect:      {bisect}\n")}'
+lshortbisect ='{label("log.bisect{if(bisect, bisectlabel)}",
+                                    "{bisect|shortbisect}")}'
--- a/mercurial/templates/map-cmdline.phases	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/map-cmdline.phases	Tue May 19 07:17:57 2015 -0500
@@ -1,73 +1,3 @@
-# Base templates. Due to name clashes with existing keywords, we have
-# to replace some keywords with 'lkeyword', for 'labelled keyword'
+%include map-cmdline.default
 changeset = '{cset}{branches}{bookmarks}{tags}{lphase}{parents}{user}{ldate}{summary}\n'
-changeset_quiet = '{lnode}'
 changeset_verbose = '{cset}{branches}{bookmarks}{tags}{lphase}{parents}{user}{ldate}{lfiles}{lfile_copies_switch}{description}\n'
-changeset_debug = '{fullcset}{branches}{bookmarks}{tags}{lphase}{parents}{manifest}{user}{ldate}{lfile_mods}{lfile_adds}{lfile_dels}{lfile_copies_switch}{extras}{description}\n'
-
-# File templates
-lfiles = '{if(files,
-               label("ui.note log.files",
-                     "files:       {files}\n"))}'
-
-lfile_mods = '{if(file_mods,
-                  label("ui.debug log.files",
-                        "files:       {file_mods}\n"))}'
-
-lfile_adds = '{if(file_adds,
-                  label("ui.debug log.files",
-                        "files+:      {file_adds}\n"))}'
-
-lfile_dels = '{if(file_dels,
-                  label("ui.debug log.files",
-                        "files-:      {file_dels}\n"))}'
-
-lfile_copies_switch = '{if(file_copies_switch,
-                           label("ui.note log.copies",
-                                 "copies:     {file_copies_switch
-                                               % ' {name} ({source})'}\n"))}'
-
-# General templates
-cset = '{label("log.changeset changeset.{phase}",
-               "changeset:   {rev}:{node|short}")}\n'
-
-lphase = '{label("log.phase",
-                 "phase:       {phase}")}\n'
-
-fullcset = '{label("log.changeset changeset.{phase}",
-                   "changeset:   {rev}:{node}")}\n'
-
-parent = '{label("log.parent changeset.{phase}",
-                  "parent:      {rev}:{node|formatnode}")}\n'
-
-lnode = '{label("log.node",
-                "{rev}:{node|short}")}\n'
-
-manifest = '{label("ui.debug log.manifest",
-                   "manifest:    {rev}:{node}")}\n'
-
-branch = '{label("log.branch",
-                 "branch:      {branch}")}\n'
-
-tag = '{label("log.tag",
-              "tag:         {tag}")}\n'
-
-bookmark = '{label("log.bookmark",
-                   "bookmark:    {bookmark}")}\n'
-
-user = '{label("log.user",
-               "user:        {author}")}\n'
-
-summary = '{if(desc|strip, "{label('log.summary',
-                                   'summary:     {desc|firstline}')}\n")}'
-
-ldate = '{label("log.date",
-                "date:        {date|date}")}\n'
-
-extra = '{label("ui.debug log.extra",
-                "extra:       {key}={value|stringescape}")}\n'
-
-description = '{if(desc|strip, "{label('ui.note log.description',
-                                       'description:')}
-                                {label('ui.note log.description',
-                                       '{desc|strip}')}\n\n")}'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templates/map-cmdline.status	Tue May 19 07:17:57 2015 -0500
@@ -0,0 +1,25 @@
+%include map-cmdline.default
+
+# Override base templates
+changeset = '{cset}{branches}{bookmarks}{tags}{parents}{user}{ldate}{summary}{lfiles}\n'
+changeset_verbose = '{cset}{branches}{bookmarks}{tags}{parents}{user}{ldate}{description}{lfiles}\n'
+changeset_debug = '{fullcset}{branches}{bookmarks}{tags}{lphase}{parents}{manifest}{user}{ldate}{extras}{description}{lfiles}\n'
+
+# Override the file templates
+lfiles = '{if(files,
+              label('ui.note log.files',
+                    'files:\n'))}{lfile_mods}{lfile_adds}{lfile_copies_switch}{lfile_dels}'
+
+# Exclude copied files, will display those in lfile_copies_switch
+lfile_adds  = '{file_adds % "{ifcontains(file, file_copies_switch,
+                                         '',
+                                         '{lfile_add}')}"}'
+lfile_add = '{label("status.added", "A {file}\n")}'
+
+lfile_copies_switch = '{file_copies_switch % "{lfile_copy_orig}{lfile_copy_dest}"'
+lfile_copy_orig = '{label("status.added", "A {name}\n")}'
+lfile_copy_dest = '{label("status.copied", "  {source}\n")}'
+
+lfile_mods = '{file_mods % "{label('status.modified', 'M {file}\n')}"}'
+
+lfile_dels = '{file_dels % "{label('status.removed', 'R {file}\n')}"}'
--- a/mercurial/templates/monoblue/map	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/monoblue/map	Tue May 19 07:17:57 2015 -0500
@@ -244,7 +244,12 @@
 filelogentry = '
   <tr class="parity{parity}">
     <td class="nowrap age">{date|rfc822date}</td>
-    <td><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a></td>
+    <td>
+      <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
+        {desc|strip|firstline|escape|nonempty}
+        <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span>
+      </a>
+    </td>
     <td class="nowrap">
       <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a>&nbsp;|&nbsp;<a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
       {rename%filelogrename}
--- a/mercurial/templates/paper/fileannotate.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/paper/fileannotate.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -37,7 +37,7 @@
 
 <div class="main">
 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
-<h3>annotate {file|escape} @ {rev}:{node|short}</h3>
+<h3>annotate {file|escape} @ {rev}:{node|short} {branch%changelogbranchname}{tags%changelogtag}{bookmarks%changelogtag}</h3>
 
 <form class="search" action="{url|urlescape}log">
 {sessionvars%hiddenformentry}
--- a/mercurial/templates/paper/filecomparison.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/paper/filecomparison.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -36,7 +36,7 @@
 
 <div class="main">
 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
-<h3>comparison {file|escape} @ {rev}:{node|short}</h3>
+<h3>comparison {file|escape} @ {rev}:{node|short} {branch%changelogbranchname}{tags%changelogtag}{bookmarks%changelogtag}</h3>
 
 <form class="search" action="{url|urlescape}log">
 <p>{sessionvars%hiddenformentry}</p>
--- a/mercurial/templates/paper/filediff.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/paper/filediff.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -36,7 +36,7 @@
 
 <div class="main">
 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
-<h3>diff {file|escape} @ {rev}:{node|short}</h3>
+<h3>diff {file|escape} @ {rev}:{node|short} {branch%changelogbranchname}{tags%changelogtag}{bookmarks%changelogtag}</h3>
 
 <form class="search" action="{url|urlescape}log">
 <p>{sessionvars%hiddenformentry}</p>
--- a/mercurial/templates/paper/filelogentry.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/paper/filelogentry.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -1,5 +1,8 @@
  <tr>
   <td class="age">{date|rfc822date}</td>
   <td class="author">{author|person}</td>
-  <td class="description"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a>{inbranch%changelogbranchname}{branches%changelogbranchhead}{tags%changelogtag}{rename%filelogrename}</td>
+  <td class="description">
+   <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a>
+   {inbranch%changelogbranchname}{branches%changelogbranchhead}{tags%changelogtag}{bookmarks%changelogtag}{rename%filelogrename}
+  </td>
  </tr>
--- a/mercurial/templates/paper/filerevision.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/paper/filerevision.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -36,7 +36,7 @@
 
 <div class="main">
 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
-<h3>view {file|escape} @ {rev}:{node|short}</h3>
+<h3>view {file|escape} @ {rev}:{node|short} {branch%changelogbranchname}{tags%changelogtag}{bookmarks%changelogtag}</h3>
 
 <form class="search" action="{url|urlescape}log">
 {sessionvars%hiddenformentry}
--- a/mercurial/templates/paper/manifest.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/paper/manifest.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -30,7 +30,7 @@
 
 <div class="main">
 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
-<h3>directory {path|escape} @ {rev}:{node|short} {tags%changelogtag}</h3>
+<h3>directory {path|escape} @ {rev}:{node|short} {branch%changelogbranchname}{tags%changelogtag}{bookmarks%changelogtag}</h3>
 
 <form class="search" action="{url|urlescape}log">
 {sessionvars%hiddenformentry}
--- a/mercurial/templates/paper/shortlogentry.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/paper/shortlogentry.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -1,5 +1,8 @@
  <tr>
   <td class="age">{date|rfc822date}</td>
   <td class="author">{author|person}</td>
-  <td class="description"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a>{inbranch%changelogbranchname}{branches%changelogbranchhead}{tags%changelogtag}{bookmarks%changelogtag}</td>
+  <td class="description">
+   <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a>
+   {inbranch%changelogbranchname}{branches%changelogbranchhead}{tags%changelogtag}{bookmarks%changelogtag}
+  </td>
  </tr>
--- a/mercurial/templates/spartan/fileannotate.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/spartan/fileannotate.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -22,12 +22,14 @@
 <table>
 <tr>
  <td class="metatag">changeset {rev}:</td>
- <td><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
+ <td><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
+</tr>
 {parent%fileannotateparent}
 {child%fileannotatechild}
 <tr>
  <td class="metatag">author:</td>
- <td>{author|obfuscate}</td></tr>
+ <td>{author|obfuscate}</td>
+</tr>
 <tr>
  <td class="metatag">date:</td>
  <td class="date age">{date|rfc822date}</td>
--- a/mercurial/templates/spartan/filerevision.tmpl	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/templates/spartan/filerevision.tmpl	Tue May 19 07:17:57 2015 -0500
@@ -22,18 +22,22 @@
 <table>
 <tr>
  <td class="metatag">changeset {rev}:</td>
- <td><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
+ <td><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
+</tr>
 {parent%filerevparent}
 {child%filerevchild}
 <tr>
  <td class="metatag">author:</td>
- <td>{author|obfuscate}</td></tr>
+ <td>{author|obfuscate}</td>
+</tr>
 <tr>
  <td class="metatag">date:</td>
- <td class="date age">{date|rfc822date}</td></tr>
+ <td class="date age">{date|rfc822date}</td>
+</tr>
 <tr>
  <td class="metatag">permissions:</td>
- <td>{permissions|permissions}</td></tr>
+ <td>{permissions|permissions}</td>
+</tr>
 <tr>
   <td class="metatag">description:</td>
   <td>{desc|strip|escape|websub|addbreaks|nonempty}</td>
--- a/mercurial/transaction.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/transaction.py	Tue May 19 07:17:57 2015 -0500
@@ -496,7 +496,7 @@
                 _playback(self.journal, self.report, self.opener, self._vfsmap,
                           self.entries, self._backupentries, False)
                 self.report(_("rollback completed\n"))
-            except Exception:
+            except BaseException:
                 self.report(_("rollback failed - please run hg recover\n"))
         finally:
             self.journal = None
--- a/mercurial/treediscovery.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/treediscovery.py	Tue May 19 07:17:57 2015 -0500
@@ -5,6 +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.
 
+import collections
 from node import nullid, short
 from i18n import _
 import util, error
@@ -56,7 +57,7 @@
     # a 'branch' here is a linear segment of history, with four parts:
     # head, root, first parent, second parent
     # (a branch always has two parents (or none) by definition)
-    unknown = util.deque(remote.branches(unknown))
+    unknown = collections.deque(remote.branches(unknown))
     while unknown:
         r = []
         while unknown:
--- a/mercurial/ui.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/ui.py	Tue May 19 07:17:57 2015 -0500
@@ -842,7 +842,7 @@
         output will be redirected if fout is not stdout.
         '''
         out = self.fout
-        if util.any(s[1] for s in self._bufferstates):
+        if any(s[1] for s in self._bufferstates):
             out = self
         return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
                            errprefix=errprefix, out=out)
@@ -902,7 +902,7 @@
         termination.
         '''
 
-        if pos is None or not self.debugflag:
+        if pos is None or not self.configbool('progress', 'debug'):
             return
 
         if unit:
--- a/mercurial/util.h	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/util.h	Tue May 19 07:17:57 2015 -0500
@@ -57,66 +57,6 @@
 
 #endif /* PY_MAJOR_VERSION */
 
-/* Backports from 2.6 */
-#if PY_VERSION_HEX < 0x02060000
-
-#define Py_TYPE(ob) (ob)->ob_type
-#define Py_SIZE(ob) (ob)->ob_size
-#define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
-
-/* Shamelessly stolen from bytesobject.h */
-#define PyBytesObject PyStringObject
-#define PyBytes_Type PyString_Type
-
-#define PyBytes_Check PyString_Check
-#define PyBytes_CheckExact PyString_CheckExact
-#define PyBytes_CHECK_INTERNED PyString_CHECK_INTERNED
-#define PyBytes_AS_STRING PyString_AS_STRING
-#define PyBytes_GET_SIZE PyString_GET_SIZE
-#define Py_TPFLAGS_BYTES_SUBCLASS Py_TPFLAGS_STRING_SUBCLASS
-
-#define PyBytes_FromStringAndSize PyString_FromStringAndSize
-#define PyBytes_FromString PyString_FromString
-#define PyBytes_FromFormatV PyString_FromFormatV
-#define PyBytes_FromFormat PyString_FromFormat
-#define PyBytes_Size PyString_Size
-#define PyBytes_AsString PyString_AsString
-#define PyBytes_Repr PyString_Repr
-#define PyBytes_Concat PyString_Concat
-#define PyBytes_ConcatAndDel PyString_ConcatAndDel
-#define _PyBytes_Resize _PyString_Resize
-#define _PyBytes_Eq _PyString_Eq
-#define PyBytes_Format PyString_Format
-#define _PyBytes_FormatLong _PyString_FormatLong
-#define PyBytes_DecodeEscape PyString_DecodeEscape
-#define _PyBytes_Join _PyString_Join
-#define PyBytes_Decode PyString_Decode
-#define PyBytes_Encode PyString_Encode
-#define PyBytes_AsEncodedObject PyString_AsEncodedObject
-#define PyBytes_AsEncodedString PyString_AsEncodedString
-#define PyBytes_AsDecodedObject PyString_AsDecodedObject
-#define PyBytes_AsDecodedString PyString_AsDecodedString
-#define PyBytes_AsStringAndSize PyString_AsStringAndSize
-#define _PyBytes_InsertThousandsGrouping _PyString_InsertThousandsGrouping
-
-#endif /* PY_VERSION_HEX */
-
-#if (PY_VERSION_HEX < 0x02050000)
-/* Definitions to get compatibility with python 2.4 and earlier which
-   does not have Py_ssize_t. See also PEP 353.
-   Note: msvc (8 or earlier) does not have ssize_t, so we use Py_ssize_t.
-*/
-typedef int Py_ssize_t;
-typedef Py_ssize_t (*lenfunc)(PyObject *);
-typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t);
-#define PyInt_FromSsize_t PyInt_FromLong
-
-#if !defined(PY_SSIZE_T_MIN)
-#define PY_SSIZE_T_MAX INT_MAX
-#define PY_SSIZE_T_MIN INT_MIN
-#endif
-#endif
-
 #ifdef _WIN32
 #ifdef _MSC_VER
 /* msvc 6.0 has problems */
--- a/mercurial/util.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/util.py	Tue May 19 07:17:57 2015 -0500
@@ -334,18 +334,6 @@
 
     return f
 
-try:
-    collections.deque.remove
-    deque = collections.deque
-except AttributeError:
-    # python 2.4 lacks deque.remove
-    class deque(collections.deque):
-        def remove(self, val):
-            for i, v in enumerate(self):
-                if v == val:
-                    del self[i]
-                    break
-
 class sortdict(dict):
     '''a simple sorted dictionary'''
     def __init__(self, data=None):
@@ -396,7 +384,7 @@
     def __init__(self, maxsize):
         self._cache = {}
         self._maxsize = maxsize
-        self._order = deque()
+        self._order = collections.deque()
 
     def __getitem__(self, key):
         value = self._cache[key]
@@ -418,12 +406,12 @@
 
     def clear(self):
         self._cache.clear()
-        self._order = deque()
+        self._order = collections.deque()
 
 def lrucachefunc(func):
     '''cache most recent results of function calls'''
     cache = {}
-    order = deque()
+    order = collections.deque()
     if func.func_code.co_argcount == 1:
         def f(arg):
             if arg not in cache:
@@ -1003,15 +991,13 @@
     f2 = testfile + ".hgtmp2"
     fd = None
     try:
-        try:
-            oslink(f1, f2)
-        except OSError:
-            return False
-
+        oslink(f1, f2)
         # nlinks() may behave differently for files on Windows shares if
         # the file is open.
         fd = posixfile(f2)
         return nlinks(f2) > 1
+    except OSError:
+        return False
     finally:
         if fd is not None:
             fd.close()
@@ -1203,7 +1189,7 @@
                 else:
                     yield chunk
         self.iter = splitbig(in_iter)
-        self._queue = deque()
+        self._queue = collections.deque()
 
     def read(self, l=None):
         """Read L bytes of data from the iterator of chunks of data.
@@ -1734,21 +1720,6 @@
         if prevhandler is not None:
             signal.signal(signal.SIGCHLD, prevhandler)
 
-try:
-    any, all = any, all
-except NameError:
-    def any(iterable):
-        for i in iterable:
-            if i:
-                return True
-        return False
-
-    def all(iterable):
-        for i in iterable:
-            if not i:
-                return False
-        return True
-
 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
     """Return the result of interpolating items in the mapping into string s.
 
--- a/mercurial/windows.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/windows.py	Tue May 19 07:17:57 2015 -0500
@@ -139,7 +139,7 @@
     return pconvert(os.path.normpath(path))
 
 def normcase(path):
-    return encoding.upper(path)
+    return encoding.upper(path) # NTFS compares via upper()
 
 # see posix.py for definitions
 normcasespec = encoding.normcasespecs.upper
@@ -162,6 +162,18 @@
 _quotere = None
 _needsshellquote = None
 def shellquote(s):
+    r"""
+    >>> shellquote(r'C:\Users\xyz')
+    '"C:\\Users\\xyz"'
+    >>> shellquote(r'C:\Users\xyz/mixed')
+    '"C:\\Users\\xyz/mixed"'
+    >>> # Would be safe not to quote too, since it is all double backslashes
+    >>> shellquote(r'C:\\Users\\xyz')
+    '"C:\\\\Users\\\\xyz"'
+    >>> # But this must be quoted
+    >>> shellquote(r'C:\\Users\\xyz/abc')
+    '"C:\\\\Users\\\\xyz/abc"'
+    """
     global _quotere
     if _quotere is None:
         _quotere = re.compile(r'(\\*)("|\\$)')
--- a/mercurial/wireproto.py	Sun May 17 22:09:37 2015 -0400
+++ b/mercurial/wireproto.py	Tue May 19 07:17:57 2015 -0500
@@ -345,6 +345,11 @@
     def getbundle(self, source, **kwargs):
         self.requirecap('getbundle', _('look up remote changes'))
         opts = {}
+        bundlecaps = kwargs.get('bundlecaps')
+        if bundlecaps is not None:
+            kwargs['bundlecaps'] = sorted(bundlecaps)
+        else:
+            bundlecaps = () # kwargs could have it to None
         for key, value in kwargs.iteritems():
             if value is None:
                 continue
@@ -362,10 +367,7 @@
                                % keytype)
             opts[key] = value
         f = self._callcompressable("getbundle", **opts)
-        bundlecaps = kwargs.get('bundlecaps')
-        if bundlecaps is None:
-            bundlecaps = () # kwargs could have it to None
-        if util.any((cap.startswith('HG2') for cap in bundlecaps)):
+        if any((cap.startswith('HG2') for cap in bundlecaps)):
             return bundle2.getunbundler(self.ui, f)
         else:
             return changegroupmod.cg1unpacker(f, 'UN')
@@ -805,11 +807,8 @@
                 else:
                     for chunk in util.filechunkiter(sopener(name), limit=size):
                         yield chunk
-        # replace with "finally:" when support for python 2.4 has been dropped
-        except Exception:
+        finally:
             sopener.mustaudit = oldaudit
-            raise
-        sopener.mustaudit = oldaudit
 
     return streamres(streamer(repo, entries, total_bytes))
 
--- a/setup.py	Sun May 17 22:09:37 2015 -0400
+++ b/setup.py	Tue May 19 07:17:57 2015 -0500
@@ -5,8 +5,8 @@
 # 'python setup.py --help' for more options
 
 import sys, platform
-if getattr(sys, 'version_info', (0, 0, 0)) < (2, 4, 0, 'final'):
-    raise SystemExit("Mercurial requires Python 2.4 or later.")
+if getattr(sys, 'version_info', (0, 0, 0)) < (2, 6, 0, 'final'):
+    raise SystemExit("Mercurial requires Python 2.6 or later.")
 
 if sys.version_info[0] >= 3:
     def b(s):
@@ -106,25 +106,24 @@
     tmpdir = tempfile.mkdtemp(prefix='hg-install-')
     devnull = oldstderr = None
     try:
-        try:
-            fname = os.path.join(tmpdir, 'funcname.c')
-            f = open(fname, 'w')
-            f.write('int main(void) {\n')
-            f.write('    %s();\n' % funcname)
-            f.write('}\n')
-            f.close()
-            # Redirect stderr to /dev/null to hide any error messages
-            # from the compiler.
-            # This will have to be changed if we ever have to check
-            # for a function on Windows.
-            devnull = open('/dev/null', 'w')
-            oldstderr = os.dup(sys.stderr.fileno())
-            os.dup2(devnull.fileno(), sys.stderr.fileno())
-            objects = cc.compile([fname], output_dir=tmpdir)
-            cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
-        except Exception:
-            return False
+        fname = os.path.join(tmpdir, 'funcname.c')
+        f = open(fname, 'w')
+        f.write('int main(void) {\n')
+        f.write('    %s();\n' % funcname)
+        f.write('}\n')
+        f.close()
+        # Redirect stderr to /dev/null to hide any error messages
+        # from the compiler.
+        # This will have to be changed if we ever have to check
+        # for a function on Windows.
+        devnull = open('/dev/null', 'w')
+        oldstderr = os.dup(sys.stderr.fileno())
+        os.dup2(devnull.fileno(), sys.stderr.fileno())
+        objects = cc.compile([fname], output_dir=tmpdir)
+        cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
         return True
+    except Exception:
+        return False
     finally:
         if oldstderr is not None:
             os.dup2(oldstderr, sys.stderr.fileno())
@@ -408,11 +407,12 @@
                 # Persist executable bit (apply it to group and other if user
                 # has it)
                 if st[stat.ST_MODE] & stat.S_IXUSR:
-                    setmode = 0755
+                    setmode = int('0755', 8)
                 else:
-                    setmode = 0644
-                os.chmod(dst, (stat.S_IMODE(st[stat.ST_MODE]) & ~0777) |
-                         setmode)
+                    setmode = int('0644', 8)
+                m = stat.S_IMODE(st[stat.ST_MODE])
+                m = (m & ~int('0777', 8)) | setmode
+                os.chmod(dst, m)
         file_util.copy_file = copyfileandsetmode
         try:
             install_lib.run(self)
@@ -483,6 +483,11 @@
 
 common_depends = ['mercurial/util.h']
 
+osutil_ldflags = []
+
+if sys.platform == 'darwin':
+    osutil_ldflags += ['-framework', 'ApplicationServices']
+
 extmodules = [
     Extension('mercurial.base85', ['mercurial/base85.c'],
               depends=common_depends),
@@ -497,21 +502,11 @@
                                     'mercurial/parsers.c',
                                     'mercurial/pathencode.c'],
               depends=common_depends),
+    Extension('mercurial.osutil', ['mercurial/osutil.c'],
+              extra_link_args=osutil_ldflags,
+              depends=common_depends),
     ]
 
-osutil_ldflags = []
-
-if sys.platform == 'darwin':
-    osutil_ldflags += ['-framework', 'ApplicationServices']
-
-# disable osutil.c under windows + python 2.4 (issue1364)
-if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
-    pymodules.append('mercurial.pure.osutil')
-else:
-    extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'],
-                                extra_link_args=osutil_ldflags,
-                                depends=common_depends))
-
 try:
     from distutils import cygwinccompiler
 
@@ -572,6 +567,8 @@
     version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
     if version:
         version = version[0]
+        if sys.version_info[0] == 3:
+            version = version.decode('utf-8')
         xcode4 = (version.startswith('Xcode') and
                   StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
         xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
--- a/tests/heredoctest.py	Sun May 17 22:09:37 2015 -0400
+++ b/tests/heredoctest.py	Tue May 19 07:17:57 2015 -0500
@@ -5,7 +5,7 @@
 while lines:
     l = lines.pop(0)
     if l.startswith('SALT'):
-        print l[:-1]
+        print(l[:-1])
     elif l.startswith('>>> '):
         snippet = l[4:]
         while lines and lines[0].startswith('... '):
@@ -13,6 +13,6 @@
             snippet += l[4:]
         c = compile(snippet, '<heredoc>', 'single')
         try:
-            exec c in globalvars
-        except Exception, inst:
-            print repr(inst)
+            exec(c, globalvars)
+        except Exception as inst:
+            print(repr(inst))
--- a/tests/hghave.py	Sun May 17 22:09:37 2015 -0400
+++ b/tests/hghave.py	Tue May 19 07:17:57 2015 -0500
@@ -225,12 +225,11 @@
     os.close(fh)
     name = tempfile.mktemp(dir='.', prefix=tempprefix)
     try:
-        try:
-            util.oslink(fn, name)
-            os.unlink(name)
-            return True
-        except OSError:
-            return False
+        util.oslink(fn, name)
+        os.unlink(name)
+        return True
+    except OSError:
+        return False
     finally:
         os.unlink(fn)
 
@@ -282,10 +281,6 @@
     except ImportError:
         return False
 
-@check("python243", "python >= 2.4.3")
-def has_python243():
-    return sys.version_info >= (2, 4, 3)
-
 @check("json", "some json module available")
 def has_json():
     try:
--- a/tests/killdaemons.py	Sun May 17 22:09:37 2015 -0400
+++ b/tests/killdaemons.py	Tue May 19 07:17:57 2015 -0500
@@ -64,7 +64,7 @@
                 os.kill(pid, 0)
             logfn('# Daemon process %d is stuck - really killing it' % pid)
             os.kill(pid, signal.SIGKILL)
-        except OSError, err:
+        except OSError as err:
             if err.errno != errno.ESRCH:
                 raise
 
--- a/tests/run-tests.py	Sun May 17 22:09:37 2015 -0400
+++ b/tests/run-tests.py	Tue May 19 07:17:57 2015 -0500
@@ -41,6 +41,8 @@
 # completes fairly quickly, includes both shell and Python scripts, and
 # includes some scripts that run daemon processes.)
 
+from __future__ import print_function
+
 from distutils import version
 import difflib
 import errno
@@ -49,6 +51,7 @@
 import shutil
 import subprocess
 import signal
+import socket
 import sys
 import tempfile
 import time
@@ -56,10 +59,15 @@
 import re
 import threading
 import killdaemons as killmod
-import Queue as queue
+try:
+    import Queue as queue
+except ImportError:
+    import queue
 from xml.dom import minidom
 import unittest
 
+osenvironb = getattr(os, 'environb', os.environ)
+
 try:
     import json
 except ImportError:
@@ -70,14 +78,46 @@
 
 processlock = threading.Lock()
 
-# subprocess._cleanup can race with any Popen.wait or Popen.poll on py24
-# http://bugs.python.org/issue1731717 for details. We shouldn't be producing
-# zombies but it's pretty harmless even if we do.
-if sys.version_info < (2, 5):
-    subprocess._cleanup = lambda: None
+if sys.version_info > (3, 5, 0):
+    PYTHON3 = True
+    xrange = range # we use xrange in one place, and we'd rather not use range
+    def _bytespath(p):
+        return p.encode('utf-8')
+
+    def _strpath(p):
+        return p.decode('utf-8')
+
+elif sys.version_info >= (3, 0, 0):
+    print('%s is only supported on Python 3.5+ and 2.6-2.7, not %s' %
+          (sys.argv[0], '.'.join(str(v) for v in sys.version_info[:3])))
+    sys.exit(70) # EX_SOFTWARE from `man 3 sysexit`
+else:
+    PYTHON3 = False
 
+    # In python 2.x, path operations are generally done using
+    # bytestrings by default, so we don't have to do any extra
+    # fiddling there. We define the wrapper functions anyway just to
+    # help keep code consistent between platforms.
+    def _bytespath(p):
+        return p
+
+    _strpath = _bytespath
+
+# For Windows support
 wifexited = getattr(os, "WIFEXITED", lambda x: False)
 
+def checkportisavailable(port):
+    """return true if a port seems free to bind on localhost"""
+    try:
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        s.bind(('localhost', port))
+        s.close()
+        return True
+    except socket.error as exc:
+        if not exc.errno == errno.EADDRINUSE:
+            raise
+        return False
+
 closefds = os.name == 'posix'
 def Popen4(cmd, wd, timeout, env=None):
     processlock.acquire()
@@ -104,10 +144,10 @@
 
     return p
 
-PYTHON = sys.executable.replace('\\', '/')
-IMPL_PATH = 'PYTHONPATH'
+PYTHON = _bytespath(sys.executable.replace('\\', '/'))
+IMPL_PATH = b'PYTHONPATH'
 if 'java' in sys.platform:
-    IMPL_PATH = 'JYTHONPATH'
+    IMPL_PATH = b'JYTHONPATH'
 
 defaults = {
     'jobs': ('HGTEST_JOBS', 1),
@@ -122,15 +162,15 @@
         try:
             path = os.path.expanduser(os.path.expandvars(filename))
             f = open(path, "rb")
-        except IOError, err:
+        except IOError as err:
             if err.errno != errno.ENOENT:
                 raise
             if warn:
-                print "warning: no such %s file: %s" % (listtype, filename)
+                print("warning: no such %s file: %s" % (listtype, filename))
             continue
 
         for line in f.readlines():
-            line = line.split('#', 1)[0].strip()
+            line = line.split(b'#', 1)[0].strip()
             if line:
                 entries[line] = filename
 
@@ -217,6 +257,8 @@
                       help='set the given config opt in the test hgrc')
     parser.add_option('--random', action="store_true",
                       help='run tests in random order')
+    parser.add_option('--profile-runner', action='store_true',
+                      help='run statprof on run-tests')
 
     for option, (envvar, default) in defaults.items():
         defaults[option] = type(default)(os.environ.get(envvar, default))
@@ -240,8 +282,8 @@
         if not os.path.basename(options.with_hg) == 'hg':
             sys.stderr.write('warning: --with-hg should specify an hg script\n')
     if options.local:
-        testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
-        hgbin = os.path.join(os.path.dirname(testdir), 'hg')
+        testdir = os.path.dirname(_bytespath(os.path.realpath(sys.argv[0])))
+        hgbin = os.path.join(os.path.dirname(testdir), b'hg')
         if os.name != 'nt' and not os.access(hgbin, os.X_OK):
             parser.error('--local specified, but %r not found or not executable'
                          % hgbin)
@@ -283,8 +325,9 @@
                 'warning: --timeout option ignored with --debug\n')
         options.timeout = 0
     if options.py3k_warnings:
-        if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
-            parser.error('--py3k-warnings can only be used on Python 2.6+')
+        if PYTHON3:
+            parser.error(
+                '--py3k-warnings can only be used on Python 2.6 and 2.7')
     if options.blacklist:
         options.blacklist = parselistfiles(options.blacklist, 'blacklist')
     if options.whitelist:
@@ -301,17 +344,22 @@
     shutil.copy(src, dst)
     os.remove(src)
 
+_unified_diff = difflib.unified_diff
+if PYTHON3:
+    import functools
+    _unified_diff = functools.partial(difflib.diff_bytes, difflib.unified_diff)
+
 def getdiff(expected, output, ref, err):
     servefail = False
     lines = []
-    for line in difflib.unified_diff(expected, output, ref, err):
-        if line.startswith('+++') or line.startswith('---'):
-            line = line.replace('\\', '/')
-            if line.endswith(' \n'):
-                line = line[:-2] + '\n'
+    for line in _unified_diff(expected, output, ref, err):
+        if line.startswith(b'+++') or line.startswith(b'---'):
+            line = line.replace(b'\\', b'/')
+            if line.endswith(b' \n'):
+                line = line[:-2] + b'\n'
         lines.append(line)
         if not servefail and line.startswith(
-                             '+  abort: child process failed to start'):
+                             b'+  abort: child process failed to start'):
             servefail = True
 
     return servefail, lines
@@ -326,7 +374,7 @@
 
 # Bytes that break XML even in a CDATA block: control characters 0-31
 # sans \t, \n and \r
-CDATA_EVIL = re.compile(r"[\000-\010\013\014\016-\037]")
+CDATA_EVIL = re.compile(br"[\000-\010\013\014\016-\037]")
 
 def cdatasafe(data):
     """Make a string safe to include in a CDATA block.
@@ -336,21 +384,20 @@
     replaces illegal bytes with ? and adds a space between the ]] so
     that it won't break the CDATA block.
     """
-    return CDATA_EVIL.sub('?', data).replace(']]>', '] ]>')
+    return CDATA_EVIL.sub(b'?', data).replace(b']]>', b'] ]>')
 
 def log(*msg):
     """Log something to stdout.
 
     Arguments are strings to print.
     """
-    iolock.acquire()
-    if verbose:
-        print verbose,
-    for m in msg:
-        print m,
-    print
-    sys.stdout.flush()
-    iolock.release()
+    with iolock:
+        if verbose:
+            print(verbose, end=' ')
+        for m in msg:
+            print(m, end=' ')
+        print()
+        sys.stdout.flush()
 
 def terminate(proc):
     """Terminate subprocess (with fallback for Python versions < 2.6)"""
@@ -408,11 +455,11 @@
 
         shell is the shell to execute tests in.
         """
-
         self.path = path
-        self.name = os.path.basename(path)
+        self.bname = os.path.basename(path)
+        self.name = _strpath(self.bname)
         self._testdir = os.path.dirname(path)
-        self.errpath = os.path.join(self._testdir, '%s.err' % self.name)
+        self.errpath = os.path.join(self._testdir, b'%s.err' % self.bname)
 
         self._threadtmp = tmpdir
         self._keeptmpdir = keeptmpdir
@@ -421,7 +468,7 @@
         self._startport = startport
         self._extraconfigopts = extraconfigopts or []
         self._py3kwarnings = py3kwarnings
-        self._shell = shell
+        self._shell = _bytespath(shell)
 
         self._aborted = False
         self._daemonpids = []
@@ -442,6 +489,11 @@
         else:
             self._refout = []
 
+    # needed to get base class __repr__ running
+    @property
+    def _testMethodName(self):
+        return self.name
+
     def __str__(self):
         return self.name
 
@@ -457,7 +509,7 @@
 
         try:
             os.mkdir(self._threadtmp)
-        except OSError, e:
+        except OSError as e:
             if e.errno != errno.EEXIST:
                 raise
 
@@ -469,7 +521,7 @@
         if os.path.exists(self.errpath):
             try:
                 os.remove(self.errpath)
-            except OSError, e:
+            except OSError as e:
                 # We might have raced another test to clean up a .err
                 # file, so ignore ENOENT when removing a previous .err
                 # file.
@@ -499,20 +551,20 @@
             except KeyboardInterrupt:
                 self._aborted = True
                 raise
-            except SkipTest, e:
+            except SkipTest as e:
                 result.addSkip(self, str(e))
                 # The base class will have already counted this as a
                 # test we "ran", but we want to exclude skipped tests
                 # from those we count towards those run.
                 result.testsRun -= 1
-            except IgnoreTest, e:
+            except IgnoreTest as e:
                 result.addIgnore(self, str(e))
                 # As with skips, ignores also should be excluded from
                 # the number of tests executed.
                 result.testsRun -= 1
-            except WarnTest, e:
+            except WarnTest as e:
                 result.addWarn(self, str(e))
-            except self.failureException, e:
+            except self.failureException as e:
                 # This differs from unittest in that we don't capture
                 # the stack trace. This is for historical reasons and
                 # this decision could be revisited in the future,
@@ -620,7 +672,7 @@
                 f.write(line)
             f.close()
 
-        vlog("# Ret was:", self._ret)
+        vlog("# Ret was:", self._ret, '(%s)' % self.name)
 
     def _run(self, env):
         # This should be implemented in child classes to run tests.
@@ -638,20 +690,20 @@
         occur.
         """
         r = [
-            (r':%s\b' % self._startport, ':$HGPORT'),
-            (r':%s\b' % (self._startport + 1), ':$HGPORT1'),
-            (r':%s\b' % (self._startport + 2), ':$HGPORT2'),
-            (r'(?m)^(saved backup bundle to .*\.hg)( \(glob\))?$',
-             r'\1 (glob)'),
+            (br':%d\b' % self._startport, b':$HGPORT'),
+            (br':%d\b' % (self._startport + 1), b':$HGPORT1'),
+            (br':%d\b' % (self._startport + 2), b':$HGPORT2'),
+            (br'(?m)^(saved backup bundle to .*\.hg)( \(glob\))?$',
+             br'\1 (glob)'),
             ]
 
         if os.name == 'nt':
             r.append(
-                (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
-                    c in '/\\' and r'[/\\]' or c.isdigit() and c or '\\' + c
-                    for c in self._testtmp), '$TESTTMP'))
+                (b''.join(c.isalpha() and b'[%s%s]' % (c.lower(), c.upper()) or
+                    c in b'/\\' and br'[/\\]' or c.isdigit() and c or b'\\' + c
+                    for c in self._testtmp), b'$TESTTMP'))
         else:
-            r.append((re.escape(self._testtmp), '$TESTTMP'))
+            r.append((re.escape(self._testtmp), b'$TESTTMP'))
 
         return r
 
@@ -663,8 +715,8 @@
         env["HGPORT"] = str(self._startport)
         env["HGPORT1"] = str(self._startport + 1)
         env["HGPORT2"] = str(self._startport + 2)
-        env["HGRCPATH"] = os.path.join(self._threadtmp, '.hgrc')
-        env["DAEMON_PIDS"] = os.path.join(self._threadtmp, 'daemon.pids')
+        env["HGRCPATH"] = os.path.join(self._threadtmp, b'.hgrc')
+        env["DAEMON_PIDS"] = os.path.join(self._threadtmp, b'daemon.pids')
         env["HGEDITOR"] = ('"' + sys.executable + '"'
                            + ' -c "import sys; sys.exit(0)"')
         env["HGMERGE"] = "internal:merge"
@@ -695,27 +747,27 @@
     def _createhgrc(self, path):
         """Create an hgrc file for this test."""
         hgrc = open(path, 'wb')
-        hgrc.write('[ui]\n')
-        hgrc.write('slash = True\n')
-        hgrc.write('interactive = False\n')
-        hgrc.write('mergemarkers = detailed\n')
-        hgrc.write('promptecho = True\n')
-        hgrc.write('[defaults]\n')
-        hgrc.write('backout = -d "0 0"\n')
-        hgrc.write('commit = -d "0 0"\n')
-        hgrc.write('shelve = --date "0 0"\n')
-        hgrc.write('tag = -d "0 0"\n')
-        hgrc.write('[devel]\n')
-        hgrc.write('all = true\n')
-        hgrc.write('[largefiles]\n')
-        hgrc.write('usercache = %s\n' %
-                   (os.path.join(self._testtmp, '.cache/largefiles')))
+        hgrc.write(b'[ui]\n')
+        hgrc.write(b'slash = True\n')
+        hgrc.write(b'interactive = False\n')
+        hgrc.write(b'mergemarkers = detailed\n')
+        hgrc.write(b'promptecho = True\n')
+        hgrc.write(b'[defaults]\n')
+        hgrc.write(b'backout = -d "0 0"\n')
+        hgrc.write(b'commit = -d "0 0"\n')
+        hgrc.write(b'shelve = --date "0 0"\n')
+        hgrc.write(b'tag = -d "0 0"\n')
+        hgrc.write(b'[devel]\n')
+        hgrc.write(b'all = true\n')
+        hgrc.write(b'[largefiles]\n')
+        hgrc.write(b'usercache = %s\n' %
+                   (os.path.join(self._testtmp, b'.cache/largefiles')))
 
         for opt in self._extraconfigopts:
             section, key = opt.split('.', 1)
             assert '=' in key, ('extra config opt %s must '
                                 'have an = for assignment' % opt)
-            hgrc.write('[%s]\n%s\n' % (section, key))
+            hgrc.write(b'[%s]\n%s\n' % (section, key))
         hgrc.close()
 
     def fail(self, msg):
@@ -777,11 +829,11 @@
 
     @property
     def refpath(self):
-        return os.path.join(self._testdir, '%s.out' % self.name)
+        return os.path.join(self._testdir, b'%s.out' % self.bname)
 
     def _run(self, env):
-        py3kswitch = self._py3kwarnings and ' -3' or ''
-        cmd = '%s%s "%s"' % (PYTHON, py3kswitch, self.path)
+        py3kswitch = self._py3kwarnings and b' -3' or b''
+        cmd = b'%s%s "%s"' % (PYTHON, py3kswitch, self.path)
         vlog("# Running", cmd)
         normalizenewlines = os.name == 'nt'
         result = self._runcommand(cmd, env,
@@ -795,25 +847,29 @@
 # Windows, but check-code.py wants a glob on these lines unconditionally.  Don't
 # warn if that is the case for anything matching these lines.
 checkcodeglobpats = [
-    re.compile(r'^pushing to \$TESTTMP/.*[^)]$'),
-    re.compile(r'^moving \S+/.*[^)]$'),
-    re.compile(r'^pulling from \$TESTTMP/.*[^)]$')
+    re.compile(br'^pushing to \$TESTTMP/.*[^)]$'),
+    re.compile(br'^moving \S+/.*[^)]$'),
+    re.compile(br'^pulling from \$TESTTMP/.*[^)]$')
 ]
 
+bchr = chr
+if PYTHON3:
+    bchr = lambda x: bytes([x])
+
 class TTest(Test):
     """A "t test" is a test backed by a .t file."""
 
     SKIPPED_PREFIX = 'skipped: '
     FAILED_PREFIX = 'hghave check failed: '
-    NEEDESCAPE = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
+    NEEDESCAPE = re.compile(br'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
 
-    ESCAPESUB = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
-    ESCAPEMAP = dict((chr(i), r'\x%02x' % i) for i in range(256))
-    ESCAPEMAP.update({'\\': '\\\\', '\r': r'\r'})
+    ESCAPESUB = re.compile(br'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
+    ESCAPEMAP = dict((bchr(i), br'\x%02x' % i) for i in range(256))
+    ESCAPEMAP.update({b'\\': b'\\\\', b'\r': br'\r'})
 
     @property
     def refpath(self):
-        return os.path.join(self._testdir, self.name)
+        return os.path.join(self._testdir, self.bname)
 
     def _run(self, env):
         f = open(self.path, 'rb')
@@ -823,13 +879,13 @@
         salt, script, after, expected = self._parsetest(lines)
 
         # Write out the generated script.
-        fname = '%s.sh' % self._testtmp
+        fname = b'%s.sh' % self._testtmp
         f = open(fname, 'wb')
         for l in script:
             f.write(l)
         f.close()
 
-        cmd = '%s "%s"' % (self._shell, fname)
+        cmd = b'%s "%s"' % (self._shell, fname)
         vlog("# Running", cmd)
 
         exitcode, output = self._runcommand(cmd, env)
@@ -846,16 +902,16 @@
 
     def _hghave(self, reqs):
         # TODO do something smarter when all other uses of hghave are gone.
-        tdir = self._testdir.replace('\\', '/')
-        proc = Popen4('%s -c "%s/hghave %s"' %
-                      (self._shell, tdir, ' '.join(reqs)),
+        tdir = self._testdir.replace(b'\\', b'/')
+        proc = Popen4(b'%s -c "%s/hghave %s"' %
+                      (self._shell, tdir, b' '.join(reqs)),
                       self._testtmp, 0, self._getenv())
         stdout, stderr = proc.communicate()
         ret = proc.wait()
         if wifexited(ret):
             ret = os.WEXITSTATUS(ret)
         if ret == 2:
-            print stdout
+            print(stdout)
             sys.exit(1)
 
         return ret == 0
@@ -864,12 +920,12 @@
         # We generate a shell script which outputs unique markers to line
         # up script results with our source. These markers include input
         # line number and the last return code.
-        salt = "SALT" + str(time.time())
+        salt = b"SALT%d" % time.time()
         def addsalt(line, inpython):
             if inpython:
-                script.append('%s %d 0\n' % (salt, line))
+                script.append(b'%s %d 0\n' % (salt, line))
             else:
-                script.append('echo %s %s $?\n' % (salt, line))
+                script.append(b'echo %s %d $?\n' % (salt, line))
 
         script = []
 
@@ -892,42 +948,42 @@
         inpython = False
 
         if self._debug:
-            script.append('set -x\n')
+            script.append(b'set -x\n')
         if os.getenv('MSYSTEM'):
-            script.append('alias pwd="pwd -W"\n')
+            script.append(b'alias pwd="pwd -W"\n')
 
         for n, l in enumerate(lines):
-            if not l.endswith('\n'):
-                l += '\n'
-            if l.startswith('#require'):
+            if not l.endswith(b'\n'):
+                l += b'\n'
+            if l.startswith(b'#require'):
                 lsplit = l.split()
-                if len(lsplit) < 2 or lsplit[0] != '#require':
+                if len(lsplit) < 2 or lsplit[0] != b'#require':
                     after.setdefault(pos, []).append('  !!! invalid #require\n')
                 if not self._hghave(lsplit[1:]):
-                    script = ["exit 80\n"]
+                    script = [b"exit 80\n"]
                     break
                 after.setdefault(pos, []).append(l)
-            elif l.startswith('#if'):
+            elif l.startswith(b'#if'):
                 lsplit = l.split()
-                if len(lsplit) < 2 or lsplit[0] != '#if':
+                if len(lsplit) < 2 or lsplit[0] != b'#if':
                     after.setdefault(pos, []).append('  !!! invalid #if\n')
                 if skipping is not None:
                     after.setdefault(pos, []).append('  !!! nested #if\n')
                 skipping = not self._hghave(lsplit[1:])
                 after.setdefault(pos, []).append(l)
-            elif l.startswith('#else'):
+            elif l.startswith(b'#else'):
                 if skipping is None:
                     after.setdefault(pos, []).append('  !!! missing #if\n')
                 skipping = not skipping
                 after.setdefault(pos, []).append(l)
-            elif l.startswith('#endif'):
+            elif l.startswith(b'#endif'):
                 if skipping is None:
                     after.setdefault(pos, []).append('  !!! missing #if\n')
                 skipping = None
                 after.setdefault(pos, []).append(l)
             elif skipping:
                 after.setdefault(pos, []).append(l)
-            elif l.startswith('  >>> '): # python inlines
+            elif l.startswith(b'  >>> '): # python inlines
                 after.setdefault(pos, []).append(l)
                 prepos = pos
                 pos = n
@@ -935,39 +991,39 @@
                     # We've just entered a Python block. Add the header.
                     inpython = True
                     addsalt(prepos, False) # Make sure we report the exit code.
-                    script.append('%s -m heredoctest <<EOF\n' % PYTHON)
+                    script.append(b'%s -m heredoctest <<EOF\n' % PYTHON)
                 addsalt(n, True)
                 script.append(l[2:])
-            elif l.startswith('  ... '): # python inlines
+            elif l.startswith(b'  ... '): # python inlines
                 after.setdefault(prepos, []).append(l)
                 script.append(l[2:])
-            elif l.startswith('  $ '): # commands
+            elif l.startswith(b'  $ '): # commands
                 if inpython:
-                    script.append('EOF\n')
+                    script.append(b'EOF\n')
                     inpython = False
                 after.setdefault(pos, []).append(l)
                 prepos = pos
                 pos = n
                 addsalt(n, False)
                 cmd = l[4:].split()
-                if len(cmd) == 2 and cmd[0] == 'cd':
-                    l = '  $ cd %s || exit 1\n' % cmd[1]
+                if len(cmd) == 2 and cmd[0] == b'cd':
+                    l = b'  $ cd %s || exit 1\n' % cmd[1]
                 script.append(l[4:])
-            elif l.startswith('  > '): # continuations
+            elif l.startswith(b'  > '): # continuations
                 after.setdefault(prepos, []).append(l)
                 script.append(l[4:])
-            elif l.startswith('  '): # results
+            elif l.startswith(b'  '): # results
                 # Queue up a list of expected results.
                 expected.setdefault(pos, []).append(l[2:])
             else:
                 if inpython:
-                    script.append('EOF\n')
+                    script.append(b'EOF\n')
                     inpython = False
                 # Non-command/result. Queue up for merged output.
                 after.setdefault(pos, []).append(l)
 
         if inpython:
-            script.append('EOF\n')
+            script.append(b'EOF\n')
         if skipping is not None:
             after.setdefault(pos, []).append('  !!! missing #endif\n')
         addsalt(n + 1, False)
@@ -988,8 +1044,8 @@
                 lout, lcmd = l.split(salt, 1)
 
             if lout:
-                if not lout.endswith('\n'):
-                    lout += ' (no-eol)\n'
+                if not lout.endswith(b'\n'):
+                    lout += b' (no-eol)\n'
 
                 # Find the expected output at the current position.
                 el = None
@@ -1008,12 +1064,12 @@
                         log('\ninfo, unknown linematch result: %r\n' % r)
                         r = False
                 if r:
-                    postout.append('  ' + el)
+                    postout.append(b'  ' + el)
                 else:
                     if self.NEEDESCAPE(lout):
-                        lout = TTest._stringescape('%s (esc)\n' %
-                                                   lout.rstrip('\n'))
-                    postout.append('  ' + lout) # Let diff deal with it.
+                        lout = TTest._stringescape(b'%s (esc)\n' %
+                                                   lout.rstrip(b'\n'))
+                    postout.append(b'  ' + lout) # Let diff deal with it.
                     if r != '': # If line failed.
                         warnonly = 3 # for sure not
                     elif warnonly == 1: # Is "not yet" and line is warn only.
@@ -1023,7 +1079,7 @@
                 # Add on last return code.
                 ret = int(lcmd.split()[1])
                 if ret != 0:
-                    postout.append('  [%s]\n' % ret)
+                    postout.append(b'  [%d]\n' % ret)
                 if pos in after:
                     # Merge in non-active test bits.
                     postout += after.pop(pos)
@@ -1042,8 +1098,8 @@
         try:
             # use \Z to ensure that the regex matches to the end of the string
             if os.name == 'nt':
-                return re.match(el + r'\r?\n\Z', l)
-            return re.match(el + r'\n\Z', l)
+                return re.match(el + br'\r?\n\Z', l)
+            return re.match(el + br'\n\Z', l)
         except re.error:
             # el is an invalid regex
             return False
@@ -1052,28 +1108,28 @@
     def globmatch(el, l):
         # The only supported special characters are * and ? plus / which also
         # matches \ on windows. Escaping of these characters is supported.
-        if el + '\n' == l:
+        if el + b'\n' == l:
             if os.altsep:
                 # matching on "/" is not needed for this line
                 for pat in checkcodeglobpats:
                     if pat.match(el):
                         return True
-                return '-glob'
+                return b'-glob'
             return True
         i, n = 0, len(el)
-        res = ''
+        res = b''
         while i < n:
-            c = el[i]
+            c = el[i:i + 1]
             i += 1
-            if c == '\\' and i < n and el[i] in '*?\\/':
+            if c == b'\\' and i < n and el[i:i + 1] in b'*?\\/':
                 res += el[i - 1:i + 1]
                 i += 1
-            elif c == '*':
-                res += '.*'
-            elif c == '?':
-                res += '.'
-            elif c == '/' and os.altsep:
-                res += '[/\\\\]'
+            elif c == b'*':
+                res += b'.*'
+            elif c == b'?':
+                res += b'.'
+            elif c == b'/' and os.altsep:
+                res += b'[/\\\\]'
             else:
                 res += re.escape(c)
         return TTest.rematch(res, l)
@@ -1083,19 +1139,23 @@
         if el == l: # perfect match (fast)
             return True
         if el:
-            if el.endswith(" (esc)\n"):
-                el = el[:-7].decode('string-escape') + '\n'
-            if el == l or os.name == 'nt' and el[:-1] + '\r\n' == l:
+            if el.endswith(b" (esc)\n"):
+                if PYTHON3:
+                    el = el[:-7].decode('unicode_escape') + '\n'
+                    el = el.encode('utf-8')
+                else:
+                    el = el[:-7].decode('string-escape') + '\n'
+            if el == l or os.name == 'nt' and el[:-1] + b'\r\n' == l:
                 return True
-            if el.endswith(" (re)\n"):
+            if el.endswith(b" (re)\n"):
                 return TTest.rematch(el[:-6], l)
-            if el.endswith(" (glob)\n"):
+            if el.endswith(b" (glob)\n"):
                 # ignore '(glob)' added to l by 'replacements'
-                if l.endswith(" (glob)\n"):
-                    l = l[:-8] + "\n"
+                if l.endswith(b" (glob)\n"):
+                    l = l[:-8] + b"\n"
                 return TTest.globmatch(el[:-8], l)
-            if os.altsep and l.replace('\\', '/') == el:
-                return '+glob'
+            if os.altsep and l.replace(b'\\', b'/') == el:
+                return b'+glob'
         return False
 
     @staticmethod
@@ -1160,6 +1220,7 @@
         self.warned = []
 
         self.times = []
+        self._firststarttime =  None
         # Data stored for the benefit of generating xunit reports.
         self.successes = []
         self.faildata = {}
@@ -1170,18 +1231,16 @@
         if self._options.first:
             self.stop()
         else:
-            iolock.acquire()
-            if not self._options.nodiff:
-                self.stream.write('\nERROR: %s output changed\n' % test)
+            with iolock:
+                if not self._options.nodiff:
+                    self.stream.write('\nERROR: %s output changed\n' % test)
 
-            self.stream.write('!')
-            self.stream.flush()
-            iolock.release()
+                self.stream.write('!')
+                self.stream.flush()
 
     def addSuccess(self, test):
-        iolock.acquire()
-        super(TestResult, self).addSuccess(test)
-        iolock.release()
+        with iolock:
+            super(TestResult, self).addSuccess(test)
         self.successes.append(test)
 
     def addError(self, test, err):
@@ -1192,26 +1251,24 @@
     # Polyfill.
     def addSkip(self, test, reason):
         self.skipped.append((test, reason))
-        iolock.acquire()
-        if self.showAll:
-            self.stream.writeln('skipped %s' % reason)
-        else:
-            self.stream.write('s')
-            self.stream.flush()
-        iolock.release()
+        with iolock:
+            if self.showAll:
+                self.stream.writeln('skipped %s' % reason)
+            else:
+                self.stream.write('s')
+                self.stream.flush()
 
     def addIgnore(self, test, reason):
         self.ignored.append((test, reason))
-        iolock.acquire()
-        if self.showAll:
-            self.stream.writeln('ignored %s' % reason)
-        else:
-            if reason != 'not retesting' and reason != "doesn't match keyword":
-                self.stream.write('i')
+        with iolock:
+            if self.showAll:
+                self.stream.writeln('ignored %s' % reason)
             else:
-                self.testsRun += 1
-            self.stream.flush()
-        iolock.release()
+                if reason not in ('not retesting', "doesn't match keyword"):
+                    self.stream.write('i')
+                else:
+                    self.testsRun += 1
+                self.stream.flush()
 
     def addWarn(self, test, reason):
         self.warned.append((test, reason))
@@ -1219,13 +1276,12 @@
         if self._options.first:
             self.stop()
 
-        iolock.acquire()
-        if self.showAll:
-            self.stream.writeln('warned %s' % reason)
-        else:
-            self.stream.write('~')
-            self.stream.flush()
-        iolock.release()
+        with iolock:
+            if self.showAll:
+                self.stream.writeln('warned %s' % reason)
+            else:
+                self.stream.write('~')
+                self.stream.flush()
 
     def addOutputMismatch(self, test, ret, got, expected):
         """Record a mismatch in test output for a particular test."""
@@ -1239,38 +1295,45 @@
         failed = False
         lines = []
 
-        iolock.acquire()
-        if self._options.nodiff:
-            pass
-        elif self._options.view:
-            os.system("%s %s %s" %
-                      (self._options.view, test.refpath, test.errpath))
-        else:
-            servefail, lines = getdiff(expected, got,
-                                       test.refpath, test.errpath)
-            if servefail:
-                self.addFailure(
-                    test,
-                    'server failed to start (HGPORT=%s)' % test._startport)
+        with iolock:
+            if self._options.nodiff:
+                pass
+            elif self._options.view:
+                v = self._options.view
+                if PYTHON3:
+                    v = _bytespath(v)
+                os.system(b"%s %s %s" %
+                          (v, test.refpath, test.errpath))
             else:
-                self.stream.write('\n')
-                for line in lines:
-                    self.stream.write(line)
-                self.stream.flush()
+                servefail, lines = getdiff(expected, got,
+                                           test.refpath, test.errpath)
+                if servefail:
+                    self.addFailure(
+                        test,
+                        'server failed to start (HGPORT=%s)' % test._startport)
+                else:
+                    self.stream.write('\n')
+                    for line in lines:
+                        if PYTHON3:
+                            self.stream.flush()
+                            self.stream.buffer.write(line)
+                            self.stream.buffer.flush()
+                        else:
+                            self.stream.write(line)
+                            self.stream.flush()
 
-        # handle interactive prompt without releasing iolock
-        if self._options.interactive:
-            self.stream.write('Accept this change? [n] ')
-            answer = sys.stdin.readline().strip()
-            if answer.lower() in ('y', 'yes'):
-                if test.name.endswith('.t'):
-                    rename(test.errpath, test.path)
-                else:
-                    rename(test.errpath, '%s.out' % test.path)
-                accepted = True
-        if not accepted and not failed:
-            self.faildata[test.name] = ''.join(lines)
-        iolock.release()
+            # handle interactive prompt without releasing iolock
+            if self._options.interactive:
+                self.stream.write('Accept this change? [n] ')
+                answer = sys.stdin.readline().strip()
+                if answer.lower() in ('y', 'yes'):
+                    if test.name.endswith('.t'):
+                        rename(test.errpath, test.path)
+                    else:
+                        rename(test.errpath, '%s.out' % test.path)
+                    accepted = True
+            if not accepted and not failed:
+                self.faildata[test.name] = b''.join(lines)
 
         return accepted
 
@@ -1282,6 +1345,8 @@
         # This module has one limitation. It can only work for Linux user
         # and not for Windows.
         test.started = os.times()
+        if self._firststarttime is None: # thread racy but irrelevant
+            self._firststarttime = test.started[4]
 
     def stopTest(self, test, interrupted=False):
         super(TestResult, self).stopTest(test)
@@ -1290,14 +1355,19 @@
 
         starttime = test.started
         endtime = test.stopped
-        self.times.append((test.name, endtime[2] - starttime[2],
-                    endtime[3] - starttime[3], endtime[4] - starttime[4]))
+        origin = self._firststarttime
+        self.times.append((test.name,
+                           endtime[2] - starttime[2], # user space CPU time
+                           endtime[3] - starttime[3], # sys  space CPU time
+                           endtime[4] - starttime[4], # real time
+                           starttime[4] - origin, # start date in run context
+                           endtime[4] - origin, # end date in run context
+                           ))
 
         if interrupted:
-            iolock.acquire()
-            self.stream.writeln('INTERRUPTED: %s (after %d seconds)' % (
-                test.name, self.times[-1][3]))
-            iolock.release()
+            with iolock:
+                self.stream.writeln('INTERRUPTED: %s (after %d seconds)' % (
+                    test.name, self.times[-1][3]))
 
 class TestSuite(unittest.TestSuite):
     """Custom unittest TestSuite that knows how to execute Mercurial tests."""
@@ -1352,14 +1422,14 @@
             def get():
                 num_tests[0] += 1
                 if getattr(test, 'should_reload', False):
-                    return self._loadtest(test.name, num_tests[0])
+                    return self._loadtest(test.bname, num_tests[0])
                 return test
             if not os.path.exists(test.path):
                 result.addSkip(test, "Doesn't exist")
                 continue
 
             if not (self._whitelist and test.name in self._whitelist):
-                if self._blacklist and test.name in self._blacklist:
+                if self._blacklist and test.bname in self._blacklist:
                     result.addSkip(test, 'blacklisted')
                     continue
 
@@ -1369,7 +1439,7 @@
 
                 if self._keywords:
                     f = open(test.path, 'rb')
-                    t = f.read().lower() + test.name.lower()
+                    t = f.read().lower() + test.bname.lower()
                     f.close()
                     ignored = False
                     for k in self._keywords.lower().split():
@@ -1460,101 +1530,92 @@
         skipped = len(result.skipped)
         ignored = len(result.ignored)
 
-        iolock.acquire()
-        self.stream.writeln('')
-
-        if not self._runner.options.noskips:
-            for test, msg in result.skipped:
-                self.stream.writeln('Skipped %s: %s' % (test.name, msg))
-        for test, msg in result.warned:
-            self.stream.writeln('Warned %s: %s' % (test.name, msg))
-        for test, msg in result.failures:
-            self.stream.writeln('Failed %s: %s' % (test.name, msg))
-        for test, msg in result.errors:
-            self.stream.writeln('Errored %s: %s' % (test.name, msg))
+        with iolock:
+            self.stream.writeln('')
 
-        if self._runner.options.xunit:
-            xuf = open(self._runner.options.xunit, 'wb')
-            try:
-                timesd = dict(
-                    (test, real) for test, cuser, csys, real in result.times)
-                doc = minidom.Document()
-                s = doc.createElement('testsuite')
-                s.setAttribute('name', 'run-tests')
-                s.setAttribute('tests', str(result.testsRun))
-                s.setAttribute('errors', "0") # TODO
-                s.setAttribute('failures', str(failed))
-                s.setAttribute('skipped', str(skipped + ignored))
-                doc.appendChild(s)
-                for tc in result.successes:
-                    t = doc.createElement('testcase')
-                    t.setAttribute('name', tc.name)
-                    t.setAttribute('time', '%.3f' % timesd[tc.name])
-                    s.appendChild(t)
-                for tc, err in sorted(result.faildata.iteritems()):
-                    t = doc.createElement('testcase')
-                    t.setAttribute('name', tc)
-                    t.setAttribute('time', '%.3f' % timesd[tc])
-                    # createCDATASection expects a unicode or it will convert
-                    # using default conversion rules, which will fail if
-                    # string isn't ASCII.
-                    err = cdatasafe(err).decode('utf-8', 'replace')
-                    cd = doc.createCDATASection(err)
-                    t.appendChild(cd)
-                    s.appendChild(t)
-                xuf.write(doc.toprettyxml(indent='  ', encoding='utf-8'))
-            finally:
-                xuf.close()
+            if not self._runner.options.noskips:
+                for test, msg in result.skipped:
+                    self.stream.writeln('Skipped %s: %s' % (test.name, msg))
+            for test, msg in result.warned:
+                self.stream.writeln('Warned %s: %s' % (test.name, msg))
+            for test, msg in result.failures:
+                self.stream.writeln('Failed %s: %s' % (test.name, msg))
+            for test, msg in result.errors:
+                self.stream.writeln('Errored %s: %s' % (test.name, msg))
 
-        if self._runner.options.json:
-            if json is None:
-                raise ImportError("json module not installed")
-            jsonpath = os.path.join(self._runner._testdir, 'report.json')
-            fp = open(jsonpath, 'w')
-            try:
-                timesd = {}
-                for test, cuser, csys, real in result.times:
-                    timesd[test] = (real, cuser, csys)
-
-                outcome = {}
-                for tc in result.successes:
-                    testresult = {'result': 'success',
-                                  'time': ('%0.3f' % timesd[tc.name][0]),
-                                  'cuser': ('%0.3f' % timesd[tc.name][1]),
-                                  'csys': ('%0.3f' % timesd[tc.name][2])}
-                    outcome[tc.name] = testresult
-
-                for tc, err in sorted(result.faildata.iteritems()):
-                    testresult = {'result': 'failure',
-                                  'time': ('%0.3f' % timesd[tc][0]),
-                                  'cuser': ('%0.3f' % timesd[tc][1]),
-                                  'csys': ('%0.3f' % timesd[tc][2])}
-                    outcome[tc] = testresult
+            if self._runner.options.xunit:
+                xuf = open(self._runner.options.xunit, 'wb')
+                try:
+                    timesd = dict((t[0], t[3]) for t in result.times)
+                    doc = minidom.Document()
+                    s = doc.createElement('testsuite')
+                    s.setAttribute('name', 'run-tests')
+                    s.setAttribute('tests', str(result.testsRun))
+                    s.setAttribute('errors', "0") # TODO
+                    s.setAttribute('failures', str(failed))
+                    s.setAttribute('skipped', str(skipped + ignored))
+                    doc.appendChild(s)
+                    for tc in result.successes:
+                        t = doc.createElement('testcase')
+                        t.setAttribute('name', tc.name)
+                        t.setAttribute('time', '%.3f' % timesd[tc.name])
+                        s.appendChild(t)
+                    for tc, err in sorted(result.faildata.items()):
+                        t = doc.createElement('testcase')
+                        t.setAttribute('name', tc)
+                        t.setAttribute('time', '%.3f' % timesd[tc])
+                        # createCDATASection expects a unicode or it will
+                        # convert using default conversion rules, which will
+                        # fail if string isn't ASCII.
+                        err = cdatasafe(err).decode('utf-8', 'replace')
+                        cd = doc.createCDATASection(err)
+                        t.appendChild(cd)
+                        s.appendChild(t)
+                    xuf.write(doc.toprettyxml(indent='  ', encoding='utf-8'))
+                finally:
+                    xuf.close()
 
-                for tc, reason in result.skipped:
-                    testresult = {'result': 'skip',
-                                  'time': ('%0.3f' % timesd[tc.name][0]),
-                                  'cuser': ('%0.3f' % timesd[tc.name][1]),
-                                  'csys': ('%0.3f' % timesd[tc.name][2])}
-                    outcome[tc.name] = testresult
-
-                jsonout = json.dumps(outcome, sort_keys=True, indent=4)
-                fp.writelines(("testreport =", jsonout))
-            finally:
-                fp.close()
+            if self._runner.options.json:
+                if json is None:
+                    raise ImportError("json module not installed")
+                jsonpath = os.path.join(self._runner._testdir, 'report.json')
+                fp = open(jsonpath, 'w')
+                try:
+                    timesd = {}
+                    for tdata in result.times:
+                        test = tdata[0]
+                        timesd[test] = tdata[1:]
 
-        self._runner._checkhglib('Tested')
+                    outcome = {}
+                    groups = [('success', ((tc, None)
+                               for tc in result.successes)),
+                              ('failure', result.failures),
+                              ('skip', result.skipped)]
+                    for res, testcases in groups:
+                        for tc, __ in testcases:
+                            tres = {'result': res,
+                                    'time': ('%0.3f' % timesd[tc.name][2]),
+                                    'cuser': ('%0.3f' % timesd[tc.name][0]),
+                                    'csys': ('%0.3f' % timesd[tc.name][1]),
+                                    'start': ('%0.3f' % timesd[tc.name][3]),
+                                    'end': ('%0.3f' % timesd[tc.name][4])}
+                            outcome[tc.name] = tres
+                    jsonout = json.dumps(outcome, sort_keys=True, indent=4)
+                    fp.writelines(("testreport =", jsonout))
+                finally:
+                    fp.close()
 
-        self.stream.writeln('# Ran %d tests, %d skipped, %d warned, %d failed.'
-            % (result.testsRun,
-               skipped + ignored, warned, failed))
-        if failed:
-            self.stream.writeln('python hash seed: %s' %
-                os.environ['PYTHONHASHSEED'])
-        if self._runner.options.time:
-            self.printtimes(result.times)
+            self._runner._checkhglib('Tested')
 
-        iolock.release()
+            self.stream.writeln(
+                '# Ran %d tests, %d skipped, %d warned, %d failed.'
+                % (result.testsRun,
+                   skipped + ignored, warned, failed))
+            if failed:
+                self.stream.writeln('python hash seed: %s' %
+                    os.environ['PYTHONHASHSEED'])
+            if self._runner.options.time:
+                self.printtimes(result.times)
 
         return result
 
@@ -1562,11 +1623,13 @@
         # iolock held by run
         self.stream.writeln('# Producing time report')
         times.sort(key=lambda t: (t[3]))
-        cols = '%7.3f %7.3f %7.3f   %s'
-        self.stream.writeln('%-7s %-7s %-7s   %s' % ('cuser', 'csys', 'real',
-                    'Test'))
-        for test, cuser, csys, real in times:
-            self.stream.writeln(cols % (cuser, csys, real, test))
+        cols = '%7.3f %7.3f %7.3f %7.3f %7.3f   %s'
+        self.stream.writeln('%-7s %-7s %-7s %-7s %-7s   %s' %
+                            ('start', 'end', 'cuser', 'csys', 'real', 'Test'))
+        for tdata in times:
+            test = tdata[0]
+            cuser, csys, real, start, end = tdata[1:6]
+            self.stream.writeln(cols % (start, end, cuser, csys, real, test))
 
 class TestRunner(object):
     """Holds context for executing tests.
@@ -1576,19 +1639,19 @@
 
     # Programs required to run tests.
     REQUIREDTOOLS = [
-        os.path.basename(sys.executable),
-        'diff',
-        'grep',
-        'unzip',
-        'gunzip',
-        'bunzip2',
-        'sed',
+        os.path.basename(_bytespath(sys.executable)),
+        b'diff',
+        b'grep',
+        b'unzip',
+        b'gunzip',
+        b'bunzip2',
+        b'sed',
     ]
 
     # Maps file extensions to test class.
     TESTTYPES = [
-        ('.py', PythonTest),
-        ('.t', TTest),
+        (b'.py', PythonTest),
+        (b'.t', TTest),
     ]
 
     def __init__(self):
@@ -1603,18 +1666,31 @@
         self._coveragefile = None
         self._createdfiles = []
         self._hgpath = None
+        self._portoffset = 0
+        self._ports = {}
 
     def run(self, args, parser=None):
         """Run the test suite."""
-        oldmask = os.umask(022)
+        oldmask = os.umask(0o22)
         try:
             parser = parser or getparser()
             options, args = parseargs(args, parser)
+            # positional arguments are paths to test files to run, so
+            # we make sure they're all bytestrings
+            args = [_bytespath(a) for a in args]
             self.options = options
 
             self._checktools()
             tests = self.findtests(args)
-            return self._run(tests)
+            if options.profile_runner:
+                import statprof
+                statprof.start()
+            result = self._run(tests)
+            if options.profile_runner:
+                statprof.stop()
+                statprof.display()
+            return result
+
         finally:
             os.umask(oldmask)
 
@@ -1623,22 +1699,26 @@
             random.shuffle(tests)
         else:
             # keywords for slow tests
-            slow = 'svn gendoc check-code-hg'.split()
+            slow = {b'svn': 10,
+                    b'gendoc': 10,
+                    b'check-code-hg': 100,
+                   }
             def sortkey(f):
                 # run largest tests first, as they tend to take the longest
                 try:
                     val = -os.stat(f).st_size
-                except OSError, e:
+                except OSError as e:
                     if e.errno != errno.ENOENT:
                         raise
                     return -1e9 # file does not exist, tell early
-                for kw in slow:
+                for kw, mul in slow.iteritems():
                     if kw in f:
-                        val *= 10
+                        val *= mul
                 return val
             tests.sort(key=sortkey)
 
-        self._testdir = os.environ['TESTDIR'] = os.getcwd()
+        self._testdir = osenvironb[b'TESTDIR'] = getattr(
+            os, 'getcwdb', os.getcwd)()
 
         if 'PYTHONHASHSEED' not in os.environ:
             # use a random python hash seed all the time
@@ -1647,12 +1727,12 @@
 
         if self.options.tmpdir:
             self.options.keep_tmpdir = True
-            tmpdir = self.options.tmpdir
+            tmpdir = _bytespath(self.options.tmpdir)
             if os.path.exists(tmpdir):
                 # Meaning of tmpdir has changed since 1.3: we used to create
                 # HGTMP inside tmpdir; now HGTMP is tmpdir.  So fail if
                 # tmpdir already exists.
-                print "error: temp dir %r already exists" % tmpdir
+                print("error: temp dir %r already exists" % tmpdir)
                 return 1
 
                 # Automatically removing tmpdir sounds convenient, but could
@@ -1666,15 +1746,26 @@
             if os.name == 'nt':
                 # without this, we get the default temp dir location, but
                 # in all lowercase, which causes troubles with paths (issue3490)
-                d = os.getenv('TMP')
-            tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
-        self._hgtmp = os.environ['HGTMP'] = os.path.realpath(tmpdir)
+                d = osenvironb.get(b'TMP', None)
+            # FILE BUG: mkdtemp works only on unicode in Python 3
+            tmpdir = tempfile.mkdtemp('', 'hgtests.', d and _strpath(d))
+            tmpdir = _bytespath(tmpdir)
+
+        self._hgtmp = osenvironb[b'HGTMP'] = (
+            os.path.realpath(tmpdir))
 
         if self.options.with_hg:
             self._installdir = None
-            self._bindir = os.path.dirname(os.path.realpath(
-                                           self.options.with_hg))
-            self._tmpbindir = os.path.join(self._hgtmp, 'install', 'bin')
+            whg = self.options.with_hg
+            # If --with-hg is not specified, we have bytes already,
+            # but if it was specified in python3 we get a str, so we
+            # have to encode it back into a bytes.
+            if PYTHON3:
+                if not isinstance(whg, bytes):
+                    whg = _bytespath(whg)
+            self._bindir = os.path.dirname(os.path.realpath(whg))
+            assert isinstance(self._bindir, bytes)
+            self._tmpbindir = os.path.join(self._hgtmp, b'install', b'bin')
             os.makedirs(self._tmpbindir)
 
             # This looks redundant with how Python initializes sys.path from
@@ -1684,25 +1775,30 @@
             # ... which means it's not really redundant at all.
             self._pythondir = self._bindir
         else:
-            self._installdir = os.path.join(self._hgtmp, "install")
-            self._bindir = os.environ["BINDIR"] = \
-                os.path.join(self._installdir, "bin")
+            self._installdir = os.path.join(self._hgtmp, b"install")
+            self._bindir = osenvironb[b"BINDIR"] = \
+                os.path.join(self._installdir, b"bin")
             self._tmpbindir = self._bindir
-            self._pythondir = os.path.join(self._installdir, "lib", "python")
+            self._pythondir = os.path.join(self._installdir, b"lib", b"python")
 
-        os.environ["BINDIR"] = self._bindir
-        os.environ["PYTHON"] = PYTHON
+        osenvironb[b"BINDIR"] = self._bindir
+        osenvironb[b"PYTHON"] = PYTHON
 
-        runtestdir = os.path.abspath(os.path.dirname(__file__))
-        path = [self._bindir, runtestdir] + os.environ["PATH"].split(os.pathsep)
+        fileb = _bytespath(__file__)
+        runtestdir = os.path.abspath(os.path.dirname(fileb))
+        if PYTHON3:
+            sepb = _bytespath(os.pathsep)
+        else:
+            sepb = os.pathsep
+        path = [self._bindir, runtestdir] + osenvironb[b"PATH"].split(sepb)
         if os.path.islink(__file__):
             # test helper will likely be at the end of the symlink
-            realfile = os.path.realpath(__file__)
+            realfile = os.path.realpath(fileb)
             realdir = os.path.abspath(os.path.dirname(realfile))
             path.insert(2, realdir)
         if self._tmpbindir != self._bindir:
             path = [self._tmpbindir] + path
-        os.environ["PATH"] = os.pathsep.join(path)
+        osenvironb[b"PATH"] = sepb.join(path)
 
         # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
         # can run .../tests/run-tests.py test-foo where test-foo
@@ -1713,20 +1809,20 @@
         # it, in case external libraries are only available via current
         # PYTHONPATH.  (In particular, the Subversion bindings on OS X
         # are in /opt/subversion.)
-        oldpypath = os.environ.get(IMPL_PATH)
+        oldpypath = osenvironb.get(IMPL_PATH)
         if oldpypath:
             pypath.append(oldpypath)
-        os.environ[IMPL_PATH] = os.pathsep.join(pypath)
+        osenvironb[IMPL_PATH] = sepb.join(pypath)
 
         if self.options.pure:
             os.environ["HGTEST_RUN_TESTS_PURE"] = "--pure"
 
-        self._coveragefile = os.path.join(self._testdir, '.coverage')
+        self._coveragefile = os.path.join(self._testdir, b'.coverage')
 
         vlog("# Using TESTDIR", self._testdir)
         vlog("# Using HGTMP", self._hgtmp)
         vlog("# Using PATH", os.environ["PATH"])
-        vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
+        vlog("# Using", IMPL_PATH, osenvironb[IMPL_PATH])
 
         try:
             return self._runtests(tests) or 0
@@ -1745,13 +1841,13 @@
                 proc = Popen4('hg st --rev "%s" -man0 .' %
                               self.options.changed, None, 0)
                 stdout, stderr = proc.communicate()
-                args = stdout.strip('\0').split('\0')
+                args = stdout.strip(b'\0').split(b'\0')
             else:
-                args = os.listdir('.')
+                args = os.listdir(b'.')
 
         return [t for t in args
-                if os.path.basename(t).startswith('test-')
-                    and (t.endswith('.py') or t.endswith('.t'))]
+                if os.path.basename(t).startswith(b'test-')
+                    and (t.endswith(b'.py') or t.endswith(b'.t'))]
 
     def _runtests(self, tests):
         try:
@@ -1768,20 +1864,23 @@
                         break
                     tests.pop(0)
                 if not tests:
-                    print "running all tests"
+                    print("running all tests")
                     tests = orig
 
             tests = [self._gettest(t, i) for i, t in enumerate(tests)]
 
             failed = False
             warned = False
+            kws = self.options.keywords
+            if kws is not None and PYTHON3:
+                kws = kws.encode('utf-8')
 
             suite = TestSuite(self._testdir,
                               jobs=self.options.jobs,
                               whitelist=self.options.whitelisted,
                               blacklist=self.options.blacklist,
                               retest=self.options.retest,
-                              keywords=self.options.keywords,
+                              keywords=kws,
                               loop=self.options.loop,
                               runs_per_test=self.options.runs_per_test,
                               tests=tests, loadtest=self._gettest)
@@ -1800,13 +1899,31 @@
                 self._outputcoverage()
         except KeyboardInterrupt:
             failed = True
-            print "\ninterrupted!"
+            print("\ninterrupted!")
 
         if failed:
             return 1
         if warned:
             return 80
 
+    def _getport(self, count):
+        port = self._ports.get(count) # do we have a cached entry?
+        if port is None:
+            port = self.options.port + self._portoffset
+            portneeded = 3
+            # above 100 tries we just give up and let test reports failure
+            for tries in xrange(100):
+                allfree = True
+                for idx in xrange(portneeded):
+                    if not checkportisavailable(port + idx):
+                        allfree = False
+                        break
+                self._portoffset += portneeded
+                if allfree:
+                    break
+            self._ports[count] = port
+        return port
+
     def _gettest(self, test, count):
         """Obtain a Test by looking at its filename.
 
@@ -1822,13 +1939,13 @@
                 break
 
         refpath = os.path.join(self._testdir, test)
-        tmpdir = os.path.join(self._hgtmp, 'child%d' % count)
+        tmpdir = os.path.join(self._hgtmp, b'child%d' % count)
 
         t = testcls(refpath, tmpdir,
                     keeptmpdir=self.options.keep_tmpdir,
                     debug=self.options.debug,
                     timeout=self.options.timeout,
-                    startport=self.options.port + count * 3,
+                    startport=self._getport(count),
                     extraconfigopts=self.options.extra_config_opt,
                     py3kwarnings=self.options.py3k_warnings,
                     shell=self.options.shell)
@@ -1852,7 +1969,7 @@
     def _usecorrectpython(self):
         """Configure the environment to use the appropriate Python in tests."""
         # Tests must use the same interpreter as us or bad things will happen.
-        pyexename = sys.platform == 'win32' and 'python.exe' or 'python'
+        pyexename = sys.platform == 'win32' and b'python.exe' or b'python'
         if getattr(os, 'symlink', None):
             vlog("# Making python executable in test path a symlink to '%s'" %
                  sys.executable)
@@ -1861,14 +1978,14 @@
                 if os.readlink(mypython) == sys.executable:
                     return
                 os.unlink(mypython)
-            except OSError, err:
+            except OSError as err:
                 if err.errno != errno.ENOENT:
                     raise
             if self._findprogram(pyexename) != sys.executable:
                 try:
                     os.symlink(sys.executable, mypython)
                     self._createdfiles.append(mypython)
-                except OSError, err:
+                except OSError as err:
                     # child processes may race, which is harmless
                     if err.errno != errno.EEXIST:
                         raise
@@ -1881,7 +1998,7 @@
                 path.remove(exedir)
             os.environ['PATH'] = os.pathsep.join([exedir] + path)
             if not self._findprogram(pyexename):
-                print "WARNING: Cannot find %s in search path" % pyexename
+                print("WARNING: Cannot find %s in search path" % pyexename)
 
     def _installhg(self):
         """Install hg into the test environment.
@@ -1889,47 +2006,51 @@
         This will also configure hg with the appropriate testing settings.
         """
         vlog("# Performing temporary installation of HG")
-        installerrs = os.path.join("tests", "install.err")
+        installerrs = os.path.join(b"tests", b"install.err")
         compiler = ''
         if self.options.compiler:
             compiler = '--compiler ' + self.options.compiler
         if self.options.pure:
-            pure = "--pure"
+            pure = b"--pure"
         else:
-            pure = ""
+            pure = b""
         py3 = ''
-        if sys.version_info[0] == 3:
-            py3 = '--c2to3'
 
         # Run installer in hg root
         script = os.path.realpath(sys.argv[0])
+        exe = sys.executable
+        if PYTHON3:
+            py3 = b'--c2to3'
+            compiler = _bytespath(compiler)
+            script = _bytespath(script)
+            exe = _bytespath(exe)
         hgroot = os.path.dirname(os.path.dirname(script))
         self._hgroot = hgroot
         os.chdir(hgroot)
-        nohome = '--home=""'
+        nohome = b'--home=""'
         if os.name == 'nt':
             # The --home="" trick works only on OS where os.sep == '/'
             # because of a distutils convert_path() fast-path. Avoid it at
             # least on Windows for now, deal with .pydistutils.cfg bugs
             # when they happen.
-            nohome = ''
-        cmd = ('%(exe)s setup.py %(py3)s %(pure)s clean --all'
-               ' build %(compiler)s --build-base="%(base)s"'
-               ' install --force --prefix="%(prefix)s"'
-               ' --install-lib="%(libdir)s"'
-               ' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1'
-               % {'exe': sys.executable, 'py3': py3, 'pure': pure,
-                  'compiler': compiler,
-                  'base': os.path.join(self._hgtmp, "build"),
-                  'prefix': self._installdir, 'libdir': self._pythondir,
-                  'bindir': self._bindir,
-                  'nohome': nohome, 'logfile': installerrs})
+            nohome = b''
+        cmd = (b'%(exe)s setup.py %(py3)s %(pure)s clean --all'
+               b' build %(compiler)s --build-base="%(base)s"'
+               b' install --force --prefix="%(prefix)s"'
+               b' --install-lib="%(libdir)s"'
+               b' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1'
+               % {b'exe': exe, b'py3': py3, b'pure': pure,
+                  b'compiler': compiler,
+                  b'base': os.path.join(self._hgtmp, b"build"),
+                  b'prefix': self._installdir, b'libdir': self._pythondir,
+                  b'bindir': self._bindir,
+                  b'nohome': nohome, b'logfile': installerrs})
 
         # setuptools requires install directories to exist.
         def makedirs(p):
             try:
                 os.makedirs(p)
-            except OSError, e:
+            except OSError as e:
                 if e.errno != errno.EEXIST:
                     raise
         makedirs(self._pythondir)
@@ -1942,7 +2063,10 @@
         else:
             f = open(installerrs, 'rb')
             for line in f:
-                sys.stdout.write(line)
+                if PYTHON3:
+                    sys.stdout.buffer.write(line)
+                else:
+                    sys.stdout.write(line)
             f.close()
             sys.exit(1)
         os.chdir(self._testdir)
@@ -1960,21 +2084,21 @@
                 f.write(line + '\n')
             f.close()
 
-        hgbat = os.path.join(self._bindir, 'hg.bat')
+        hgbat = os.path.join(self._bindir, b'hg.bat')
         if os.path.isfile(hgbat):
             # hg.bat expects to be put in bin/scripts while run-tests.py
             # installation layout put it in bin/ directly. Fix it
             f = open(hgbat, 'rb')
             data = f.read()
             f.close()
-            if '"%~dp0..\python" "%~dp0hg" %*' in data:
-                data = data.replace('"%~dp0..\python" "%~dp0hg" %*',
-                                    '"%~dp0python" "%~dp0hg" %*')
+            if b'"%~dp0..\python" "%~dp0hg" %*' in data:
+                data = data.replace(b'"%~dp0..\python" "%~dp0hg" %*',
+                                    b'"%~dp0python" "%~dp0hg" %*')
                 f = open(hgbat, 'wb')
                 f.write(data)
                 f.close()
             else:
-                print 'WARNING: cannot fix hg.bat reference to python.exe'
+                print('WARNING: cannot fix hg.bat reference to python.exe')
 
         if self.options.anycoverage:
             custom = os.path.join(self._testdir, 'sitecustomize.py')
@@ -1987,7 +2111,7 @@
             covdir = os.path.join(self._installdir, '..', 'coverage')
             try:
                 os.mkdir(covdir)
-            except OSError, e:
+            except OSError as e:
                 if e.errno != errno.EEXIST:
                     raise
 
@@ -2001,7 +2125,7 @@
             # The pythondir has been inferred from --with-hg flag.
             # We cannot expect anything sensible here.
             return
-        expecthg = os.path.join(self._pythondir, 'mercurial')
+        expecthg = os.path.join(self._pythondir, b'mercurial')
         actualhg = self._gethgpath()
         if os.path.abspath(actualhg) != os.path.abspath(expecthg):
             sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
@@ -2013,10 +2137,13 @@
         if self._hgpath is not None:
             return self._hgpath
 
-        cmd = '%s -c "import mercurial; print (mercurial.__path__[0])"'
-        pipe = os.popen(cmd % PYTHON)
+        cmd = b'%s -c "import mercurial; print (mercurial.__path__[0])"'
+        cmd = cmd % PYTHON
+        if PYTHON3:
+            cmd = _strpath(cmd)
+        pipe = os.popen(cmd)
         try:
-            self._hgpath = pipe.read().strip()
+            self._hgpath = _bytespath(pipe.read().strip())
         finally:
             pipe.close()
 
@@ -2052,7 +2179,9 @@
 
     def _findprogram(self, program):
         """Search PATH for a executable program"""
-        for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
+        dpb = _bytespath(os.defpath)
+        sepb = _bytespath(os.pathsep)
+        for p in osenvironb.get(b'PATH', dpb).split(sepb):
             name = os.path.join(p, program)
             if os.name == 'nt' or os.access(name, os.X_OK):
                 return name
@@ -2067,7 +2196,7 @@
             if found:
                 vlog("# Found prerequisite", p, "at", found)
             else:
-                print "WARNING: Did not find prerequisite tool: %s " % p
+                print("WARNING: Did not find prerequisite tool: %s " % p)
 
 if __name__ == '__main__':
     runner = TestRunner()
--- a/tests/test-acl.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-acl.t	Tue May 19 07:17:57 2015 -0500
@@ -91,33 +91,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   updating the branch cache
   listing keys for "phases"
@@ -151,33 +133,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: changes have source "push" - skipping
@@ -214,33 +178,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "fred"
@@ -287,33 +233,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "fred"
@@ -357,33 +285,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "fred"
@@ -432,33 +342,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "barney"
@@ -504,33 +396,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "fred"
@@ -581,33 +455,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "fred"
@@ -655,33 +511,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "barney"
@@ -731,33 +569,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "barney"
@@ -811,33 +631,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "wilma"
@@ -894,33 +696,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "barney"
@@ -972,33 +756,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "betty"
@@ -1061,33 +827,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "barney"
@@ -1144,33 +892,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "fred"
@@ -1223,33 +953,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "fred"
@@ -1304,33 +1016,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "fred"
@@ -1384,33 +1078,15 @@
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: foo/Bar/file.txt 1/3 files (33.33%)
-  bundling: foo/file.txt 2/3 files (66.67%)
-  bundling: quux/file.py 3/3 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
   adding manifests
-  manifests: 1/3 chunks (33.33%)
-  manifests: 2/3 chunks (66.67%)
-  manifests: 3/3 chunks (100.00%)
   adding file changes
   adding foo/Bar/file.txt revisions
-  files: 1/3 chunks (33.33%)
   adding foo/file.txt revisions
-  files: 2/3 chunks (66.67%)
   adding quux/file.py revisions
-  files: 3/3 chunks (100.00%)
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "fred"
@@ -1504,41 +1180,17 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
-  bundling: 1/4 changesets (25.00%)
-  bundling: 2/4 changesets (50.00%)
-  bundling: 3/4 changesets (75.00%)
-  bundling: 4/4 changesets (100.00%)
-  bundling: 1/4 manifests (25.00%)
-  bundling: 2/4 manifests (50.00%)
-  bundling: 3/4 manifests (75.00%)
-  bundling: 4/4 manifests (100.00%)
-  bundling: abc.txt 1/4 files (25.00%)
-  bundling: foo/Bar/file.txt 2/4 files (50.00%)
-  bundling: foo/file.txt 3/4 files (75.00%)
-  bundling: quux/file.py 4/4 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
-  changesets: 4 chunks
   add changeset e8fc755d4d82
   adding manifests
-  manifests: 1/4 chunks (25.00%)
-  manifests: 2/4 chunks (50.00%)
-  manifests: 3/4 chunks (75.00%)
-  manifests: 4/4 chunks (100.00%)
   adding file changes
   adding abc.txt revisions
-  files: 1/4 chunks (25.00%)
   adding foo/Bar/file.txt revisions
-  files: 2/4 chunks (50.00%)
   adding foo/file.txt revisions
-  files: 3/4 chunks (75.00%)
   adding quux/file.py revisions
-  files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "astro"
@@ -1590,41 +1242,17 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
-  bundling: 1/4 changesets (25.00%)
-  bundling: 2/4 changesets (50.00%)
-  bundling: 3/4 changesets (75.00%)
-  bundling: 4/4 changesets (100.00%)
-  bundling: 1/4 manifests (25.00%)
-  bundling: 2/4 manifests (50.00%)
-  bundling: 3/4 manifests (75.00%)
-  bundling: 4/4 manifests (100.00%)
-  bundling: abc.txt 1/4 files (25.00%)
-  bundling: foo/Bar/file.txt 2/4 files (50.00%)
-  bundling: foo/file.txt 3/4 files (75.00%)
-  bundling: quux/file.py 4/4 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
-  changesets: 4 chunks
   add changeset e8fc755d4d82
   adding manifests
-  manifests: 1/4 chunks (25.00%)
-  manifests: 2/4 chunks (50.00%)
-  manifests: 3/4 chunks (75.00%)
-  manifests: 4/4 chunks (100.00%)
   adding file changes
   adding abc.txt revisions
-  files: 1/4 chunks (25.00%)
   adding foo/Bar/file.txt revisions
-  files: 2/4 chunks (50.00%)
   adding foo/file.txt revisions
-  files: 3/4 chunks (75.00%)
   adding quux/file.py revisions
-  files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "astro"
@@ -1674,41 +1302,17 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
-  bundling: 1/4 changesets (25.00%)
-  bundling: 2/4 changesets (50.00%)
-  bundling: 3/4 changesets (75.00%)
-  bundling: 4/4 changesets (100.00%)
-  bundling: 1/4 manifests (25.00%)
-  bundling: 2/4 manifests (50.00%)
-  bundling: 3/4 manifests (75.00%)
-  bundling: 4/4 manifests (100.00%)
-  bundling: abc.txt 1/4 files (25.00%)
-  bundling: foo/Bar/file.txt 2/4 files (50.00%)
-  bundling: foo/file.txt 3/4 files (75.00%)
-  bundling: quux/file.py 4/4 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
-  changesets: 4 chunks
   add changeset e8fc755d4d82
   adding manifests
-  manifests: 1/4 chunks (25.00%)
-  manifests: 2/4 chunks (50.00%)
-  manifests: 3/4 chunks (75.00%)
-  manifests: 4/4 chunks (100.00%)
   adding file changes
   adding abc.txt revisions
-  files: 1/4 chunks (25.00%)
   adding foo/Bar/file.txt revisions
-  files: 2/4 chunks (50.00%)
   adding foo/file.txt revisions
-  files: 3/4 chunks (75.00%)
   adding quux/file.py revisions
-  files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "astro"
@@ -1754,41 +1358,17 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
-  bundling: 1/4 changesets (25.00%)
-  bundling: 2/4 changesets (50.00%)
-  bundling: 3/4 changesets (75.00%)
-  bundling: 4/4 changesets (100.00%)
-  bundling: 1/4 manifests (25.00%)
-  bundling: 2/4 manifests (50.00%)
-  bundling: 3/4 manifests (75.00%)
-  bundling: 4/4 manifests (100.00%)
-  bundling: abc.txt 1/4 files (25.00%)
-  bundling: foo/Bar/file.txt 2/4 files (50.00%)
-  bundling: foo/file.txt 3/4 files (75.00%)
-  bundling: quux/file.py 4/4 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
-  changesets: 4 chunks
   add changeset e8fc755d4d82
   adding manifests
-  manifests: 1/4 chunks (25.00%)
-  manifests: 2/4 chunks (50.00%)
-  manifests: 3/4 chunks (75.00%)
-  manifests: 4/4 chunks (100.00%)
   adding file changes
   adding abc.txt revisions
-  files: 1/4 chunks (25.00%)
   adding foo/Bar/file.txt revisions
-  files: 2/4 chunks (50.00%)
   adding foo/file.txt revisions
-  files: 3/4 chunks (75.00%)
   adding quux/file.py revisions
-  files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "astro"
@@ -1828,41 +1408,17 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
-  bundling: 1/4 changesets (25.00%)
-  bundling: 2/4 changesets (50.00%)
-  bundling: 3/4 changesets (75.00%)
-  bundling: 4/4 changesets (100.00%)
-  bundling: 1/4 manifests (25.00%)
-  bundling: 2/4 manifests (50.00%)
-  bundling: 3/4 manifests (75.00%)
-  bundling: 4/4 manifests (100.00%)
-  bundling: abc.txt 1/4 files (25.00%)
-  bundling: foo/Bar/file.txt 2/4 files (50.00%)
-  bundling: foo/file.txt 3/4 files (75.00%)
-  bundling: quux/file.py 4/4 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
-  changesets: 4 chunks
   add changeset e8fc755d4d82
   adding manifests
-  manifests: 1/4 chunks (25.00%)
-  manifests: 2/4 chunks (50.00%)
-  manifests: 3/4 chunks (75.00%)
-  manifests: 4/4 chunks (100.00%)
   adding file changes
   adding abc.txt revisions
-  files: 1/4 chunks (25.00%)
   adding foo/Bar/file.txt revisions
-  files: 2/4 chunks (50.00%)
   adding foo/file.txt revisions
-  files: 3/4 chunks (75.00%)
   adding quux/file.py revisions
-  files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "george"
@@ -1919,41 +1475,17 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
-  bundling: 1/4 changesets (25.00%)
-  bundling: 2/4 changesets (50.00%)
-  bundling: 3/4 changesets (75.00%)
-  bundling: 4/4 changesets (100.00%)
-  bundling: 1/4 manifests (25.00%)
-  bundling: 2/4 manifests (50.00%)
-  bundling: 3/4 manifests (75.00%)
-  bundling: 4/4 manifests (100.00%)
-  bundling: abc.txt 1/4 files (25.00%)
-  bundling: foo/Bar/file.txt 2/4 files (50.00%)
-  bundling: foo/file.txt 3/4 files (75.00%)
-  bundling: quux/file.py 4/4 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
-  changesets: 4 chunks
   add changeset e8fc755d4d82
   adding manifests
-  manifests: 1/4 chunks (25.00%)
-  manifests: 2/4 chunks (50.00%)
-  manifests: 3/4 chunks (75.00%)
-  manifests: 4/4 chunks (100.00%)
   adding file changes
   adding abc.txt revisions
-  files: 1/4 chunks (25.00%)
   adding foo/Bar/file.txt revisions
-  files: 2/4 chunks (50.00%)
   adding foo/file.txt revisions
-  files: 3/4 chunks (75.00%)
   adding quux/file.py revisions
-  files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "george"
@@ -2009,41 +1541,17 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
-  bundling: 1/4 changesets (25.00%)
-  bundling: 2/4 changesets (50.00%)
-  bundling: 3/4 changesets (75.00%)
-  bundling: 4/4 changesets (100.00%)
-  bundling: 1/4 manifests (25.00%)
-  bundling: 2/4 manifests (50.00%)
-  bundling: 3/4 manifests (75.00%)
-  bundling: 4/4 manifests (100.00%)
-  bundling: abc.txt 1/4 files (25.00%)
-  bundling: foo/Bar/file.txt 2/4 files (50.00%)
-  bundling: foo/file.txt 3/4 files (75.00%)
-  bundling: quux/file.py 4/4 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
-  changesets: 4 chunks
   add changeset e8fc755d4d82
   adding manifests
-  manifests: 1/4 chunks (25.00%)
-  manifests: 2/4 chunks (50.00%)
-  manifests: 3/4 chunks (75.00%)
-  manifests: 4/4 chunks (100.00%)
   adding file changes
   adding abc.txt revisions
-  files: 1/4 chunks (25.00%)
   adding foo/Bar/file.txt revisions
-  files: 2/4 chunks (50.00%)
   adding foo/file.txt revisions
-  files: 3/4 chunks (75.00%)
   adding quux/file.py revisions
-  files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "george"
@@ -2088,41 +1596,17 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
-  bundling: 1/4 changesets (25.00%)
-  bundling: 2/4 changesets (50.00%)
-  bundling: 3/4 changesets (75.00%)
-  bundling: 4/4 changesets (100.00%)
-  bundling: 1/4 manifests (25.00%)
-  bundling: 2/4 manifests (50.00%)
-  bundling: 3/4 manifests (75.00%)
-  bundling: 4/4 manifests (100.00%)
-  bundling: abc.txt 1/4 files (25.00%)
-  bundling: foo/Bar/file.txt 2/4 files (50.00%)
-  bundling: foo/file.txt 3/4 files (75.00%)
-  bundling: quux/file.py 4/4 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
-  changesets: 4 chunks
   add changeset e8fc755d4d82
   adding manifests
-  manifests: 1/4 chunks (25.00%)
-  manifests: 2/4 chunks (50.00%)
-  manifests: 3/4 chunks (75.00%)
-  manifests: 4/4 chunks (100.00%)
   adding file changes
   adding abc.txt revisions
-  files: 1/4 chunks (25.00%)
   adding foo/Bar/file.txt revisions
-  files: 2/4 chunks (50.00%)
   adding foo/file.txt revisions
-  files: 3/4 chunks (75.00%)
   adding quux/file.py revisions
-  files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "astro"
@@ -2172,41 +1656,17 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
-  bundling: 1/4 changesets (25.00%)
-  bundling: 2/4 changesets (50.00%)
-  bundling: 3/4 changesets (75.00%)
-  bundling: 4/4 changesets (100.00%)
-  bundling: 1/4 manifests (25.00%)
-  bundling: 2/4 manifests (50.00%)
-  bundling: 3/4 manifests (75.00%)
-  bundling: 4/4 manifests (100.00%)
-  bundling: abc.txt 1/4 files (25.00%)
-  bundling: foo/Bar/file.txt 2/4 files (50.00%)
-  bundling: foo/file.txt 3/4 files (75.00%)
-  bundling: quux/file.py 4/4 files (100.00%)
   adding changesets
-  changesets: 1 chunks
   add changeset ef1ea85a6374
-  changesets: 2 chunks
   add changeset f9cafe1212c8
-  changesets: 3 chunks
   add changeset 911600dab2ae
-  changesets: 4 chunks
   add changeset e8fc755d4d82
   adding manifests
-  manifests: 1/4 chunks (25.00%)
-  manifests: 2/4 chunks (50.00%)
-  manifests: 3/4 chunks (75.00%)
-  manifests: 4/4 chunks (100.00%)
   adding file changes
   adding abc.txt revisions
-  files: 1/4 chunks (25.00%)
   adding foo/Bar/file.txt revisions
-  files: 2/4 chunks (50.00%)
   adding foo/file.txt revisions
-  files: 3/4 chunks (75.00%)
   adding quux/file.py revisions
-  files: 4/4 chunks (100.00%)
   added 4 changesets with 4 changes to 4 files (+1 heads)
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "george"
--- a/tests/test-archive.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-archive.t	Tue May 19 07:17:57 2015 -0500
@@ -145,7 +145,7 @@
   test/baz/bletch
   test/foo
 
-  $ hg archive --debug -t tbz2 -X baz test.tar.bz2
+  $ hg archive --debug -t tbz2 -X baz test.tar.bz2 --config progress.debug=true
   archiving: 0/4 files (0.00%)
   archiving: .hgsub 1/4 files (25.00%)
   archiving: .hgsubstate 2/4 files (50.00%)
--- a/tests/test-backout.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-backout.t	Tue May 19 07:17:57 2015 -0500
@@ -42,6 +42,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 3 draft (draft)
 
 commit option
 
@@ -69,6 +70,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 4 draft (draft)
 
   $ echo ypples > a
   $ hg commit -d '5 0' -m ypples
@@ -83,6 +85,7 @@
   branch: default
   commit: 1 unresolved (clean)
   update: (current)
+  phases: 5 draft (draft)
 
 file that was removed is recreated
 (this also tests that editor is not invoked if the commit message is
@@ -110,6 +113,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 3 draft (draft)
 
 backout of backout is as if nothing happened
 
@@ -124,6 +128,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 4 draft (draft)
 
 across branch
 
@@ -144,6 +149,7 @@
   branch: default
   commit: (clean)
   update: 1 new changesets (update)
+  phases: 2 draft (draft)
 
 should fail
 
@@ -160,6 +166,7 @@
   branch: default
   commit: (clean)
   update: 1 new changesets, 2 branch heads (merge)
+  phases: 3 draft (draft)
 
 should fail
 
@@ -172,6 +179,7 @@
   branch: default
   commit: (clean)
   update: 1 new changesets, 2 branch heads (merge)
+  phases: 3 draft (draft)
 
 backout with merge
 
@@ -189,6 +197,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 1 draft (draft)
 
 remove line 1
 
@@ -213,6 +222,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 5 draft (draft)
 
 check line 1 is back
 
@@ -241,6 +251,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 3 draft (draft)
 
 without --merge
   $ hg backout -d '3 0' 1 --tool=true
@@ -258,6 +269,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 3 draft (draft)
 
 with --merge
   $ hg backout --merge -d '3 0' 1 --tool=true
@@ -302,6 +314,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 5 draft (draft)
 
 backout of merge should fail
 
@@ -332,6 +345,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 6 draft (draft)
 
   $ hg rollback
   repository tip rolled back to revision 4 (undo commit)
@@ -344,6 +358,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 5 draft (draft)
 
   $ hg backout -d '6 0' --parent 3 4 --tool=true
   removing c
@@ -354,6 +369,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 6 draft (draft)
 
   $ cd ..
 
@@ -394,6 +410,7 @@
   branch: branch2
   commit: 1 removed
   update: (current)
+  phases: 3 draft (draft)
 
 with --merge
 (this also tests that editor is invoked if '--edit' is specified
@@ -424,6 +441,7 @@
   branch: branch2
   commit: 1 removed (merge)
   update: (current)
+  phases: 4 draft (draft)
   $ hg update -q -C 2
 
 on branch2 with branch1 not merged, so file1 should still exist:
@@ -440,6 +458,7 @@
   branch: branch2
   commit: (clean)
   update: 1 new changesets, 2 branch heads (merge)
+  phases: 4 draft (draft)
 
 on branch2 with branch1 merged, so file1 should be gone:
 
@@ -458,6 +477,7 @@
   branch: branch2
   commit: (clean)
   update: (current)
+  phases: 5 draft (draft)
 
 on branch1, so no file1 and file2:
 
@@ -474,6 +494,7 @@
   branch: branch1
   commit: (clean)
   update: (current)
+  phases: 5 draft (draft)
 
   $ cd ..
 
@@ -553,6 +574,7 @@
   branch: default
   commit: 1 unresolved (clean)
   update: (current)
+  phases: 3 draft (draft)
   $ hg resolve --all --debug
   picked tool 'internal:merge' for foo (binary False symlink False)
   merging foo
@@ -570,6 +592,7 @@
   branch: default
   commit: 1 modified, 1 unknown
   update: (current)
+  phases: 3 draft (draft)
   $ cat foo
   one
   two
--- a/tests/test-bisect.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-bisect.t	Tue May 19 07:17:57 2015 -0500
@@ -190,6 +190,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 32 draft (draft)
   $ hg bisect -g 1
   Testing changeset 16:a2e6ea4973e9 (30 changesets remaining, ~4 tests)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-bisect3.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-bisect3.t	Tue May 19 07:17:57 2015 -0500
@@ -230,3 +230,20 @@
   I 2:e1355ee1f23e
   G 1:ce7c85e06a9f
   G 0:b4e73ffab476
+
+  $ hg --config extensions.color= --color=debug log --quiet --style bisect
+  [log.bisect| ] 14:cbf2f3105bbf
+  [log.bisect| ] 13:e07efca37c43
+  [log.bisect bisect.bad|B] 12:98c6b56349c0
+  [log.bisect bisect.bad|B] 11:03f491376e63
+  [log.bisect bisect.bad|B] 10:c012b15e2409
+  [log.bisect bisect.untested|U] 9:2197c557e14c
+  [log.bisect bisect.untested|U] 8:e74a86251f58
+  [log.bisect bisect.skipped|S] 7:a5f87041c899
+  [log.bisect bisect.good|G] 6:7d997bedcd8d
+  [log.bisect bisect.good|G] 5:2dd1875f1028
+  [log.bisect bisect.good|G] 4:2a1daef14cd4
+  [log.bisect bisect.ignored|I] 3:8417d459b90c
+  [log.bisect bisect.ignored|I] 2:e1355ee1f23e
+  [log.bisect bisect.good|G] 1:ce7c85e06a9f
+  [log.bisect bisect.good|G] 0:b4e73ffab476
--- a/tests/test-bookmarks.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-bookmarks.t	Tue May 19 07:17:57 2015 -0500
@@ -393,6 +393,7 @@
   bookmarks: *Z Y x  y
   commit: (clean)
   update: 1 new changesets, 2 branch heads (merge)
+  phases: 3 draft (draft)
 
 test id
 
@@ -538,6 +539,7 @@
   bookmarks: *Z Y x  y
   commit: 1 added, 1 unknown (new branch head)
   update: 2 new changesets (update)
+  phases: 5 draft (draft)
   $ hg update
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updating bookmark Z
--- a/tests/test-bundle.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-bundle.t	Tue May 19 07:17:57 2015 -0500
@@ -629,7 +629,7 @@
 
 == bundling
 
-  $ hg bundle bundle.hg part --debug
+  $ hg bundle bundle.hg part --debug --config progress.debug=true
   query 1; heads
   searching for changes
   all remote heads known locally
--- a/tests/test-bundle2-format.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-bundle2-format.t	Tue May 19 07:17:57 2015 -0500
@@ -336,7 +336,7 @@
 
 bundling debug
 
-  $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
+  $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2 --config progress.debug=true
   start emission of HG20 stream
   bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
   start of parts
@@ -349,7 +349,7 @@
 
 unbundling debug
 
-  $ hg statbundle2 --debug < ../out.hg2
+  $ hg statbundle2 --debug --config progress.debug=true < ../out.hg2
   start processing of HG20 stream
   reading bundle2 stream parameters
   ignoring unknown parameter 'e|! 7/'
@@ -383,7 +383,7 @@
 Test part
 =================
 
-  $ hg bundle2 --parts ../parts.hg2 --debug
+  $ hg bundle2 --parts ../parts.hg2 --debug --config progress.debug=true
   start emission of HG20 stream
   bundle parameter: 
   start of parts
@@ -436,7 +436,7 @@
       payload: 0 bytes
   parts count:   7
 
-  $ hg statbundle2 --debug < ../parts.hg2
+  $ hg statbundle2 --debug --config progress.debug=true < ../parts.hg2
   start processing of HG20 stream
   reading bundle2 stream parameters
   options count: 0
@@ -515,7 +515,7 @@
 
 Process the bundle
 
-  $ hg unbundle2 --debug < ../parts.hg2
+  $ hg unbundle2 --debug --config progress.debug=true < ../parts.hg2
   start processing of HG20 stream
   reading bundle2 stream parameters
   start extraction of bundle2 parts
@@ -704,7 +704,7 @@
   @  0:3903775176ed draft test  a
   
 
-  $ hg bundle2 --debug --rev '8+7+5+4' ../rev.hg2
+  $ hg bundle2 --debug --config progress.debug=true --rev '8+7+5+4' ../rev.hg2
   4 changesets found
   list of changesets:
   32af7686d403cf45b5d95f2d70cebea587ac806a
--- a/tests/test-check-code.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-check-code.t	Tue May 19 07:17:57 2015 -0500
@@ -19,20 +19,8 @@
   > EOF
   $ cat > non-py24.py <<EOF
   > # Using builtins that does not exist in Python 2.4
-  > if any():
-  >     x = all()
+  > if True:
   >     y = format(x)
-  >     # next(generator) is new in 2.6
-  >     z = next(x)
-  >     # but generator.next() is okay
-  >     x.next()
-  >     # and we can make our own next
-  >     def next(stuff):
-  >         pass
-  > 
-  > # Do not complain about our own definition
-  > def any(x):
-  >     pass
   > 
   > # try/except/finally block does not exist in Python 2.4
   >     try:
@@ -51,18 +39,6 @@
   >     finally:
   >         pass
   > 
-  > # yield inside a try/finally block is not allowed in Python 2.4
-  >     try:
-  >         pass
-  >         yield 1
-  >     finally:
-  >         pass
-  >     try:
-  >         yield
-  >         pass
-  >     finally:
-  >         pass
-  > 
   > EOF
   $ cat > classstyle.py <<EOF
   > class newstyle_class(object):
@@ -92,33 +68,15 @@
   ./quote.py:5:
    > '"""', 42+1, """and
    missing whitespace in expression
-  ./non-py24.py:2:
-   > if any():
-   any/all/format not available in Python 2.4
   ./non-py24.py:3:
-   >     x = all()
-   any/all/format not available in Python 2.4
-  ./non-py24.py:4:
    >     y = format(x)
-   any/all/format not available in Python 2.4
-  ./non-py24.py:6:
-   >     z = next(x)
-   no next(foo) in Python 2.4 and 2.5, use foo.next() instead
-  ./non-py24.py:18:
-   >     try:
-   no try/except/finally in Python 2.4
-  ./non-py24.py:35:
-   >     try:
-   no yield inside try/finally in Python 2.4
-  ./non-py24.py:40:
-   >     try:
-   no yield inside try/finally in Python 2.4
+   format not available in Python 2.4
   ./classstyle.py:4:
    > class oldstyle_class:
    old-style class, use class foo(object)
   ./classstyle.py:7:
    > class empty():
-   class foo() not available in Python 2.4, use class foo(object)
+   class foo() creates old style object, use class foo(object)
   [1]
   $ cat > python3-compat.py << EOF
   > foo <> bar
--- a/tests/test-clone.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-clone.t	Tue May 19 07:17:57 2015 -0500
@@ -64,7 +64,7 @@
 No update, with debug option:
 
 #if hardlink
-  $ hg --debug clone -U . ../c
+  $ hg --debug clone -U . ../c --config progress.debug=true
   linking: 1
   linking: 2
   linking: 3
--- a/tests/test-command-template.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-command-template.t	Tue May 19 07:17:57 2015 -0500
@@ -95,7 +95,7 @@
   8
 
 Add a commit with empty description, to ensure that the templates
-following below omit it properly.
+below will omit the description line.
 
   $ echo c >> c
   $ hg add c
@@ -108,32 +108,52 @@
   $ hg log --style default > style.out
   $ cmp log.out style.out || diff -u log.out style.out
   $ hg log -T phases > phases.out
-  $ diff -u log.out phases.out | grep "phase:"
+  $ diff -U 0 log.out phases.out | grep -v '^---\|^+++'
+  @@ -2,0 +3 @@
   +phase:       draft
+  @@ -6,0 +8 @@
   +phase:       draft
+  @@ -11,0 +14 @@
   +phase:       draft
+  @@ -17,0 +21 @@
   +phase:       draft
+  @@ -24,0 +29 @@
   +phase:       draft
+  @@ -31,0 +37 @@
   +phase:       draft
+  @@ -36,0 +43 @@
   +phase:       draft
+  @@ -41,0 +49 @@
   +phase:       draft
+  @@ -46,0 +55 @@
   +phase:       draft
+  @@ -51,0 +61 @@
   +phase:       draft
 
   $ hg log -v > log.out
   $ hg log -v --style default > style.out
   $ cmp log.out style.out || diff -u log.out style.out
   $ hg log -v -T phases > phases.out
-  $ diff -u log.out phases.out | grep phase:
+  $ diff -U 0 log.out phases.out | grep -v '^---\|^+++'
+  @@ -2,0 +3 @@
   +phase:       draft
+  @@ -7,0 +9 @@
   +phase:       draft
+  @@ -15,0 +18 @@
   +phase:       draft
+  @@ -24,0 +28 @@
   +phase:       draft
+  @@ -33,0 +38 @@
   +phase:       draft
+  @@ -43,0 +49 @@
   +phase:       draft
+  @@ -50,0 +57 @@
   +phase:       draft
+  @@ -58,0 +66 @@
   +phase:       draft
+  @@ -66,0 +75 @@
   +phase:       draft
+  @@ -77,0 +87 @@
   +phase:       draft
 
   $ hg log -q > log.out
@@ -160,32 +180,52 @@
   $ hg --color=debug log --style default > style.out
   $ cmp log.out style.out || diff -u log.out style.out
   $ hg --color=debug log -T phases > phases.out
-  $ diff -u log.out phases.out | grep phase:
+  $ diff -U 0 log.out phases.out | grep -v '^---\|^+++'
+  @@ -2,0 +3 @@
   +[log.phase|phase:       draft]
+  @@ -6,0 +8 @@
   +[log.phase|phase:       draft]
+  @@ -11,0 +14 @@
   +[log.phase|phase:       draft]
+  @@ -17,0 +21 @@
   +[log.phase|phase:       draft]
+  @@ -24,0 +29 @@
   +[log.phase|phase:       draft]
+  @@ -31,0 +37 @@
   +[log.phase|phase:       draft]
+  @@ -36,0 +43 @@
   +[log.phase|phase:       draft]
+  @@ -41,0 +49 @@
   +[log.phase|phase:       draft]
+  @@ -46,0 +55 @@
   +[log.phase|phase:       draft]
+  @@ -51,0 +61 @@
   +[log.phase|phase:       draft]
 
   $ hg --color=debug -v log > log.out
   $ hg --color=debug -v log --style default > style.out
   $ cmp log.out style.out || diff -u log.out style.out
   $ hg --color=debug -v log -T phases > phases.out
-  $ diff -u log.out phases.out | grep phase:
+  $ diff -U 0 log.out phases.out | grep -v '^---\|^+++'
+  @@ -2,0 +3 @@
   +[log.phase|phase:       draft]
+  @@ -7,0 +9 @@
   +[log.phase|phase:       draft]
+  @@ -15,0 +18 @@
   +[log.phase|phase:       draft]
+  @@ -24,0 +28 @@
   +[log.phase|phase:       draft]
+  @@ -33,0 +38 @@
   +[log.phase|phase:       draft]
+  @@ -43,0 +49 @@
   +[log.phase|phase:       draft]
+  @@ -50,0 +57 @@
   +[log.phase|phase:       draft]
+  @@ -58,0 +66 @@
   +[log.phase|phase:       draft]
+  @@ -66,0 +75 @@
   +[log.phase|phase:       draft]
+  @@ -77,0 +87 @@
   +[log.phase|phase:       draft]
 
   $ hg --color=debug -q log > log.out
@@ -952,11 +992,11 @@
 
   $ hg log --style notexist
   abort: style 'notexist' not found
-  (available styles: bisect, changelog, compact, default, phases, xml)
+  (available styles: bisect, changelog, compact, default, phases, status, xml)
   [255]
 
   $ hg log -T list
-  available styles: bisect, changelog, compact, default, phases, xml
+  available styles: bisect, changelog, compact, default, phases, status, xml
   abort: specify a template
   [255]
 
@@ -1898,6 +1938,8 @@
 
 Age filter:
 
+  $ hg init unstable-hash
+  $ cd unstable-hash
   $ hg log --template '{date|age}\n' > /dev/null || exit 1
 
   >>> from datetime import datetime, timedelta
@@ -1911,6 +1953,15 @@
   $ hg log -l1 --template '{date|age}\n'
   7 years from now
 
+  $ cd ..
+  $ rm -rf unstable-hash
+
+Add a dummy commit to make up for the instability of the above:
+
+  $ echo a > a
+  $ hg add a
+  $ hg ci -m future
+
 Count filter:
 
   $ hg log -l1 --template '{node|count} {node|short|count}\n'
@@ -1953,6 +2004,476 @@
   abort: template filter 'upper' is not compatible with keyword 'date'
   [255]
 
+Add a commit that does all possible modifications at once
+
+  $ echo modify >> third
+  $ touch b
+  $ hg add b
+  $ hg mv fourth fifth
+  $ hg rm a
+  $ hg ci -m "Modify, add, remove, rename"
+
+Check the status template
+
+  $ cat <<EOF >> $HGRCPATH
+  > [extensions]
+  > color=
+  > EOF
+
+  $ hg log -T status -r 10
+  changeset:   10:0f9759ec227a
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     Modify, add, remove, rename
+  files:
+  M third
+  A b
+  A fifth
+  R a
+  R fourth
+  
+  $ hg log -T status -C -r 10
+  changeset:   10:0f9759ec227a
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     Modify, add, remove, rename
+  files:
+  M third
+  A b
+  A fifth
+    fourth
+  R a
+  R fourth
+  
+  $ hg log -T status -C -r 10 -v
+  changeset:   10:0f9759ec227a
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  description:
+  Modify, add, remove, rename
+  
+  files:
+  M third
+  A b
+  A fifth
+    fourth
+  R a
+  R fourth
+  
+  $ hg log -T status -C -r 10 --debug
+  changeset:   10:0f9759ec227a4859c2014a345cd8a859022b7c6c
+  tag:         tip
+  phase:       secret
+  parent:      9:bf9dfba36635106d6a73ccc01e28b762da60e066
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    8:89dd546f2de0a9d6d664f58d86097eb97baba567
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  extra:       branch=default
+  description:
+  Modify, add, remove, rename
+  
+  files:
+  M third
+  A b
+  A fifth
+    fourth
+  R a
+  R fourth
+  
+  $ hg log -T status -C -r 10 --quiet
+  10:0f9759ec227a
+  $ hg --color=debug log -T status -r 10
+  [log.changeset changeset.secret|changeset:   10:0f9759ec227a]
+  [log.tag|tag:         tip]
+  [log.user|user:        test]
+  [log.date|date:        Thu Jan 01 00:00:00 1970 +0000]
+  [log.summary|summary:     Modify, add, remove, rename]
+  [ui.note log.files|files:]
+  [status.modified|M third]
+  [status.added|A b]
+  [status.added|A fifth]
+  [status.removed|R a]
+  [status.removed|R fourth]
+  
+  $ hg --color=debug log -T status -C -r 10
+  [log.changeset changeset.secret|changeset:   10:0f9759ec227a]
+  [log.tag|tag:         tip]
+  [log.user|user:        test]
+  [log.date|date:        Thu Jan 01 00:00:00 1970 +0000]
+  [log.summary|summary:     Modify, add, remove, rename]
+  [ui.note log.files|files:]
+  [status.modified|M third]
+  [status.added|A b]
+  [status.added|A fifth]
+  [status.copied|  fourth]
+  [status.removed|R a]
+  [status.removed|R fourth]
+  
+  $ hg --color=debug log -T status -C -r 10 -v
+  [log.changeset changeset.secret|changeset:   10:0f9759ec227a]
+  [log.tag|tag:         tip]
+  [log.user|user:        test]
+  [log.date|date:        Thu Jan 01 00:00:00 1970 +0000]
+  [ui.note log.description|description:]
+  [ui.note log.description|Modify, add, remove, rename]
+  
+  [ui.note log.files|files:]
+  [status.modified|M third]
+  [status.added|A b]
+  [status.added|A fifth]
+  [status.copied|  fourth]
+  [status.removed|R a]
+  [status.removed|R fourth]
+  
+  $ hg --color=debug log -T status -C -r 10 --debug
+  [log.changeset changeset.secret|changeset:   10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
+  [log.tag|tag:         tip]
+  [log.phase|phase:       secret]
+  [log.parent changeset.secret|parent:      9:bf9dfba36635106d6a73ccc01e28b762da60e066]
+  [log.parent changeset.public|parent:      -1:0000000000000000000000000000000000000000]
+  [ui.debug log.manifest|manifest:    8:89dd546f2de0a9d6d664f58d86097eb97baba567]
+  [log.user|user:        test]
+  [log.date|date:        Thu Jan 01 00:00:00 1970 +0000]
+  [ui.debug log.extra|extra:       branch=default]
+  [ui.note log.description|description:]
+  [ui.note log.description|Modify, add, remove, rename]
+  
+  [ui.note log.files|files:]
+  [status.modified|M third]
+  [status.added|A b]
+  [status.added|A fifth]
+  [status.copied|  fourth]
+  [status.removed|R a]
+  [status.removed|R fourth]
+  
+  $ hg --color=debug log -T status -C -r 10 --quiet
+  [log.node|10:0f9759ec227a]
+
+Check the bisect template
+
+  $ hg bisect -g 1
+  $ hg bisect -b 3 --noupdate
+  Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
+  $ hg log -T bisect -r 0:4
+  changeset:   0:1e4e1b8f71e0
+  bisect:      good (implicit)
+  user:        User Name <user@hostname>
+  date:        Mon Jan 12 13:46:40 1970 +0000
+  summary:     line 1
+  
+  changeset:   1:b608e9d1a3f0
+  bisect:      good
+  user:        A. N. Other <other@place>
+  date:        Tue Jan 13 17:33:20 1970 +0000
+  summary:     other 1
+  
+  changeset:   2:97054abb4ab8
+  bisect:      untested
+  user:        other@place
+  date:        Wed Jan 14 21:20:00 1970 +0000
+  summary:     no person
+  
+  changeset:   3:10e46f2dcbf4
+  bisect:      bad
+  user:        person
+  date:        Fri Jan 16 01:06:40 1970 +0000
+  summary:     no user, no domain
+  
+  changeset:   4:bbe44766e73d
+  bisect:      bad (implicit)
+  branch:      foo
+  user:        person
+  date:        Sat Jan 17 04:53:20 1970 +0000
+  summary:     new branch
+  
+  $ hg log --debug -T bisect -r 0:4
+  changeset:   0:1e4e1b8f71e05681d422154f5421e385fec3454f
+  bisect:      good (implicit)
+  phase:       public
+  parent:      -1:0000000000000000000000000000000000000000
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
+  user:        User Name <user@hostname>
+  date:        Mon Jan 12 13:46:40 1970 +0000
+  files+:      a
+  extra:       branch=default
+  description:
+  line 1
+  line 2
+  
+  
+  changeset:   1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
+  bisect:      good
+  phase:       public
+  parent:      0:1e4e1b8f71e05681d422154f5421e385fec3454f
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
+  user:        A. N. Other <other@place>
+  date:        Tue Jan 13 17:33:20 1970 +0000
+  files+:      b
+  extra:       branch=default
+  description:
+  other 1
+  other 2
+  
+  other 3
+  
+  
+  changeset:   2:97054abb4ab824450e9164180baf491ae0078465
+  bisect:      untested
+  phase:       public
+  parent:      1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
+  user:        other@place
+  date:        Wed Jan 14 21:20:00 1970 +0000
+  files+:      c
+  extra:       branch=default
+  description:
+  no person
+  
+  
+  changeset:   3:10e46f2dcbf4823578cf180f33ecf0b957964c47
+  bisect:      bad
+  phase:       public
+  parent:      2:97054abb4ab824450e9164180baf491ae0078465
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    3:cb5a1327723bada42f117e4c55a303246eaf9ccc
+  user:        person
+  date:        Fri Jan 16 01:06:40 1970 +0000
+  files:       c
+  extra:       branch=default
+  description:
+  no user, no domain
+  
+  
+  changeset:   4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
+  bisect:      bad (implicit)
+  branch:      foo
+  phase:       draft
+  parent:      3:10e46f2dcbf4823578cf180f33ecf0b957964c47
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    3:cb5a1327723bada42f117e4c55a303246eaf9ccc
+  user:        person
+  date:        Sat Jan 17 04:53:20 1970 +0000
+  extra:       branch=foo
+  description:
+  new branch
+  
+  
+  $ hg log -v -T bisect -r 0:4
+  changeset:   0:1e4e1b8f71e0
+  bisect:      good (implicit)
+  user:        User Name <user@hostname>
+  date:        Mon Jan 12 13:46:40 1970 +0000
+  files:       a
+  description:
+  line 1
+  line 2
+  
+  
+  changeset:   1:b608e9d1a3f0
+  bisect:      good
+  user:        A. N. Other <other@place>
+  date:        Tue Jan 13 17:33:20 1970 +0000
+  files:       b
+  description:
+  other 1
+  other 2
+  
+  other 3
+  
+  
+  changeset:   2:97054abb4ab8
+  bisect:      untested
+  user:        other@place
+  date:        Wed Jan 14 21:20:00 1970 +0000
+  files:       c
+  description:
+  no person
+  
+  
+  changeset:   3:10e46f2dcbf4
+  bisect:      bad
+  user:        person
+  date:        Fri Jan 16 01:06:40 1970 +0000
+  files:       c
+  description:
+  no user, no domain
+  
+  
+  changeset:   4:bbe44766e73d
+  bisect:      bad (implicit)
+  branch:      foo
+  user:        person
+  date:        Sat Jan 17 04:53:20 1970 +0000
+  description:
+  new branch
+  
+  
+  $ hg --color=debug log -T bisect -r 0:4
+  [log.changeset changeset.public|changeset:   0:1e4e1b8f71e0]
+  [log.bisect bisect.good|bisect:      good (implicit)]
+  [log.user|user:        User Name <user@hostname>]
+  [log.date|date:        Mon Jan 12 13:46:40 1970 +0000]
+  [log.summary|summary:     line 1]
+  
+  [log.changeset changeset.public|changeset:   1:b608e9d1a3f0]
+  [log.bisect bisect.good|bisect:      good]
+  [log.user|user:        A. N. Other <other@place>]
+  [log.date|date:        Tue Jan 13 17:33:20 1970 +0000]
+  [log.summary|summary:     other 1]
+  
+  [log.changeset changeset.public|changeset:   2:97054abb4ab8]
+  [log.bisect bisect.untested|bisect:      untested]
+  [log.user|user:        other@place]
+  [log.date|date:        Wed Jan 14 21:20:00 1970 +0000]
+  [log.summary|summary:     no person]
+  
+  [log.changeset changeset.public|changeset:   3:10e46f2dcbf4]
+  [log.bisect bisect.bad|bisect:      bad]
+  [log.user|user:        person]
+  [log.date|date:        Fri Jan 16 01:06:40 1970 +0000]
+  [log.summary|summary:     no user, no domain]
+  
+  [log.changeset changeset.draft|changeset:   4:bbe44766e73d]
+  [log.bisect bisect.bad|bisect:      bad (implicit)]
+  [log.branch|branch:      foo]
+  [log.user|user:        person]
+  [log.date|date:        Sat Jan 17 04:53:20 1970 +0000]
+  [log.summary|summary:     new branch]
+  
+  $ hg --color=debug log --debug -T bisect -r 0:4
+  [log.changeset changeset.public|changeset:   0:1e4e1b8f71e05681d422154f5421e385fec3454f]
+  [log.bisect bisect.good|bisect:      good (implicit)]
+  [log.phase|phase:       public]
+  [log.parent changeset.public|parent:      -1:0000000000000000000000000000000000000000]
+  [log.parent changeset.public|parent:      -1:0000000000000000000000000000000000000000]
+  [ui.debug log.manifest|manifest:    0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
+  [log.user|user:        User Name <user@hostname>]
+  [log.date|date:        Mon Jan 12 13:46:40 1970 +0000]
+  [ui.debug log.files|files+:      a]
+  [ui.debug log.extra|extra:       branch=default]
+  [ui.note log.description|description:]
+  [ui.note log.description|line 1
+  line 2]
+  
+  
+  [log.changeset changeset.public|changeset:   1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
+  [log.bisect bisect.good|bisect:      good]
+  [log.phase|phase:       public]
+  [log.parent changeset.public|parent:      0:1e4e1b8f71e05681d422154f5421e385fec3454f]
+  [log.parent changeset.public|parent:      -1:0000000000000000000000000000000000000000]
+  [ui.debug log.manifest|manifest:    1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
+  [log.user|user:        A. N. Other <other@place>]
+  [log.date|date:        Tue Jan 13 17:33:20 1970 +0000]
+  [ui.debug log.files|files+:      b]
+  [ui.debug log.extra|extra:       branch=default]
+  [ui.note log.description|description:]
+  [ui.note log.description|other 1
+  other 2
+  
+  other 3]
+  
+  
+  [log.changeset changeset.public|changeset:   2:97054abb4ab824450e9164180baf491ae0078465]
+  [log.bisect bisect.untested|bisect:      untested]
+  [log.phase|phase:       public]
+  [log.parent changeset.public|parent:      1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
+  [log.parent changeset.public|parent:      -1:0000000000000000000000000000000000000000]
+  [ui.debug log.manifest|manifest:    2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
+  [log.user|user:        other@place]
+  [log.date|date:        Wed Jan 14 21:20:00 1970 +0000]
+  [ui.debug log.files|files+:      c]
+  [ui.debug log.extra|extra:       branch=default]
+  [ui.note log.description|description:]
+  [ui.note log.description|no person]
+  
+  
+  [log.changeset changeset.public|changeset:   3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
+  [log.bisect bisect.bad|bisect:      bad]
+  [log.phase|phase:       public]
+  [log.parent changeset.public|parent:      2:97054abb4ab824450e9164180baf491ae0078465]
+  [log.parent changeset.public|parent:      -1:0000000000000000000000000000000000000000]
+  [ui.debug log.manifest|manifest:    3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
+  [log.user|user:        person]
+  [log.date|date:        Fri Jan 16 01:06:40 1970 +0000]
+  [ui.debug log.files|files:       c]
+  [ui.debug log.extra|extra:       branch=default]
+  [ui.note log.description|description:]
+  [ui.note log.description|no user, no domain]
+  
+  
+  [log.changeset changeset.draft|changeset:   4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
+  [log.bisect bisect.bad|bisect:      bad (implicit)]
+  [log.branch|branch:      foo]
+  [log.phase|phase:       draft]
+  [log.parent changeset.public|parent:      3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
+  [log.parent changeset.public|parent:      -1:0000000000000000000000000000000000000000]
+  [ui.debug log.manifest|manifest:    3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
+  [log.user|user:        person]
+  [log.date|date:        Sat Jan 17 04:53:20 1970 +0000]
+  [ui.debug log.extra|extra:       branch=foo]
+  [ui.note log.description|description:]
+  [ui.note log.description|new branch]
+  
+  
+  $ hg --color=debug log -v -T bisect -r 0:4
+  [log.changeset changeset.public|changeset:   0:1e4e1b8f71e0]
+  [log.bisect bisect.good|bisect:      good (implicit)]
+  [log.user|user:        User Name <user@hostname>]
+  [log.date|date:        Mon Jan 12 13:46:40 1970 +0000]
+  [ui.note log.files|files:       a]
+  [ui.note log.description|description:]
+  [ui.note log.description|line 1
+  line 2]
+  
+  
+  [log.changeset changeset.public|changeset:   1:b608e9d1a3f0]
+  [log.bisect bisect.good|bisect:      good]
+  [log.user|user:        A. N. Other <other@place>]
+  [log.date|date:        Tue Jan 13 17:33:20 1970 +0000]
+  [ui.note log.files|files:       b]
+  [ui.note log.description|description:]
+  [ui.note log.description|other 1
+  other 2
+  
+  other 3]
+  
+  
+  [log.changeset changeset.public|changeset:   2:97054abb4ab8]
+  [log.bisect bisect.untested|bisect:      untested]
+  [log.user|user:        other@place]
+  [log.date|date:        Wed Jan 14 21:20:00 1970 +0000]
+  [ui.note log.files|files:       c]
+  [ui.note log.description|description:]
+  [ui.note log.description|no person]
+  
+  
+  [log.changeset changeset.public|changeset:   3:10e46f2dcbf4]
+  [log.bisect bisect.bad|bisect:      bad]
+  [log.user|user:        person]
+  [log.date|date:        Fri Jan 16 01:06:40 1970 +0000]
+  [ui.note log.files|files:       c]
+  [ui.note log.description|description:]
+  [ui.note log.description|no user, no domain]
+  
+  
+  [log.changeset changeset.draft|changeset:   4:bbe44766e73d]
+  [log.bisect bisect.bad|bisect:      bad (implicit)]
+  [log.branch|branch:      foo]
+  [log.user|user:        person]
+  [log.date|date:        Sat Jan 17 04:53:20 1970 +0000]
+  [ui.note log.description|description:]
+  [ui.note log.description|new branch]
+  
+  
+  $ hg bisect --reset
+
 Error on syntax:
 
   $ echo 'x = "f' >> t
@@ -2242,6 +2763,39 @@
   hg: parse error: date expects a date information
   [255]
 
+Test integer literal:
+
+  $ hg log -Ra -r0 -T '{(0)}\n'
+  0
+  $ hg log -Ra -r0 -T '{(123)}\n'
+  123
+  $ hg log -Ra -r0 -T '{(-4)}\n'
+  -4
+  $ hg log -Ra -r0 -T '{(-)}\n'
+  hg: parse error at 2: integer literal without digits
+  [255]
+  $ hg log -Ra -r0 -T '{(-a)}\n'
+  hg: parse error at 2: integer literal without digits
+  [255]
+
+top-level integer literal is interpreted as symbol (i.e. variable name):
+
+  $ hg log -Ra -r0 -T '{1}\n'
+  
+  $ hg log -Ra -r0 -T '{if("t", "{1}")}\n'
+  
+  $ hg log -Ra -r0 -T '{1|stringify}\n'
+  
+
+unless explicit symbol is expected:
+
+  $ hg log -Ra -r0 -T '{desc|1}\n'
+  hg: parse error: expected a symbol, got 'integer'
+  [255]
+  $ hg log -Ra -r0 -T '{1()}\n'
+  hg: parse error: expected a symbol, got 'integer'
+  [255]
+
 Test string escaping:
 
   $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
@@ -2273,6 +2827,25 @@
   <>\n<]>
   <>\n<
 
+Test exception in quoted template. single backslash before quotation mark is
+stripped before parsing:
+
+  $ cat <<'EOF' > escquotetmpl
+  > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
+  > EOF
+  $ cd latesttag
+  $ hg log -r 2 --style ../escquotetmpl
+  " \" \" \\" head1
+
+  $ hg log -r 2 -T esc --config templates.esc='{\"invalid\"}\n'
+  hg: parse error at 1: syntax error
+  [255]
+  $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
+  valid
+  $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
+  valid
+  $ cd ..
+
 Test leading backslashes:
 
   $ cd latesttag
@@ -2517,12 +3090,12 @@
   2 bar* foo 
   1 
   0 
-  $ hg log --template "{rev} {currentbookmark}\n"
+  $ hg log --template "{rev} {activebookmark}\n"
   2 bar
   1 
   0 
   $ hg bookmarks --inactive bar
-  $ hg log --template "{rev} {currentbookmark}\n"
+  $ hg log --template "{rev} {activebookmark}\n"
   2 
   1 
   0 
@@ -2547,7 +3120,9 @@
 Test splitlines
 
   $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
-  @  foo future
+  @  foo Modify, add, remove, rename
+  |
+  o  foo future
   |
   o  foo third
   |
@@ -2581,6 +3156,8 @@
   o
   |
   o
+  |
+  o
   
   o
   |\
@@ -2606,7 +3183,9 @@
 Test word function (including index out of bounds graceful failure)
 
   $ hg log -Gv -R a --template "{word('1', desc)}"
-  @
+  @  add,
+  |
+  o
   |
   o
   |
@@ -2630,7 +3209,9 @@
 Test word third parameter used as splitter
 
   $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
-  @  future
+  @  M
+  |
+  o  future
   |
   o  third
   |
@@ -2661,8 +3242,13 @@
   hg: parse error: word expects two or three arguments, got 7
   [255]
 
+Test word for integer literal
+
+  $ hg log -R a --template "{word(2, desc)}\n" -r0
+  line
+
 Test word for invalid numbers
 
-  $ hg log -Gv -R a --template "{word(2, desc)}"
-  hg: parse error: Use strings like '3' for numbers passed to word function
+  $ hg log -Gv -R a --template "{word('a', desc)}"
+  hg: parse error: word expects an integer index
   [255]
--- a/tests/test-commit-amend.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-commit-amend.t	Tue May 19 07:17:57 2015 -0500
@@ -72,6 +72,7 @@
   branch: default
   commit: 1 added, 1 unknown
   update: (current)
+  phases: 2 draft (draft)
   $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend
   transaction abort!
   rollback completed
@@ -83,6 +84,7 @@
   branch: default
   commit: 1 added, 1 unknown
   update: (current)
+  phases: 2 draft (draft)
 
 Add new file:
   $ hg ci --amend -m 'amend base1 new file'
--- a/tests/test-commit-interactive.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-commit-interactive.t	Tue May 19 07:17:57 2015 -0500
@@ -81,6 +81,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 1 draft (draft)
 
 Rename empty file
 
--- a/tests/test-commit.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-commit.t	Tue May 19 07:17:57 2015 -0500
@@ -302,7 +302,7 @@
   $ cd commitmsg
   $ echo changed > changed
   $ echo removed > removed
-  $ hg book currentbookmark
+  $ hg book activebookmark
   $ hg ci -qAm init
 
   $ hg rm removed
@@ -317,7 +317,7 @@
   HG: --
   HG: user: test
   HG: branch 'default'
-  HG: bookmark 'currentbookmark'
+  HG: bookmark 'activebookmark'
   HG: added added
   HG: changed changed
   HG: removed removed
@@ -354,7 +354,7 @@
   HG: --
   HG: user: test
   HG: branch 'default'
-  HG: bookmark 'currentbookmark'
+  HG: bookmark 'activebookmark'
   HG: subrepo sub
   HG: added .hgsub
   HG: added added
@@ -376,22 +376,22 @@
   > [committemplate]
   > changeset.commit.normal = HG: this is "commit.normal" template
   >     HG: {extramsg}
-  >     {if(currentbookmark,
-  >    "HG: bookmark '{currentbookmark}' is activated\n",
+  >     {if(activebookmark,
+  >    "HG: bookmark '{activebookmark}' is activated\n",
   >    "HG: no bookmark is activated\n")}{subrepos %
   >    "HG: subrepo '{subrepo}' is changed\n"}
   > 
   > changeset.commit = HG: this is "commit" template
   >     HG: {extramsg}
-  >     {if(currentbookmark,
-  >    "HG: bookmark '{currentbookmark}' is activated\n",
+  >     {if(activebookmark,
+  >    "HG: bookmark '{activebookmark}' is activated\n",
   >    "HG: no bookmark is activated\n")}{subrepos %
   >    "HG: subrepo '{subrepo}' is changed\n"}
   > 
   > changeset = HG: this is customized commit template
   >     HG: {extramsg}
-  >     {if(currentbookmark,
-  >    "HG: bookmark '{currentbookmark}' is activated\n",
+  >     {if(activebookmark,
+  >    "HG: bookmark '{activebookmark}' is activated\n",
   >    "HG: no bookmark is activated\n")}{subrepos %
   >    "HG: subrepo '{subrepo}' is changed\n"}
   > EOF
@@ -404,7 +404,7 @@
   $ HGEDITOR=cat hg commit -S -q
   HG: this is "commit.normal" template
   HG: Leave message empty to abort commit.
-  HG: bookmark 'currentbookmark' is activated
+  HG: bookmark 'activebookmark' is activated
   HG: subrepo 'sub' is changed
   HG: subrepo 'sub2' is changed
   abort: empty commit message
@@ -416,7 +416,7 @@
   > # now, "changeset.commit" should be chosen for "hg commit"
   > EOF
 
-  $ hg bookmark --inactive currentbookmark
+  $ hg bookmark --inactive activebookmark
   $ hg forget .hgsub
   $ HGEDITOR=cat hg commit -q
   HG: this is "commit" template
@@ -544,6 +544,18 @@
        0         0       6  .....       0 26d3ca0dfd18 000000000000 000000000000 (re)
        1         6       7  .....       1 d267bddd54f7 26d3ca0dfd18 000000000000 (re)
 
+Test making empty commits
+  $ hg commit --config ui.allowemptycommit=True -m "empty commit"
+  $ hg log -r . -v --stat
+  changeset:   2:d809f3644287
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  description:
+  empty commit
+  
+  
+  
 verify pathauditor blocks evil filepaths
   $ cat > evil-commit.py <<EOF
   > from mercurial import ui, hg, context, node
@@ -568,7 +580,7 @@
 #endif
 
   $ hg rollback -f
-  repository tip rolled back to revision 1 (undo commit)
+  repository tip rolled back to revision 2 (undo commit)
   $ cat > evil-commit.py <<EOF
   > from mercurial import ui, hg, context, node
   > notrc = "HG~1/hgrc"
@@ -586,7 +598,7 @@
   [255]
 
   $ hg rollback -f
-  repository tip rolled back to revision 1 (undo commit)
+  repository tip rolled back to revision 2 (undo commit)
   $ cat > evil-commit.py <<EOF
   > from mercurial import ui, hg, context, node
   > notrc = "HG8B6C~2/hgrc"
--- a/tests/test-completion.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-completion.t	Tue May 19 07:17:57 2015 -0500
@@ -234,7 +234,7 @@
   debugcommands: 
   debugcomplete: options
   debugdag: tags, branches, dots, spaces
-  debugdata: changelog, manifest
+  debugdata: changelog, manifest, dir
   debugdate: extended
   debugdirstate: nodates, datesort
   debugdiscovery: old, nonheads, ssh, remotecmd, insecure
@@ -242,7 +242,7 @@
   debugfsinfo: 
   debuggetbundle: head, common, type
   debugignore: 
-  debugindex: changelog, manifest, format
+  debugindex: changelog, manifest, dir, format
   debugindexdot: 
   debuginstall: 
   debugknown: 
@@ -255,7 +255,7 @@
   debugpvec: 
   debugrebuilddirstate: rev
   debugrename: rev
-  debugrevlog: changelog, manifest, dump
+  debugrevlog: changelog, manifest, dir, dump
   debugrevspec: optimize
   debugsetparents: 
   debugsub: rev
--- a/tests/test-convert-filemap.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-convert-filemap.t	Tue May 19 07:17:57 2015 -0500
@@ -438,7 +438,7 @@
   $ hg ci -m 'merging something'
   $ cd ..
   $ echo "53792d18237d2b64971fa571936869156655338d 6d955580116e82c4b029bd30f321323bae71a7f0" >> branchpruning-hg2/.hg/shamap
-  $ hg convert --filemap branchpruning/filemap branchpruning branchpruning-hg2 --debug
+  $ hg convert --filemap branchpruning/filemap branchpruning branchpruning-hg2 --debug --config progress.debug=true
   run hg source pre-conversion action
   run hg sink pre-conversion action
   scanning source...
--- a/tests/test-convert-svn-encoding.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-convert-svn-encoding.t	Tue May 19 07:17:57 2015 -0500
@@ -10,7 +10,7 @@
 
 Convert while testing all possible outputs
 
-  $ hg --debug convert svn-repo A-hg
+  $ hg --debug convert svn-repo A-hg --config progress.debug=1
   initializing destination A-hg repository
   reparent to file://*/svn-repo (glob)
   run hg sink pre-conversion action
--- a/tests/test-copy-move-merge.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-copy-move-merge.t	Tue May 19 07:17:57 2015 -0500
@@ -35,13 +35,11 @@
    preserving a for resolve of c
   removing a
    b: remote moved from a -> m
-  updating: b 1/2 files (50.00%)
   picked tool 'internal:merge' for b (binary False symlink False)
   merging a and b to b
   my b@add3f11052fa+ other b@17c05bb7fcb6 ancestor a@b8bf91eeebbc
    premerge successful
    c: remote moved from a -> m
-  updating: c 2/2 files (100.00%)
   picked tool 'internal:merge' for c (binary False symlink False)
   merging a and c to c
   my c@add3f11052fa+ other c@17c05bb7fcb6 ancestor a@b8bf91eeebbc
--- a/tests/test-copy.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-copy.t	Tue May 19 07:17:57 2015 -0500
@@ -19,6 +19,7 @@
   branch: default
   commit: 1 copied
   update: (current)
+  phases: 1 draft (draft)
   $ hg --debug commit -m "2"
   committing files:
   b
--- a/tests/test-double-merge.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-double-merge.t	Tue May 19 07:17:57 2015 -0500
@@ -38,13 +38,11 @@
    preserving foo for resolve of bar
    preserving foo for resolve of foo
    bar: remote copied from foo -> m
-  updating: bar 1/2 files (50.00%)
   picked tool 'internal:merge' for bar (binary False symlink False)
   merging foo and bar to bar
   my bar@6a0df1dad128+ other bar@484bf6903104 ancestor foo@e6dc8efe11cc
    premerge successful
    foo: versions differ -> m
-  updating: foo 2/2 files (100.00%)
   picked tool 'internal:merge' for foo (binary False symlink False)
   merging foo
   my foo@6a0df1dad128+ other foo@484bf6903104 ancestor foo@e6dc8efe11cc
--- a/tests/test-globalopts.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-globalopts.t	Tue May 19 07:17:57 2015 -0500
@@ -88,6 +88,7 @@
   [255]
   $ hg -R b ann a/a
   abort: a/a not under root '$TESTTMP/b' (glob)
+  (consider using '--cwd b')
   [255]
   $ hg log
   abort: no repository found in '$TESTTMP' (.hg not found)!
--- a/tests/test-graft.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-graft.t	Tue May 19 07:17:57 2015 -0500
@@ -154,7 +154,6 @@
    ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
    preserving b for resolve of b
    b: local copied/moved from a -> m
-  updating: b 1/1 files (100.00%)
   picked tool 'internal:merge' for b (binary False symlink False)
   merging b and a to b
   my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
@@ -170,7 +169,6 @@
    ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
    e: remote is newer -> g
   getting e
-  updating: e 1/1 files (100.00%)
    b: remote unchanged -> k
   committing files:
   e
@@ -184,10 +182,8 @@
    preserving e for resolve of e
    d: remote is newer -> g
   getting d
-  updating: d 1/2 files (50.00%)
    b: remote unchanged -> k
    e: versions differ -> m
-  updating: e 2/2 files (100.00%)
   picked tool 'internal:merge' for e (binary False symlink False)
   merging e
   my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
--- a/tests/test-hardlinks.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-hardlinks.t	Tue May 19 07:17:57 2015 -0500
@@ -57,7 +57,7 @@
 
 Create hardlinked clone r2:
 
-  $ hg clone -U --debug r1 r2
+  $ hg clone -U --debug r1 r2 --config progress.debug=true
   linking: 1
   linking: 2
   linking: 3
--- a/tests/test-hgweb-commands.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-hgweb-commands.t	Tue May 19 07:17:57 2015 -0500
@@ -762,22 +762,34 @@
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/cad8025a2e87">branch commit with null character: </a><span class="branchhead">unstable</span> <span class="tag">tip</span> <span class="tag">something</span> </td>
+    <td class="description">
+     <a href="/rev/cad8025a2e87">branch commit with null character: </a>
+     <span class="branchhead">unstable</span> <span class="tag">tip</span> <span class="tag">something</span> 
+    </td>
    </tr>
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/1d22e65f027e">branch</a><span class="branchhead">stable</span> </td>
+    <td class="description">
+     <a href="/rev/1d22e65f027e">branch</a>
+     <span class="branchhead">stable</span> 
+    </td>
    </tr>
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/a4f92ed23982">Added tag 1.0 for changeset 2ef0ac749a14</a><span class="branchhead">default</span> </td>
+    <td class="description">
+     <a href="/rev/a4f92ed23982">Added tag 1.0 for changeset 2ef0ac749a14</a>
+     <span class="branchhead">default</span> 
+    </td>
    </tr>
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> <span class="tag">anotherthing</span> </td>
+    <td class="description">
+     <a href="/rev/2ef0ac749a14">base</a>
+     <span class="tag">1.0</span> <span class="tag">anotherthing</span> 
+    </td>
    </tr>
   
   </tbody>
@@ -1024,7 +1036,10 @@
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> <span class="tag">anotherthing</span> </td>
+    <td class="description">
+     <a href="/rev/2ef0ac749a14">base</a>
+     <span class="tag">1.0</span> <span class="tag">anotherthing</span> 
+    </td>
    </tr>
   
   </tbody>
@@ -1280,7 +1295,7 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>view foo @ 1:a4f92ed23982</h3>
+  <h3>view foo @ 1:a4f92ed23982 </h3>
   
   <form class="search" action="/log">
   
@@ -1404,7 +1419,7 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>view foo @ 2:1d22e65f027e</h3>
+  <h3>view foo @ 2:1d22e65f027e <span class="branchname">stable</span> </h3>
   
   <form class="search" action="/log">
   
--- a/tests/test-hgweb-diffs.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-hgweb-diffs.t	Tue May 19 07:17:57 2015 -0500
@@ -249,7 +249,7 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>diff b @ 1:559edbd9ed20</h3>
+  <h3>diff b @ 1:559edbd9ed20 <span class="tag">tip</span> </h3>
   
   <form class="search" action="/log">
   <p></p>
@@ -525,7 +525,7 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>diff a @ 1:559edbd9ed20</h3>
+  <h3>diff a @ 1:559edbd9ed20 <span class="tag">tip</span> </h3>
   
   <form class="search" action="/log">
   <p></p>
@@ -628,7 +628,7 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>comparison a @ 0:0cd96de13884</h3>
+  <h3>comparison a @ 0:0cd96de13884 </h3>
   
   <form class="search" action="/log">
   <p></p>
@@ -755,7 +755,7 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>comparison a @ 2:d73db4d812ff</h3>
+  <h3>comparison a @ 2:d73db4d812ff <span class="tag">tip</span> </h3>
   
   <form class="search" action="/log">
   <p></p>
@@ -884,7 +884,7 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>comparison a @ 3:20e80271eb7a</h3>
+  <h3>comparison a @ 3:20e80271eb7a <span class="tag">tip</span> </h3>
   
   <form class="search" action="/log">
   <p></p>
@@ -1019,7 +1019,7 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>comparison e @ 5:41d9fc4a6ae1</h3>
+  <h3>comparison e @ 5:41d9fc4a6ae1 <span class="tag">tip</span> </h3>
   
   <form class="search" action="/log">
   <p></p>
--- a/tests/test-hgweb-filelog.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-hgweb-filelog.t	Tue May 19 07:17:57 2015 -0500
@@ -8,8 +8,13 @@
   $ echo a > a
   $ hg ci -Am "first a"
   adding a
+  $ hg tag -r 1 a-tag
+  $ hg bookmark -r 1 a-bookmark
   $ hg rm a
   $ hg ci -m "del a"
+  $ hg branch a-branch
+  marked working directory as branch a-branch
+  (branches are permanent and global, did you want a bookmark?)
   $ echo b > a
   $ hg ci -Am "second a"
   adding a
@@ -20,69 +25,86 @@
   $ echo c >> c
   $ hg ci -m "change c"
   $ hg log -p
-  changeset:   6:b7682196df1c
+  changeset:   7:46c1a66bd8fc
+  branch:      a-branch
   tag:         tip
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     change c
   
-  diff -r 1a6696706df2 -r b7682196df1c c
+  diff -r c9637d3cc8ef -r 46c1a66bd8fc c
   --- a/c	Thu Jan 01 00:00:00 1970 +0000
   +++ b/c	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,1 +1,2 @@
    b
   +c
   
-  changeset:   5:1a6696706df2
+  changeset:   6:c9637d3cc8ef
+  branch:      a-branch
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     mv b
   
-  diff -r 52e848cdcd88 -r 1a6696706df2 b
+  diff -r 958bd88be4eb -r c9637d3cc8ef b
   --- a/b	Thu Jan 01 00:00:00 1970 +0000
   +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,1 +0,0 @@
   -b
-  diff -r 52e848cdcd88 -r 1a6696706df2 c
+  diff -r 958bd88be4eb -r c9637d3cc8ef c
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/c	Thu Jan 01 00:00:00 1970 +0000
   @@ -0,0 +1,1 @@
   +b
   
-  changeset:   4:52e848cdcd88
+  changeset:   5:958bd88be4eb
+  branch:      a-branch
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     del2 a
   
-  diff -r 01de2d66a28d -r 52e848cdcd88 a
+  diff -r 3f41bc784e7e -r 958bd88be4eb a
   --- a/a	Thu Jan 01 00:00:00 1970 +0000
   +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,1 +0,0 @@
   -b
   
-  changeset:   3:01de2d66a28d
+  changeset:   4:3f41bc784e7e
+  branch:      a-branch
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     second a
   
-  diff -r be3ebcc91739 -r 01de2d66a28d a
+  diff -r 292258f86fdf -r 3f41bc784e7e a
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/a	Thu Jan 01 00:00:00 1970 +0000
   @@ -0,0 +1,1 @@
   +b
   
-  changeset:   2:be3ebcc91739
+  changeset:   3:292258f86fdf
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     del a
   
-  diff -r 5ed941583260 -r be3ebcc91739 a
+  diff -r 94c9dd5ca9b4 -r 292258f86fdf a
   --- a/a	Thu Jan 01 00:00:00 1970 +0000
   +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,1 +0,0 @@
   -a
   
+  changeset:   2:94c9dd5ca9b4
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     Added tag a-tag for changeset 5ed941583260
+  
+  diff -r 5ed941583260 -r 94c9dd5ca9b4 .hgtags
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/.hgtags	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +5ed941583260248620985524192fdc382ef57c36 a-tag
+  
   changeset:   1:5ed941583260
+  bookmark:    a-bookmark
+  tag:         a-tag
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     first a
@@ -135,29 +157,29 @@
   <img src="/static/hglogo.png" alt="mercurial" /></a>
   </div>
   <ul>
-  <li><a href="/shortlog/01de2d66a28d">log</a></li>
-  <li><a href="/graph/01de2d66a28d">graph</a></li>
+  <li><a href="/shortlog/3f41bc784e7e">log</a></li>
+  <li><a href="/graph/3f41bc784e7e">graph</a></li>
   <li><a href="/tags">tags</a></li>
   <li><a href="/bookmarks">bookmarks</a></li>
   <li><a href="/branches">branches</a></li>
   </ul>
   <ul>
-  <li><a href="/rev/01de2d66a28d">changeset</a></li>
-  <li><a href="/file/01de2d66a28d">browse</a></li>
+  <li><a href="/rev/3f41bc784e7e">changeset</a></li>
+  <li><a href="/file/3f41bc784e7e">browse</a></li>
   </ul>
   <ul>
-  <li><a href="/file/01de2d66a28d/a">file</a></li>
-  <li><a href="/diff/01de2d66a28d/a">diff</a></li>
-  <li><a href="/comparison/01de2d66a28d/a">comparison</a></li>
-  <li><a href="/annotate/01de2d66a28d/a">annotate</a></li>
+  <li><a href="/file/3f41bc784e7e/a">file</a></li>
+  <li><a href="/diff/3f41bc784e7e/a">diff</a></li>
+  <li><a href="/comparison/3f41bc784e7e/a">comparison</a></li>
+  <li><a href="/annotate/3f41bc784e7e/a">annotate</a></li>
   <li class="active">file log</li>
-  <li><a href="/raw-file/01de2d66a28d/a">raw</a></li>
+  <li><a href="/raw-file/3f41bc784e7e/a">raw</a></li>
   </ul>
   <ul>
   <li><a href="/help">help</a></li>
   </ul>
   <div class="atom-logo">
-  <a href="/atom-log/01de2d66a28d/a" title="subscribe to atom feed">
+  <a href="/atom-log/3f41bc784e7e/a" title="subscribe to atom feed">
   <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
   </a>
   </div>
@@ -175,8 +197,8 @@
   </form>
   
   <div class="navigate">
-  <a href="/log/01de2d66a28d/a?revcount=30">less</a>
-  <a href="/log/01de2d66a28d/a?revcount=120">more</a>
+  <a href="/log/3f41bc784e7e/a?revcount=30">less</a>
+  <a href="/log/3f41bc784e7e/a?revcount=120">more</a>
   | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
   
   <table class="bigtable">
@@ -191,20 +213,26 @@
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/01de2d66a28d">second a</a></td>
+    <td class="description">
+     <a href="/rev/3f41bc784e7e">second a</a>
+     <span class="branchname">a-branch</span> 
+    </td>
    </tr>
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/5ed941583260">first a</a></td>
+    <td class="description">
+     <a href="/rev/5ed941583260">first a</a>
+     <span class="tag">a-tag</span> <span class="tag">a-bookmark</span> 
+    </td>
    </tr>
   
   </tbody>
   </table>
   
   <div class="navigate">
-  <a href="/log/01de2d66a28d/a?revcount=30">less</a>
-  <a href="/log/01de2d66a28d/a?revcount=120">more</a>
+  <a href="/log/3f41bc784e7e/a?revcount=30">less</a>
+  <a href="/log/3f41bc784e7e/a?revcount=120">more</a>
   | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> 
   </div>
   
@@ -220,7 +248,7 @@
 
 second version - two revisions
 
-  $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'log/3/a')
+  $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'log/4/a')
   200 Script output follows
   
   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
@@ -246,29 +274,29 @@
   <img src="/static/hglogo.png" alt="mercurial" /></a>
   </div>
   <ul>
-  <li><a href="/shortlog/01de2d66a28d">log</a></li>
-  <li><a href="/graph/01de2d66a28d">graph</a></li>
+  <li><a href="/shortlog/3f41bc784e7e">log</a></li>
+  <li><a href="/graph/3f41bc784e7e">graph</a></li>
   <li><a href="/tags">tags</a></li>
   <li><a href="/bookmarks">bookmarks</a></li>
   <li><a href="/branches">branches</a></li>
   </ul>
   <ul>
-  <li><a href="/rev/01de2d66a28d">changeset</a></li>
-  <li><a href="/file/01de2d66a28d">browse</a></li>
+  <li><a href="/rev/3f41bc784e7e">changeset</a></li>
+  <li><a href="/file/3f41bc784e7e">browse</a></li>
   </ul>
   <ul>
-  <li><a href="/file/01de2d66a28d/a">file</a></li>
-  <li><a href="/diff/01de2d66a28d/a">diff</a></li>
-  <li><a href="/comparison/01de2d66a28d/a">comparison</a></li>
-  <li><a href="/annotate/01de2d66a28d/a">annotate</a></li>
+  <li><a href="/file/3f41bc784e7e/a">file</a></li>
+  <li><a href="/diff/3f41bc784e7e/a">diff</a></li>
+  <li><a href="/comparison/3f41bc784e7e/a">comparison</a></li>
+  <li><a href="/annotate/3f41bc784e7e/a">annotate</a></li>
   <li class="active">file log</li>
-  <li><a href="/raw-file/01de2d66a28d/a">raw</a></li>
+  <li><a href="/raw-file/3f41bc784e7e/a">raw</a></li>
   </ul>
   <ul>
   <li><a href="/help">help</a></li>
   </ul>
   <div class="atom-logo">
-  <a href="/atom-log/01de2d66a28d/a" title="subscribe to atom feed">
+  <a href="/atom-log/3f41bc784e7e/a" title="subscribe to atom feed">
   <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
   </a>
   </div>
@@ -286,8 +314,8 @@
   </form>
   
   <div class="navigate">
-  <a href="/log/01de2d66a28d/a?revcount=30">less</a>
-  <a href="/log/01de2d66a28d/a?revcount=120">more</a>
+  <a href="/log/3f41bc784e7e/a?revcount=30">less</a>
+  <a href="/log/3f41bc784e7e/a?revcount=120">more</a>
   | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
   
   <table class="bigtable">
@@ -302,20 +330,26 @@
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/01de2d66a28d">second a</a></td>
+    <td class="description">
+     <a href="/rev/3f41bc784e7e">second a</a>
+     <span class="branchname">a-branch</span> 
+    </td>
    </tr>
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/5ed941583260">first a</a></td>
+    <td class="description">
+     <a href="/rev/5ed941583260">first a</a>
+     <span class="tag">a-tag</span> <span class="tag">a-bookmark</span> 
+    </td>
    </tr>
   
   </tbody>
   </table>
   
   <div class="navigate">
-  <a href="/log/01de2d66a28d/a?revcount=30">less</a>
-  <a href="/log/01de2d66a28d/a?revcount=120">more</a>
+  <a href="/log/3f41bc784e7e/a?revcount=30">less</a>
+  <a href="/log/3f41bc784e7e/a?revcount=120">more</a>
   | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> 
   </div>
   
@@ -331,7 +365,7 @@
 
 first deleted - one revision
 
-  $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'log/2/a')
+  $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'log/3/a')
   200 Script output follows
   
   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
@@ -413,7 +447,10 @@
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/5ed941583260">first a</a></td>
+    <td class="description">
+     <a href="/rev/5ed941583260">first a</a>
+     <span class="tag">a-tag</span> <span class="tag">a-bookmark</span> 
+    </td>
    </tr>
   
   </tbody>
@@ -519,7 +556,10 @@
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
     <td class="author">test</td>
-    <td class="description"><a href="/rev/5ed941583260">first a</a></td>
+    <td class="description">
+     <a href="/rev/5ed941583260">first a</a>
+     <span class="tag">a-tag</span> <span class="tag">a-bookmark</span> 
+    </td>
    </tr>
   
   </tbody>
@@ -634,8 +674,8 @@
   <a href="/graph?style=spartan">graph</a>
   <a href="/tags?style=spartan">tags</a>
   <a href="/branches?style=spartan">branches</a>
-  <a href="/file/b7682196df1c/c?style=spartan">file</a>
-  <a href="/annotate/b7682196df1c/c?style=spartan">annotate</a>
+  <a href="/file/46c1a66bd8fc/c?style=spartan">file</a>
+  <a href="/annotate/46c1a66bd8fc/c?style=spartan">annotate</a>
   <a href="/help?style=spartan">help</a>
   <a type="application/rss+xml" href="/rss-log/tip/c">rss</a>
   <a type="application/atom+xml" href="/atom-log/tip/c" title="Atom feed for test:c">atom</a>
@@ -643,19 +683,19 @@
   
   <h2><a href="/">Mercurial</a>  / c revision history</h2>
   
-  <p>navigate: <small class="navigate"><a href="/log/1a6696706df2/c?style=spartan">(0)</a> <a href="/log/tip/c?style=spartan">tip</a> </small></p>
+  <p>navigate: <small class="navigate"><a href="/log/c9637d3cc8ef/c?style=spartan">(0)</a> <a href="/log/tip/c?style=spartan">tip</a> </small></p>
   
   <table class="logEntry parity0">
    <tr>
     <th class="label"><span class="age">Thu, 01 Jan 1970 00:00:00 +0000</span>:</th>
-    <th class="firstline"><a href="/rev/b7682196df1c?style=spartan">change c</a></th>
+    <th class="firstline"><a href="/rev/46c1a66bd8fc?style=spartan">change c</a></th>
    </tr>
    <tr>
     <th class="revision">revision 1:</th>
     <td class="node">
-     <a href="/file/b7682196df1c/c?style=spartan">b7682196df1c</a>
-     <a href="/diff/b7682196df1c/c?style=spartan">(diff)</a>
-     <a href="/annotate/b7682196df1c/c?style=spartan">(annotate)</a>
+     <a href="/file/46c1a66bd8fc/c?style=spartan">46c1a66bd8fc</a>
+     <a href="/diff/46c1a66bd8fc/c?style=spartan">(diff)</a>
+     <a href="/annotate/46c1a66bd8fc/c?style=spartan">(annotate)</a>
     </td>
    </tr>
    
@@ -673,14 +713,14 @@
   <table class="logEntry parity1">
    <tr>
     <th class="label"><span class="age">Thu, 01 Jan 1970 00:00:00 +0000</span>:</th>
-    <th class="firstline"><a href="/rev/1a6696706df2?style=spartan">mv b</a></th>
+    <th class="firstline"><a href="/rev/c9637d3cc8ef?style=spartan">mv b</a></th>
    </tr>
    <tr>
     <th class="revision">revision 0:</th>
     <td class="node">
-     <a href="/file/1a6696706df2/c?style=spartan">1a6696706df2</a>
-     <a href="/diff/1a6696706df2/c?style=spartan">(diff)</a>
-     <a href="/annotate/1a6696706df2/c?style=spartan">(annotate)</a>
+     <a href="/file/c9637d3cc8ef/c?style=spartan">c9637d3cc8ef</a>
+     <a href="/diff/c9637d3cc8ef/c?style=spartan">(diff)</a>
+     <a href="/annotate/c9637d3cc8ef/c?style=spartan">(annotate)</a>
     </td>
    </tr>
    
@@ -731,7 +771,7 @@
       <description>a revision history</description>
       <item>
       <title>second a</title>
-      <link>http://*:$HGPORT/log01de2d66a28d/a</link> (glob)
+      <link>http://*:$HGPORT/log3f41bc784e7e/a</link> (glob)
       <description><![CDATA[second a]]></description>
       <author>&#116;&#101;&#115;&#116;</author>
       <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
@@ -760,9 +800,9 @@
    <updated>1970-01-01T00:00:00+00:00</updated>
   
    <entry>
-    <title>second a</title>
-    <id>http://*:$HGPORT/#changeset-01de2d66a28df5549090991dccda788726948517</id> (glob)
-    <link href="http://*:$HGPORT/rev/01de2d66a28d"/> (glob)
+    <title>[a-branch] second a</title>
+    <id>http://*:$HGPORT/#changeset-3f41bc784e7e73035c6d47112c6cc7efb673adf8</id> (glob)
+    <link href="http://*:$HGPORT/rev/3f41bc784e7e"/> (glob)
     <author>
      <name>test</name>
      <email>&#116;&#101;&#115;&#116;</email>
@@ -773,11 +813,11 @@
   	<table xmlns="http://www.w3.org/1999/xhtml">
   	<tr>
   		<th style="text-align:left;">changeset</th>
-  		<td>01de2d66a28d</td>
+  		<td>3f41bc784e7e</td>
                 </tr>
                 <tr>
                                 <th style="text-align:left;">branch</th>
-                                <td></td>
+                                <td>a-branch</td>
                 </tr>
                 <tr>
                                 <th style="text-align:left;">bookmark</th>
@@ -824,11 +864,11 @@
                 </tr>
                 <tr>
                                 <th style="text-align:left;">bookmark</th>
-  		<td></td>
+  		<td>a-bookmark</td>
   	</tr>
   	<tr>
   		<th style="text-align:left;">tag</th>
-  		<td></td>
+  		<td>a-tag</td>
   	</tr>
   	<tr>
   		<th style="text-align:left;">user</th>
--- a/tests/test-hgweb-removed.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-hgweb-removed.t	Tue May 19 07:17:57 2015 -0500
@@ -185,7 +185,7 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>diff a @ 1:c78f6c5cbea9</h3>
+  <h3>diff a @ 1:c78f6c5cbea9 <span class="tag">tip</span> </h3>
   
   <form class="search" action="/log">
   <p></p>
--- a/tests/test-highlight.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-highlight.t	Tue May 19 07:17:57 2015 -0500
@@ -105,7 +105,7 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>view primes.py @ 0:853dcd4de2a6</h3>
+  <h3>view primes.py @ 0:853dcd4de2a6 <span class="tag">tip</span> </h3>
   
   <form class="search" action="/log">
   
@@ -236,7 +236,7 @@
   
   <div class="main">
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
-  <h3>annotate primes.py @ 0:853dcd4de2a6</h3>
+  <h3>annotate primes.py @ 0:853dcd4de2a6 <span class="tag">tip</span> </h3>
   
   <form class="search" action="/log">
   
--- a/tests/test-histedit-edit.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-histedit-edit.t	Tue May 19 07:17:57 2015 -0500
@@ -246,6 +246,7 @@
   branch: default
   commit: 1 added (new branch head)
   update: 1 new changesets (update)
+  phases: 7 draft (draft)
   hist:   1 remaining (histedit --continue)
 
 (test also that editor is invoked if histedit is continued for
--- a/tests/test-histedit-no-change.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-histedit-no-change.t	Tue May 19 07:17:57 2015 -0500
@@ -183,6 +183,7 @@
   branch: default
   commit: 1 added, 1 unknown (new branch head)
   update: 6 new changesets (update)
+  phases: 7 draft (draft)
   hist:   2 remaining (histedit --continue)
 
   $ hg histedit --abort 2>&1 | fixbundle
--- a/tests/test-http.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-http.t	Tue May 19 07:17:57 2015 -0500
@@ -166,7 +166,6 @@
   > getpass.getpass = newgetpass
   > EOF
 
-#if python243
   $ hg id http://localhost:$HGPORT2/
   abort: http authorization required for http://localhost:$HGPORT2/
   [255]
@@ -180,7 +179,6 @@
   password: 5fed3813f7f5
   $ hg id http://user:pass@localhost:$HGPORT2/
   5fed3813f7f5
-#endif
   $ echo '[auth]' >> .hg/hgrc
   $ echo 'l.schemes=http' >> .hg/hgrc
   $ echo 'l.prefix=lo' >> .hg/hgrc
@@ -192,7 +190,6 @@
   5fed3813f7f5
   $ hg id http://user@localhost:$HGPORT2/
   5fed3813f7f5
-#if python243
   $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
   streaming all changes
   7 files to transfer, 916 bytes of data
@@ -291,7 +288,6 @@
   "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524
   "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases
 
-#endif
   $ cd ..
 
 clone of serve with repo in root and unserved subrepo (issue2970)
--- a/tests/test-hybridencode.py	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-hybridencode.py	Tue May 19 07:17:57 2015 -0500
@@ -460,3 +460,9 @@
           'VWXYZ-1234567890-xxxxxxxxx-xxxxxxxxx-xxxxxxxx-xxxx'
           'xxxxx-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwww'
           'wwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww.i')
+
+print "paths outside data/ can be encoded"
+show('metadata/dir/00manifest.i')
+show('metadata/12345678/12345678/12345678/12345678/12345678/'
+          '12345678/12345678/12345678/12345678/12345678/12345678/'
+          '12345678/12345678/00manifest.i')
--- a/tests/test-hybridencode.py.out	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-hybridencode.py.out	Tue May 19 07:17:57 2015 -0500
@@ -491,3 +491,10 @@
 A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPRSTUVWXYZ-1234567890-xxxxxxxxx-xxxxxxxxx-xxxxxxxx-xxxxxxxxx-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww.i'
 B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx93352aa50377751d9e5ebdf52da1e6e69a6887a6.i'
 
+paths outside data/ can be encoded
+A = 'metadata/dir/00manifest.i'
+B = 'metadata/dir/00manifest.i'
+
+A = 'metadata/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/00manifest.i'
+B = 'dh/ata/12345678/12345678/12345678/12345678/12345678/12345678/12345678/00manife0a4da1f89aa2aa9eb0896eb451288419049781b4.i'
+
--- a/tests/test-import.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-import.t	Tue May 19 07:17:57 2015 -0500
@@ -990,6 +990,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 2 draft (draft)
 
   $ hg diff --git -c tip
   diff --git a/lib/place-holder b/lib/place-holder
@@ -1018,6 +1019,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 2 draft (draft)
 
   $ hg diff --git -c tip
   diff --git a/lib/place-holder b/lib/place-holder
--- a/tests/test-issue1802.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-issue1802.t	Tue May 19 07:17:57 2015 -0500
@@ -58,7 +58,6 @@
    branchmerge: True, force: False, partial: False
    ancestor: a03b0deabf2b, local: d6fa54f68ae1+, remote: 2d8bcf2dda39
    a: update permissions -> e
-  updating: a 1/1 files (100.00%)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
--- a/tests/test-issue522.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-issue522.t	Tue May 19 07:17:57 2015 -0500
@@ -33,7 +33,6 @@
    ancestor: bbd179dfa0a7, local: 71766447bdbb+, remote: 4d9e78aaceee
    foo: remote is newer -> g
   getting foo
-  updating: foo 1/1 files (100.00%)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
--- a/tests/test-issue672.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-issue672.t	Tue May 19 07:17:57 2015 -0500
@@ -36,10 +36,8 @@
    ancestor: 81f4b099af3d, local: c64f439569a9+, remote: c12dcd37c90a
    1: other deleted -> r
   removing 1
-  updating: 1 1/2 files (50.00%)
    1a: remote created -> g
   getting 1a
-  updating: 1a 2/2 files (100.00%)
    2: remote unchanged -> k
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
@@ -68,7 +66,6 @@
    ancestor: c64f439569a9, local: e327dca35ac8+, remote: 746e9549ea96
    preserving 1a for resolve of 1a
    1a: local copied/moved from 1 -> m
-  updating: 1a 1/1 files (100.00%)
   picked tool 'internal:merge' for 1a (binary False symlink False)
   merging 1a and 1 to 1a
   my 1a@e327dca35ac8+ other 1@746e9549ea96 ancestor 1@81f4b099af3d
@@ -92,7 +89,6 @@
    preserving 1 for resolve of 1a
   removing 1
    1a: remote moved from 1 -> m
-  updating: 1a 1/1 files (100.00%)
   picked tool 'internal:merge' for 1a (binary False symlink False)
   merging 1 and 1a to 1a
   my 1a@746e9549ea96+ other 1a@e327dca35ac8 ancestor 1@81f4b099af3d
--- a/tests/test-largefiles-misc.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-largefiles-misc.t	Tue May 19 07:17:57 2015 -0500
@@ -228,6 +228,7 @@
   branch: default
   commit: 1 subrepos
   update: (current)
+  phases: 2 draft (draft)
   $ hg st
   $ hg st -S
   A subrepo/large.txt
@@ -245,6 +246,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 3 draft (draft)
   $ echo "rev 2" > subrepo/large.txt
   $ hg st -S
   M subrepo/large.txt
@@ -254,6 +256,7 @@
   branch: default
   commit: 1 subrepos
   update: (current)
+  phases: 3 draft (draft)
   $ hg ci -m "this commit should fail without -S"
   abort: uncommitted changes in subrepository 'subrepo'
   (use --subrepos for recursive commit)
@@ -567,6 +570,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 1 draft (draft)
   largefiles: (no remote repo)
 
 check messages when there is no files to upload:
@@ -581,6 +585,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 1 draft (draft)
   largefiles: (no files to upload)
   $ hg -R clone2 outgoing --large
   comparing with $TESTTMP/issue3651/src (glob)
@@ -608,6 +613,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 2 draft (draft)
   largefiles: 1 entities for 1 files to upload
   $ hg -R clone2 outgoing --large
   comparing with $TESTTMP/issue3651/src (glob)
@@ -643,6 +649,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 3 draft (draft)
   largefiles: 1 entities for 3 files to upload
   $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
   comparing with $TESTTMP/issue3651/src (glob)
@@ -656,7 +663,7 @@
   
   $ hg -R clone2 cat -r 1 clone2/.hglf/b
   89e6c98d92887913cadf06b2adb97f26cde4849b
-  $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
+  $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
   comparing with $TESTTMP/issue3651/src (glob)
   query 1; heads
   searching for changes
@@ -692,6 +699,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 6 draft (draft)
   largefiles: 3 entities for 3 files to upload
   $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
   comparing with $TESTTMP/issue3651/src (glob)
@@ -710,7 +718,7 @@
   c801c9cfe94400963fcb683246217d5db77f9a9a
   $ hg -R clone2 cat -r 4 clone2/.hglf/b
   13f9ed0898e315bf59dc2973fec52037b6f441a2
-  $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
+  $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
   comparing with $TESTTMP/issue3651/src (glob)
   query 1; heads
   searching for changes
@@ -750,6 +758,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 6 draft (draft)
   largefiles: 2 entities for 1 files to upload
   $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
   comparing with $TESTTMP/issue3651/src (glob)
@@ -761,7 +770,7 @@
   largefiles to upload (2 entities):
   b
   
-  $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
+  $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
   comparing with $TESTTMP/issue3651/src (glob)
   query 1; heads
   searching for changes
@@ -1017,7 +1026,7 @@
 
   $ hg -R no-largefiles -q pull --rebase
   Invoking status precommit hook
-  ? normal3
+  M normal3
 
 (test reverting)
 
--- a/tests/test-largefiles-wireproto.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-largefiles-wireproto.t	Tue May 19 07:17:57 2015 -0500
@@ -264,7 +264,7 @@
 
 largefiles pulled on update - no server side problems:
   $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
-  $ hg -R http-clone --debug up --config largefiles.usercache=http-clone-usercache
+  $ hg -R http-clone --debug up --config largefiles.usercache=http-clone-usercache --config progress.debug=true
   resolving manifests
    branchmerge: False, force: False, partial: False
    ancestor: 000000000000, local: 000000000000+, remote: cf03e5bb9936
--- a/tests/test-largefiles.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-largefiles.t	Tue May 19 07:17:57 2015 -0500
@@ -67,6 +67,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 2 draft (draft)
   largefiles: (no remote repo)
 
 Commit preserved largefile contents.
@@ -999,6 +1000,7 @@
   branch: default
   commit: (clean)
   update: 7 new changesets (update)
+  phases: 8 draft (draft)
 
   $ rm "${USERCACHE}"/*
   $ hg clone --all-largefiles -u 1 a a-clone1
@@ -1021,6 +1023,7 @@
   branch: default
   commit: (clean)
   update: 6 new changesets (update)
+  phases: 8 draft (draft)
 
   $ rm "${USERCACHE}"/*
   $ hg clone --all-largefiles -U a a-clone-u
@@ -1030,6 +1033,7 @@
   branch: default
   commit: (clean)
   update: 8 new changesets (update)
+  phases: 8 draft (public)
 
 Show computed destination directory:
 
--- a/tests/test-log.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-log.t	Tue May 19 07:17:57 2015 -0500
@@ -148,7 +148,7 @@
 
   $ hg log -f -l1 --style something
   abort: style 'something' not found
-  (available styles: bisect, changelog, compact, default, phases, xml)
+  (available styles: bisect, changelog, compact, default, phases, status, xml)
   [255]
 
 -f, phases style
--- a/tests/test-manifestv2.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-manifestv2.t	Tue May 19 07:17:57 2015 -0500
@@ -1,4 +1,69 @@
-Check that entry is added to .hg/requires
+Create repo with old manifest
+
+  $ hg init existing
+  $ cd existing
+  $ echo footext > foo
+  $ hg add foo
+  $ hg commit -m initial
+
+We're using v1, so no manifestv2 entry is in requires yet.
+
+  $ grep manifestv2 .hg/requires
+  [1]
+
+Let's clone this with manifestv2 enabled to switch to the new format for
+future commits.
+
+  $ cd ..
+  $ hg clone --pull existing new --config experimental.manifestv2=1
+  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 new
+
+Check that entry was added to .hg/requires.
+
+  $ grep manifestv2 .hg/requires
+  manifestv2
+
+Make a new commit.
+
+  $ echo newfootext > foo
+  $ hg commit -m new
+
+Check that the manifest actually switched to v2.
+
+  $ hg debugdata -m 0
+  foo\x0021e958b1dca695a60ee2e9cf151753204ee0f9e9 (esc)
+
+  $ hg debugdata -m 1
+  \x00 (esc)
+  \x00foo\x00 (esc)
+  I\xab\x7f\xb8(\x83\xcas\x15\x9d\xc2\xd3\xd3:5\x08\xbad5_ (esc)
+
+Check that manifestv2 is used if the requirement is present, even if it's
+disabled in the config.
+
+  $ echo newerfootext > foo
+  $ hg --config experimental.manifestv2=False commit -m newer
+
+  $ hg debugdata -m 2
+  \x00 (esc)
+  \x00foo\x00 (esc)
+  \xa6\xb1\xfb\xef]\x91\xa1\x19`\xf3.#\x90S\xf8\x06 \xe2\x19\x00 (esc)
+
+Check that we can still read v1 manifests.
+
+  $ hg files -r 0
+  foo
+
+  $ cd ..
+
+Check that entry is added to .hg/requires on repo creation
 
   $ hg --config experimental.manifestv2=True init repo
   $ cd repo
--- a/tests/test-merge-commit.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-merge-commit.t	Tue May 19 07:17:57 2015 -0500
@@ -73,7 +73,6 @@
    ancestor: 0f2ff26688b9, local: 2263c1be0967+, remote: 0555950ead28
    preserving bar for resolve of bar
    bar: versions differ -> m
-  updating: bar 1/1 files (100.00%)
   picked tool 'internal:merge' for bar (binary False symlink False)
   merging bar
   my bar@2263c1be0967+ other bar@0555950ead28 ancestor bar@0f2ff26688b9
@@ -160,7 +159,6 @@
    ancestor: 0f2ff26688b9, local: 2263c1be0967+, remote: 3ffa6b9e35f0
    preserving bar for resolve of bar
    bar: versions differ -> m
-  updating: bar 1/1 files (100.00%)
   picked tool 'internal:merge' for bar (binary False symlink False)
   merging bar
   my bar@2263c1be0967+ other bar@3ffa6b9e35f0 ancestor bar@0f2ff26688b9
--- a/tests/test-merge-criss-cross.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-merge-criss-cross.t	Tue May 19 07:17:57 2015 -0500
@@ -82,9 +82,7 @@
    preserving f2 for resolve of f2
    f1: remote is newer -> g
   getting f1
-  updating: f1 1/2 files (50.00%)
    f2: versions differ -> m
-  updating: f2 2/2 files (100.00%)
   picked tool 'internal:dump' for f2 (binary False symlink False)
   merging f2
   my f2@3b08d01b0ab5+ other f2@adfe50279922 ancestor f2@40494bf2444c
@@ -151,7 +149,6 @@
   
    f1: remote is newer -> g
   getting f1
-  updating: f1 1/1 files (100.00%)
    f2: remote unchanged -> k
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
@@ -194,7 +191,6 @@
   
    f2: remote is newer -> g
   getting f2
-  updating: f2 1/1 files (100.00%)
    f1: remote unchanged -> k
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
@@ -259,7 +255,6 @@
   
    f1: remote is newer -> g
   getting f1
-  updating: f1 1/1 files (100.00%)
    f2: remote unchanged -> k
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
--- a/tests/test-merge-types.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-merge-types.t	Tue May 19 07:17:57 2015 -0500
@@ -36,7 +36,6 @@
    ancestor: c334dc3be0da, local: 521a1e40188f+, remote: 3574f3e69b1c
    preserving a for resolve of a
    a: versions differ -> m
-  updating: a 1/1 files (100.00%)
   picked tool 'internal:merge' for a (binary False symlink True)
   merging a
   my a@521a1e40188f+ other a@3574f3e69b1c ancestor a@c334dc3be0da
@@ -70,7 +69,6 @@
    ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
    preserving a for resolve of a
    a: versions differ -> m
-  updating: a 1/1 files (100.00%)
   picked tool 'internal:merge' for a (binary False symlink True)
   merging a
   my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
@@ -104,7 +102,6 @@
    ancestor: c334dc3be0da, local: c334dc3be0da+, remote: 521a1e40188f
    preserving a for resolve of a
    a: versions differ -> m
-  updating: a 1/1 files (100.00%)
   (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
--- a/tests/test-merge1.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-merge1.t	Tue May 19 07:17:57 2015 -0500
@@ -40,6 +40,7 @@
   branch: default
   commit: (interrupted update)
   update: 1 new changesets (update)
+  phases: 2 draft (draft)
   $ rmdir b
   $ hg up
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -49,6 +50,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 2 draft (draft)
 
 Prepare a basic merge
 
--- a/tests/test-merge7.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-merge7.t	Tue May 19 07:17:57 2015 -0500
@@ -86,7 +86,6 @@
    ancestor: 96b70246a118, local: 50c3a7e29886+, remote: 40d11a4173a8
    preserving test.txt for resolve of test.txt
    test.txt: versions differ -> m
-  updating: test.txt 1/1 files (100.00%)
   picked tool 'internal:merge' for test.txt (binary False symlink False)
   merging test.txt
   my test.txt@50c3a7e29886+ other test.txt@40d11a4173a8 ancestor test.txt@96b70246a118
--- a/tests/test-module-imports.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-module-imports.t	Tue May 19 07:17:57 2015 -0500
@@ -20,10 +20,7 @@
 hidden by deduplication algorithm in the cycle detector, so fixing
 these may expose other cycles.
 
-  $ hg locate 'mercurial/**.py' | sed 's-\\-/-g' | xargs python "$import_checker"
-  mercurial/crecord.py mixed imports
-     stdlib:    fcntl, termios
-     relative:  curses
+  $ hg locate 'mercurial/**.py' 'hgext/**.py' | sed 's-\\-/-g' | python "$import_checker" -
   mercurial/dispatch.py mixed imports
      stdlib:    commands
      relative:  error, extensions, fancyopts, hg, hook, util
@@ -40,3 +37,5 @@
      stdlib:    formatter
      relative:  config, error, scmutil, util
   Import cycle: mercurial.cmdutil -> mercurial.context -> mercurial.subrepo -> mercurial.cmdutil
+  Import cycle: hgext.largefiles.basestore -> hgext.largefiles.localstore -> hgext.largefiles.basestore
+  Import cycle: mercurial.commands -> mercurial.commandserver -> mercurial.dispatch -> mercurial.commands
--- a/tests/test-mq-qpush-fail.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-mq-qpush-fail.t	Tue May 19 07:17:57 2015 -0500
@@ -34,7 +34,23 @@
   $ $PYTHON -c 'print "\xe9"' > message
   $ cat .hg/patches/bad-patch >> message
   $ mv message .hg/patches/bad-patch
-  $ hg qpush -a && echo 'qpush succeeded?!'
+  $ cat > $TESTTMP/wrapplayback.py <<EOF
+  > import os
+  > from mercurial import extensions, transaction
+  > def wrapplayback(orig,
+  >                  journal, report, opener, vfsmap, entries, backupentries,
+  >                  unlink=True):
+  >     orig(journal, report, opener, vfsmap, entries, backupentries, unlink)
+  >     # Touching files truncated at "transaction.abort" causes
+  >     # forcible re-loading invalidated filecache properties
+  >     # (including repo.changelog)
+  >     for f, o, _ignore in entries:
+  >         if o or not unlink:
+  >             os.utime(opener.join(f), (0.0, 0.0))
+  > def extsetup(ui):
+  >     extensions.wrapfunction(transaction, '_playback', wrapplayback)
+  > EOF
+  $ hg qpush -a --config extensions.wrapplayback=$TESTTMP/wrapplayback.py  && echo 'qpush succeeded?!'
   applying patch1
   applying patch2
   applying bad-patch
--- a/tests/test-obsolete.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-obsolete.t	Tue May 19 07:17:57 2015 -0500
@@ -164,6 +164,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 3 draft (draft)
   remote: 3 outgoing
 
   $ hg summary --remote --hidden
@@ -172,6 +173,7 @@
   branch: default
   commit: (clean)
   update: 3 new changesets, 4 branch heads (merge)
+  phases: 6 draft (draft)
   remote: 3 outgoing
 
 check that various commands work well with filtering
@@ -307,32 +309,32 @@
   added 4 changesets with 4 changes to 4 files (+1 heads)
   (run 'hg heads' to see heads, 'hg merge' to merge)
   $ hg debugobsolete
+  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
   245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
-  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
+  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
   ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
-  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
-  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
 
 Rollback//Transaction support
 
   $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
   $ hg debugobsolete
+  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
   245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
-  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
+  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
   ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
-  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
-  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 (Thu Jan 01 00:22:20 1970 +0000) {'user': 'test'}
   $ hg rollback -n
   repository tip rolled back to revision 3 (undo debugobsolete)
   $ hg rollback
   repository tip rolled back to revision 3 (undo debugobsolete)
   $ hg debugobsolete
+  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
   245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
-  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
+  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
   ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
-  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
-  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
 
   $ cd ..
 
@@ -411,11 +413,11 @@
   (run 'hg heads' to see heads, 'hg merge' to merge)
   $ hg debugobsolete
   1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
+  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
   245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
-  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
+  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
   ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
-  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
-  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
 
 
 On push
@@ -426,11 +428,11 @@
   no changes found
   [1]
   $ hg -R ../tmpc debugobsolete
+  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
   245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
-  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
+  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
   ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
-  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
-  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
   1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
 
 detect outgoing obsolete and unstable
@@ -574,11 +576,11 @@
 
   $ hg debugobsolete
   1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
+  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
   245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
-  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
+  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
   ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
-  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
-  5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
   94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
   cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
 
--- a/tests/test-phases.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-phases.t	Tue May 19 07:17:57 2015 -0500
@@ -36,6 +36,8 @@
 Draft commit are properly created over public one:
 
   $ hg phase --public .
+  $ hg phase
+  1: public
   $ hglog
   1 0 B
   0 0 A
@@ -86,6 +88,9 @@
   $ hg merge 4 # E
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
+  $ hg phase
+  6: draft
+  4: secret
   $ hg ci -m "merge B' and E"
   $ hglog
   7 2 merge B' and E
@@ -242,6 +247,25 @@
   1 0 B
   0 0 A
 
+Test summary
+
+  $ hg summary -R clone-dest --verbose
+  parent: -1:000000000000  (no revision checked out)
+  branch: default
+  commit: (clean)
+  update: 5 new changesets (update)
+  phases: (public)
+  $ hg summary -R initialrepo
+  parent: 7:17a481b3bccb tip
+   merge B' and E
+  branch: default
+  commit: (clean)
+  update: 1 new changesets, 2 branch heads (merge)
+  phases: 3 draft, 3 secret (secret)
+  $ hg summary -R initialrepo --quiet
+  parent: 7:17a481b3bccb tip
+  update: 1 new changesets, 2 branch heads (merge)
+
 Test revset
 
   $ cd initialrepo
--- a/tests/test-push-hook-lock.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-push-hook-lock.t	Tue May 19 07:17:57 2015 -0500
@@ -26,7 +26,7 @@
   $ echo bar >> 3/foo
   $ hg --cwd 3 ci -m bar
 
-  $ hg --cwd 3 push ../2
+  $ hg --cwd 3 push ../2 --config experimental.bundle2-exp=False
   pushing to ../2
   searching for changes
   adding changesets
@@ -36,3 +36,15 @@
   lock:  user *, process * (*s) (glob)
   wlock: free
 
+  $ hg --cwd 1 --config extensions.strip= strip tip -q
+  $ hg --cwd 2 --config extensions.strip= strip tip -q
+  $ hg --cwd 3 push ../2 --config experimental.bundle2-exp=True
+  pushing to ../2
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  lock:  user *, process * (*s) (glob)
+  wlock: user *, process * (*s) (glob)
+
--- a/tests/test-push-warn.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-push-warn.t	Tue May 19 07:17:57 2015 -0500
@@ -40,7 +40,6 @@
   query 1; heads
   searching for changes
   taking quick initial sample
-  searching: 2 queries
   query 2; still undecided: 1, sample size is: 1
   2 total queries
   listing keys for "phases"
--- a/tests/test-rebase-abort.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-rebase-abort.t	Tue May 19 07:17:57 2015 -0500
@@ -321,3 +321,4 @@
   branch: default
   commit: (clean)
   update: 1 new changesets, 2 branch heads (merge)
+  phases: 4 draft (draft)
--- a/tests/test-rebase-conflicts.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-rebase-conflicts.t	Tue May 19 07:17:57 2015 -0500
@@ -223,7 +223,6 @@
   ignoring null merge rebase of 6
   ignoring null merge rebase of 8
   rebasing 9:e31216eec445 "more changes to f1"
-  rebasing: 9:e31216eec445 5/6 changesets (83.33%)
    future parents are 2 and -1
   rebase status stored
    update to 2:4bc80088dc6b
@@ -232,10 +231,8 @@
    ancestor: d79e2059b5c0+, local: d79e2059b5c0+, remote: 4bc80088dc6b
    f2.txt: other deleted -> r
   removing f2.txt
-  updating: f2.txt 1/2 files (50.00%)
    f1.txt: remote created -> g
   getting f1.txt
-  updating: f1.txt 2/2 files (100.00%)
    merge against 9:e31216eec445
      detach base 8:8e4e2c1a07ae
     searching for copies back to rev 3
@@ -244,14 +241,12 @@
    ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445
    f1.txt: remote is newer -> g
   getting f1.txt
-  updating: f1.txt 1/1 files (100.00%)
   committing files:
   f1.txt
   committing manifest
   committing changelog
   rebased as 19c888675e13
   rebasing 10:2f2496ddf49d "merge" (tip)
-  rebasing: 10:2f2496ddf49d 6/6 changesets (100.00%)
    future parents are 11 and 7
   rebase status stored
    already in target
@@ -263,7 +258,6 @@
    ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d
    f1.txt: remote is newer -> g
   getting f1.txt
-  updating: f1.txt 1/1 files (100.00%)
   committing files:
   f1.txt
   committing manifest
@@ -276,50 +270,27 @@
    ancestor: 2a7f09cac94c, local: 2a7f09cac94c+, remote: d79e2059b5c0
    f1.txt: other deleted -> r
   removing f1.txt
-  updating: f1.txt 1/2 files (50.00%)
    f2.txt: remote created -> g
   getting f2.txt
-  updating: f2.txt 2/2 files (100.00%)
   3 changesets found
   list of changesets:
   4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
   e31216eec445e44352c5f01588856059466a24c9
   2f2496ddf49d69b5ef23ad8cf9fb2e0e4faf0ac2
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: f1.txt 1/1 files (100.00%)
   saved backup bundle to $TESTTMP/issue4041/.hg/strip-backup/e31216eec445-15f7a814-backup.hg (glob)
   3 changesets found
   list of changesets:
   4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
   19c888675e133ab5dff84516926a65672eaf04d9
   2a7f09cac94c7f4b73ebd5cd1a62d3b2e8e336bf
-  bundling: 1/3 changesets (33.33%)
-  bundling: 2/3 changesets (66.67%)
-  bundling: 3/3 changesets (100.00%)
-  bundling: 1/3 manifests (33.33%)
-  bundling: 2/3 manifests (66.67%)
-  bundling: 3/3 manifests (100.00%)
-  bundling: f1.txt 1/1 files (100.00%)
   adding branch
   adding changesets
-  changesets: 1 chunks
   add changeset 4c9fbe56a16f
-  changesets: 2 chunks
   add changeset 19c888675e13
-  changesets: 3 chunks
   add changeset 2a7f09cac94c
   adding manifests
-  manifests: 1/2 chunks (50.00%)
-  manifests: 2/2 chunks (100.00%)
-  manifests: 3/2 chunks (150.00%)
   adding file changes
   adding f1.txt revisions
-  files: 1/1 chunks (100.00%)
   added 2 changesets with 2 changes to 1 files
   invalid branchheads cache (served): tip differs
   rebase completed
--- a/tests/test-rebase-parameters.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-rebase-parameters.t	Tue May 19 07:17:57 2015 -0500
@@ -476,6 +476,7 @@
   branch: default
   commit: 1 modified, 1 unresolved (merge)
   update: (current)
+  phases: 3 draft (draft)
   rebase: 0 rebased, 1 remaining (rebase --continue)
 
   $ hg resolve -l
--- a/tests/test-rebase-scenario-global.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-rebase-scenario-global.t	Tue May 19 07:17:57 2015 -0500
@@ -397,7 +397,7 @@
   abort: can't remove original changesets with unrebased descendants
   (use --keep to keep original changesets)
   [255]
-  $ hg rebase -r '2::8' -d 1 --keep
+  $ hg rebase -r '2::8' -d 1 -k
   rebasing 2:c9e50f6cdc55 "C"
   rebasing 3:ffd453c31098 "D"
   rebasing 6:3d8a618087a7 "G"
--- a/tests/test-relink.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-relink.t	Tue May 19 07:17:57 2015 -0500
@@ -70,7 +70,7 @@
 
 relink
 
-  $ hg relink --debug | fix_path
+  $ hg relink --debug --config progress.debug=true | fix_path
   relinking $TESTTMP/repo/.hg/store to $TESTTMP/clone/.hg/store
   tip has 2 files, estimated total number of files: 3
   collecting: 00changelog.i 1/3 files (33.33%)
--- a/tests/test-rename-dir-merge.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-rename-dir-merge.t	Tue May 19 07:17:57 2015 -0500
@@ -43,14 +43,11 @@
   removing a/a
    a/b: other deleted -> r
   removing a/b
-  updating: a/b 2/5 files (40.00%)
    b/a: remote created -> g
   getting b/a
    b/b: remote created -> g
   getting b/b
-  updating: b/b 4/5 files (80.00%)
    b/c: remote directory rename - move from a/c -> dm
-  updating: b/c 5/5 files (100.00%)
   moving a/c to b/c (glob)
   3 files updated, 0 files merged, 2 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
@@ -89,7 +86,6 @@
    branchmerge: True, force: False, partial: False
    ancestor: f9b20c0d4c51, local: 397f8b00a740+, remote: ce36d17b18fb
    b/c: local directory rename - get from a/c -> dg
-  updating: b/c 1/1 files (100.00%)
   getting a/c to b/c
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
--- a/tests/test-rename-merge1.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-rename-merge1.t	Tue May 19 07:17:57 2015 -0500
@@ -40,9 +40,7 @@
   removing a
    b2: remote created -> g
   getting b2
-  updating: b2 1/2 files (50.00%)
    b: remote moved from a -> m
-  updating: b 2/2 files (100.00%)
   picked tool 'internal:merge' for b (binary False symlink False)
   merging a and b to b
   my b@044f8520aeeb+ other b@85c198ef2f6c ancestor a@af1939970a1c
@@ -181,7 +179,6 @@
    ancestor: 19d7f95df299, local: 0084274f6b67+, remote: 5d32493049f0
    newfile: remote created -> g
   getting newfile
-  updating: newfile 1/1 files (100.00%)
   note: possible conflict - file was deleted and renamed to:
    newfile
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-rename-merge2.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-rename-merge2.t	Tue May 19 07:17:57 2015 -0500
@@ -90,13 +90,11 @@
    preserving rev for resolve of rev
    a: remote unchanged -> k
    b: remote copied from a -> m
-  updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging a and b to b
   my b@e300d1c794ec+ other b@4ce40f5aca24 ancestor a@924404dff337
    premerge successful
    rev: versions differ -> m
-  updating: rev 2/2 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
@@ -128,15 +126,12 @@
    preserving rev for resolve of rev
    a: remote is newer -> g
   getting a
-  updating: a 1/3 files (33.33%)
    b: local copied/moved from a -> m
-  updating: b 2/3 files (66.67%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b and a to b
   my b@86a2aa42fc76+ other a@f4db7e329e71 ancestor a@924404dff337
    premerge successful
    rev: versions differ -> m
-  updating: rev 3/3 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
@@ -168,13 +163,11 @@
    preserving rev for resolve of rev
   removing a
    b: remote moved from a -> m
-  updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging a and b to b
   my b@e300d1c794ec+ other b@bdb19105162a ancestor a@924404dff337
    premerge successful
    rev: versions differ -> m
-  updating: rev 2/2 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
@@ -204,13 +197,11 @@
    preserving b for resolve of b
    preserving rev for resolve of rev
    b: local copied/moved from a -> m
-  updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b and a to b
   my b@02963e448370+ other a@f4db7e329e71 ancestor a@924404dff337
    premerge successful
    rev: versions differ -> m
-  updating: rev 2/2 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
@@ -240,9 +231,7 @@
    preserving rev for resolve of rev
    b: remote created -> g
   getting b
-  updating: b 1/2 files (50.00%)
    rev: versions differ -> m
-  updating: rev 2/2 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
@@ -271,7 +260,6 @@
    ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 97c705ade336
    preserving rev for resolve of rev
    rev: versions differ -> m
-  updating: rev 1/1 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
@@ -301,12 +289,9 @@
    preserving rev for resolve of rev
    a: other deleted -> r
   removing a
-  updating: a 1/3 files (33.33%)
    b: remote created -> g
   getting b
-  updating: b 2/3 files (66.67%)
    rev: versions differ -> m
-  updating: rev 3/3 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
@@ -334,7 +319,6 @@
    ancestor: 924404dff337, local: 02963e448370+, remote: 97c705ade336
    preserving rev for resolve of rev
    rev: versions differ -> m
-  updating: rev 1/1 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
@@ -360,14 +344,12 @@
    preserving b for resolve of b
    preserving rev for resolve of rev
    b: both renamed from a -> m
-  updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
   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
-  updating: rev 2/2 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
@@ -402,9 +384,7 @@
    preserving rev for resolve of rev
    c: remote created -> g
   getting c
-  updating: c 1/2 files (50.00%)
    rev: versions differ -> m
-  updating: rev 2/2 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
@@ -434,14 +414,12 @@
    preserving b for resolve of b
    preserving rev for resolve of rev
    b: both created -> m
-  updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
   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
-  updating: rev 2/2 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
@@ -469,16 +447,13 @@
    preserving rev for resolve of rev
    a: other deleted -> r
   removing a
-  updating: a 1/3 files (33.33%)
    b: both created -> m
-  updating: b 2/3 files (66.67%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
   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
-  updating: rev 3/3 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
@@ -505,16 +480,13 @@
    preserving rev for resolve of rev
    a: remote is newer -> g
   getting a
-  updating: a 1/3 files (33.33%)
    b: both created -> m
-  updating: b 2/3 files (66.67%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
   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
-  updating: rev 3/3 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
@@ -542,16 +514,13 @@
    preserving rev for resolve of rev
    a: other deleted -> r
   removing a
-  updating: a 1/3 files (33.33%)
    b: both created -> m
-  updating: b 2/3 files (66.67%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
   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
-  updating: rev 3/3 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
@@ -578,16 +547,13 @@
    preserving rev for resolve of rev
    a: remote is newer -> g
   getting a
-  updating: a 1/3 files (33.33%)
    b: both created -> m
-  updating: b 2/3 files (66.67%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
   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
-  updating: rev 3/3 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
@@ -615,14 +581,12 @@
    preserving rev for resolve of rev
    a: remote unchanged -> k
    b: both created -> m
-  updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
   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
-  updating: rev 2/2 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
@@ -652,16 +616,13 @@
    preserving rev for resolve of rev
    a: prompt recreating -> g
   getting a
-  updating: a 1/3 files (33.33%)
    b: both created -> m
-  updating: b 2/3 files (66.67%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
   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
-  updating: rev 3/3 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
@@ -690,16 +651,13 @@
    preserving b for resolve of b
    preserving rev for resolve of rev
    a: prompt keep -> a
-  updating: a 1/3 files (33.33%)
    b: both created -> m
-  updating: b 2/3 files (66.67%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
   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
-  updating: rev 3/3 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
@@ -730,14 +688,12 @@
    preserving rev for resolve of rev
   removing a
    b: remote moved from a -> m
-  updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging a and b to b
   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
-  updating: rev 2/2 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
@@ -767,14 +723,12 @@
    preserving b for resolve of b
    preserving rev for resolve of rev
    b: local copied/moved from a -> m
-  updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b and a to b
   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
-  updating: rev 2/2 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
@@ -810,15 +764,12 @@
    preserving rev for resolve of rev
    c: remote created -> g
   getting c
-  updating: c 1/3 files (33.33%)
    b: local copied/moved from a -> m
-  updating: b 2/3 files (66.67%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b and a to b
   my b@02963e448370+ other a@2b958612230f ancestor a@924404dff337
    premerge successful
    rev: versions differ -> m
-  updating: rev 3/3 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
   my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
@@ -875,7 +826,7 @@
   $ mkdir 7 8
   $ echo m > 7/f
   $ echo m > 8/f
-  $ hg merge -f --tool internal:dump -v --debug -r2 | sed '/^updating:/,$d' 2> /dev/null
+  $ hg merge -f --tool internal:dump -v --debug -r2 | sed '/^ 0\/f: both created -> m/,$d' 2> /dev/null
     searching for copies back to rev 1
     unmatched files in local:
      5/g
--- a/tests/test-rename.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-rename.t	Tue May 19 07:17:57 2015 -0500
@@ -20,6 +20,7 @@
   branch: default
   commit: 1 renamed
   update: (current)
+  phases: 1 draft (draft)
   $ hg status -C
   A d2/c
     d1/d11/a1
--- a/tests/test-revert.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-revert.t	Tue May 19 07:17:57 2015 -0500
@@ -360,6 +360,7 @@
   branch: default
   commit: 2 modified, 1 removed (merge)
   update: (current)
+  phases: 3 draft (draft)
 
 clarifies who added what
 
--- a/tests/test-revset.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-revset.t	Tue May 19 07:17:57 2015 -0500
@@ -1,5 +1,27 @@
   $ HGENCODING=utf-8
   $ export HGENCODING
+  $ cat > testrevset.py << EOF
+  > import mercurial.revset
+  > 
+  > baseset = mercurial.revset.baseset
+  > 
+  > def r3232(repo, subset, x):
+  >     """"simple revset that return [3,2,3,2]
+  > 
+  >     revisions duplicated on purpose.
+  >     """
+  >     if 3 not in subset:
+  >        if 2 in subset:
+  >            return baseset([2,2])
+  >        return baseset()
+  >     return baseset([3,3,2,2])
+  > 
+  > mercurial.revset.symbols['r3232'] = r3232
+  > EOF
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > testrevset=$TESTTMP/testrevset.py
+  > EOF
 
   $ try() {
   >   hg debugrevspec --debug "$@"
@@ -281,7 +303,7 @@
   hg: parse error: date requires a string
   [255]
   $ log 'date'
-  hg: parse error: can't use date here
+  abort: unknown revision 'date'!
   [255]
   $ log 'date('
   hg: parse error at 5: not a prefix: end
@@ -289,11 +311,40 @@
   $ log 'date(tip)'
   abort: invalid date: 'tip'
   [255]
-  $ log '"date"'
+  $ log '0:date'
+  abort: unknown revision 'date'!
+  [255]
+  $ log '::"date"'
   abort: unknown revision 'date'!
   [255]
+  $ hg book date -r 4
+  $ log '0:date'
+  0
+  1
+  2
+  3
+  4
+  $ log '::date'
+  0
+  1
+  2
+  4
+  $ log '::"date"'
+  0
+  1
+  2
+  4
   $ log 'date(2005) and 1::'
   4
+  $ hg book -d date
+
+Test that symbols only get parsed as functions if there's an opening
+parenthesis.
+
+  $ hg book only -r 9
+  $ log 'only(only)'   # Outer "only" is a function, inner "only" is the bookmark
+  8
+  9
 
 ancestor can accept 0 or more arguments
 
@@ -311,6 +362,9 @@
   0
   $ log 'ancestor(1,2,3,4,5)'
   1
+
+test ancestors
+
   $ log 'ancestors(5)'
   0
   1
@@ -318,6 +372,12 @@
   5
   $ log 'ancestor(ancestors(5))'
   0
+  $ log '::r3232()'
+  0
+  1
+  2
+  3
+
   $ log 'author(bob)'
   2
   $ log 'author("re:bob|test")'
@@ -555,6 +615,24 @@
   <baseset+ [8, 9]>
   8
   9
+  $ try --optimize '(9)%(5)'
+  (only
+    (group
+      ('symbol', '9'))
+    (group
+      ('symbol', '5')))
+  * optimized:
+  (func
+    ('symbol', 'only')
+    (list
+      ('symbol', '9')
+      ('symbol', '5')))
+  * set:
+  <baseset+ [8, 9, 2, 4]>
+  2
+  4
+  8
+  9
 
 Test the order of operations
 
@@ -796,6 +874,51 @@
   4
   5
 
+test that `or` operation skips duplicated revisions from right-hand side
+
+  $ try 'reverse(1::5) or ancestors(4)'
+  (or
+    (func
+      ('symbol', 'reverse')
+      (dagrange
+        ('symbol', '1')
+        ('symbol', '5')))
+    (func
+      ('symbol', 'ancestors')
+      ('symbol', '4')))
+  * set:
+  <addset
+    <baseset [5, 3, 1]>,
+    <generatorset+>>
+  5
+  3
+  1
+  0
+  2
+  4
+  $ try 'sort(ancestors(4) or reverse(1::5))'
+  (func
+    ('symbol', 'sort')
+    (or
+      (func
+        ('symbol', 'ancestors')
+        ('symbol', '4'))
+      (func
+        ('symbol', 'reverse')
+        (dagrange
+          ('symbol', '1')
+          ('symbol', '5')))))
+  * set:
+  <addset+
+    <generatorset+>,
+    <baseset [5, 3, 1]>>
+  0
+  1
+  2
+  3
+  4
+  5
+
 check that conversion to only works
   $ try --optimize '::3 - ::1'
   (minus
@@ -1306,8 +1429,7 @@
   <addset
     <baseset [9]>,
     <filteredset
-      <filteredset
-        <fullreposet+ 0:9>>>>
+      <fullreposet+ 0:9>>>
   9
 
   $ try 'd(2:5)'
--- a/tests/test-run-tests.py	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-run-tests.py	Tue May 19 07:17:57 2015 -0500
@@ -3,6 +3,7 @@
 run-test.t only checks positive matches and can not see warnings
 (both by design)
 """
+from __future__ import print_function
 
 import os, re
 # this is hack to make sure no escape characters are inserted into the output
@@ -11,27 +12,37 @@
 import doctest
 run_tests = __import__('run-tests')
 
+def prn(ex):
+    m = ex.args[0]
+    if isinstance(m, str):
+        print(m)
+    else:
+        print(m.decode('utf-8'))
+
 def lm(expected, output):
     r"""check if output matches expected
 
     does it generally work?
-        >>> lm('H*e (glob)\n', 'Here\n')
+        >>> lm(b'H*e (glob)\n', b'Here\n')
         True
 
     fail on bad test data
-        >>> try: lm('a\n','a')
-        ... except AssertionError, ex: print ex
+        >>> try: lm(b'a\n',b'a')
+        ... except AssertionError as ex: print(ex)
         missing newline
-        >>> try: lm('single backslash\n', 'single \backslash\n')
-        ... except AssertionError, ex: print ex
+        >>> try: lm(b'single backslash\n', b'single \backslash\n')
+        ... except AssertionError as ex: prn(ex)
         single backslash or unknown char
     """
-    assert expected.endswith('\n') and output.endswith('\n'), 'missing newline'
-    assert not re.search(r'[^ \w\\/\r\n()*?]', expected + output), \
-           'single backslash or unknown char'
+    assert (expected.endswith(b'\n')
+            and output.endswith(b'\n')), 'missing newline'
+    assert not re.search(br'[^ \w\\/\r\n()*?]', expected + output), \
+           b'single backslash or unknown char'
     match = run_tests.TTest.linematch(expected, output)
     if isinstance(match, str):
         return 'special: ' + match
+    elif isinstance(match, bytes):
+        return 'special: ' + match.decode('utf-8')
     else:
         return bool(match) # do not return match object
 
@@ -43,15 +54,15 @@
         >>> os.altsep = True
 
     valid match on windows
-        >>> lm('g/a*/d (glob)\n', 'g\\abc/d\n')
+        >>> lm(b'g/a*/d (glob)\n', b'g\\abc/d\n')
         True
 
     direct matching, glob unnecessary
-        >>> lm('g/b (glob)\n', 'g/b\n')
+        >>> lm(b'g/b (glob)\n', b'g/b\n')
         'special: -glob'
 
     missing glob
-        >>> lm('/g/c/d/fg\n', '\\g\\c\\d/fg\n')
+        >>> lm(b'/g/c/d/fg\n', b'\\g\\c\\d/fg\n')
         'special: +glob'
 
     restore os.altsep
@@ -67,15 +78,15 @@
         >>> os.altsep = False
 
     backslash does not match slash
-        >>> lm('h/a* (glob)\n', 'h\\ab\n')
+        >>> lm(b'h/a* (glob)\n', b'h\\ab\n')
         False
 
     direct matching glob can not be recognized
-        >>> lm('h/b (glob)\n', 'h/b\n')
+        >>> lm(b'h/b (glob)\n', b'h/b\n')
         True
 
     missing glob can not not be recognized
-        >>> lm('/h/c/df/g/\n', '\\h/c\\df/g\\\n')
+        >>> lm(b'/h/c/df/g/\n', b'\\h/c\\df/g\\\n')
         False
 
     restore os.altsep
--- a/tests/test-run-tests.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-run-tests.t	Tue May 19 07:17:57 2015 -0500
@@ -39,8 +39,8 @@
   > EOF
 
   >>> fh = open('test-failure-unicode.t', 'wb')
-  >>> fh.write(u'  $ echo babar\u03b1\n'.encode('utf-8'))
-  >>> fh.write(u'  l\u03b5\u03b5t\n'.encode('utf-8'))
+  >>> fh.write(u'  $ echo babar\u03b1\n'.encode('utf-8')) and None
+  >>> fh.write(u'  l\u03b5\u03b5t\n'.encode('utf-8')) and None
 
   $ $TESTDIR/run-tests.py --with-hg=`which hg`
   
@@ -258,8 +258,8 @@
 
 failures in parallel with --first should only print one failure
   >>> f = open('test-nothing.t', 'w')
-  >>> f.write('foo\n' * 1024)
-  >>> f.write('  $ sleep 1')
+  >>> f.write('foo\n' * 1024) and None
+  >>> f.write('  $ sleep 1') and None
   $ $TESTDIR/run-tests.py --with-hg=`which hg` --jobs 2 --first
   
   --- $TESTTMP/test-failure*.t (glob)
@@ -398,8 +398,8 @@
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
   # Producing time report
-  cuser   csys    real      Test
-  \s*[\d\.]{5}   \s*[\d\.]{5}   \s*[\d\.]{5}   test-success.t (re)
+  start   end     cuser   csys    real      Test
+  \s*[\d\.]{5}   \s*[\d\.]{5}   \s*[\d\.]{5}   \s*[\d\.]{5}   \s*[\d\.]{5}   test-success.t (re)
 
 test for --time with --job enabled
 ====================================
@@ -408,8 +408,8 @@
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
   # Producing time report
-  cuser   csys    real      Test
-  \s*[\d\.]{5}   \s*[\d\.]{5}   \s*[\d\.]{5}   test-success.t (re)
+  start   end     cuser   csys    real      Test
+  \s*[\d\.]{5}   \s*[\d\.]{5}   \s*[\d\.]{5}   \s*[\d\.]{5}   \s*[\d\.]{5}   test-success.t (re)
 
 Skips
 ================
@@ -480,23 +480,75 @@
       "test-failure.t": [\{] (re)
           "csys": "\s*[\d\.]{4,5}", ? (re)
           "cuser": "\s*[\d\.]{4,5}", ? (re)
+          "end": "\s*[\d\.]{4,5}", ? (re)
           "result": "failure", ? (re)
+          "start": "\s*[\d\.]{4,5}", ? (re)
           "time": "\s*[\d\.]{4,5}" (re)
       }, ? (re)
       "test-skip.t": {
           "csys": "\s*[\d\.]{4,5}", ? (re)
           "cuser": "\s*[\d\.]{4,5}", ? (re)
+          "end": "\s*[\d\.]{4,5}", ? (re)
           "result": "skip", ? (re)
+          "start": "\s*[\d\.]{4,5}", ? (re)
           "time": "\s*[\d\.]{4,5}" (re)
       }, ? (re)
       "test-success.t": [\{] (re)
           "csys": "\s*[\d\.]{4,5}", ? (re)
           "cuser": "\s*[\d\.]{4,5}", ? (re)
+          "end": "\s*[\d\.]{4,5}", ? (re)
           "result": "success", ? (re)
+          "start": "\s*[\d\.]{4,5}", ? (re)
           "time": "\s*[\d\.]{4,5}" (re)
       }
   } (no-eol)
 
+Test that failed test accepted through interactive are properly reported:
+
+  $ cp test-failure.t backup
+  $ echo y | $TESTDIR/run-tests.py --with-hg=`which hg` --json -i
+  
+  --- $TESTTMP/test-failure.t
+  +++ $TESTTMP/test-failure.t.err
+  @@ -1,4 +1,4 @@
+     $ echo babar
+  -  rataxes
+  +  babar
+   This is a noop statement so that
+   this test is still more bytes than success.
+  Accept this change? [n] ..s
+  Skipped test-skip.t: skipped
+  # Ran 2 tests, 1 skipped, 0 warned, 0 failed.
+
+  $ cat report.json
+  testreport ={
+      "test-failure.t": [\{] (re)
+          "csys": "\s*[\d\.]{4,5}", ? (re)
+          "cuser": "\s*[\d\.]{4,5}", ? (re)
+          "end": "\s*[\d\.]{4,5}", ? (re)
+          "result": "success", ? (re)
+          "start": "\s*[\d\.]{4,5}", ? (re)
+          "time": "\s*[\d\.]{4,5}" (re)
+      }, ? (re)
+      "test-skip.t": {
+          "csys": "\s*[\d\.]{4,5}", ? (re)
+          "cuser": "\s*[\d\.]{4,5}", ? (re)
+          "end": "\s*[\d\.]{4,5}", ? (re)
+          "result": "skip", ? (re)
+          "start": "\s*[\d\.]{4,5}", ? (re)
+          "time": "\s*[\d\.]{4,5}" (re)
+      }, ? (re)
+      "test-success.t": [\{] (re)
+          "csys": "\s*[\d\.]{4,5}", ? (re)
+          "cuser": "\s*[\d\.]{4,5}", ? (re)
+          "end": "\s*[\d\.]{4,5}", ? (re)
+          "result": "success", ? (re)
+          "start": "\s*[\d\.]{4,5}", ? (re)
+          "time": "\s*[\d\.]{4,5}" (re)
+      }
+  } (no-eol)
+  $ mv backup test-failure.t
+
 #endif
 
 backslash on end of line with glob matching is handled properly
--- a/tests/test-setdiscovery.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-setdiscovery.t	Tue May 19 07:17:57 2015 -0500
@@ -14,13 +14,13 @@
   >     hg -R a debugdiscovery b --verbose --old
   >     echo
   >     echo "% -- a -> b set"
-  >     hg -R a debugdiscovery b --verbose --debug
+  >     hg -R a debugdiscovery b --verbose --debug --config progress.debug=true
   >     echo
   >     echo "% -- b -> a tree"
-  >     hg -R b debugdiscovery a --verbose --old
+  >     hg -R b debugdiscovery a --verbose --old --config
   >     echo
   >     echo "% -- b -> a set"
-  >     hg -R b debugdiscovery a --verbose --debug
+  >     hg -R b debugdiscovery a --verbose --debug --config progress.debug=true
   >     cd ..
   > }
 
@@ -305,7 +305,7 @@
   updating to branch b
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
-  $ hg -R a debugdiscovery b --debug --verbose
+  $ hg -R a debugdiscovery b --debug --verbose --config progress.debug=true
   comparing with b
   query 1; heads
   searching for changes
--- a/tests/test-shelve.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-shelve.t	Tue May 19 07:17:57 2015 -0500
@@ -782,6 +782,7 @@
   bookmarks: *test
   commit: 2 unknown (clean)
   update: (current)
+  phases: 5 draft (draft)
 
   $ hg shelve --delete --stat
   abort: options '--delete' and '--stat' may not be used together
@@ -862,4 +863,45 @@
   c
   x
   x
-  $ cd ..
+
+shelve --patch and shelve --stat should work with a single valid shelfname
+
+  $ hg up --clean .
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg shelve --list
+  $ echo 'patch a' > shelf-patch-a
+  $ hg add shelf-patch-a
+  $ hg shelve
+  shelved as default
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo 'patch b' > shelf-patch-b
+  $ hg add shelf-patch-b
+  $ hg shelve
+  shelved as default-01
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg shelve --patch default default-01
+  abort: --patch expects a single shelf
+  [255]
+  $ hg shelve --stat default default-01
+  abort: --stat expects a single shelf
+  [255]
+  $ hg shelve --patch default
+  default         (* ago)    changes to 'create conflict' (glob)
+  
+  diff --git a/shelf-patch-a b/shelf-patch-a
+  new file mode 100644
+  --- /dev/null
+  +++ b/shelf-patch-a
+  @@ -0,0 +1,1 @@
+  +patch a
+  $ hg shelve --stat default
+  default         (* ago)    changes to 'create conflict' (glob)
+   shelf-patch-a |  1 +
+   1 files changed, 1 insertions(+), 0 deletions(-)
+  $ hg shelve --patch nonexistentshelf
+  abort: cannot find shelf nonexistentshelf
+  [255]
+  $ hg shelve --stat nonexistentshelf
+  abort: cannot find shelf nonexistentshelf
+  [255]
+
--- a/tests/test-ssh.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-ssh.t	Tue May 19 07:17:57 2015 -0500
@@ -398,7 +398,7 @@
   pushing to ssh://user@dummy/*/remote (glob)
   searching for changes
   remote: Permission denied
-  remote: abort: prechangegroup.hg-ssh hook failed
+  remote: abort: pretxnopen.hg-ssh hook failed
   remote: Permission denied
   remote: pushkey-abort: prepushkey.hg-ssh hook failed
   updating 6c0482d977a3 to public failed!
--- a/tests/test-strip.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-strip.t	Tue May 19 07:17:57 2015 -0500
@@ -526,6 +526,7 @@
   branch: default
   commit: 1 modified, 1 unknown, 1 unresolved
   update: (current)
+  phases: 2 draft (draft)
   mq:     3 unapplied
 
   $ echo c > b
@@ -553,6 +554,7 @@
   branch: default
   commit: 1 modified, 1 unknown
   update: (current)
+  phases: 1 draft (draft)
   mq:     3 unapplied
 
 Strip adds, removes, modifies with --keep
--- a/tests/test-subrepo-deep-nested-change.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-subrepo-deep-nested-change.t	Tue May 19 07:17:57 2015 -0500
@@ -209,6 +209,29 @@
   sub1/sub2/folder/bar (glob)
   sub1/sub2/x.txt (glob)
 
+  $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
+  .hgsub
+  .hgsubstate
+  foo/bar/abc (glob)
+  main
+  sub1/.hgsub (glob)
+  sub1/.hgsubstate (glob)
+  sub1/foo (glob)
+  sub1/sub1 (glob)
+  sub1/sub2/folder/bar (glob)
+  sub1/sub2/x.txt (glob)
+
+  $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
+  .hgsub
+  .hgsubstate
+  main
+  sub1/.hgsub (glob)
+  sub1/.hgsubstate (glob)
+  sub1/sub1 (glob)
+  sub1/sub2/folder/test.txt (glob)
+  sub1/sub2/sub2 (glob)
+  sub1/sub2/test.txt (glob)
+
   $ hg files -S -r '.^' sub1/sub2/folder
   sub1/sub2/folder/test.txt (glob)
 
@@ -329,17 +352,17 @@
 
 Exclude normal files from main and sub-sub repo
 
-  $ hg --config extensions.largefiles= archive -S -X '**.txt' ../archive_lf.tgz
+  $ hg --config extensions.largefiles= archive -S -X '**.txt' -p '.' ../archive_lf.tgz
   $ tar -tzf ../archive_lf.tgz | sort
-  archive_lf/.hgsub
-  archive_lf/.hgsubstate
-  archive_lf/large.bin
-  archive_lf/main
-  archive_lf/sub1/.hgsub
-  archive_lf/sub1/.hgsubstate
-  archive_lf/sub1/sub1
-  archive_lf/sub1/sub2/large.bin
-  archive_lf/sub1/sub2/sub2
+  .hgsub
+  .hgsubstate
+  large.bin
+  main
+  sub1/.hgsub
+  sub1/.hgsubstate
+  sub1/sub1
+  sub1/sub2/large.bin
+  sub1/sub2/sub2
 
 Include normal files from within a largefiles subrepo
 
--- a/tests/test-subrepo-git.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-subrepo-git.t	Tue May 19 07:17:57 2015 -0500
@@ -325,13 +325,13 @@
   ../archive_x/s
   ../archive_x/s/g
 
-  $ hg -R ../tc archive -S ../archive.tgz 2>/dev/null
-  $ tar -tzf ../archive.tgz | sort
-  archive/.hg_archival.txt
-  archive/.hgsub
-  archive/.hgsubstate
-  archive/a
-  archive/s/g
+  $ hg -R ../tc archive -S ../archive.tgz --prefix '.' 2>/dev/null
+  $ tar -tzf ../archive.tgz | sort | grep -v pax_global_header
+  .hg_archival.txt
+  .hgsub
+  .hgsubstate
+  a
+  s/g
 
 create nested repo
 
@@ -1105,5 +1105,21 @@
   ? s/c.c
   ? s/cpp.cpp
   ? s/foobar.orig
+  $ hg revert --all -q
+
+make sure we show changed files, rather than changed subtrees
+  $ mkdir s/foo
+  $ touch s/foo/bwuh
+  $ hg add s/foo/bwuh
+  $ hg commit -S -m "add bwuh"
+  committing subrepository s
+  $ hg status -S --change .
+  M .hgsubstate
+  A s/foo/bwuh
+  ? s/barfoo
+  ? s/c.c
+  ? s/cpp.cpp
+  ? s/foobar.orig
+  ? s/snake.python.orig
 
   $ cd ..
--- a/tests/test-subrepo-recursion.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-subrepo-recursion.t	Tue May 19 07:17:57 2015 -0500
@@ -312,7 +312,7 @@
 
 Test archiving to zip file (unzip output is unstable):
 
-  $ hg archive --subrepos ../archive.zip
+  $ hg archive --subrepos --prefix '.' ../archive.zip
   \r (no-eol) (esc)
   archiving [                                           ] 0/3\r (no-eol) (esc)
   archiving [                                           ] 0/3\r (no-eol) (esc)
@@ -340,6 +340,23 @@
   archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
                                                               \r (no-eol) (esc)
 
+(unzip date formating is unstable, we do not care about it and glob it out)
+
+  $ unzip -l ../archive.zip
+  Archive:  ../archive.zip
+    Length      Date    Time    Name
+  ---------  ---------- -----   ----
+        172  ?????????? 00:00   .hg_archival.txt (glob)
+         10  ?????????? 00:00   .hgsub (glob)
+         45  ?????????? 00:00   .hgsubstate (glob)
+          3  ?????????? 00:00   x.txt (glob)
+         10  ?????????? 00:00   foo/.hgsub (glob)
+         45  ?????????? 00:00   foo/.hgsubstate (glob)
+          9  ?????????? 00:00   foo/y.txt (glob)
+          9  ?????????? 00:00   foo/bar/z.txt (glob)
+  ---------                     -------
+        303                     8 files
+
 Test archiving a revision that references a subrepo that is not yet
 cloned:
 
@@ -363,7 +380,7 @@
 
   $ cd ../empty
 #if hardlink
-  $ hg archive --subrepos -r tip ../archive.tar.gz
+  $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz
   \r (no-eol) (esc)
   archiving [                                           ] 0/3\r (no-eol) (esc)
   archiving [                                           ] 0/3\r (no-eol) (esc)
@@ -413,7 +430,7 @@
 #else
 Note there's a slight output glitch on non-hardlink systems: the last
 "linking" progress topic never gets closed, leading to slight output corruption on that platform.
-  $ hg archive --subrepos -r tip ../archive.tar.gz
+  $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz
   \r (no-eol) (esc)
   archiving [                                           ] 0/3\r (no-eol) (esc)
   archiving [                                           ] 0/3\r (no-eol) (esc)
@@ -437,14 +454,14 @@
 Archive + subrepos uses '/' for all component separators
 
   $ tar -tzf ../archive.tar.gz | sort
-  archive/.hg_archival.txt
-  archive/.hgsub
-  archive/.hgsubstate
-  archive/foo/.hgsub
-  archive/foo/.hgsubstate
-  archive/foo/bar/z.txt
-  archive/foo/y.txt
-  archive/x.txt
+  .hg_archival.txt
+  .hgsub
+  .hgsubstate
+  foo/.hgsub
+  foo/.hgsubstate
+  foo/bar/z.txt
+  foo/y.txt
+  x.txt
 
 The newly cloned subrepos contain no working copy:
 
--- a/tests/test-subrepo-svn.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-subrepo-svn.t	Tue May 19 07:17:57 2015 -0500
@@ -72,6 +72,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 2 draft (draft)
   $ hg ci -moops
   nothing changed
   [1]
@@ -96,6 +97,7 @@
   branch: default
   commit: 1 modified, 1 subrepos
   update: (current)
+  phases: 2 draft (draft)
   $ hg commit --subrepos -m 'Message!' | grep -v Updating
   committing subrepository s
   Sending*s/alpha (glob)
@@ -136,6 +138,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 3 draft (draft)
 
   $ echo a > s/a
 
@@ -548,7 +551,7 @@
 
 Test archive
 
-  $ hg archive -S ../archive-all --debug
+  $ hg archive -S ../archive-all --debug --config progress.debug=true
   archiving: 0/2 files (0.00%)
   archiving: .hgsub 1/2 files (50.00%)
   archiving: .hgsubstate 2/2 files (100.00%)
@@ -560,7 +563,7 @@
   archiving (s): 1/2 files (50.00%)
   archiving (s): 2/2 files (100.00%)
 
-  $ hg archive -S ../archive-exclude --debug -X **old
+  $ hg archive -S ../archive-exclude --debug --config progress.debug=true -X **old
   archiving: 0/2 files (0.00%)
   archiving: .hgsub 1/2 files (50.00%)
   archiving: .hgsubstate 2/2 files (100.00%)
--- a/tests/test-subrepo.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-subrepo.t	Tue May 19 07:17:57 2015 -0500
@@ -38,6 +38,7 @@
   branch: default
   commit: 1 added, 1 subrepos
   update: (current)
+  phases: 1 draft (draft)
   $ hg ci -m1
 
 test handling .hgsubstate "added" explicitly.
@@ -83,6 +84,7 @@
   branch: default
   commit: 1 subrepos
   update: (current)
+  phases: 2 draft (draft)
   $ hg co -C 1
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg sum
@@ -91,6 +93,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 2 draft (draft)
 
 commands that require a clean repo should respect subrepos
 
@@ -113,6 +116,7 @@
   branch: default
   commit: 1 subrepos
   update: (current)
+  phases: 2 draft (draft)
   $ hg ci -m2
   committing subrepository s
   committing subrepository s/ss (glob)
@@ -122,6 +126,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 3 draft (draft)
 
 test handling .hgsubstate "modified" explicitly.
 
@@ -255,7 +260,6 @@
    branchmerge: True, force: False, partial: False
    ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
    .hgsubstate: versions differ -> m
-  updating: .hgsubstate 1/1 files (100.00%)
   subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
     subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
   getting subrepo t
@@ -264,7 +268,6 @@
    ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
    t: remote is newer -> g
   getting t
-  updating: t 1/1 files (100.00%)
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   $ hg debugsub
@@ -283,7 +286,6 @@
    branchmerge: True, force: False, partial: False
    ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
    .hgsubstate: versions differ -> m
-  updating: .hgsubstate 1/1 files (100.00%)
   subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
     subrepo t: both sides changed 
    subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198)
@@ -295,7 +297,6 @@
    ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
    preserving t for resolve of t
    t: versions differ -> m
-  updating: t 1/1 files (100.00%)
   picked tool 'internal:merge' for t (binary False symlink False)
   merging t
   my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
--- a/tests/test-treediscovery-legacy.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-treediscovery-legacy.t	Tue May 19 07:17:57 2015 -0500
@@ -8,7 +8,7 @@
   > EOF
   $ cp $HGRCPATH $HGRCPATH-withcap
 
-  $ CAP="getbundle known changegroupsubset"
+  $ CAP="getbundle known changegroupsubset bundle2"
   $ . "$TESTDIR/notcapable"
   $ cp $HGRCPATH $HGRCPATH-nocap
   $ cp $HGRCPATH-withcap $HGRCPATH
--- a/tests/test-treediscovery.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-treediscovery.t	Tue May 19 07:17:57 2015 -0500
@@ -2,7 +2,7 @@
 
 Tests discovery against servers without getbundle support:
 
-  $ CAP=getbundle
+  $ CAP="getbundle bundle2"
   $ . "$TESTDIR/notcapable"
   $ cat >> $HGRCPATH <<EOF
   > [ui]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-treemanifest.t	Tue May 19 07:17:57 2015 -0500
@@ -0,0 +1,278 @@
+
+Set up repo
+
+  $ hg --config experimental.treemanifest=True init repo
+  $ cd repo
+
+Requirements get set on init
+
+  $ grep treemanifest .hg/requires
+  treemanifest
+
+Without directories, looks like any other repo
+
+  $ echo 0 > a
+  $ echo 0 > b
+  $ hg ci -Aqm initial
+  $ hg debugdata -m 0
+  a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
+  b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
+
+Submanifest is stored in separate revlog
+
+  $ mkdir dir1
+  $ echo 1 > dir1/a
+  $ echo 1 > dir1/b
+  $ echo 1 > e
+  $ hg ci -Aqm 'add dir1'
+  $ hg debugdata -m 1
+  a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
+  b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
+  dir1\x008b3ffd73f901e83304c83d33132c8e774ceac44ed (esc)
+  e\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
+  $ hg debugdata --dir dir1 0
+  a\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
+  b\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
+
+Can add nested directories
+
+  $ mkdir dir1/dir1
+  $ echo 2 > dir1/dir1/a
+  $ echo 2 > dir1/dir1/b
+  $ mkdir dir1/dir2
+  $ echo 2 > dir1/dir2/a
+  $ echo 2 > dir1/dir2/b
+  $ hg ci -Aqm 'add dir1/dir1'
+  $ hg files -r .
+  a
+  b
+  dir1/a (glob)
+  dir1/b (glob)
+  dir1/dir1/a (glob)
+  dir1/dir1/b (glob)
+  dir1/dir2/a (glob)
+  dir1/dir2/b (glob)
+  e
+
+Revision is not created for unchanged directory
+
+  $ mkdir dir2
+  $ echo 3 > dir2/a
+  $ hg add dir2
+  adding dir2/a (glob)
+  $ hg debugindex --dir dir1 > before
+  $ hg ci -qm 'add dir2'
+  $ hg debugindex --dir dir1 > after
+  $ diff before after
+  $ rm before after
+
+Removing directory does not create an revlog entry
+
+  $ hg rm dir1/dir1
+  removing dir1/dir1/a (glob)
+  removing dir1/dir1/b (glob)
+  $ hg debugindex --dir dir1/dir1 > before
+  $ hg ci -qm 'remove dir1/dir1'
+  $ hg debugindex --dir dir1/dir1 > after
+  $ diff before after
+  $ rm before after
+
+Check that hg files (calls treemanifest.walk()) works
+
+  $ hg co 'desc("add dir2")'
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg files -r . dir1
+  dir1/a (glob)
+  dir1/b (glob)
+  dir1/dir1/a (glob)
+  dir1/dir1/b (glob)
+  dir1/dir2/a (glob)
+  dir1/dir2/b (glob)
+
+Check that status between revisions works (calls treemanifest.matches())
+
+  $ hg status --rev 'desc("add dir1")' --rev . dir1
+  A dir1/dir1/a
+  A dir1/dir1/b
+  A dir1/dir2/a
+  A dir1/dir2/b
+
+Merge creates 2-parent revision of directory revlog
+
+  $ echo 5 > dir1/a
+  $ hg ci -Aqm 'modify dir1/a'
+  $ hg co '.^'
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo 6 > dir1/b
+  $ hg ci -Aqm 'modify dir1/b'
+  $ hg merge 'desc("modify dir1/a")'
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m 'conflict-free merge involving dir1/'
+  $ cat dir1/a
+  5
+  $ 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
+
+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.)
+
+  $ hg co 'desc("add dir2")'
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo 8 > dir2/a
+  $ hg ci -m 'modify dir2/a'
+  created new head
+
+  $ hg debugindex --dir dir2 > before
+  $ hg merge 'desc("modify dir1/a")'
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg revert -r 'desc("modify dir2/a")' .
+  reverting dir1/a (glob)
+  $ hg ci -m 'merge, keeping parent 1'
+  $ hg debugindex --dir dir2 > after
+  $ diff before after
+  $ rm before after
+
+Merge keeping directory from parent 2 does not create revlog entry. (Note that
+dir2's manifest does change, but only because dir2/a's filelog changes.)
+
+  $ hg co 'desc("modify dir2/a")'
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg debugindex --dir dir1 > before
+  $ hg merge 'desc("modify dir1/a")'
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg revert -r 'desc("modify dir1/a")' .
+  reverting dir2/a (glob)
+  $ hg ci -m 'merge, keeping parent 2'
+  created new head
+  $ hg debugindex --dir dir1 > after
+  $ diff before after
+  $ rm before after
+
+Create flat source repo for tests with mixed flat/tree manifests
+
+  $ cd ..
+  $ hg init repo-flat
+  $ cd repo-flat
+
+Create a few commits with flat manifest
+
+  $ echo 0 > a
+  $ echo 0 > b
+  $ echo 0 > e
+  $ for d in dir1 dir1/dir1 dir1/dir2 dir2
+  > do
+  >   mkdir $d
+  >   echo 0 > $d/a
+  >   echo 0 > $d/b
+  > done
+  $ hg ci -Aqm initial
+
+  $ echo 1 > a
+  $ echo 1 > dir1/a
+  $ echo 1 > dir1/dir1/a
+  $ hg ci -Aqm 'modify on branch 1'
+
+  $ hg co 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo 2 > b
+  $ echo 2 > dir1/b
+  $ echo 2 > dir1/dir1/b
+  $ hg ci -Aqm 'modify on branch 2'
+
+  $ hg merge 1
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m 'merge of flat manifests to new flat manifest'
+
+Create clone with tree manifests enabled
+
+  $ cd ..
+  $ hg clone --pull --config experimental.treemanifest=1 repo-flat repo-mixed
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 17 changes to 11 files
+  updating to branch default
+  11 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd repo-mixed
+  $ test -f .hg/store/meta
+  [1]
+  $ grep treemanifest .hg/requires
+  treemanifest
+
+Commit should store revlog per directory
+
+  $ hg co 1
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo 3 > a
+  $ echo 3 > dir1/a
+  $ echo 3 > dir1/dir1/a
+  $ hg ci -m 'first tree'
+  created new head
+  $ find .hg/store/meta | sort
+  .hg/store/meta
+  .hg/store/meta/dir1
+  .hg/store/meta/dir1/00manifest.i
+  .hg/store/meta/dir1/dir1
+  .hg/store/meta/dir1/dir1/00manifest.i
+  .hg/store/meta/dir1/dir2
+  .hg/store/meta/dir1/dir2/00manifest.i
+  .hg/store/meta/dir2
+  .hg/store/meta/dir2/00manifest.i
+
+Merge of two trees
+
+  $ hg co 2
+  6 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg merge 1
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m 'merge of flat manifests to new tree manifest'
+  created new head
+  $ hg diff -r 3
+
+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
+       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
+
+
+Status across flat/tree boundary should work
+
+  $ hg status --rev '.^' --rev .
+  M a
+  M dir1/a
+  M dir1/dir1/a
+
+
+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
+  $ 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
--- a/tests/test-up-local-change.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-up-local-change.t	Tue May 19 07:17:57 2015 -0500
@@ -49,9 +49,7 @@
    preserving a for resolve of a
    b: remote created -> g
   getting b
-  updating: b 1/2 files (50.00%)
    a: versions differ -> m
-  updating: a 2/2 files (100.00%)
   picked tool 'true' for a (binary False symlink False)
   merging a
   my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
@@ -72,9 +70,7 @@
    preserving a for resolve of a
    b: other deleted -> r
   removing b
-  updating: b 1/2 files (50.00%)
    a: versions differ -> m
-  updating: a 2/2 files (100.00%)
   picked tool 'true' for a (binary False symlink False)
   merging a
   my a@1e71731e6fbb+ other a@c19d34741b0a ancestor a@1e71731e6fbb
@@ -103,9 +99,7 @@
    preserving a for resolve of a
    b: remote created -> g
   getting b
-  updating: b 1/2 files (50.00%)
    a: versions differ -> m
-  updating: a 2/2 files (100.00%)
   picked tool 'true' for a (binary False symlink False)
   merging a
   my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
--- a/tests/test-update-reverse.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-update-reverse.t	Tue May 19 07:17:57 2015 -0500
@@ -72,10 +72,8 @@
   removing side1
    side2: other deleted -> r
   removing side2
-  updating: side2 2/3 files (66.67%)
    main: remote created -> g
   getting main
-  updating: main 3/3 files (100.00%)
   1 files updated, 0 files merged, 2 files removed, 0 files unresolved
 
   $ ls
--- a/tests/test-url-rev.t	Sun May 17 22:09:37 2015 -0400
+++ b/tests/test-url-rev.t	Tue May 19 07:17:57 2015 -0500
@@ -101,6 +101,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 4 draft (draft)
   remote: 2 outgoing
   $ hg -q outgoing '../clone#foo'
   2:faba9097cad4
@@ -110,6 +111,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 4 draft (draft)
   remote: 1 outgoing
 
   $ hg -q --cwd ../clone incoming '../repo#foo'
@@ -282,6 +284,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 1 draft (draft)
   remote: 1 outgoing
 
   $ hg summary --remote --config paths.default='../clone#foo' --config paths.default-push='../clone'
@@ -290,6 +293,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 1 draft (draft)
   remote: 2 outgoing
 
   $ hg summary --remote --config paths.default='../clone' --config paths.default-push='../clone#foo'
@@ -298,6 +302,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 1 draft (draft)
   remote: 1 outgoing
 
   $ hg clone -q -r 0 . ../another
@@ -311,6 +316,7 @@
   branch: default
   commit: (clean)
   update: (current)
+  phases: 1 draft (draft)
   remote: 1 outgoing
 
   $ cd ..