changeset 26167:7187f6e923d5

merge with stable
author Matt Mackall <mpm@selenic.com>
date Thu, 03 Sep 2015 18:32:17 -0500
parents bb6936bec727 (diff) 0dda3692ec9b (current diff)
children b907051df5b7
files mercurial/hgweb/hgweb_mod.py
diffstat 171 files changed, 3295 insertions(+), 1139 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Thu Sep 03 22:25:16 2015 +0800
+++ b/Makefile	Thu Sep 03 18:32:17 2015 -0500
@@ -157,14 +157,12 @@
 	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
+deb:
+	mkdir -p packages/debian-unknown
+	contrib/builddeb --release unknown
 
 docker-debian-jessie:
-	mkdir -p packages/debian/jessie
+	mkdir -p packages/debian-jessie
 	contrib/dockerdeb jessie
 
 fedora20:
--- a/contrib/builddeb	Thu Sep 03 22:25:16 2015 +0800
+++ b/contrib/builddeb	Thu Sep 03 18:32:17 2015 -0500
@@ -7,13 +7,23 @@
 . $(dirname $0)/packagelib.sh
 
 BUILD=1
-DEBBUILDDIR="$PWD/debbuild"
+CLEANUP=1
+DEBVERSION=jessie
 while [ "$1" ]; do
     case "$1" in
-    --prepare )
+    --release )
+        shift
+        DEBVERSION="$1"
+        shift
+        ;;
+    --cleanup )
         shift
         BUILD=
         ;;
+    --build )
+        shift
+        CLEANUP=
+        ;;
     --debbuilddir )
         shift
         DEBBUILDDIR="$1"
@@ -26,10 +36,9 @@
     esac
 done
 
-set -u
+trap "if [ '$CLEANUP' ] ; then rm -r '$PWD/debian' ; fi" EXIT
 
-rm -rf $DEBBUILDDIR
-mkdir -p $DEBBUILDDIR
+set -u
 
 if [ ! -d .hg ]; then
     echo 'You are not inside a Mercurial repository!' 1>&2
@@ -38,25 +47,38 @@
 
 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
+control=debian/control
+changelog=debian/changelog
 
 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
+    if [ -d debian ] ; then
+        echo "Error! debian control directory already exists!"
+        exit 1
     fi
-else
-    echo "Prepared sources for $version-$release $control are in $DEBBUILDDIR - use like:"
-    echo "dpkg-deb --build $DEBBUILDDIR"
+
+    cp -r $PWD/contrib/debian debian
+    chmod -R 0755 debian
+
+    # 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/" < $changelog > $changelog.tmp
+    date=$(date --rfc-2822)
+    sed "s/__DATE__/$date/" < $changelog.tmp > $changelog
+    rm $changelog.tmp
+
+    debuild -us -uc -b
+    if [ $? != 0 ]; then
+        echo 'debuild failed!'
+        exit 1
+    fi
+
 fi
+if [ "$CLEANUP" ] ; then
+    echo
+    OUTPUTDIR=${OUTPUTDIR:=packages/debian-$DEBVERSION}
+    find ../mercurial*.deb ../mercurial_*.build ../mercurial_*.changes \
+          -type f -newer $control -print0 | \
+      xargs -Inarf -0 mv narf "$OUTPUTDIR"
+    echo "Built packages for $version-$release:"
+    find "$OUTPUTDIR" -type f -newer $control -name '*.deb'
+fi
--- a/contrib/buildrpm	Thu Sep 03 22:25:16 2015 +0800
+++ b/contrib/buildrpm	Thu Sep 03 18:32:17 2015 -0500
@@ -56,6 +56,7 @@
     RPMPYTHONVER=%{nil}
 fi
 
+mkdir -p $RPMBUILDDIR/SOURCES
 $HG archive -t tgz $RPMBUILDDIR/SOURCES/mercurial-$version-$release.tar.gz
 if [ "$PYTHONVER" ]; then
 (
@@ -79,6 +80,7 @@
 )
 fi
 
+mkdir -p $RPMBUILDDIR/SPECS
 rpmspec=$RPMBUILDDIR/SPECS/mercurial.spec
 
 sed -e "s,^Version:.*,Version: $version," \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/debian/changelog	Thu Sep 03 18:32:17 2015 -0500
@@ -0,0 +1,5 @@
+mercurial (__VERSION__) unstable; urgency=medium
+
+  * Automated build performed by upstream.
+
+ -- Mercurial Devel <mercurial-devel@selenic.com>  __DATE__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/debian/compat	Thu Sep 03 18:32:17 2015 -0500
@@ -0,0 +1,1 @@
+9
--- a/contrib/debian/control	Thu Sep 03 22:25:16 2015 +0800
+++ b/contrib/debian/control	Thu Sep 03 18:32:17 2015 -0500
@@ -1,9 +1,47 @@
-Package: mercurial
-Version: __VERSION__
+Source: mercurial
 Section: vcs
 Priority: optional
+Maintainer: Mercurial Developers <mercurial-devel@selenic.com>
+Build-Depends:
+ debhelper (>= 7),
+ dh-python,
+ python-all
+Standards-Version: 3.9.4
+X-Python-Version: >= 2.6
+
+Package: mercurial
+Depends:
+ python,
+ ${shlibs:Depends},
+ ${misc:Depends},
+ ${python:Depends},
+ mercurial-common (= ${source:Version})
+Architecture: any
+Description: fast, easy to use, distributed revision control tool.
+ Mercurial is a fast, lightweight Source Control Management system designed
+ for efficient handling of very large distributed projects.
+ .
+ Its features include:
+  * O(1) delta-compressed file storage and retrieval scheme
+  * Complete cross-indexing of files and changesets for efficient exploration
+    of project history
+  * Robust SHA1-based integrity checking and append-only storage model
+  * Decentralized development model with arbitrary merging between trees
+  * Easy-to-use command-line interface
+  * Integrated stand-alone web interface
+  * Small Python codebase
+
+Package: mercurial-common
 Architecture: all
-Depends: python
-Conflicts: mercurial-common
-Maintainer: Mercurial Developers <mercurial-devel@selenic.com>
-Description: Mercurial (probably nightly) package built by upstream.
+Depends:
+ ${misc:Depends},
+ ${python:Depends},
+Recommends: mercurial (= ${source:Version}), ca-certificates
+Breaks: mercurial (<< ${source:Version})
+Replaces: mercurial (<< 2.6.3)
+Description: easy-to-use, scalable distributed version control system (common files)
+ Mercurial is a fast, lightweight Source Control Management system designed
+ for efficient handling of very large distributed projects.
+ .
+ This package contains the architecture independent components of Mercurial,
+ and is generally useless without the mercurial package.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/debian/copyright	Thu Sep 03 18:32:17 2015 -0500
@@ -0,0 +1,27 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: mercurial
+Source: http://www.selenic.com/mercurial/
+
+Files: *
+Copyright: 2005-2015, Matt Mackall <mpm@selenic.com> and others.
+License: GPL-2+
+ This program is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ .
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE.  See the GNU General Public License for more
+ details.
+ .
+ You should have received a copy of the GNU General Public
+ License along with this package; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ Boston, MA  02110-1301 USA
+ .
+ On Debian systems, the full text of the GNU General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/GPL-2'.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/debian/rules	Thu Sep 03 18:32:17 2015 -0500
@@ -0,0 +1,29 @@
+#!/usr/bin/make -f
+# Uncomment this to turn on verbose mode.
+# export DH_VERBOSE=1
+
+CPUS=$(shell cat /proc/cpuinfo | grep -E ^processor | wc -l)
+
+%:
+	dh $@ --with python2
+
+override_dh_auto_test:
+	http_proxy='' dh_auto_test -- TESTFLAGS="-j$(CPUS)"
+
+override_dh_python2:
+	dh_python2
+	find debian/mercurial/usr/share -type d -empty -delete
+
+override_dh_install:
+	python$(PYVERS) setup.py install --root $(CURDIR)/debian/mercurial --install-layout=deb
+	# remove arch-independent python stuff
+	find $(CURDIR)/debian/mercurial/usr/lib \
+		! -name '*.so' ! -type d -delete , \
+		-type d -empty -delete
+	python$(PYVERS) setup.py install --root $(CURDIR)/debian/mercurial-common --install-layout=deb
+	make install-doc PREFIX=$(CURDIR)/debian/mercurial-common/usr
+	# remove arch-dependent python stuff
+	find $(CURDIR)/debian/mercurial-common/usr/lib \
+		-name '*.so' ! -type d -delete , \
+		-type d -empty -delete
+	rm $(CURDIR)/debian/mercurial-common/usr/bin/hg
--- a/contrib/dockerdeb	Thu Sep 03 22:25:16 2015 +0800
+++ b/contrib/dockerdeb	Thu Sep 03 18:32:17 2015 -0500
@@ -8,32 +8,27 @@
 
 checkdocker
 
+DEBPLATFORM="$1"
 PLATFORM="debian-$1"
 shift # extra params are passed to build process
 
+OUTPUTDIR=${OUTPUTDIR:=$ROOTDIR/packages/$PLATFORM}
+
 initcontainer $PLATFORM
 
-DEBBUILDDIR=$ROOTDIR/packages/$PLATFORM
-contrib/builddeb --debbuilddir $DEBBUILDDIR/staged --prepare
+# debuild only appears to be able to save built debs etc to .., so we
+# have to share the .. of the current directory with the docker
+# container and hope it's writable. Whee.
+dn=$(basename $PWD)
 
-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"
+    $DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \
+            sh -c "cd /mnt/$dn && 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
+$DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \
+  sh -c "cd /mnt/$dn && DEB_BUILD_OPTIONS='${DEB_BUILD_OPTIONS:=}' contrib/builddeb --build --release $DEBPLATFORM"
+contrib/builddeb --cleanup --release $DEBPLATFORM
 if [ $(uname) = "Darwin" ] ; then
-    $DOCKER run -u $DBUILDUSER --rm -v $DEBBUILDDIR:$DSHARED -v $PWD:/mnt/hg $CONTAINER \
-            sh -c "cd /mnt/hg && make clean"
+    $DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \
+            sh -c "cd /mnt/$dn && 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"
--- a/contrib/import-checker.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/contrib/import-checker.py	Thu Sep 03 18:32:17 2015 -0500
@@ -164,7 +164,7 @@
     for m in ['msvcrt', '_winreg']:
         yield m
     # These get missed too
-    for m in 'ctypes', 'email':
+    for m in 'ctypes', 'email', 'multiprocessing':
         yield m
     yield 'builtins' # python3 only
     for m in 'fcntl', 'grp', 'pwd', 'termios':  # Unix only
@@ -200,7 +200,10 @@
             for name in files:
                 if name == '__init__.py':
                     continue
-                if not (name.endswith('.py') or name.endswith('.so')
+                if not (name.endswith('.py')
+                        or name.endswith('.so')
+                        or name.endswith('.pyc')
+                        or name.endswith('.pyo')
                         or name.endswith('.pyd')):
                     continue
                 full_path = os.path.join(top, name)
--- a/contrib/revsetbenchmarks.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/contrib/revsetbenchmarks.py	Thu Sep 03 18:32:17 2015 -0500
@@ -33,6 +33,8 @@
     """update the repo to a revision"""
     try:
         check_call(['hg', 'update', '--quiet', '--check', str(rev)])
+        check_output(['make', 'local'],
+                     stderr=None)  # suppress output except for error/warning
     except CalledProcessError as exc:
         print >> sys.stderr, 'update to revision %s failed, aborting' % rev
         sys.exit(exc.returncode)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/showstack.py	Thu Sep 03 18:32:17 2015 -0500
@@ -0,0 +1,17 @@
+# showstack.py - extension to dump a Python stack trace on signal
+#
+# binds to both SIGQUIT (Ctrl-\) and SIGINFO (Ctrl-T on BSDs)
+
+import sys, signal, traceback
+
+def sigshow(*args):
+    sys.stderr.write("\n")
+    traceback.print_stack(args[1], limit=10, file=sys.stderr)
+    sys.stderr.write("----\n")
+
+def extsetup(ui):
+    signal.signal(signal.SIGQUIT, sigshow)
+    try:
+        signal.signal(signal.SIGINFO, sigshow)
+    except AttributeError:
+        pass
--- a/contrib/vagrant/Vagrantfile	Thu Sep 03 22:25:16 2015 +0800
+++ b/contrib/vagrant/Vagrantfile	Thu Sep 03 18:32:17 2015 -0500
@@ -1,9 +1,8 @@
 # -*- mode: ruby -*-
 
 Vagrant.configure('2') do |config|
-  # Debian 7.4 32-bit i386 without configuration management software
-  config.vm.box = "puppetlabs/debian-7.4-32-nocm"
-  #config.vm.box = "pnd/debian-wheezy32-basebox"
+  # Debian 8.1 x86_64 without configuration management software
+  config.vm.box = "debian/jessie64"
   config.vm.hostname = "tests"
 
   config.vm.define "tests" do |conf|
--- a/contrib/vim/patchreview.txt	Thu Sep 03 22:25:16 2015 +0800
+++ b/contrib/vim/patchreview.txt	Thu Sep 03 18:32:17 2015 -0500
@@ -30,7 +30,7 @@
 software development projects. This plugin provides that missing
 functionality.
 
-It also improves on |:diffpatch|'s behaviour of creating the patched files in
+It also improves on |:diffpatch|'s behavior of creating the patched files in
 the same directory as original file which can lead to project workspace
 pollution.
 
--- a/hgext/convert/__init__.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/convert/__init__.py	Thu Sep 03 18:32:17 2015 -0500
@@ -316,6 +316,9 @@
         ``convert.git.remoteprefix`` as a prefix followed by a /. The default
         is 'remote'.
 
+    :convert.git.skipsubmodules: does not convert root level .gitmodules files
+        or files with 160000 mode indicating a submodule. Default is False.
+
     Perforce Source
     ###############
 
--- a/hgext/convert/common.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/convert/common.py	Thu Sep 03 18:32:17 2015 -0500
@@ -82,6 +82,13 @@
     def after(self):
         pass
 
+    def targetfilebelongstosource(self, targetfilename):
+        """Returns true if the given targetfile belongs to the source repo. This
+        is useful when only a subdirectory of the target belongs to the source
+        repo."""
+        # For normal full repo converts, this is always True.
+        return True
+
     def setrevmap(self, revmap):
         """set the map of already-converted revisions"""
         pass
--- a/hgext/convert/convcmd.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/convert/convcmd.py	Thu Sep 03 18:32:17 2015 -0500
@@ -120,6 +120,9 @@
                          item=file, total=self.filecount)
         return self.source.getfile(file, rev)
 
+    def targetfilebelongstosource(self, targetfilename):
+        return self.source.targetfilebelongstosource(targetfilename)
+
     def lookuprev(self, rev):
         return self.source.lookuprev(rev)
 
--- a/hgext/convert/filemap.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/convert/filemap.py	Thu Sep 03 18:32:17 2015 -0500
@@ -42,6 +42,7 @@
         self.include = {}
         self.exclude = {}
         self.rename = {}
+        self.targetprefixes = None
         if path:
             if self.parse(path):
                 raise util.Abort(_('errors in filemap'))
@@ -100,6 +101,30 @@
                 pass
         return '', name, ''
 
+    def istargetfile(self, filename):
+        """Return true if the given target filename is covered as a destination
+        of the filemap. This is useful for identifying what parts of the target
+        repo belong to the source repo and what parts don't."""
+        if self.targetprefixes is None:
+            self.targetprefixes = set()
+            for before, after in self.rename.iteritems():
+                self.targetprefixes.add(after)
+
+        # If "." is a target, then all target files are considered from the
+        # source.
+        if not self.targetprefixes or '.' in self.targetprefixes:
+            return True
+
+        filename = normalize(filename)
+        for pre, suf in rpairs(filename):
+            # This check is imperfect since it doesn't account for the
+            # include/exclude list, but it should work in filemaps that don't
+            # apply include/exclude to the same source directories they are
+            # renaming.
+            if pre in self.targetprefixes:
+                return True
+        return False
+
     def __call__(self, name):
         if self.include:
             inc = self.lookup(name, self.include)[0]
@@ -410,6 +435,9 @@
 
         return files, ncopies, ncleanp2
 
+    def targetfilebelongstosource(self, targetfilename):
+        return self.filemapper.istargetfile(targetfilename)
+
     def getfile(self, name, rev):
         realname, realrev = rev
         return self.base.getfile(realname, realrev)
--- a/hgext/convert/git.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/convert/git.py	Thu Sep 03 18:32:17 2015 -0500
@@ -224,6 +224,8 @@
         lcount = len(difftree)
         i = 0
 
+        skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules',
+                                            False)
         def add(entry, f, isdest):
             seen.add(f)
             h = entry[3]
@@ -232,6 +234,9 @@
             renamesource = (not isdest and entry[4][0] == 'R')
 
             if f == '.gitmodules':
+                if skipsubmodules:
+                    return
+
                 subexists[0] = True
                 if entry[4] == 'D' or renamesource:
                     subdeleted[0] = True
@@ -239,7 +244,8 @@
                 else:
                     changes.append(('.hgsub', ''))
             elif entry[1] == '160000' or entry[0] == ':160000':
-                subexists[0] = True
+                if not skipsubmodules:
+                    subexists[0] = True
             else:
                 if renamesource:
                     h = hex(nullid)
@@ -377,28 +383,31 @@
     def getbookmarks(self):
         bookmarks = {}
 
-        # Interesting references in git are prefixed
-        prefix = 'refs/heads/'
-        prefixlen = len(prefix)
+        # Handle local and remote branches
+        remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote')
+        reftypes = [
+            # (git prefix, hg prefix)
+            ('refs/remotes/origin/', remoteprefix + '/'),
+            ('refs/heads/', '')
+        ]
 
-        # factor two commands
-        remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote')
-        gitcmd = { remoteprefix + '/': 'git ls-remote --heads origin',
-                                   '': 'git show-ref'}
+        exclude = set([
+            'refs/remotes/origin/HEAD',
+        ])
 
-        # Origin heads
-        for reftype in gitcmd:
-            try:
-                fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE)
-                for line in fh:
-                    line = line.strip()
-                    rev, name = line.split(None, 1)
-                    if not name.startswith(prefix):
+        try:
+            fh = self.gitopen('git show-ref', err=subprocess.PIPE)
+            for line in fh:
+                line = line.strip()
+                rev, name = line.split(None, 1)
+                # Process each type of branch
+                for gitprefix, hgprefix in reftypes:
+                    if not name.startswith(gitprefix) or name in exclude:
                         continue
-                    name = '%s%s' % (reftype, name[prefixlen:])
+                    name = '%s%s' % (hgprefix, name[len(gitprefix):])
                     bookmarks[name] = rev
-            except Exception:
-                pass
+        except Exception:
+            pass
 
         return bookmarks
 
--- a/hgext/convert/hg.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/convert/hg.py	Thu Sep 03 18:32:17 2015 -0500
@@ -23,6 +23,7 @@
 from mercurial.node import bin, hex, nullid
 from mercurial import hg, util, context, bookmarks, error, scmutil, exchange
 from mercurial import phases
+from mercurial import merge as mergemod
 
 from common import NoRepo, commit, converter_source, converter_sink, mapfile
 
@@ -176,14 +177,58 @@
 
         return fp.getvalue()
 
+    def _calculatemergedfiles(self, source, p1ctx, p2ctx):
+        """Calculates the files from p2 that we need to pull in when merging p1
+        and p2, given that the merge is coming from the given source.
+
+        This prevents us from losing files that only exist in the target p2 and
+        that don't come from the source repo (like if you're merging multiple
+        repositories together).
+        """
+        anc = [p1ctx.ancestor(p2ctx)]
+        # Calculate what files are coming from p2
+        actions, diverge, rename = mergemod.calculateupdates(
+            self.repo, p1ctx, p2ctx, anc,
+            True,  # branchmerge
+            True,  # force
+            False, # partial
+            False, # acceptremote
+            False, # followcopies
+        )
+
+        for file, (action, info, msg) in actions.iteritems():
+            if source.targetfilebelongstosource(file):
+                # If the file belongs to the source repo, ignore the p2
+                # since it will be covered by the existing fileset.
+                continue
+
+            # If the file requires actual merging, abort. We don't have enough
+            # context to resolve merges correctly.
+            if action in ['m', 'dm', 'cd', 'dc']:
+                raise util.Abort(_("unable to convert merge commit "
+                    "since target parents do not merge cleanly (file "
+                    "%s, parents %s and %s)") % (file, p1ctx,
+                                                 p2ctx))
+            elif action == 'k':
+                # 'keep' means nothing changed from p1
+                continue
+            else:
+                # Any other change means we want to take the p2 version
+                yield file
+
     def putcommit(self, files, copies, parents, commit, source, revmap, full,
                   cleanp2):
         files = dict(files)
 
         def getfilectx(repo, memctx, f):
-            if p2ctx and f in cleanp2 and f not in copies:
+            if p2ctx and f in p2files and f not in copies:
                 self.ui.debug('reusing %s from p2\n' % f)
-                return p2ctx[f]
+                try:
+                    return p2ctx[f]
+                except error.ManifestLookupError:
+                    # If the file doesn't exist in p2, then we're syncing a
+                    # delete, so just return None.
+                    return None
             try:
                 v = files[f]
             except KeyError:
@@ -255,6 +300,7 @@
         while parents:
             p1 = p2
             p2 = parents.pop(0)
+            p1ctx = self.repo[p1]
             p2ctx = None
             if p2 != nullid:
                 p2ctx = self.repo[p2]
@@ -262,6 +308,13 @@
             if full:
                 fileset.update(self.repo[p1])
                 fileset.update(self.repo[p2])
+
+            if p2ctx:
+                p2files = set(cleanp2)
+                for file in self._calculatemergedfiles(source, p1ctx, p2ctx):
+                    p2files.add(file)
+                    fileset.add(file)
+
             ctx = context.memctx(self.repo, (p1, p2), text, fileset,
                                  getfilectx, commit.author, commit.date, extra)
 
@@ -378,9 +431,6 @@
 class mercurial_source(converter_source):
     def __init__(self, ui, path, revs=None):
         converter_source.__init__(self, ui, path, revs)
-        if revs and len(revs) > 1:
-            raise util.Abort(_("mercurial source does not support specifying "
-                               "multiple revisions"))
         self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
         self.ignored = set()
         self.saverev = ui.configbool('convert', 'hg.saverev', False)
@@ -415,7 +465,7 @@
             else:
                 self.keep = util.always
             if revs:
-                self._heads = [self.repo[revs[0]].node()]
+                self._heads = [self.repo[r].node() for r in revs]
             else:
                 self._heads = self.repo.heads()
         else:
--- a/hgext/eol.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/eol.py	Thu Sep 03 18:32:17 2015 -0500
@@ -23,7 +23,7 @@
 ``native`` is an alias for checking out in the platform's default line
 ending: ``LF`` on Unix (including Mac OS X) and ``CRLF`` on
 Windows. Note that ``BIN`` (do nothing to line endings) is Mercurial's
-default behaviour; it is only needed if you need to override a later,
+default behavior; it is only needed if you need to override a later,
 more general pattern.
 
 The optional ``[repository]`` section specifies the line endings to
--- a/hgext/highlight/highlight.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/highlight/highlight.py	Thu Sep 03 18:32:17 2015 -0500
@@ -49,7 +49,12 @@
         try:
             lexer = guess_lexer(text[:1024], stripnl=False)
         except (ClassNotFound, ValueError):
-            lexer = TextLexer(stripnl=False)
+            # Don't highlight unknown files
+            return
+
+    # Don't highlight text files
+    if isinstance(lexer, TextLexer):
+        return
 
     formatter = HtmlFormatter(nowrap=True, style=style)
 
--- a/hgext/histedit.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/histedit.py	Thu Sep 03 18:32:17 2015 -0500
@@ -38,7 +38,7 @@
  #  f, fold = use commit, but combine it with the one above
  #  r, roll = like fold, but discard this commit's description
  #  d, drop = remove commit from history
- #  m, mess = edit message without changing commit content
+ #  m, mess = edit commit message without changing commit content
  #
 
 In this file, lines beginning with ``#`` are ignored. You must specify a rule
@@ -60,7 +60,7 @@
  #  f, fold = use commit, but combine it with the one above
  #  r, roll = like fold, but discard this commit's description
  #  d, drop = remove commit from history
- #  m, mess = edit message without changing commit content
+ #  m, mess = edit commit message without changing commit content
  #
 
 At which point you close the editor and ``histedit`` starts working. When you
@@ -144,7 +144,7 @@
 repo, you can add a ``--force`` option.
 
 Histedit rule lines are truncated to 80 characters by default. You
-can customise this behaviour by setting a different length in your
+can customise this behavior by setting a different length in your
 configuration file::
 
   [histedit]
@@ -198,7 +198,7 @@
 #  f, fold = use commit, but combine it with the one above
 #  r, roll = like fold, but discard this commit's description
 #  d, drop = remove commit from history
-#  m, mess = edit message without changing commit content
+#  m, mess = edit commit message without changing commit content
 #
 """)
 
@@ -675,9 +675,9 @@
     destination repository. If URL of the destination is omitted, the
     'default-push' (or 'default') path will be used.
 
-    For safety, this command is aborted, also if there are ambiguous
-    outgoing revisions which may confuse users: for example, there are
-    multiple branches containing outgoing revisions.
+    For safety, this command is also aborted if there are ambiguous
+    outgoing revisions which may confuse users: for example, if there
+    are multiple branches containing outgoing revisions.
 
     Use "min(outgoing() and ::.)" or similar revset specification
     instead of --outgoing to specify edit target revision exactly in
@@ -778,7 +778,7 @@
         return
     elif goal == 'abort':
         state.read()
-        mapping, tmpnodes, leafs, _ntm = processreplacement(state)
+        tmpnodes, leafs = newnodestoabort(state)
         ui.debug('restore wc to old parent %s\n' % node.short(state.topmost))
 
         # Recover our old commits if necessary
@@ -791,13 +791,9 @@
             os.remove(backupfile)
 
         # check whether we should update away
-        parentnodes = [c.node() for c in repo[None].parents()]
-        for n in leafs | set([state.parentctxnode]):
-            if n in parentnodes:
-                hg.clean(repo, state.topmost)
-                break
-        else:
-            pass
+        if repo.unfiltered().revs('parents() and (%n  or %ln::)',
+                                  state.parentctxnode, leafs | tmpnodes):
+            hg.clean(repo, state.topmost)
         cleanupnode(ui, repo, 'created', tmpnodes)
         cleanupnode(ui, repo, 'temp', leafs)
         state.clear()
@@ -1009,6 +1005,25 @@
                 hint=_('do you want to use the drop action?'))
     return parsed
 
+def newnodestoabort(state):
+    """process the list of replacements to return
+
+    1) the list of final node
+    2) the list of temporary node
+
+    This meant to be used on abort as less data are required in this case.
+    """
+    replacements = state.replacements
+    allsuccs = set()
+    replaced = set()
+    for rep in replacements:
+        allsuccs.update(rep[1])
+        replaced.add(rep[0])
+    newnodes = allsuccs - replaced
+    tmpnodes = allsuccs & replaced
+    return newnodes, tmpnodes
+
+
 def processreplacement(state):
     """process the list of replacements to return
 
@@ -1019,15 +1034,15 @@
     allsuccs = set()
     replaced = set()
     fullmapping = {}
-    # initialise basic set
-    # fullmapping record all operation recorded in replacement
+    # initialize basic set
+    # fullmapping records all operations recorded in replacement
     for rep in replacements:
         allsuccs.update(rep[1])
         replaced.add(rep[0])
         fullmapping.setdefault(rep[0], set()).update(rep[1])
     new = allsuccs - replaced
     tmpnodes = allsuccs & replaced
-    # Reduce content fullmapping  into direct relation between original nodes
+    # Reduce content fullmapping into direct relation between original nodes
     # and final node created during history edition
     # Dropped changeset are replaced by an empty list
     toproceed = set(fullmapping)
@@ -1113,6 +1128,10 @@
     lock = None
     try:
         lock = repo.lock()
+        # do not let filtering get in the way of the cleanse
+        # we should probably get ride of obsolescence marker created during the
+        # histedit, but we currently do not have such information.
+        repo = repo.unfiltered()
         # Find all node that need to be stripped
         # (we hg %lr instead of %ln to silently ignore unknown item
         nm = repo.changelog.nodemap
--- a/hgext/mq.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/mq.py	Thu Sep 03 18:32:17 2015 -0500
@@ -28,7 +28,7 @@
 
 By default, mq will automatically use git patches when required to
 avoid losing file mode changes, copy records, binary files or empty
-files creations or deletions. This behaviour can be configured with::
+files creations or deletions. This behavior can be configured with::
 
   [mq]
   git = auto/keep/yes/no
--- a/hgext/progress.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/progress.py	Thu Sep 03 18:32:17 2015 -0500
@@ -10,3 +10,8 @@
 This extension has been merged into core, you can remove it from your config.
 See hg help config.progress for configuration options.
 """
+# Note for extension authors: ONLY specify testedwith = 'internal' for
+# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
+# be specifying the version(s) of Mercurial they are tested with, or
+# leave the attribute unspecified.
+testedwith = 'internal'
--- a/hgext/rebase.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/hgext/rebase.py	Thu Sep 03 18:32:17 2015 -0500
@@ -218,7 +218,7 @@
             if srcf or basef or destf:
                 raise util.Abort(
                     _('abort and continue do not allow specifying revisions'))
-            if opts.get('tool', False):
+            if abortf and opts.get('tool', False):
                 ui.warn(_('tool option will be ignored\n'))
 
             try:
--- a/mercurial/ancestor.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/ancestor.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,12 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import collections
 import heapq
-from node import nullrev
+
+from .node import nullrev
 
 def commonancestorsheads(pfunc, *nodes):
     """Returns a set with the heads of all common ancestors of all nodes,
--- a/mercurial/archival.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/archival.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,14 +5,27 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import match as matchmod
-import cmdutil
-import scmutil, util, encoding
-import cStringIO, os, tarfile, time, zipfile
-import zlib, gzip
+from __future__ import absolute_import
+
+import cStringIO
+import gzip
+import os
 import struct
-import error
+import tarfile
+import time
+import zipfile
+import zlib
+
+from .i18n import _
+
+from . import (
+    cmdutil,
+    encoding,
+    error,
+    match as matchmod,
+    scmutil,
+    util,
+)
 
 # from unzip source code:
 _UNX_IFREG = 0x8000
--- a/mercurial/base85.c	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/base85.c	Thu Sep 03 18:32:17 2015 -0500
@@ -21,7 +21,7 @@
 static void
 b85prep(void)
 {
-	int i;
+	unsigned i;
 
 	memset(b85dec, 0, sizeof(b85dec));
 	for (i = 0; i < sizeof(b85chars); i++)
--- a/mercurial/bookmarks.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/bookmarks.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,11 +5,22 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
+import errno
 import os
-from mercurial.i18n import _
-from mercurial.node import hex, bin
-from mercurial import encoding, util, obsolete, lock as lockmod
-import errno
+
+from .i18n import _
+from .node import (
+    bin,
+    hex,
+)
+from . import (
+    encoding,
+    lock as lockmod,
+    obsolete,
+    util,
+)
 
 class bmstore(dict):
     """Storage for bookmarks.
@@ -79,6 +90,11 @@
         can be copied back on rollback.
         '''
         repo = self._repo
+        if (repo.ui.configbool('devel', 'all-warnings')
+                or repo.ui.configbool('devel', 'check-locks')):
+            l = repo._wlockref and repo._wlockref()
+            if l is None or not l.held:
+                repo.ui.develwarn('bookmarks write with no wlock')
         self._writerepo(repo)
         repo.invalidatevolatilesets()
 
--- a/mercurial/branchmap.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/branchmap.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,13 +5,28 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from node import bin, hex, nullid, nullrev
-import encoding
-import scmutil
-import util
+from __future__ import absolute_import
+
+import array
+import struct
 import time
-from array import array
-from struct import calcsize, pack, unpack
+
+from .node import (
+    bin,
+    hex,
+    nullid,
+    nullrev,
+)
+from . import (
+    encoding,
+    scmutil,
+    util,
+)
+
+array = array.array
+calcsize = struct.calcsize
+pack = struct.pack
+unpack = struct.unpack
 
 def _filename(repo):
     """name of a branchcache file for a given repo or repoview"""
--- a/mercurial/bundle2.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/bundle2.py	Thu Sep 03 18:32:17 2015 -0500
@@ -145,19 +145,25 @@
 preserve.
 """
 
+from __future__ import absolute_import
+
 import errno
-import sys
-import util
-import struct
-import urllib
+import re
 import string
-import obsolete
-import pushkey
-import url
-import re
+import struct
+import sys
+import urllib
 
-import changegroup, error, tags
-from i18n import _
+from .i18n import _
+from . import (
+    changegroup,
+    error,
+    obsolete,
+    pushkey,
+    tags,
+    url,
+    util,
+)
 
 _pack = struct.pack
 _unpack = struct.unpack
@@ -841,6 +847,12 @@
                 outdebug(ui, 'payload chunk size: %i' % len(chunk))
                 yield _pack(_fpayloadsize, len(chunk))
                 yield chunk
+        except GeneratorExit:
+            # GeneratorExit means that nobody is listening for our
+            # results anyway, so just bail quickly rather than trying
+            # to produce an error part.
+            ui.debug('bundle2-generatorexit\n')
+            raise
         except BaseException as exc:
             # backup exception data for later
             ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
@@ -1233,7 +1245,7 @@
     # we need to make sure we trigger the creation of a transaction object used
     # for the whole processing scope.
     op.gettransaction()
-    import exchange
+    from . import exchange
     cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
     if not isinstance(cg, changegroup.cg1unpacker):
         raise util.Abort(_('%s: not a bundle version 1.0') %
--- a/mercurial/bundlerepo.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/bundlerepo.py	Thu Sep 03 18:32:17 2015 -0500
@@ -11,12 +11,33 @@
 were part of the actual repository.
 """
 
-from node import nullid
-from i18n import _
-import os, tempfile, shutil
-import changegroup, util, mdiff, discovery, cmdutil, scmutil, exchange
-import localrepo, changelog, manifest, filelog, revlog, error, phases, bundle2
-import pathutil
+from __future__ import absolute_import
+
+import os
+import shutil
+import tempfile
+
+from .i18n import _
+from .node import nullid
+
+from . import (
+    bundle2,
+    changegroup,
+    changelog,
+    cmdutil,
+    discovery,
+    error,
+    exchange,
+    filelog,
+    localrepo,
+    manifest,
+    mdiff,
+    pathutil,
+    phases,
+    revlog,
+    scmutil,
+    util,
+)
 
 class bundlerevlog(revlog.revlog):
     def __init__(self, opener, indexfile, bundle, linkmapper):
--- a/mercurial/changegroup.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/changegroup.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,12 +5,32 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
+import bz2
+import os
+import struct
+import tempfile
 import weakref
-from i18n import _
-from node import nullrev, nullid, hex, short
-import mdiff, util, dagutil
-import struct, os, bz2, zlib, tempfile
-import discovery, error, phases, branchmap
+import zlib
+
+from .i18n import _
+from .node import (
+    hex,
+    nullid,
+    nullrev,
+    short,
+)
+
+from . import (
+    branchmap,
+    dagutil,
+    discovery,
+    error,
+    mdiff,
+    phases,
+    util,
+)
 
 _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s"
 _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s"
@@ -103,7 +123,7 @@
         cleanup = filename
 
         if bundletype == "HG20":
-            import bundle2
+            from . import bundle2
             bundle = bundle2.bundle20(ui)
             part = bundle.newpart('changegroup', data=cg.getchunks())
             part.addparam('version', cg.version)
--- a/mercurial/changelog.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/changelog.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,21 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from node import bin, hex, nullid
-from i18n import _
-import util, error, revlog, encoding
+from __future__ import absolute_import
+
+from .i18n import _
+from .node import (
+    bin,
+    hex,
+    nullid,
+)
+
+from . import (
+    encoding,
+    error,
+    revlog,
+    util,
+)
 
 _defaultextra = {'branch': 'default'}
 
@@ -172,6 +184,9 @@
         self.rev(self.node(0))
         return self._nodecache
 
+    def reachableroots(self, minroot, heads, roots, includepath=False):
+        return self.index.reachableroots2(minroot, heads, roots, includepath)
+
     def headrevs(self):
         if self.filteredrevs:
             try:
--- a/mercurial/cmdutil.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/cmdutil.py	Thu Sep 03 18:32:17 2015 -0500
@@ -10,7 +10,7 @@
 import os, sys, errno, re, tempfile, cStringIO, shutil
 import util, scmutil, templater, patch, error, templatekw, revlog, copies
 import match as matchmod
-import context, repair, graphmod, revset, phases, obsolete, pathutil
+import repair, graphmod, revset, phases, obsolete, pathutil
 import changelog
 import bookmarks
 import encoding
@@ -848,6 +848,8 @@
     :updatefunc: a function that update a repo to a given node
                  updatefunc(<repo>, <node>)
     """
+    # avoid cycle context -> subrepo -> cmdutil
+    import context
     tmpname, message, user, date, branch, nodeid, p1, p2 = \
         patch.extract(ui, hunk)
 
@@ -1412,15 +1414,32 @@
 
         self.cache = {}
 
+        # find correct templates for current mode
+        tmplmodes = [
+            (True, None),
+            (self.ui.verbose, 'verbose'),
+            (self.ui.quiet, 'quiet'),
+            (self.ui.debugflag, 'debug'),
+        ]
+
+        self._parts = {'header': '', 'footer': '', 'changeset': 'changeset'}
+        for mode, postfix in tmplmodes:
+            for t in self._parts:
+                cur = t
+                if postfix:
+                    cur += "_" + postfix
+                if mode and cur in self.t:
+                    self._parts[t] = cur
+
     def _show(self, ctx, copies, matchfn, props):
         '''show a single changeset or file revision'''
 
         showlist = templatekw.showlist
 
-        # showparents() behaviour depends on ui trace level which
-        # causes unexpected behaviours at templating level and makes
+        # showparents() behavior depends on ui trace level which
+        # causes unexpected behaviors at templating level and makes
         # it harder to extract it in a standalone function. Its
-        # behaviour cannot be changed so leave it here for now.
+        # behavior cannot be changed so leave it here for now.
         def showparents(**args):
             ctx = args['ctx']
             parents = [[('rev', p.rev()),
@@ -1438,27 +1457,10 @@
         props['revcache'] = {'copies': copies}
         props['cache'] = self.cache
 
-        # find correct templates for current mode
-
-        tmplmodes = [
-            (True, None),
-            (self.ui.verbose, 'verbose'),
-            (self.ui.quiet, 'quiet'),
-            (self.ui.debugflag, 'debug'),
-        ]
-
-        types = {'header': '', 'footer':'', 'changeset': 'changeset'}
-        for mode, postfix  in tmplmodes:
-            for type in types:
-                cur = postfix and ('%s_%s' % (type, postfix)) or type
-                if mode and cur in self.t:
-                    types[type] = cur
-
         try:
-
             # write header
-            if types['header']:
-                h = templater.stringify(self.t(types['header'], **props))
+            if self._parts['header']:
+                h = templater.stringify(self.t(self._parts['header'], **props))
                 if self.buffered:
                     self.header[ctx.rev()] = h
                 else:
@@ -1467,15 +1469,14 @@
                         self.ui.write(h)
 
             # write changeset metadata, then patch if requested
-            key = types['changeset']
+            key = self._parts['changeset']
             self.ui.write(templater.stringify(self.t(key, **props)))
             self.showpatch(ctx.node(), matchfn)
 
-            if types['footer']:
+            if self._parts['footer']:
                 if not self.footer:
-                    self.footer = templater.stringify(self.t(types['footer'],
-                                                      **props))
-
+                    self.footer = templater.stringify(
+                        self.t(self._parts['footer'], **props))
         except KeyError as inst:
             msg = _("%s: no key named '%s'")
             raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
@@ -1928,7 +1929,7 @@
         followfirst = 1
     else:
         followfirst = 0
-    # --follow with FILE behaviour depends on revs...
+    # --follow with FILE behavior depends on revs...
     it = iter(revs)
     startrev = it.next()
     followdescendants = startrev < next(it, startrev)
@@ -2049,7 +2050,7 @@
     return expr, filematcher
 
 def _logrevs(repo, opts):
-    # Default --rev value depends on --follow but --follow behaviour
+    # Default --rev value depends on --follow but --follow behavior
     # depends on revisions resolved from --rev...
     follow = opts.get('follow') or opts.get('follow_first')
     if opts.get('rev'):
@@ -2464,6 +2465,9 @@
     return commitfunc(ui, repo, message, matcher, opts)
 
 def amend(ui, repo, commitfunc, old, extra, pats, opts):
+    # avoid cycle context -> subrepo -> cmdutil
+    import context
+
     # amend will reuse the existing user if not specified, but the obsolete
     # marker creation requires that the current user's name is specified.
     if obsolete.isenabled(repo, obsolete.createmarkersopt):
--- a/mercurial/commands.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/commands.py	Thu Sep 03 18:32:17 2015 -0500
@@ -13,7 +13,7 @@
 import hg, scmutil, util, revlog, copies, error, bookmarks
 import patch, help, encoding, templatekw, discovery
 import archival, changegroup, cmdutil, hbisect
-import sshserver, hgweb, commandserver
+import sshserver, hgweb
 import extensions
 from hgweb import server as hgweb_server
 import merge as mergemod
@@ -2700,9 +2700,12 @@
               pa.distance(pb), rel))
 
 @command('debugrebuilddirstate|debugrebuildstate',
-    [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
+    [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
+     ('', 'minimal', None, _('only rebuild files that are inconsistent with '
+                             'the working copy parent')),
+    ],
     _('[-r REV]'))
-def debugrebuilddirstate(ui, repo, rev):
+def debugrebuilddirstate(ui, repo, rev, **opts):
     """rebuild the dirstate as it would look like for the given revision
 
     If no revision is specified the first current parent will be used.
@@ -2711,13 +2714,33 @@
     The actual working directory content or existing dirstate
     information such as adds or removes is not considered.
 
+    ``minimal`` will only rebuild the dirstate status for files that claim to be
+    tracked but are not in the parent manifest, or that exist in the parent
+    manifest but are not in the dirstate. It will not change adds, removes, or
+    modified files that are in the working copy parent.
+
     One use of this command is to make the next :hg:`status` invocation
     check the actual file content.
     """
     ctx = scmutil.revsingle(repo, rev)
     wlock = repo.wlock()
     try:
-        repo.dirstate.rebuild(ctx.node(), ctx.manifest())
+        dirstate = repo.dirstate
+
+        # See command doc for what minimal does.
+        if opts.get('minimal'):
+            dirstatefiles = set(dirstate)
+            ctxfiles = set(ctx.manifest().keys())
+            for file in (dirstatefiles | ctxfiles):
+                indirstate = file in dirstatefiles
+                inctx = file in ctxfiles
+
+                if indirstate and not inctx and dirstate[file] != 'a':
+                    dirstate.drop(file)
+                elif inctx and not indirstate:
+                    dirstate.normallookup(file)
+        else:
+            dirstate.rebuild(ctx.node(), ctx.manifest())
     finally:
         wlock.release()
 
@@ -2933,7 +2956,7 @@
     expansion.
     """
     if ui.verbose:
-        tree = revset.parse(expr)
+        tree = revset.parse(expr, lookup=repo.__contains__)
         ui.note(revset.prettyformat(tree), "\n")
         newtree = revset.findaliases(ui, tree)
         if newtree != tree:
@@ -2945,7 +2968,7 @@
         if opts["optimize"]:
             weight, optimizedtree = revset.optimize(newtree, True)
             ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
-    func = revset.match(ui, expr)
+    func = revset.match(ui, expr, repo)
     revs = func(repo)
     if ui.verbose:
         ui.note("* set:\n", revset.prettyformatset(revs), "\n")
@@ -3948,6 +3971,7 @@
     section = None
     if name and '.' in name:
         name, section = name.split('.', 1)
+        section = section.lower()
 
     text = help.help_(ui, name, **opts)
 
@@ -5251,18 +5275,14 @@
                 # this lets simultaneous -r, -b options continue working
                 opts.setdefault('rev', []).append("null")
 
-    dest = ui.expandpath(dest or 'default-push', dest or 'default')
-    dest, branches = hg.parseurl(dest, opts.get('branch'))
+    path = ui.paths.getpath(dest, default='default')
+    if not path:
+        raise util.Abort(_('default repository not configured!'),
+                         hint=_('see the "path" section in "hg help config"'))
+    dest, branches = path.pushloc, (path.branch, opts.get('branch') or [])
     ui.status(_('pushing to %s\n') % util.hidepassword(dest))
     revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
-    try:
-        other = hg.peer(repo, opts, dest)
-    except error.RepoError:
-        if dest == "default-push":
-            raise util.Abort(_("default repository not configured!"),
-                    hint=_('see the "path" section in "hg help config"'))
-        else:
-            raise
+    other = hg.peer(repo, opts, dest)
 
     if revs:
         revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
@@ -5713,6 +5733,7 @@
         s.serve_forever()
 
     if opts["cmdserver"]:
+        import commandserver
         service = commandserver.createservice(ui, repo, opts)
         return cmdutil.service(opts, initfn=service.init, runfn=service.run)
 
@@ -6248,7 +6269,7 @@
                         raise util.Abort(_("tag '%s' is not a global tag") % n)
                     else:
                         raise util.Abort(_("tag '%s' is not a local tag") % n)
-            rev_ = nullid
+            rev_ = 'null'
             if not message:
                 # we don't translate commit messages
                 message = 'Removed tag %s' % ', '.join(names)
--- a/mercurial/config.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/config.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,16 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import error, util
-import os, errno
+from __future__ import absolute_import
+
+import errno
+import os
+
+from .i18n import _
+from . import (
+    error,
+    util,
+)
 
 class config(object):
     def __init__(self, data=None, includepaths=[]):
--- a/mercurial/copies.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/copies.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,15 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import util, pathutil
+from __future__ import absolute_import
+
 import heapq
 
+from . import (
+    pathutil,
+    util,
+)
+
 def _findlimit(repo, a, b):
     """
     Find the last revision that needs to be checked to ensure that a full
@@ -185,6 +191,9 @@
     return cm
 
 def _backwardrenames(a, b):
+    if a._repo.ui.configbool('experimental', 'disablecopytrace'):
+        return {}
+
     # Even though we're not taking copies into account, 1:n rename situations
     # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
     # arbitrarily pick one of the renames.
@@ -258,6 +267,12 @@
     if c2.node() is None and c1.node() == repo.dirstate.p1():
         return repo.dirstate.copies(), {}, {}, {}
 
+    # Copy trace disabling is explicitly below the node == p1 logic above
+    # because the logic above is required for a simple copy to be kept across a
+    # rebase.
+    if repo.ui.configbool('experimental', 'disablecopytrace'):
+        return {}, {}, {}, {}
+
     limit = _findlimit(repo, c1.rev(), c2.rev())
     if limit is None:
         # no common ancestor, no copies
@@ -507,7 +522,12 @@
     copies between fromrev and rev.
     '''
     exclude = {}
-    if skiprev is not None:
+    if (skiprev is not None and
+        not repo.ui.configbool('experimental', 'disablecopytrace')):
+        # disablecopytrace skips this line, but not the entire function because
+        # the line below is O(size of the repo) during a rebase, while the rest
+        # of the function is much faster (and is required for carrying copy
+        # metadata across the rebase anyway).
         exclude = pathcopies(repo[fromrev], repo[skiprev])
     for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
         # copies.pathcopies returns backward renames, so dst might not
--- a/mercurial/crecord.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/crecord.py	Thu Sep 03 18:32:17 2015 -0500
@@ -8,11 +8,23 @@
 # This code is based on the Mark Edgington's crecord extension.
 # (Itself based on Bryan O'Sullivan's record extension.)
 
-from i18n import _
-import patch as patchmod
-import util, encoding
+from __future__ import absolute_import
 
-import os, re, sys, struct, signal, tempfile, locale, cStringIO
+import cStringIO
+import locale
+import os
+import re
+import signal
+import struct
+import sys
+import tempfile
+
+from .i18n import _
+from . import (
+    encoding,
+    patch as patchmod,
+    util,
+)
 
 # This is required for ncurses to display non-ASCII characters in default user
 # locale encoding correctly.  --immerrr
@@ -21,7 +33,8 @@
 # os.name is one of: 'posix', 'nt', 'dos', 'os2', 'mac', or 'ce'
 if os.name == 'posix':
     import curses
-    import fcntl, termios
+    import fcntl
+    import termios
 else:
     # I have no idea if wcurses works with crecord...
     try:
--- a/mercurial/dagparser.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/dagparser.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,13 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import re, string
-import util
-from i18n import _
+from __future__ import absolute_import
+
+import re
+import string
+
+from .i18n import _
+from . import util
 
 def parsedag(desc):
     '''parses a DAG from a concise textual description; generates events
--- a/mercurial/dagutil.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/dagutil.py	Thu Sep 03 18:32:17 2015 -0500
@@ -6,9 +6,10 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from node import nullrev
-from i18n import _
+from __future__ import absolute_import
 
+from .i18n import _
+from .node import nullrev
 
 class basedag(object):
     '''generic interface for DAGs
--- a/mercurial/demandimport.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/demandimport.py	Thu Sep 03 18:32:17 2015 -0500
@@ -24,8 +24,11 @@
   b = __import__(a)
 '''
 
-import os, sys
-from contextlib import contextmanager
+from __future__ import absolute_import
+
+import contextlib
+import os
+import sys
 
 # __builtin__ in Python 2, builtins in Python 3.
 try:
@@ -33,26 +36,21 @@
 except ImportError:
     import builtins
 
+contextmanager = contextlib.contextmanager
+
 _origimport = __import__
 
 nothing = object()
 
-try:
-    # Python 3 doesn't have relative imports nor level -1.
-    level = -1
-    if sys.version_info[0] >= 3:
-        level = 0
-    _origimport(builtins.__name__, {}, {}, None, level)
-except TypeError: # no level argument
-    def _import(name, globals, locals, fromlist, level):
-        "call _origimport with no level argument"
-        return _origimport(name, globals, locals, fromlist)
-else:
-    _import = _origimport
+# Python 3 doesn't have relative imports nor level -1.
+level = -1
+if sys.version_info[0] >= 3:
+    level = 0
+_import = _origimport
 
-def _hgextimport(importfunc, name, globals, *args):
+def _hgextimport(importfunc, name, globals, *args, **kwargs):
     try:
-        return importfunc(name, globals, *args)
+        return importfunc(name, globals, *args, **kwargs)
     except ImportError:
         if not globals:
             raise
@@ -63,7 +61,7 @@
         if nameroot != contextroot:
             raise
         # retry to import with "hgext_" prefix
-        return importfunc(hgextname, globals, *args)
+        return importfunc(hgextname, globals, *args, **kwargs)
 
 class _demandmod(object):
     """module demand-loader and proxy"""
@@ -135,15 +133,44 @@
                 return locals[base]
         return _demandmod(name, globals, locals, level)
     else:
-        if level != -1:
-            # from . import b,c,d or from .a import b,c,d
-            return _origimport(name, globals, locals, fromlist, level)
+        # There is a fromlist.
         # from a import b,c,d
+        # from . import b,c,d
+        # from .a import b,c,d
+
+        # level == -1: relative and absolute attempted (Python 2 only).
+        # level >= 0: absolute only (Python 2 w/ absolute_import and Python 3).
+        # The modern Mercurial convention is to use absolute_import everywhere,
+        # so modern Mercurial code will have level >= 0.
+
+        if level >= 0:
+            # Mercurial's enforced import style does not use
+            # "from a import b,c,d" or "from .a import b,c,d" syntax. In
+            # addition, this appears to be giving errors with some modules
+            # for unknown reasons. Since we shouldn't be using this syntax
+            # much, work around the problems.
+            if name:
+                return _hgextimport(_origimport, name, globals, locals,
+                                    fromlist, level)
+
+            mod = _hgextimport(_origimport, name, globals, locals, level=level)
+            for x in fromlist:
+                # Missing symbols mean they weren't defined in the module
+                # itself which means they are sub-modules.
+                if getattr(mod, x, nothing) is nothing:
+                    setattr(mod, x,
+                            _demandmod(x, mod.__dict__, locals, level=level))
+
+            return mod
+
+        # But, we still need to support lazy loading of standard library and 3rd
+        # party modules. So handle level == -1.
         mod = _hgextimport(_origimport, name, globals, locals)
         # recurse down the module chain
         for comp in name.split('.')[1:]:
             if getattr(mod, comp, nothing) is nothing:
-                setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
+                setattr(mod, comp,
+                        _demandmod(comp, mod.__dict__, mod.__dict__))
             mod = getattr(mod, comp)
         for x in fromlist:
             # set requested submodules for demand load
@@ -152,6 +179,7 @@
         return mod
 
 ignore = [
+    '__future__',
     '_hashlib',
     '_xmlplus',
     'fcntl',
--- a/mercurial/discovery.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/discovery.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,10 +5,23 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from node import nullid, short
-from i18n import _
-import util, setdiscovery, treediscovery, phases, obsolete, bookmarks
-import branchmap
+from __future__ import absolute_import
+
+from .i18n import _
+from .node import (
+    nullid,
+    short,
+)
+
+from . import (
+    bookmarks,
+    branchmap,
+    obsolete,
+    phases,
+    setdiscovery,
+    treediscovery,
+    util,
+)
 
 def findcommonincoming(repo, remote, heads=None, force=False):
     """Return a tuple (common, anyincoming, heads) used to identify the common
--- a/mercurial/dispatch.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/dispatch.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,13 +5,37 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
+from __future__ import absolute_import
+
+import atexit
 import difflib
-import util, commands, hg, fancyopts, extensions, hook, error
-import cmdutil, encoding
-import ui as uimod
-import demandimport
+import errno
+import os
+import pdb
+import re
+import shlex
+import signal
+import socket
+import sys
+import time
+import traceback
+
+
+from .i18n import _
+
+from . import (
+    cmdutil,
+    commands,
+    demandimport,
+    encoding,
+    error,
+    extensions,
+    fancyopts,
+    hg,
+    hook,
+    ui as uimod,
+    util,
+)
 
 class request(object):
     def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
@@ -866,6 +890,8 @@
             except error.RequirementError:
                 raise
             except error.RepoError:
+                if rpath and rpath[-1]: # invalid -R path
+                    raise
                 if cmd not in commands.optionalrepo.split():
                     if (cmd in commands.inferrepo.split() and
                         args and not path): # try to infer -R from command args
@@ -909,7 +935,7 @@
         format = 'text'
 
     try:
-        from mercurial import lsprof
+        from . import lsprof
     except ImportError:
         raise util.Abort(_(
             'lsprof not available - install from '
@@ -922,7 +948,7 @@
         p.disable()
 
         if format == 'kcachegrind':
-            import lsprofcalltree
+            from . import lsprofcalltree
             calltree = lsprofcalltree.KCacheGrind(p)
             calltree.output(fp)
         else:
--- a/mercurial/error.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/error.py	Thu Sep 03 18:32:17 2015 -0500
@@ -11,6 +11,8 @@
 imports.
 """
 
+from __future__ import absolute_import
+
 # Do not import anything here, please
 
 class HintException(Exception):
@@ -32,7 +34,7 @@
         # Python 2.6+ complain about the 'message' property being deprecated
         self.lookupmessage = message
         if isinstance(name, str) and len(name) == 20:
-            from node import short
+            from .node import short
             name = short(name)
         RevlogError.__init__(self, '%s@%s: %s' % (index, name, message))
 
@@ -78,7 +80,7 @@
     """Exception raised when a {rev,file}set references an unknown identifier"""
 
     def __init__(self, function, symbols):
-        from i18n import _
+        from .i18n import _
         ParseError.__init__(self, _("unknown identifier: %s") % function)
         self.function = function
         self.symbols = symbols
@@ -173,7 +175,7 @@
     """
 
     def __init__(self, filename, node, tombstone):
-        from node import short
+        from .node import short
         RevlogError.__init__(self, '%s:%s' % (filename, short(node)))
         self.tombstone = tombstone
 
--- a/mercurial/exchange.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/exchange.py	Thu Sep 03 18:32:17 2015 -0500
@@ -571,7 +571,7 @@
 
 @b2partsgenerator('bookmarks')
 def _pushb2bookmarks(pushop, bundler):
-    """handle phase push through bundle2"""
+    """handle bookmark push through bundle2"""
     if 'bookmarks' in pushop.stepsdone:
         return
     b2caps = bundle2.bundle2caps(pushop.remote)
@@ -1419,7 +1419,7 @@
                 op = bundle2.bundleoperation(repo, lambda: tr,
                                              captureoutput=captureoutput)
                 try:
-                    r = bundle2.processbundle(repo, cg, op=op)
+                    op = bundle2.processbundle(repo, cg, op=op)
                 finally:
                     r = op.reply
                     if captureoutput and r is not None:
--- a/mercurial/extensions.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/extensions.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,21 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import imp, os
-import util, cmdutil, error
-from i18n import _, gettext
+from __future__ import absolute_import
+
+import imp
+import os
+
+from .i18n import (
+    _,
+    gettext,
+)
+
+from . import (
+    cmdutil,
+    error,
+    util,
+)
 
 _extensions = {}
 _aftercallbacks = {}
--- a/mercurial/fancyopts.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/fancyopts.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,12 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import getopt
-import util
-from i18n import _
+
+from .i18n import _
+from . import util
 
 def gnugetopt(args, options, longoptions):
     """Parse options mostly like getopt.gnu_getopt.
--- a/mercurial/filelog.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/filelog.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,8 +5,16 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import error, mdiff, revlog
-import re, struct
+from __future__ import absolute_import
+
+import re
+import struct
+
+from . import (
+    error,
+    mdiff,
+    revlog,
+)
 
 _mdre = re.compile('\1\n')
 def parsemeta(text):
--- a/mercurial/filemerge.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/filemerge.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,11 +5,25 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from node import short
-from i18n import _
-import util, simplemerge, match, error, templater, templatekw
-import os, tempfile, re, filecmp
-import tagmerge
+from __future__ import absolute_import
+
+import filecmp
+import os
+import re
+import tempfile
+
+from .i18n import _
+from .node import short
+
+from . import (
+    error,
+    match,
+    simplemerge,
+    tagmerge,
+    templatekw,
+    templater,
+    util,
+)
 
 def _toolstr(ui, tool, part, default=""):
     return ui.config("merge-tools", tool + "." + part, default)
@@ -213,15 +227,12 @@
             util.copyfile(back, a) # restore from backup and try again
     return 1 # continue merging
 
-@internaltool('merge', True,
-              _("merging %s incomplete! "
-                "(edit conflicts, then use 'hg resolve --mark')\n"))
-def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
+def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode):
     """
     Uses the internal non-interactive simple merge algorithm for merging
     files. It will fail if there are any conflicts and leave markers in
     the partially merged file. Markers will have two sections, one for each side
-    of merge."""
+    of merge, unless mode equals 'union' which suppresses the markers."""
     tool, toolpath, binary, symlink = toolconf
     if symlink:
         repo.ui.warn(_('warning: internal :merge cannot merge symlinks '
@@ -233,10 +244,33 @@
 
         ui = repo.ui
 
-        r = simplemerge.simplemerge(ui, a, b, c, label=labels)
+        r = simplemerge.simplemerge(ui, a, b, c, label=labels, mode=mode)
         return True, r
     return False, 0
 
+@internaltool('union', True,
+              _("merging %s incomplete! "
+                "(edit conflicts, then use 'hg resolve --mark')\n"))
+def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
+    """
+    Uses the internal non-interactive simple merge algorithm for merging
+    files. It will use both left and right sides for conflict regions.
+    No markers are inserted."""
+    return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
+                  files, labels, 'union')
+
+@internaltool('merge', True,
+              _("merging %s incomplete! "
+                "(edit conflicts, then use 'hg resolve --mark')\n"))
+def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
+    """
+    Uses the internal non-interactive simple merge algorithm for merging
+    files. It will fail if there are any conflicts and leave markers in
+    the partially merged file. Markers will have two sections, one for each side
+    of merge."""
+    return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
+                  files, labels, 'merge')
+
 @internaltool('merge3', True,
               _("merging %s incomplete! "
                 "(edit conflicts, then use 'hg resolve --mark')\n"))
--- a/mercurial/fileset.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/fileset.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,17 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import re
-import parser, error, util, merge
-from i18n import _
+
+from .i18n import _
+from . import (
+    error,
+    merge,
+    parser,
+    util,
+)
 
 elements = {
     # token-type: binding-strength, primary, prefix, infix, suffix
@@ -410,7 +418,7 @@
         # i18n: "subrepo" is a keyword
         pat = getstring(x, _("subrepo requires a pattern or no arguments"))
 
-        import match as matchmod # avoid circular import issues
+        from . import match as matchmod # avoid circular import issues
         fast = not matchmod.patkind(pat)
         if fast:
             def m(s):
--- a/mercurial/formatter.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/formatter.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,13 +5,23 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import cPickle
-from node import hex, short
-from i18n import _
-import encoding, util
-import templater
 import os
 
+from .i18n import _
+from .node import (
+    hex,
+    short,
+)
+
+from . import (
+    encoding,
+    templater,
+    util,
+)
+
 class baseformatter(object):
     def __init__(self, ui, topic, opts):
         self._ui = ui
--- a/mercurial/graphmod.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/graphmod.py	Thu Sep 03 18:32:17 2015 -0500
@@ -17,11 +17,16 @@
 Data depends on type.
 """
 
-from mercurial.node import nullrev
-import util
+from __future__ import absolute_import
 
 import heapq
 
+from .node import nullrev
+from . import (
+    revset,
+    util,
+)
+
 CHANGESET = 'C'
 
 def groupbranchiter(revs, parentsfunc, firstbranch=()):
@@ -233,8 +238,6 @@
     if not revs:
         return
 
-    cl = repo.changelog
-    lowestrev = revs.min()
     gpcache = {}
 
     if repo.ui.configbool('experimental', 'graph-group-branches', False):
@@ -244,7 +247,8 @@
         if firstbranchrevset:
             firstbranch = repo.revs(firstbranchrevset)
         parentrevs = repo.changelog.parentrevs
-        revs = list(groupbranchiter(revs, parentrevs, firstbranch))
+        revs = groupbranchiter(revs, parentrevs, firstbranch)
+        revs = revset.baseset(revs)
 
     for rev in revs:
         ctx = repo[rev]
@@ -256,7 +260,7 @@
         for mpar in mpars:
             gp = gpcache.get(mpar)
             if gp is None:
-                gp = gpcache[mpar] = grandparent(cl, lowestrev, revs, mpar)
+                gp = gpcache[mpar] = revset.reachableroots(repo, revs, [mpar])
             if not gp:
                 parents.append(mpar)
             else:
@@ -354,24 +358,6 @@
         yield (cur, type, data, (col, color), edges)
         seen = next
 
-def grandparent(cl, lowestrev, roots, head):
-    """Return all ancestors of head in roots which revision is
-    greater or equal to lowestrev.
-    """
-    pending = set([head])
-    seen = set()
-    kept = set()
-    llowestrev = max(nullrev, lowestrev)
-    while pending:
-        r = pending.pop()
-        if r >= llowestrev and r not in seen:
-            if r in roots:
-                kept.add(r)
-            else:
-                pending.update([p for p in cl.parentrevs(r)])
-            seen.add(r)
-    return sorted(kept)
-
 def asciiedges(type, char, lines, seen, rev, parents):
     """adds edge info to changelog DAG walk suitable for ascii()"""
     if rev not in seen:
--- a/mercurial/hbisect.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/hbisect.py	Thu Sep 03 18:32:17 2015 -0500
@@ -8,12 +8,20 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import collections
 import os
-import error
-from i18n import _
-from node import short, hex
-import util
+
+from .i18n import _
+from .node import (
+    hex,
+    short,
+)
+from . import (
+    error,
+    util,
+)
 
 def bisect(changelog, state):
     """find the next node (if any) for testing during a bisect search.
--- a/mercurial/help/config.txt	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/help/config.txt	Thu Sep 03 18:32:17 2015 -0500
@@ -1752,6 +1752,14 @@
     Whether to require that inbound pushes be transported over SSL to
     prevent password sniffing. Default is True.
 
+``refreshinterval``
+    How frequently directory listings re-scan the filesystem for new
+    repositories, in seconds. This is relevant when wildcards are used
+    to define paths. Depending on how much filesystem traversal is
+    required, refreshing may negatively impact performance.
+
+    Default is 20. Values less than or equal to 0 always refresh.
+
 ``staticurl``
     Base URL to use for static files. If unset, static files (e.g. the
     hgicon.png favicon) will be served by the CGI script itself. Use
--- a/mercurial/help/glossary.txt	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/help/glossary.txt	Thu Sep 03 18:32:17 2015 -0500
@@ -32,12 +32,12 @@
     explicitly with a named branch, but it can also be done locally,
     using bookmarks or clones and anonymous branches.
 
-    Example: "The experimental branch".
+    Example: "The experimental branch."
 
     (Verb) The action of creating a child changeset which results in
     its parent having more than one child.
 
-    Example: "I'm going to branch at X".
+    Example: "I'm going to branch at X."
 
 Branch, anonymous
     Every time a new child changeset is created from a parent that is not
@@ -135,7 +135,7 @@
     See 'Changeset, child'.
 
 Close changeset
-    See 'Head, closed branch'
+    See 'Head, closed branch'.
 
 Closed branch
     See 'Branch, closed'.
@@ -144,11 +144,11 @@
     (Noun) An entire or partial copy of a repository. The partial
     clone must be in the form of a revision and its ancestors.
 
-    Example: "Is your clone up to date?".
+    Example: "Is your clone up to date?"
 
     (Verb) The process of creating a clone, using :hg:`clone`.
 
-    Example: "I'm going to clone the repository".
+    Example: "I'm going to clone the repository."
 
 Closed branch head
     See 'Head, closed branch'.
@@ -398,13 +398,13 @@
 Update
     (Noun) Another synonym of changeset.
 
-    Example: "I've pushed an update".
+    Example: "I've pushed an update."
 
     (Verb) This term is usually used to describe updating the state of
     the working directory to that of a specific changeset. See
     :hg:`help update`.
 
-    Example: "You should update".
+    Example: "You should update."
 
 Working directory
     See 'Directory, working'.
--- a/mercurial/help/templates.txt	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/help/templates.txt	Thu Sep 03 18:32:17 2015 -0500
@@ -69,6 +69,10 @@
 
    $ hg log -r 0 --template "{date(date, '%Y')}\n"
 
+- Display date in UTC::
+
+   $ hg log -r 0 --template "{localdate(date, 'UTC')|date}\n"
+
 - Output the description set to a fill-width of 30::
 
    $ hg log -r 0 --template "{fill(desc, 30)}"
--- a/mercurial/hg.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/hg.py	Thu Sep 03 18:32:17 2015 -0500
@@ -6,17 +6,41 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-from lock import release
-from node import nullid
+from __future__ import absolute_import
+
+import errno
+import os
+import shutil
+
+from .i18n import _
+from .node import nullid
 
-import localrepo, bundlerepo, unionrepo, httppeer, sshpeer, statichttprepo
-import bookmarks, lock, util, extensions, error, node, scmutil, phases, url
-import cmdutil, discovery, repoview, exchange
-import ui as uimod
-import merge as mergemod
-import verify as verifymod
-import errno, os, shutil
+from . import (
+    bookmarks,
+    bundlerepo,
+    cmdutil,
+    discovery,
+    error,
+    exchange,
+    extensions,
+    httppeer,
+    localrepo,
+    lock,
+    merge as mergemod,
+    node,
+    phases,
+    repoview,
+    scmutil,
+    sshpeer,
+    statichttprepo,
+    ui as uimod,
+    unionrepo,
+    url,
+    util,
+    verify as verifymod,
+)
+
+release = lock.release
 
 def _local(path):
     path = util.expandpath(util.urllocalpath(path))
@@ -558,7 +582,10 @@
                     try:
                         uprev = destrepo.lookup(checkout)
                     except error.RepoLookupError:
-                        pass
+                        try:
+                            uprev = destrepo.lookup(update)
+                        except error.RepoLookupError:
+                            pass
                 if uprev is None:
                     try:
                         uprev = destrepo._bookmarks['@']
--- a/mercurial/hgweb/hgweb_mod.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/hgweb/hgweb_mod.py	Thu Sep 03 18:32:17 2015 -0500
@@ -6,7 +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 os, re
+import os
 from mercurial import ui, hg, hook, error, encoding, templater, util, repoview
 from mercurial.templatefilters import websub
 from mercurial.i18n import _
@@ -60,8 +60,92 @@
         urlel = os.path.dirname(urlel)
     return reversed(breadcrumb)
 
+class requestcontext(object):
+    """Holds state/context for an individual request.
+
+    Servers can be multi-threaded. Holding state on the WSGI application
+    is prone to race conditions. Instances of this class exist to hold
+    mutable and race-free state for requests.
+    """
+    def __init__(self, app):
+        object.__setattr__(self, 'app', app)
+        object.__setattr__(self, 'repo', app.repo)
+
+        object.__setattr__(self, 'archives', ('zip', 'gz', 'bz2'))
+
+        object.__setattr__(self, 'maxchanges',
+                           self.configint('web', 'maxchanges', 10))
+        object.__setattr__(self, 'stripecount',
+                           self.configint('web', 'stripes', 1))
+        object.__setattr__(self, 'maxshortchanges',
+                           self.configint('web', 'maxshortchanges', 60))
+        object.__setattr__(self, 'maxfiles',
+                           self.configint('web', 'maxfiles', 10))
+        object.__setattr__(self, 'allowpull',
+                           self.configbool('web', 'allowpull', True))
+
+        # we use untrusted=False to prevent a repo owner from using
+        # web.templates in .hg/hgrc to get access to any file readable
+        # by the user running the CGI script
+        object.__setattr__(self, 'templatepath',
+                           self.config('web', 'templates', untrusted=False))
+
+        # This object is more expensive to build than simple config values.
+        # It is shared across requests. The app will replace the object
+        # if it is updated. Since this is a reference and nothing should
+        # modify the underlying object, it should be constant for the lifetime
+        # of the request.
+        object.__setattr__(self, 'websubtable', app.websubtable)
+
+    # Proxy unknown reads and writes to the application instance
+    # until everything is moved to us.
+    def __getattr__(self, name):
+        return getattr(self.app, name)
+
+    def __setattr__(self, name, value):
+        return setattr(self.app, name, value)
+
+    # Servers are often run by a user different from the repo owner.
+    # Trust the settings from the .hg/hgrc files by default.
+    def config(self, section, name, default=None, untrusted=True):
+        return self.repo.ui.config(section, name, default,
+                                   untrusted=untrusted)
+
+    def configbool(self, section, name, default=False, untrusted=True):
+        return self.repo.ui.configbool(section, name, default,
+                                       untrusted=untrusted)
+
+    def configint(self, section, name, default=None, untrusted=True):
+        return self.repo.ui.configint(section, name, default,
+                                      untrusted=untrusted)
+
+    def configlist(self, section, name, default=None, untrusted=True):
+        return self.repo.ui.configlist(section, name, default,
+                                       untrusted=untrusted)
+
+    archivespecs = {
+        'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None),
+        'gz': ('application/x-gzip', 'tgz', '.tar.gz', None),
+        'zip': ('application/zip', 'zip', '.zip', None),
+    }
+
+    def archivelist(self, nodeid):
+        allowed = self.configlist('web', 'allow_archive')
+        for typ, spec in self.archivespecs.iteritems():
+            if typ in allowed or self.configbool('web', 'allow%s' % typ):
+                yield {'type': typ, 'extension': spec[2], 'node': nodeid}
 
 class hgweb(object):
+    """HTTP server for individual repositories.
+
+    Instances of this class serve HTTP responses for a particular
+    repository.
+
+    Instances are typically used as WSGI applications.
+
+    Some servers are multi-threaded. On these servers, there may
+    be multiple active threads inside __call__.
+    """
     def __init__(self, repo, name=None, baseui=None):
         if isinstance(repo, str):
             if baseui:
@@ -84,16 +168,10 @@
         r.baseui.setconfig('progress', 'disable', 'true', 'hgweb')
         self.repo = r
         hook.redirect(True)
-        self.repostate = ((-1, -1), (-1, -1))
+        self.repostate = None
         self.mtime = -1
         self.reponame = name
-        self.archives = 'zip', 'gz', 'bz2'
-        self.stripecount = 1
-        # we use untrusted=False to prevent a repo owner from using
-        # web.templates in .hg/hgrc to get access to any file readable
-        # by the user running the CGI script
-        self.templatepath = self.config('web', 'templates', untrusted=False)
-        self.websubtable = self.loadwebsub()
+        self.websubtable = webutil.getwebsubs(r)
 
     # The CGI scripts are often run by a user different from the repo owner.
     # Trust the settings from the .hg/hgrc files by default.
@@ -101,14 +179,6 @@
         return self.repo.ui.config(section, name, default,
                                    untrusted=untrusted)
 
-    def configbool(self, section, name, default=False, untrusted=True):
-        return self.repo.ui.configbool(section, name, default,
-                                       untrusted=untrusted)
-
-    def configlist(self, section, name, default=None, untrusted=True):
-        return self.repo.ui.configlist(section, name, default,
-                                       untrusted=untrusted)
-
     def _getview(self, repo):
         """The 'web.view' config controls changeset filter to hgweb. Possible
         values are ``served``, ``visible`` and ``all``. Default is ``served``.
@@ -129,7 +199,7 @@
         else:
             return repo.filtered('served')
 
-    def refresh(self, request=None):
+    def refresh(self):
         repostate = []
         mtime = 0
         # file of interrests mtime and size
@@ -144,22 +214,17 @@
         if repostate != self.repostate:
             r = hg.repository(self.repo.baseui, self.repo.url())
             self.repo = self._getview(r)
-            self.maxchanges = int(self.config("web", "maxchanges", 10))
-            self.stripecount = int(self.config("web", "stripes", 1))
-            self.maxshortchanges = int(self.config("web", "maxshortchanges",
-                                                   60))
-            self.maxfiles = int(self.config("web", "maxfiles", 10))
-            self.allowpull = self.configbool("web", "allowpull", True)
-            encoding.encoding = self.config("web", "encoding",
-                                            encoding.encoding)
             # update these last to avoid threads seeing empty settings
             self.repostate = repostate
             # mtime is needed for ETag
             self.mtime = mtime
-        if request:
-            self.repo.ui.environ = request.env
 
     def run(self):
+        """Start a server from CGI environment.
+
+        Modern servers should be using WSGI and should avoid this
+        method, if possible.
+        """
         if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
             raise RuntimeError("This function is only intended to be "
                                "called while running as a CGI script.")
@@ -167,12 +232,25 @@
         wsgicgi.launch(self)
 
     def __call__(self, env, respond):
+        """Run the WSGI application.
+
+        This may be called by multiple threads.
+        """
         req = wsgirequest(env, respond)
         return self.run_wsgi(req)
 
     def run_wsgi(self, req):
+        """Internal method to run the WSGI application.
 
-        self.refresh(req)
+        This is typically only called by Mercurial. External consumers
+        should be using instances of this class as the WSGI application.
+        """
+        self.refresh()
+        rctx = requestcontext(self)
+
+        # This state is global across all threads.
+        encoding.encoding = rctx.config('web', 'encoding', encoding.encoding)
+        rctx.repo.ui.environ = req.env
 
         # work with CGI variables to create coherent structure
         # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
@@ -203,7 +281,7 @@
                 if query:
                     raise ErrorResponse(HTTP_NOT_FOUND)
                 if cmd in perms:
-                    self.check_perm(req, perms[cmd])
+                    self.check_perm(rctx, req, perms[cmd])
                 return protocol.call(self.repo, req, cmd)
             except ErrorResponse as inst:
                 # A client that sends unbundle without 100-continue will
@@ -249,7 +327,7 @@
 
             if cmd == 'archive':
                 fn = req.form['node'][0]
-                for type_, spec in self.archive_specs.iteritems():
+                for type_, spec in rctx.archivespecs.iteritems():
                     ext = spec[2]
                     if fn.endswith(ext):
                         req.form['node'] = [fn[:-len(ext)]]
@@ -264,22 +342,22 @@
 
             # check read permissions non-static content
             if cmd != 'static':
-                self.check_perm(req, None)
+                self.check_perm(rctx, req, None)
 
             if cmd == '':
                 req.form['cmd'] = [tmpl.cache['default']]
                 cmd = req.form['cmd'][0]
 
-            if self.configbool('web', 'cache', True):
+            if rctx.configbool('web', 'cache', True):
                 caching(self, req) # sets ETag header or raises NOT_MODIFIED
             if cmd not in webcommands.__all__:
                 msg = 'no such method: %s' % cmd
                 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
             elif cmd == 'file' and 'raw' in req.form.get('style', []):
                 self.ctype = ctype
-                content = webcommands.rawfile(self, req, tmpl)
+                content = webcommands.rawfile(rctx, req, tmpl)
             else:
-                content = getattr(webcommands, cmd)(self, req, tmpl)
+                content = getattr(webcommands, cmd)(rctx, req, tmpl)
                 req.respond(HTTP_OK, ctype)
 
             return content
@@ -301,47 +379,6 @@
                 return ['']
             return tmpl('error', error=inst.message)
 
-    def loadwebsub(self):
-        websubtable = []
-        websubdefs = self.repo.ui.configitems('websub')
-        # we must maintain interhg backwards compatibility
-        websubdefs += self.repo.ui.configitems('interhg')
-        for key, pattern in websubdefs:
-            # grab the delimiter from the character after the "s"
-            unesc = pattern[1]
-            delim = re.escape(unesc)
-
-            # identify portions of the pattern, taking care to avoid escaped
-            # delimiters. the replace format and flags are optional, but
-            # delimiters are required.
-            match = re.match(
-                r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
-                % (delim, delim, delim), pattern)
-            if not match:
-                self.repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
-                                  % (key, pattern))
-                continue
-
-            # we need to unescape the delimiter for regexp and format
-            delim_re = re.compile(r'(?<!\\)\\%s' % delim)
-            regexp = delim_re.sub(unesc, match.group(1))
-            format = delim_re.sub(unesc, match.group(2))
-
-            # the pattern allows for 6 regexp flags, so set them if necessary
-            flagin = match.group(3)
-            flags = 0
-            if flagin:
-                for flag in flagin.upper():
-                    flags |= re.__dict__[flag]
-
-            try:
-                regexp = re.compile(regexp, flags)
-                websubtable.append((regexp, format))
-            except re.error:
-                self.repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
-                                  % (key, regexp))
-        return websubtable
-
     def templater(self, req):
 
         # determine scheme, port and server name
@@ -410,18 +447,6 @@
                                             })
         return tmpl
 
-    def archivelist(self, nodeid):
-        allowed = self.configlist("web", "allow_archive")
-        for i, spec in self.archive_specs.iteritems():
-            if i in allowed or self.configbool("web", "allow" + i):
-                yield {"type" : i, "extension" : spec[2], "node" : nodeid}
-
-    archive_specs = {
-        'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None),
-        'gz': ('application/x-gzip', 'tgz', '.tar.gz', None),
-        'zip': ('application/zip', 'zip', '.zip', None),
-        }
-
-    def check_perm(self, req, op):
+    def check_perm(self, rctx, req, op):
         for permhook in permhooks:
-            permhook(self, req, op)
+            permhook(rctx, req, op)
--- a/mercurial/hgweb/hgwebdir_mod.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/hgweb/hgwebdir_mod.py	Thu Sep 03 18:32:17 2015 -0500
@@ -79,17 +79,30 @@
     return name, str(port), path
 
 class hgwebdir(object):
-    refreshinterval = 20
+    """HTTP server for multiple repositories.
 
+    Given a configuration, different repositories will be served depending
+    on the request path.
+
+    Instances are typically used as WSGI applications.
+    """
     def __init__(self, conf, baseui=None):
         self.conf = conf
         self.baseui = baseui
+        self.ui = None
         self.lastrefresh = 0
         self.motd = None
         self.refresh()
 
     def refresh(self):
-        if self.lastrefresh + self.refreshinterval > time.time():
+        refreshinterval = 20
+        if self.ui:
+            refreshinterval = self.ui.configint('web', 'refreshinterval',
+                                                refreshinterval)
+
+        # refreshinterval <= 0 means to always refresh.
+        if (refreshinterval > 0 and
+            self.lastrefresh + refreshinterval > time.time()):
             return
 
         if self.baseui:
--- a/mercurial/hgweb/request.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/hgweb/request.py	Thu Sep 03 18:32:17 2015 -0500
@@ -40,6 +40,12 @@
     return form
 
 class wsgirequest(object):
+    """Higher-level API for a WSGI request.
+
+    WSGI applications are invoked with 2 arguments. They are used to
+    instantiate instances of this class, which provides higher-level APIs
+    for obtaining request parameters, writing HTTP output, etc.
+    """
     def __init__(self, wsgienv, start_response):
         version = wsgienv['wsgi.version']
         if (version < (1, 0)) or (version >= (2, 0)):
--- a/mercurial/hgweb/webcommands.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/hgweb/webcommands.py	Thu Sep 03 18:32:17 2015 -0500
@@ -639,35 +639,10 @@
 
     The ``branches`` template is rendered.
     """
-    tips = []
-    heads = web.repo.heads()
-    parity = paritygen(web.stripecount)
-    sortkey = lambda item: (not item[1], item[0].rev())
-
-    def entries(limit, **map):
-        count = 0
-        if not tips:
-            for tag, hs, tip, closed in web.repo.branchmap().iterbranches():
-                tips.append((web.repo[tip], closed))
-        for ctx, closed in sorted(tips, key=sortkey, reverse=True):
-            if limit > 0 and count >= limit:
-                return
-            count += 1
-            if closed:
-                status = 'closed'
-            elif ctx.node() not in heads:
-                status = 'inactive'
-            else:
-                status = 'open'
-            yield {'parity': parity.next(),
-                   'branch': ctx.branch(),
-                   'status': status,
-                   'node': ctx.hex(),
-                   'date': ctx.date()}
-
+    entries = webutil.branchentries(web.repo, web.stripecount)
+    latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
     return tmpl('branches', node=hex(web.repo.changelog.tip()),
-                entries=lambda **x: entries(0, **x),
-                latestentry=lambda **x: entries(1, **x))
+                entries=entries, latestentry=latestentry)
 
 @webcommand('summary')
 def summary(web, req, tmpl):
@@ -710,18 +685,6 @@
                    'date': web.repo[n].date(),
                    'node': hex(n)}
 
-    def branches(**map):
-        parity = paritygen(web.stripecount)
-
-        b = web.repo.branchmap()
-        l = [(-web.repo.changelog.rev(tip), tip, tag)
-             for tag, heads, tip, closed in b.iterbranches()]
-        for r, n, t in sorted(l):
-            yield {'parity': parity.next(),
-                   'branch': t,
-                   'node': hex(n),
-                   'date': web.repo[n].date()}
-
     def changelist(**map):
         parity = paritygen(web.stripecount, offset=start - end)
         l = [] # build a list in forward order for efficiency
@@ -761,7 +724,7 @@
                 lastchange=tip.date(),
                 tags=tagentries,
                 bookmarks=bookmarks,
-                branches=branches,
+                branches=webutil.branchentries(web.repo, web.stripecount, 10),
                 shortlog=changelist,
                 node=tip.hex(),
                 symrev='tip',
@@ -1115,7 +1078,7 @@
                 raise ErrorResponse(HTTP_NOT_FOUND,
                     'file(s) not found: %s' % file[0])
 
-    mimetype, artype, extension, encoding = web.archive_specs[type_]
+    mimetype, artype, extension, encoding = web.archivespecs[type_]
     headers = [
         ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
         ]
--- a/mercurial/hgweb/webutil.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/hgweb/webutil.py	Thu Sep 03 18:32:17 2015 -0500
@@ -7,6 +7,7 @@
 # GNU General Public License version 2 or any later version.
 
 import os, copy
+import re
 from mercurial import match, patch, error, ui, util, pathutil, context
 from mercurial.i18n import _
 from mercurial.node import hex, nullid, short
@@ -199,11 +200,42 @@
     for t in repo.nodebookmarks(node):
         yield tmpl(t1, bookmark=t, **args)
 
+def branchentries(repo, stripecount, limit=0):
+    tips = []
+    heads = repo.heads()
+    parity = paritygen(stripecount)
+    sortkey = lambda item: (not item[1], item[0].rev())
+
+    def entries(**map):
+        count = 0
+        if not tips:
+            for tag, hs, tip, closed in repo.branchmap().iterbranches():
+                tips.append((repo[tip], closed))
+        for ctx, closed in sorted(tips, key=sortkey, reverse=True):
+            if limit > 0 and count >= limit:
+                return
+            count += 1
+            if closed:
+                status = 'closed'
+            elif ctx.node() not in heads:
+                status = 'inactive'
+            else:
+                status = 'open'
+            yield {
+                'parity': parity.next(),
+                'branch': ctx.branch(),
+                'status': status,
+                'node': ctx.hex(),
+                'date': ctx.date()
+            }
+
+    return entries
+
 def cleanpath(repo, path):
     path = path.lstrip('/')
     return pathutil.canonpath(repo.root, '', path)
 
-def changeidctx (repo, changeid):
+def changeidctx(repo, changeid):
     try:
         ctx = repo[changeid]
     except error.RepoError:
@@ -212,11 +244,11 @@
 
     return ctx
 
-def changectx (repo, req):
+def changectx(repo, req):
     changeid = "tip"
     if 'node' in req.form:
         changeid = req.form['node'][0]
-        ipos=changeid.find(':')
+        ipos = changeid.find(':')
         if ipos != -1:
             changeid = changeid[(ipos + 1):]
     elif 'manifest' in req.form:
@@ -227,7 +259,7 @@
 def basechangectx(repo, req):
     if 'node' in req.form:
         changeid = req.form['node'][0]
-        ipos=changeid.find(':')
+        ipos = changeid.find(':')
         if ipos != -1:
             changeid = changeid[:ipos]
             return changeidctx(repo, changeid)
@@ -509,3 +541,44 @@
     # default termwidth breaks under mod_wsgi
     def termwidth(self):
         return 80
+
+def getwebsubs(repo):
+    websubtable = []
+    websubdefs = repo.ui.configitems('websub')
+    # we must maintain interhg backwards compatibility
+    websubdefs += repo.ui.configitems('interhg')
+    for key, pattern in websubdefs:
+        # grab the delimiter from the character after the "s"
+        unesc = pattern[1]
+        delim = re.escape(unesc)
+
+        # identify portions of the pattern, taking care to avoid escaped
+        # delimiters. the replace format and flags are optional, but
+        # delimiters are required.
+        match = re.match(
+            r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
+            % (delim, delim, delim), pattern)
+        if not match:
+            repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
+                              % (key, pattern))
+            continue
+
+        # we need to unescape the delimiter for regexp and format
+        delim_re = re.compile(r'(?<!\\)\\%s' % delim)
+        regexp = delim_re.sub(unesc, match.group(1))
+        format = delim_re.sub(unesc, match.group(2))
+
+        # the pattern allows for 6 regexp flags, so set them if necessary
+        flagin = match.group(3)
+        flags = 0
+        if flagin:
+            for flag in flagin.upper():
+                flags |= re.__dict__[flag]
+
+        try:
+            regexp = re.compile(regexp, flags)
+            websubtable.append((regexp, format))
+        except re.error:
+            repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
+                         % (key, regexp))
+    return websubtable
--- a/mercurial/hook.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/hook.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,19 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import os, sys, time
-import extensions, util, demandimport, error
+from __future__ import absolute_import
+
+import os
+import sys
+import time
+
+from .i18n import _
+from . import (
+    demandimport,
+    error,
+    extensions,
+    util,
+)
 
 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
     '''call python hook. hook is callable object, looked up as
--- a/mercurial/httppeer.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/httppeer.py	Thu Sep 03 18:32:17 2015 -0500
@@ -6,12 +6,28 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from node import nullid
-from i18n import _
+from __future__ import absolute_import
+
+import errno
+import httplib
+import os
+import socket
 import tempfile
-import changegroup, statichttprepo, error, httpconnection, url, util, wireproto
-import os, urllib, urllib2, zlib, httplib
-import errno, socket
+import urllib
+import urllib2
+import zlib
+
+from .i18n import _
+from .node import nullid
+from . import (
+    changegroup,
+    error,
+    httpconnection,
+    statichttprepo,
+    url,
+    util,
+    wireproto,
+)
 
 def zgenerator(f):
     zd = zlib.decompressobj()
--- a/mercurial/i18n.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/i18n.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,8 +5,14 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import encoding
-import gettext as gettextmod, sys, os, locale
+from __future__ import absolute_import
+
+import gettext as gettextmod
+import locale
+import os
+import sys
+
+from . import encoding
 
 # modelled after templater.templatepath:
 if getattr(sys, 'frozen', None) is not None:
--- a/mercurial/localrepo.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/localrepo.py	Thu Sep 03 18:32:17 2015 -0500
@@ -300,6 +300,7 @@
         if create:
             self._writerequirements()
 
+        self._dirstatevalidatewarned = False
 
         self._branchcaches = {}
         self._revbranchcache = None
@@ -354,6 +355,10 @@
         manifestcachesize = self.ui.configint('format', 'manifestcachesize')
         if manifestcachesize is not None:
             self.svfs.options['manifestcachesize'] = manifestcachesize
+        # experimental config: format.aggressivemergedeltas
+        aggressivemergedeltas = self.ui.configbool('format',
+            'aggressivemergedeltas', False)
+        self.svfs.options['aggressivemergedeltas'] = aggressivemergedeltas
 
     def _writerequirements(self):
         scmutil.writerequires(self.vfs, self.requirements)
@@ -469,19 +474,19 @@
 
     @repofilecache('dirstate')
     def dirstate(self):
-        warned = [0]
-        def validate(node):
-            try:
-                self.changelog.rev(node)
-                return node
-            except error.LookupError:
-                if not warned[0]:
-                    warned[0] = True
-                    self.ui.warn(_("warning: ignoring unknown"
-                                   " working parent %s!\n") % short(node))
-                return nullid
+        return dirstate.dirstate(self.vfs, self.ui, self.root,
+                                 self._dirstatevalidate)
 
-        return dirstate.dirstate(self.vfs, self.ui, self.root, validate)
+    def _dirstatevalidate(self, node):
+        try:
+            self.changelog.rev(node)
+            return node
+        except error.LookupError:
+            if not self._dirstatevalidatewarned:
+                self._dirstatevalidatewarned = True
+                self.ui.warn(_("warning: ignoring unknown"
+                               " working parent %s!\n") % short(node))
+            return nullid
 
     def __getitem__(self, changeid):
         if changeid is None or changeid == wdirrev:
--- a/mercurial/lock.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/lock.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,10 +5,19 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import util, error
-import errno, os, socket, time
+from __future__ import absolute_import
+
+import errno
+import os
+import socket
+import time
 import warnings
 
+from . import (
+    error,
+    util,
+)
+
 class lock(object):
     '''An advisory lock held by one process to control access to a set
     of files.  Non-cooperating processes or incorrectly written scripts
@@ -56,7 +65,7 @@
         timeout = self.timeout
         while True:
             try:
-                self.trylock()
+                self._trylock()
                 return self.timeout - timeout
             except error.LockHeld as inst:
                 if timeout != 0:
@@ -67,14 +76,16 @@
                 raise error.LockHeld(errno.ETIMEDOUT, inst.filename, self.desc,
                                      inst.locker)
 
-    def trylock(self):
+    def _trylock(self):
         if self.held:
             self.held += 1
             return
         if lock._host is None:
             lock._host = socket.gethostname()
         lockname = '%s:%s' % (lock._host, self.pid)
-        while not self.held:
+        retry = 5
+        while not self.held and retry:
+            retry -= 1
             try:
                 self.vfs.makelock(lockname, self.f)
                 self.held = 1
--- a/mercurial/mail.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/mail.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,10 +5,22 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import util, encoding, sslutil
-import os, smtplib, socket, quopri, time, sys
+from __future__ import absolute_import
+
 import email
+import os
+import quopri
+import smtplib
+import socket
+import sys
+import time
+
+from .i18n import _
+from . import (
+    encoding,
+    sslutil,
+    util,
+)
 
 _oldheaderinit = email.Header.Header.__init__
 def _unifiedheaderinit(self, *args, **kw):
@@ -19,10 +31,10 @@
     constructor, and 2.7 removed this parameter.
 
     Default argument is continuation_ws=' ', which means that the
-    behaviour is different in <2.7 and 2.7
+    behavior is different in <2.7 and 2.7
 
-    We consider the 2.7 behaviour to be preferable, but need
-    to have an unified behaviour for versions 2.4 to 2.7
+    We consider the 2.7 behavior to be preferable, but need
+    to have an unified behavior for versions 2.4 to 2.7
     """
     # override continuation_ws
     kw['continuation_ws'] = ' '
--- a/mercurial/match.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/match.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,17 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import copy, os, re
-import util, pathutil
-from i18n import _
+from __future__ import absolute_import
+
+import copy
+import os
+import re
+
+from .i18n import _
+from . import (
+    pathutil,
+    util,
+)
 
 propertycache = util.propertycache
 
--- a/mercurial/merge.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/merge.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,13 +5,29 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
+import errno
+import os
+import shutil
 import struct
 
-from node import nullid, nullrev, hex, bin
-from i18n import _
-from mercurial import obsolete
-import error as errormod, util, filemerge, copies, subrepo, worker
-import errno, os, shutil
+from .i18n import _
+from .node import (
+    bin,
+    hex,
+    nullid,
+    nullrev,
+)
+from . import (
+    copies,
+    error as errormod,
+    filemerge,
+    obsolete,
+    subrepo,
+    util,
+    worker,
+)
 
 _pack = struct.pack
 _unpack = struct.unpack
--- a/mercurial/minirst.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/minirst.py	Thu Sep 03 18:32:17 2015 -0500
@@ -18,11 +18,16 @@
 when adding support for new constructs.
 """
 
-import re
-import util, encoding
-from i18n import _
+from __future__ import absolute_import
 
 import cgi
+import re
+
+from .i18n import _
+from . import (
+    encoding,
+    util,
+)
 
 def section(s):
     return "%s\n%s\n\n" % (s, "\"" * encoding.colwidth(s))
@@ -651,13 +656,17 @@
 def format(text, width=80, indent=0, keep=None, style='plain', section=None):
     """Parse and format the text according to width."""
     blocks, pruned = parse(text, indent, keep or [])
+    parents = []
     if section:
         sections = getsections(blocks)
         blocks = []
         i = 0
         while i < len(sections):
             name, nest, b = sections[i]
+            del parents[nest:]
+            parents.append(name)
             if name == section:
+                b[0]['path'] = parents[3:]
                 blocks.extend(b)
 
                 ## Also show all subnested sections
@@ -669,6 +678,14 @@
     if style == 'html':
         text = formathtml(blocks)
     else:
+        if len([b for b in blocks if b['type'] == 'definition']) > 1:
+            i = 0
+            while i < len(blocks):
+                if blocks[i]['type'] == 'definition':
+                    if 'path' in blocks[i]:
+                        blocks[i]['lines'][0] = '"%s"' % '.'.join(
+                            blocks[i]['path'])
+                i += 1
         text = ''.join(formatblock(b, width) for b in blocks)
     if keep is None:
         return text
@@ -710,6 +727,21 @@
             if not secs:
                 # add an initial empty section
                 secs = [('', 0, [])]
+            if b['type'] != 'margin':
+                pointer = 1
+                bindent = b['indent']
+                while pointer < len(secs):
+                    section = secs[-pointer][2][0]
+                    if section['type'] != 'margin':
+                        sindent = section['indent']
+                        if len(section['lines']) > 1:
+                            sindent += len(section['lines'][1]) - \
+                              len(section['lines'][1].lstrip(' '))
+                        if bindent >= sindent:
+                            break
+                    pointer += 1
+                if pointer > 1:
+                    secs.append(('', secs[-pointer][1] + 1, []))
             secs[-1][2].append(b)
     return secs
 
--- a/mercurial/namespaces.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/namespaces.py	Thu Sep 03 18:32:17 2015 -0500
@@ -1,6 +1,10 @@
-from i18n import _
-from mercurial import util
-import templatekw
+from __future__ import absolute_import
+
+from .i18n import _
+from . import (
+    templatekw,
+    util,
+)
 
 def tolist(val):
     """
--- a/mercurial/node.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/node.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,6 +5,8 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import binascii
 
 nullrev = -1
--- a/mercurial/parser.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/parser.py	Thu Sep 03 18:32:17 2015 -0500
@@ -16,8 +16,10 @@
 # an action is a tree node name, a tree label, and an optional match
 # __call__(program) parses program into a labeled tree
 
-import error
-from i18n import _
+from __future__ import absolute_import
+
+from .i18n import _
+from . import error
 
 class parser(object):
     def __init__(self, elements, methods=None):
--- a/mercurial/parsers.c	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/parsers.c	Thu Sep 03 18:32:17 2015 -0500
@@ -253,8 +253,11 @@
 
 			if (normed == NULL)
 				goto quit;
-			if (PyDict_SetItem(file_foldmap, normed, k) == -1)
+			if (PyDict_SetItem(file_foldmap, normed, k) == -1) {
+				Py_DECREF(normed);
 				goto quit;
+			}
+			Py_DECREF(normed);
 		}
 	}
 	return file_foldmap;
@@ -475,14 +478,14 @@
 			      &str, &readlen))
 		goto quit;
 
-	if (readlen < 0)
-		goto quit;
-
 	len = readlen;
 
 	/* read parents */
-	if (len < 40)
+	if (len < 40) {
+		PyErr_SetString(
+			PyExc_ValueError, "too little data for parents");
 		goto quit;
+	}
 
 	parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
 	if (!parents)
@@ -679,7 +682,7 @@
 } nodetree;
 
 /*
- * This class has two behaviours.
+ * This class has two behaviors.
  *
  * When used in a list-like way (with integer keys), we decode an
  * entry in a RevlogNG index file on demand. Our last entry is a
@@ -702,8 +705,8 @@
 	PyObject *headrevs;    /* cache, invalidated on changes */
 	PyObject *filteredrevs;/* filtered revs set */
 	nodetree *nt;          /* base-16 trie */
-	int ntlength;          /* # nodes in use */
-	int ntcapacity;        /* # nodes allocated */
+	unsigned ntlength;          /* # nodes in use */
+	unsigned ntcapacity;        /* # nodes allocated */
 	int ntdepth;           /* maximum depth of tree */
 	int ntsplits;          /* # splits performed */
 	int ntrev;             /* last rev scanned */
@@ -1043,13 +1046,12 @@
 	return newlist;
 }
 
-/* arg should be Py_ssize_t but Python 2.4 do not support the n format */
-static int check_filter(PyObject *filter, unsigned long arg) {
+static int check_filter(PyObject *filter, Py_ssize_t arg) {
 	if (filter) {
 		PyObject *arglist, *result;
 		int isfiltered;
 
-		arglist = Py_BuildValue("(k)", arg);
+		arglist = Py_BuildValue("(n)", arg);
 		if (!arglist) {
 			return -1;
 		}
@@ -1105,6 +1107,162 @@
 		phases[i] = phases[parent_2];
 }
 
+static PyObject *reachableroots2(indexObject *self, PyObject *args)
+{
+
+	/* Input */
+	long minroot;
+	PyObject *includepatharg = NULL;
+	int includepath = 0;
+	/* heads and roots are lists */
+	PyObject *heads = NULL;
+	PyObject *roots = NULL;
+	PyObject *reachable = NULL;
+
+	PyObject *val;
+	Py_ssize_t len = index_length(self) - 1;
+	long revnum;
+	Py_ssize_t k;
+	Py_ssize_t i;
+	Py_ssize_t l;
+	int r;
+	int parents[2];
+
+	/* Internal data structure:
+	 * tovisit: array of length len+1 (all revs + nullrev), filled upto lentovisit
+	 * revstates: array of length len+1 (all revs + nullrev) */
+	int *tovisit = NULL;
+	long lentovisit = 0;
+	enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 };
+	char *revstates = NULL;
+
+	/* Get arguments */
+	if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads,
+			      &PyList_Type, &roots,
+			      &PyBool_Type, &includepatharg))
+		goto bail;
+
+	if (includepatharg == Py_True)
+		includepath = 1;
+
+	/* Initialize return set */
+	reachable = PyList_New(0);
+	if (reachable == NULL)
+		goto bail;
+
+	/* Initialize internal datastructures */
+	tovisit = (int *)malloc((len + 1) * sizeof(int));
+	if (tovisit == NULL) {
+		PyErr_NoMemory();
+		goto bail;
+	}
+
+	revstates = (char *)calloc(len + 1, 1);
+	if (revstates == NULL) {
+		PyErr_NoMemory();
+		goto bail;
+	}
+
+	l = PyList_GET_SIZE(roots);
+	for (i = 0; i < l; i++) {
+		revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i));
+		if (revnum == -1 && PyErr_Occurred())
+			goto bail;
+		/* If root is out of range, e.g. wdir(), it must be unreachable
+		 * from heads. So we can just ignore it. */
+		if (revnum + 1 < 0 || revnum + 1 >= len + 1)
+			continue;
+		revstates[revnum + 1] |= RS_ROOT;
+	}
+
+	/* Populate tovisit with all the heads */
+	l = PyList_GET_SIZE(heads);
+	for (i = 0; i < l; i++) {
+		revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i));
+		if (revnum == -1 && PyErr_Occurred())
+			goto bail;
+		if (revnum + 1 < 0 || revnum + 1 >= len + 1) {
+			PyErr_SetString(PyExc_IndexError, "head out of range");
+			goto bail;
+		}
+		if (!(revstates[revnum + 1] & RS_SEEN)) {
+			tovisit[lentovisit++] = (int)revnum;
+			revstates[revnum + 1] |= RS_SEEN;
+		}
+	}
+
+	/* Visit the tovisit list and find the reachable roots */
+	k = 0;
+	while (k < lentovisit) {
+		/* Add the node to reachable if it is a root*/
+		revnum = tovisit[k++];
+		if (revstates[revnum + 1] & RS_ROOT) {
+			revstates[revnum + 1] |= RS_REACHABLE;
+			val = PyInt_FromLong(revnum);
+			if (val == NULL)
+				goto bail;
+			r = PyList_Append(reachable, val);
+			Py_DECREF(val);
+			if (r < 0)
+				goto bail;
+			if (includepath == 0)
+				continue;
+		}
+
+		/* Add its parents to the list of nodes to visit */
+		if (revnum == -1)
+			continue;
+		r = index_get_parents(self, revnum, parents, (int)len - 1);
+		if (r < 0)
+			goto bail;
+		for (i = 0; i < 2; i++) {
+			if (!(revstates[parents[i] + 1] & RS_SEEN)
+			    && parents[i] >= minroot) {
+				tovisit[lentovisit++] = parents[i];
+				revstates[parents[i] + 1] |= RS_SEEN;
+			}
+		}
+	}
+
+	/* Find all the nodes in between the roots we found and the heads
+	 * and add them to the reachable set */
+	if (includepath == 1) {
+		long minidx = minroot;
+		if (minidx < 0)
+			minidx = 0;
+		for (i = minidx; i < len; i++) {
+			if (!(revstates[i + 1] & RS_SEEN))
+				continue;
+			r = index_get_parents(self, i, parents, (int)len - 1);
+			/* Corrupted index file, error is set from
+			 * index_get_parents */
+			if (r < 0)
+				goto bail;
+			if (((revstates[parents[0] + 1] |
+			      revstates[parents[1] + 1]) & RS_REACHABLE)
+			    && !(revstates[i + 1] & RS_REACHABLE)) {
+				revstates[i + 1] |= RS_REACHABLE;
+				val = PyInt_FromLong(i);
+				if (val == NULL)
+					goto bail;
+				r = PyList_Append(reachable, val);
+				Py_DECREF(val);
+				if (r < 0)
+					goto bail;
+			}
+		}
+	}
+
+	free(revstates);
+	free(tovisit);
+	return reachable;
+bail:
+	Py_XDECREF(reachable);
+	free(revstates);
+	free(tovisit);
+	return NULL;
+}
+
 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
 {
 	PyObject *roots = Py_None;
@@ -2011,16 +2169,18 @@
  */
 static PyObject *index_ancestors(indexObject *self, PyObject *args)
 {
+	PyObject *ret;
 	PyObject *gca = index_commonancestorsheads(self, args);
 	if (gca == NULL)
 		return NULL;
 
 	if (PyList_GET_SIZE(gca) <= 1) {
-		Py_INCREF(gca);
 		return gca;
 	}
 
-	return find_deepest(self, gca);
+	ret = find_deepest(self, gca);
+	Py_DECREF(gca);
+	return ret;
 }
 
 /*
@@ -2282,6 +2442,8 @@
 	 "get an index entry"},
 	{"computephasesmapsets", (PyCFunction)compute_phases_map_sets,
 			METH_VARARGS, "compute phases"},
+	{"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS,
+		"reachableroots"},
 	{"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
 	 "get head revisions"}, /* Can do filtering since 3.2 */
 	{"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
@@ -2502,12 +2664,10 @@
 static PyObject *fm1readmarkers(PyObject *self, PyObject *args) {
 	const char *data;
 	Py_ssize_t datalen;
-	/* only unsigned long because python 2.4, should be Py_ssize_t */
-	unsigned long offset, stop;
+	Py_ssize_t offset, stop;
 	PyObject *markers = NULL;
 
-	/* replace kk with nn when we drop Python 2.4 */
-	if (!PyArg_ParseTuple(args, "s#kk", &data, &datalen, &offset, &stop)) {
+	if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) {
 		return NULL;
 	}
 	data += offset;
--- a/mercurial/pathencode.c	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/pathencode.c	Thu Sep 03 18:32:17 2015 -0500
@@ -684,6 +684,8 @@
 
 	hashobj = PyObject_CallMethod(shaobj, "digest", "");
 	Py_DECREF(shaobj);
+	if (hashobj == NULL)
+		return -1;
 
 	if (!PyString_Check(hashobj) || PyString_GET_SIZE(hashobj) != 20) {
 		PyErr_SetString(PyExc_TypeError,
--- a/mercurial/pathutil.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/pathutil.py	Thu Sep 03 18:32:17 2015 -0500
@@ -1,8 +1,15 @@
-import os, errno, stat, posixpath
+from __future__ import absolute_import
 
-import encoding
-import util
-from i18n import _
+import errno
+import os
+import posixpath
+import stat
+
+from .i18n import _
+from . import (
+    encoding,
+    util,
+)
 
 def _lowerclean(s):
     return encoding.hfsignoreclean(s.lower())
--- a/mercurial/peer.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/peer.py	Thu Sep 03 18:32:17 2015 -0500
@@ -6,11 +6,91 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import error
+from __future__ import absolute_import
+
+from .i18n import _
+from . import (
+    error,
+    util,
+)
+
+# abstract batching support
+
+class future(object):
+    '''placeholder for a value to be set later'''
+    def set(self, value):
+        if util.safehasattr(self, 'value'):
+            raise error.RepoError("future is already set")
+        self.value = value
+
+class batcher(object):
+    '''base class for batches of commands submittable in a single request
+
+    All methods invoked on instances of this class are simply queued and
+    return a a future for the result. Once you call submit(), all the queued
+    calls are performed and the results set in their respective futures.
+    '''
+    def __init__(self):
+        self.calls = []
+    def __getattr__(self, name):
+        def call(*args, **opts):
+            resref = future()
+            self.calls.append((name, args, opts, resref,))
+            return resref
+        return call
+    def submit(self):
+        pass
+
+class localbatch(batcher):
+    '''performs the queued calls directly'''
+    def __init__(self, local):
+        batcher.__init__(self)
+        self.local = local
+    def submit(self):
+        for name, args, opts, resref in self.calls:
+            resref.set(getattr(self.local, name)(*args, **opts))
+
+def batchable(f):
+    '''annotation for batchable methods
+
+    Such methods must implement a coroutine as follows:
+
+    @batchable
+    def sample(self, one, two=None):
+        # Handle locally computable results first:
+        if not one:
+            yield "a local result", None
+        # Build list of encoded arguments suitable for your wire protocol:
+        encargs = [('one', encode(one),), ('two', encode(two),)]
+        # Create future for injection of encoded result:
+        encresref = future()
+        # Return encoded arguments and future:
+        yield encargs, encresref
+        # Assuming the future to be filled with the result from the batched
+        # request now. Decode it:
+        yield decode(encresref.value)
+
+    The decorator returns a function which wraps this coroutine as a plain
+    method, but adds the original method as an attribute called "batchable",
+    which is used by remotebatch to split the call into separate encoding and
+    decoding phases.
+    '''
+    def plain(*args, **opts):
+        batchable = f(*args, **opts)
+        encargsorres, encresref = batchable.next()
+        if not encresref:
+            return encargsorres # a local result in this case
+        self = args[0]
+        encresref.set(self._submitone(f.func_name, encargsorres))
+        return batchable.next()
+    setattr(plain, 'batchable', f)
+    return plain
 
 class peerrepository(object):
 
+    def batch(self):
+        return localbatch(self)
+
     def capable(self, name):
         '''tell whether repo supports named capability.
         return False if not supported.
--- a/mercurial/phases.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/phases.py	Thu Sep 03 18:32:17 2015 -0500
@@ -100,11 +100,23 @@
 
 """
 
-import os
+from __future__ import absolute_import
+
 import errno
-from node import nullid, nullrev, bin, hex, short
-from i18n import _
-import util, error
+import os
+
+from .i18n import _
+from .node import (
+    bin,
+    hex,
+    nullid,
+    nullrev,
+    short,
+)
+from . import (
+    error,
+    util,
+)
 
 allphases = public, draft, secret = range(3)
 trackedphases = allphases[1:]
--- a/mercurial/posix.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/posix.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,11 +5,26 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import encoding
-import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata
+from __future__ import absolute_import
+
+import errno
+import fcntl
+import getpass
+import grp
+import os
+import pwd
+import re
 import select
-import fcntl, re
+import socket
+import stat
+import sys
+import tempfile
+import unicodedata
+
+from .i18n import _
+from . import (
+    encoding,
+)
 
 posixfile = open
 normpath = os.path.normpath
@@ -459,7 +474,8 @@
 
 def termwidth():
     try:
-        import termios, array
+        import array
+        import termios
         for dev in (sys.stderr, sys.stdout, sys.stdin):
             try:
                 try:
--- a/mercurial/progress.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/progress.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,13 +5,14 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import sys
-import time
 import threading
-from mercurial import encoding
+import time
 
-from mercurial.i18n import _
-
+from .i18n import _
+from . import encoding
 
 def spacejoin(*args):
     return ' '.join(s for s in args if s)
--- a/mercurial/pushkey.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/pushkey.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,7 +5,14 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import bookmarks, phases, obsolete, encoding
+from __future__ import absolute_import
+
+from . import (
+    bookmarks,
+    encoding,
+    obsolete,
+    phases,
+)
 
 def _nslist(repo):
     n = {}
--- a/mercurial/repair.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/repair.py	Thu Sep 03 18:32:17 2015 -0500
@@ -6,11 +6,19 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from mercurial import changegroup, exchange, util, bundle2
-from mercurial.node import short
-from mercurial.i18n import _
+from __future__ import absolute_import
+
 import errno
 
+from .i18n import _
+from .node import short
+from . import (
+    bundle2,
+    changegroup,
+    exchange,
+    util,
+)
+
 def _bundle(repo, bases, heads, node, suffix, compress=True):
     """create a bundle with the specified revisions as a backup"""
     usebundle2 = (repo.ui.configbool('experimental', 'bundle2-exp', True) and
--- a/mercurial/repoview.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/repoview.py	Thu Sep 03 18:32:17 2015 -0500
@@ -6,15 +6,20 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import heapq
+from __future__ import absolute_import
+
 import copy
-import error
-import phases
-import util
-import obsolete
+import heapq
 import struct
-import tags as tagsmod
-from node import nullrev
+
+from .node import nullrev
+from . import (
+    error,
+    obsolete,
+    phases,
+    tags as tagsmod,
+    util,
+)
 
 def hideablerevs(repo):
     """Revisions candidates to be hidden
--- a/mercurial/revlog.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/revlog.py	Thu Sep 03 18:32:17 2015 -0500
@@ -100,11 +100,10 @@
 #  4 bytes: compressed length
 #  4 bytes: base rev
 #  4 bytes: link rev
-# 32 bytes: parent 1 nodeid
-# 32 bytes: parent 2 nodeid
-# 32 bytes: nodeid
+# 20 bytes: parent 1 nodeid
+# 20 bytes: parent 2 nodeid
+# 20 bytes: nodeid
 indexformatv0 = ">4l20s20s20s"
-v0shaoffset = 56
 
 class revlogoldio(object):
     def __init__(self):
@@ -150,7 +149,6 @@
 #  4 bytes: parent 2 rev
 # 32 bytes: nodeid
 indexformatng = ">Qiiiiii20s12x"
-ngshaoffset = 32
 versionformat = ">I"
 
 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
@@ -212,6 +210,7 @@
         self._chunkcache = (0, '')
         self._chunkcachesize = 65536
         self._maxchainlen = None
+        self._aggressivemergedeltas = False
         self.index = []
         self._pcache = {}
         self._nodecache = {nullid: nullrev}
@@ -229,6 +228,8 @@
                 self._chunkcachesize = opts['chunkcachesize']
             if 'maxchainlen' in opts:
                 self._maxchainlen = opts['maxchainlen']
+            if 'aggressivemergedeltas' in opts:
+                self._aggressivemergedeltas = opts['aggressivemergedeltas']
 
         if self._chunkcachesize <= 0:
             raise RevlogError(_('revlog chunk cache size %r is not greater '
@@ -1235,6 +1236,25 @@
             return ('u', text)
         return ("", bin)
 
+    def _isgooddelta(self, d, textlen):
+        """Returns True if the given delta is good. Good means that it is within
+        the disk span, disk size, and chain length bounds that we know to be
+        performant."""
+        if d is None:
+            return False
+
+        # - 'dist' is the distance from the base revision -- bounding it limits
+        #   the amount of I/O we need to do.
+        # - 'compresseddeltalen' is the sum of the total size of deltas we need
+        #   to apply -- bounding it limits the amount of CPU we consume.
+        dist, l, data, base, chainbase, chainlen, compresseddeltalen = d
+        if (dist > textlen * 4 or l > textlen or
+            compresseddeltalen > textlen * 2 or
+            (self._maxchainlen and chainlen > self._maxchainlen)):
+            return False
+
+        return True
+
     def _addrevision(self, node, text, transaction, link, p1, p2, flags,
                      cachedelta, ifh, dfh):
         """internal function to add revisions to the log
@@ -1315,19 +1335,6 @@
         basecache = self._basecache
         p1r, p2r = self.rev(p1), self.rev(p2)
 
-        # should we try to build a delta?
-        if prev != nullrev:
-            if self._generaldelta:
-                if p1r >= basecache[1]:
-                    d = builddelta(p1r)
-                elif p2r >= basecache[1]:
-                    d = builddelta(p2r)
-                else:
-                    d = builddelta(prev)
-            else:
-                d = builddelta(prev)
-            dist, l, data, base, chainbase, chainlen, compresseddeltalen = d
-
         # full versions are inserted when the needed deltas
         # become comparable to the uncompressed text
         if text is None:
@@ -1336,13 +1343,42 @@
         else:
             textlen = len(text)
 
-        # - 'dist' is the distance from the base revision -- bounding it limits
-        #   the amount of I/O we need to do.
-        # - 'compresseddeltalen' is the sum of the total size of deltas we need
-        #   to apply -- bounding it limits the amount of CPU we consume.
-        if (d is None or dist > textlen * 4 or l > textlen or
-            compresseddeltalen > textlen * 2 or
-            (self._maxchainlen and chainlen > self._maxchainlen)):
+        # should we try to build a delta?
+        if prev != nullrev:
+            if self._generaldelta:
+                if p2r != nullrev and self._aggressivemergedeltas:
+                    d = builddelta(p1r)
+                    d2 = builddelta(p2r)
+                    p1good = self._isgooddelta(d, textlen)
+                    p2good = self._isgooddelta(d2, textlen)
+                    if p1good and p2good:
+                        # If both are good deltas, choose the smallest
+                        if d2[1] < d[1]:
+                            d = d2
+                    elif p2good:
+                        # If only p2 is good, use it
+                        d = d2
+                    elif p1good:
+                        pass
+                    else:
+                        # Neither is good, try against prev to hopefully save us
+                        # a fulltext.
+                        d = builddelta(prev)
+                else:
+                    # Pick whichever parent is closer to us (to minimize the
+                    # chance of having to build a fulltext). Since
+                    # nullrev == -1, any non-merge commit will always pick p1r.
+                    drev = p2r if p2r > p1r else p1r
+                    d = builddelta(drev)
+                    # If the chosen delta will result in us making a full text,
+                    # give it one last try against prev.
+                    if drev != prev and not self._isgooddelta(d, textlen):
+                        d = builddelta(prev)
+            else:
+                d = builddelta(prev)
+            dist, l, data, base, chainbase, chainlen, compresseddeltalen = d
+
+        if not self._isgooddelta(d, textlen):
             text = buildtext()
             data = self.compress(text)
             l = len(data[1]) + len(data[0])
--- a/mercurial/revset.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/revset.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,16 +5,25 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import re
-import parser, util, error, hbisect, phases
-import node
+from __future__ import absolute_import
+
 import heapq
-import match as matchmod
-from i18n import _
-import encoding
-import obsolete as obsmod
-import pathutil
-import repoview
+import re
+
+from .i18n import _
+from . import (
+    encoding,
+    error,
+    hbisect,
+    match as matchmod,
+    node,
+    obsolete as obsmod,
+    parser,
+    pathutil,
+    phases,
+    repoview,
+    util,
+)
 
 def _revancestors(repo, revs, followfirst):
     """Like revlog.ancestors(), but supports followfirst."""
@@ -78,19 +87,17 @@
 
     return generatorset(iterate(), iterasc=True)
 
-def _revsbetween(repo, roots, heads):
-    """Return all paths between roots and heads, inclusive of both endpoint
-    sets."""
+def _reachablerootspure(repo, minroot, roots, heads, includepath):
+    """return (heads(::<roots> and ::<heads>))
+
+    If includepath is True, return (<roots>::<heads>)."""
     if not roots:
-        return baseset()
+        return []
     parentrevs = repo.changelog.parentrevs
+    roots = set(roots)
     visit = list(heads)
     reachable = set()
     seen = {}
-    # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
-    # (and if it is not, it should.)
-    minroot = min(roots)
-    roots = set(roots)
     # prefetch all the things! (because python is slow)
     reached = reachable.add
     dovisit = visit.append
@@ -101,6 +108,8 @@
         rev = nextvisit()
         if rev in roots:
             reached(rev)
+            if not includepath:
+                continue
         parents = parentrevs(rev)
         seen[rev] = parents
         for parent in parents:
@@ -108,11 +117,30 @@
                 dovisit(parent)
     if not reachable:
         return baseset()
+    if not includepath:
+        return reachable
     for rev in sorted(seen):
         for parent in seen[rev]:
             if parent in reachable:
                 reached(rev)
-    return baseset(sorted(reachable))
+    return reachable
+
+def reachableroots(repo, roots, heads, includepath=False):
+    """return (heads(::<roots> and ::<heads>))
+
+    If includepath is True, return (<roots>::<heads>)."""
+    if not roots:
+        return baseset()
+    minroot = roots.min()
+    roots = list(roots)
+    heads = list(heads)
+    try:
+        revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
+    except AttributeError:
+        revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
+    revs = baseset(revs)
+    revs.sort()
+    return revs
 
 elements = {
     # token-type: binding-strength, primary, prefix, infix, suffix
@@ -178,6 +206,21 @@
     if symletters is None:
         symletters = _symletters
 
+    if program and lookup:
+        # attempt to parse old-style ranges first to deal with
+        # things like old-tag which contain query metacharacters
+        parts = program.split(':', 1)
+        if all(lookup(sym) for sym in parts if sym):
+            if parts[0]:
+                yield ('symbol', parts[0], 0)
+            if len(parts) > 1:
+                s = len(parts[0])
+                yield (':', None, s)
+                if parts[1]:
+                    yield ('symbol', parts[1], s + 1)
+            yield ('end', None, len(program))
+            return
+
     pos, l = 0, len(program)
     while pos < l:
         c = program[pos]
@@ -382,7 +425,8 @@
 
 def dagrange(repo, subset, x, y):
     r = fullreposet(repo)
-    xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
+    xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
+                         includepath=True)
     # XXX We should combine with subset first: 'subset & baseset(...)'. This is
     # necessary to ensure we preserve the order in subset.
     return xs & subset
@@ -391,8 +435,13 @@
     return getset(repo, getset(repo, subset, x), y)
 
 def orset(repo, subset, *xs):
-    rs = [getset(repo, subset, x) for x in xs]
-    return _combinesets(rs)
+    assert xs
+    if len(xs) == 1:
+        return getset(repo, subset, xs[0])
+    p = len(xs) // 2
+    a = orset(repo, subset, *xs[:p])
+    b = orset(repo, subset, *xs[p:])
+    return a + b
 
 def notset(repo, subset, x):
     return subset - getset(repo, subset, x)
@@ -990,34 +1039,37 @@
     return limit(repo, subset, x)
 
 def _follow(repo, subset, x, name, followfirst=False):
-    l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
+    l = getargs(x, 0, 1, _("%s takes no arguments or a pattern") % name)
     c = repo['.']
     if l:
-        x = getstring(l[0], _("%s expected a filename") % name)
-        if x in c:
-            cx = c[x]
-            s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
-            # include the revision responsible for the most recent version
-            s.add(cx.introrev())
-        else:
-            return baseset()
+        x = getstring(l[0], _("%s expected a pattern") % name)
+        matcher = matchmod.match(repo.root, repo.getcwd(), [x],
+                                 ctx=repo[None], default='path')
+
+        s = set()
+        for fname in c:
+            if matcher(fname):
+                fctx = c[fname]
+                s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
+                # include the revision responsible for the most recent version
+                s.add(fctx.introrev())
     else:
         s = _revancestors(repo, baseset([c.rev()]), followfirst)
 
     return subset & s
 
 def follow(repo, subset, x):
-    """``follow([file])``
+    """``follow([pattern])``
     An alias for ``::.`` (ancestors of the working directory's first parent).
-    If a filename is specified, the history of the given file is followed,
-    including copies.
+    If pattern is specified, the histories of files matching given
+    pattern is followed, including copies.
     """
     return _follow(repo, subset, x, 'follow')
 
 def _followfirst(repo, subset, x):
-    # ``followfirst([file])``
-    # Like ``follow([file])`` but follows only the first parent of
-    # every revision or file revision.
+    # ``followfirst([pattern])``
+    # Like ``follow([pattern])`` but follows only the first parent of
+    # every revisions or files revisions.
     return _follow(repo, subset, x, '_followfirst', followfirst=True)
 
 def getall(repo, subset, x):
@@ -1415,8 +1467,10 @@
     default push location.
     """
     # Avoid cycles.
-    import discovery
-    import hg
+    from . import (
+        discovery,
+        hg,
+    )
     # i18n: "outgoing" is a keyword
     l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
     # i18n: "outgoing" is a keyword
@@ -1597,7 +1651,7 @@
     synonym for the current local branch.
     """
 
-    import hg # avoid start-up nasties
+    from . import hg # avoid start-up nasties
     # i18n: "remote" is a keyword
     l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
 
@@ -2013,14 +2067,17 @@
             r = int(t)
             if str(r) != t or r not in cl:
                 raise ValueError
+            revs = [r]
         except ValueError:
-            r = repo[t].rev()
-        if r in seen:
-            continue
-        if (r in subset
-            or r == node.nullrev and isinstance(subset, fullreposet)):
-            ls.append(r)
-        seen.add(r)
+            revs = stringset(repo, subset, t)
+
+        for r in revs:
+            if r in seen:
+                continue
+            if (r in subset
+                or r == node.nullrev and isinstance(subset, fullreposet)):
+                ls.append(r)
+            seen.add(r)
     return baseset(ls)
 
 # for internal use
@@ -2654,6 +2711,27 @@
     if repo:
         lookup = repo.__contains__
     tree = parse(spec, lookup)
+    return _makematcher(ui, tree, repo)
+
+def matchany(ui, specs, repo=None):
+    """Create a matcher that will include any revisions matching one of the
+    given specs"""
+    if not specs:
+        def mfunc(repo, subset=None):
+            return baseset()
+        return mfunc
+    if not all(specs):
+        raise error.ParseError(_("empty query"))
+    lookup = None
+    if repo:
+        lookup = repo.__contains__
+    if len(specs) == 1:
+        tree = parse(specs[0], lookup)
+    else:
+        tree = ('or',) + tuple(parse(s, lookup) for s in specs)
+    return _makematcher(ui, tree, repo)
+
+def _makematcher(ui, tree, repo):
     if ui:
         tree = findaliases(ui, tree, showwarning=ui.warn)
     tree = foldconcat(tree)
@@ -2813,6 +2891,7 @@
         """True if the set will iterate in descending order"""
         raise NotImplementedError()
 
+    @util.cachefunc
     def min(self):
         """return the minimum element in the set"""
         if self.fastasc is not None:
@@ -2821,6 +2900,7 @@
             raise ValueError('arg is an empty sequence')
         return min(self)
 
+    @util.cachefunc
     def max(self):
         """return the maximum element in the set"""
         if self.fastdesc is not None:
@@ -2896,6 +2976,8 @@
     """
     def __init__(self, data=()):
         if not isinstance(data, list):
+            if isinstance(data, set):
+                self._set = data
             data = list(data)
         self._list = data
         self._ascending = None
@@ -3073,20 +3155,6 @@
     def __repr__(self):
         return '<%s %r>' % (type(self).__name__, self._subset)
 
-# this function will be removed, or merged to addset or orset, when
-# - scmutil.revrange() can be rewritten to not combine calculated smartsets
-# - or addset can handle more than two sets without balanced tree
-def _combinesets(subsets):
-    """Create balanced tree of addsets representing union of given sets"""
-    if not subsets:
-        return baseset()
-    if len(subsets) == 1:
-        return subsets[0]
-    p = len(subsets) // 2
-    xs = _combinesets(subsets[:p])
-    ys = _combinesets(subsets[p:])
-    return addset(xs, ys)
-
 def _iterordered(ascending, iter1, iter2):
     """produce an ordered iteration from two iterators with the same order
 
--- a/mercurial/scmutil.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/scmutil.py	Thu Sep 03 18:32:17 2015 -0500
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 from i18n import _
-from mercurial.node import nullrev, wdirrev
+from mercurial.node import wdirrev
 import util, error, osutil, revset, similar, encoding, phases
 import pathutil
 import match as matchmod
@@ -586,6 +586,8 @@
             raise util.Abort('this vfs is read only')
         return self.vfs(path, mode, *args, **kw)
 
+    def join(self, path, *insidef):
+        return self.vfs.join(path, *insidef)
 
 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
     '''yield every hg repository under path, always recursively.
@@ -690,6 +692,11 @@
         raise util.Abort(_('empty revision set'))
     return repo[l.last()]
 
+def _pairspec(revspec):
+    tree = revset.parse(revspec)
+    tree = revset.optimize(tree, True)[1]  # fix up "x^:y" -> "(x^):y"
+    return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
+
 def revpair(repo, revs):
     if not revs:
         return repo.dirstate.p1(), None
@@ -711,67 +718,21 @@
     if first is None:
         raise util.Abort(_('empty revision range'))
 
-    if first == second and len(revs) == 1 and _revrangesep not in revs[0]:
+    # if top-level is range expression, the result must always be a pair
+    if first == second and len(revs) == 1 and not _pairspec(revs[0]):
         return repo.lookup(first), None
 
     return repo.lookup(first), repo.lookup(second)
 
-_revrangesep = ':'
-
 def revrange(repo, revs):
     """Yield revision as strings from a list of revision specifications."""
-
-    def revfix(repo, val, defval):
-        if not val and val != 0 and defval is not None:
-            return defval
-        return repo[val].rev()
-
-    subsets = []
-
-    revsetaliases = [alias for (alias, _) in
-                     repo.ui.configitems("revsetalias")]
-
+    allspecs = []
     for spec in revs:
-        # attempt to parse old-style ranges first to deal with
-        # things like old-tag which contain query metacharacters
-        try:
-            # ... except for revset aliases without arguments. These
-            # should be parsed as soon as possible, because they might
-            # clash with a hash prefix.
-            if spec in revsetaliases:
-                raise error.RepoLookupError
-
-            if isinstance(spec, int):
-                subsets.append(revset.baseset([spec]))
-                continue
-
-            if _revrangesep in spec:
-                start, end = spec.split(_revrangesep, 1)
-                if start in revsetaliases or end in revsetaliases:
-                    raise error.RepoLookupError
-
-                start = revfix(repo, start, 0)
-                end = revfix(repo, end, len(repo) - 1)
-                if end == nullrev and start < 0:
-                    start = nullrev
-                if start < end:
-                    l = revset.spanset(repo, start, end + 1)
-                else:
-                    l = revset.spanset(repo, start, end - 1)
-                subsets.append(l)
-                continue
-            elif spec and spec in repo: # single unquoted rev
-                rev = revfix(repo, spec, None)
-                subsets.append(revset.baseset([rev]))
-                continue
-        except error.RepoLookupError:
-            pass
-
-        # fall through to new-style queries if old-style fails
-        m = revset.match(repo.ui, spec, repo)
-        subsets.append(m(repo))
-
-    return revset._combinesets(subsets)
+        if isinstance(spec, int):
+            spec = revset.formatspec('rev(%d)', spec)
+        allspecs.append(spec)
+    m = revset.matchany(repo.ui, allspecs, repo)
+    return m(repo)
 
 def expandpats(pats):
     '''Expand bare globs when running on windows.
@@ -1103,7 +1064,7 @@
     Mercurial either atomic renames or appends for files under .hg,
     so to ensure the cache is reliable we need the filesystem to be able
     to tell us if a file has been replaced. If it can't, we fallback to
-    recreating the object on every call (essentially the same behaviour as
+    recreating the object on every call (essentially the same behavior as
     propertycache).
 
     '''
--- a/mercurial/setdiscovery.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/setdiscovery.py	Thu Sep 03 18:32:17 2015 -0500
@@ -40,11 +40,20 @@
 classified with it (since all ancestors or descendants will be marked as well).
 """
 
+from __future__ import absolute_import
+
 import collections
-from node import nullid, nullrev
-from i18n import _
 import random
-import util, dagutil
+
+from .i18n import _
+from .node import (
+    nullid,
+    nullrev,
+)
+from . import (
+    dagutil,
+    util,
+)
 
 def _updatesample(dag, nodes, sample, quicksamplesize=0):
     """update an existing sample to match the expected size
@@ -138,22 +147,12 @@
     sample = _limitsample(ownheads, initialsamplesize)
     # indices between sample and externalized version must match
     sample = list(sample)
-    if remote.local():
-        # stopgap until we have a proper localpeer that supports batch()
-        srvheadhashes = remote.heads()
-        yesno = remote.known(dag.externalizeall(sample))
-    elif remote.capable('batch'):
-        batch = remote.batch()
-        srvheadhashesref = batch.heads()
-        yesnoref = batch.known(dag.externalizeall(sample))
-        batch.submit()
-        srvheadhashes = srvheadhashesref.value
-        yesno = yesnoref.value
-    else:
-        # compatibility with pre-batch, but post-known remotes during 1.9
-        # development
-        srvheadhashes = remote.heads()
-        sample = []
+    batch = remote.batch()
+    srvheadhashesref = batch.heads()
+    yesnoref = batch.known(dag.externalizeall(sample))
+    batch.submit()
+    srvheadhashes = srvheadhashesref.value
+    yesno = yesnoref.value
 
     if cl.tip() == nullid:
         if srvheadhashes != [nullid]:
--- a/mercurial/simplemerge.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/simplemerge.py	Thu Sep 03 18:32:17 2015 -0500
@@ -16,9 +16,17 @@
 # mbp: "you know that thing where cvs gives you conflict markers?"
 # s: "i hate that."
 
-from i18n import _
-import scmutil, util, mdiff
-import sys, os
+from __future__ import absolute_import
+
+import os
+import sys
+
+from .i18n import _
+from . import (
+    mdiff,
+    scmutil,
+    util,
+)
 
 class CantReprocessAndShowBase(Exception):
     pass
@@ -92,9 +100,9 @@
                 newline = '\r\n'
             elif self.a[0].endswith('\r'):
                 newline = '\r'
-        if name_a:
+        if name_a and start_marker:
             start_marker = start_marker + ' ' + name_a
-        if name_b:
+        if name_b and end_marker:
             end_marker = end_marker + ' ' + name_b
         if name_base and base_marker:
             base_marker = base_marker + ' ' + name_base
@@ -112,17 +120,20 @@
                     yield self.b[i]
             elif what == 'conflict':
                 self.conflicts = True
-                yield start_marker + newline
+                if start_marker is not None:
+                    yield start_marker + newline
                 for i in range(t[3], t[4]):
                     yield self.a[i]
                 if base_marker is not None:
                     yield base_marker + newline
                     for i in range(t[1], t[2]):
                         yield self.base[i]
-                yield mid_marker + newline
+                if mid_marker is not None:
+                    yield mid_marker + newline
                 for i in range(t[5], t[6]):
                     yield self.b[i]
-                yield end_marker + newline
+                if end_marker is not None:
+                    yield end_marker + newline
             else:
                 raise ValueError(what)
 
@@ -345,18 +356,24 @@
                 raise util.Abort(msg)
         return text
 
-    name_a = local
-    name_b = other
-    name_base = None
-    labels = opts.get('label', [])
-    if len(labels) > 0:
-        name_a = labels[0]
-    if len(labels) > 1:
-        name_b = labels[1]
-    if len(labels) > 2:
-        name_base = labels[2]
-    if len(labels) > 3:
-        raise util.Abort(_("can only specify three labels."))
+    mode = opts.get('mode','merge')
+    if mode == 'union':
+        name_a = None
+        name_b = None
+        name_base = None
+    else:
+        name_a = local
+        name_b = other
+        name_base = None
+        labels = opts.get('label', [])
+        if len(labels) > 0:
+            name_a = labels[0]
+        if len(labels) > 1:
+            name_b = labels[1]
+        if len(labels) > 2:
+            name_base = labels[2]
+        if len(labels) > 3:
+            raise util.Abort(_("can only specify three labels."))
 
     try:
         localtext = readfile(local)
@@ -374,7 +391,11 @@
 
     m3 = Merge3Text(basetext, localtext, othertext)
     extrakwargs = {}
-    if name_base is not None:
+    if mode == 'union':
+        extrakwargs['start_marker'] = None
+        extrakwargs['mid_marker'] = None
+        extrakwargs['end_marker'] = None
+    elif name_base is not None:
         extrakwargs['base_marker'] = '|||||||'
         extrakwargs['name_base'] = name_base
     for line in m3.merge_lines(name_a=name_a, name_b=name_b, **extrakwargs):
@@ -383,7 +404,7 @@
     if not opts.get('print'):
         out.close()
 
-    if m3.conflicts:
+    if m3.conflicts and not mode == 'union':
         if not opts.get('quiet'):
             ui.warn(_("warning: conflicts during merge.\n"))
         return 1
--- a/mercurial/sshpeer.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/sshpeer.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,16 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import re
-from i18n import _
-import util, error, wireproto
+
+from .i18n import _
+from . import (
+    error,
+    util,
+    wireproto,
+)
 
 class remotelock(object):
     def __init__(self, repo):
--- a/mercurial/sshserver.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/sshserver.py	Thu Sep 03 18:32:17 2015 -0500
@@ -6,8 +6,16 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import util, hook, wireproto
-import os, sys
+from __future__ import absolute_import
+
+import os
+import sys
+
+from . import (
+    hook,
+    util,
+    wireproto,
+)
 
 class sshserver(wireproto.abstractserverproto):
     def __init__(self, ui, repo):
--- a/mercurial/sslutil.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/sslutil.py	Thu Sep 03 18:32:17 2015 -0500
@@ -6,10 +6,15 @@
 #
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
-import os, sys, ssl
+
+from __future__ import absolute_import
 
-from mercurial import util
-from mercurial.i18n import _
+import os
+import ssl
+import sys
+
+from .i18n import _
+from . import util
 
 _canloaddefaultcerts = False
 try:
--- a/mercurial/statichttprepo.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/statichttprepo.py	Thu Sep 03 18:32:17 2015 -0500
@@ -7,10 +7,26 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import changelog, byterange, url, error, namespaces
-import localrepo, manifest, util, scmutil, store
-import urllib, urllib2, errno, os
+from __future__ import absolute_import
+
+import errno
+import os
+import urllib
+import urllib2
+
+from .i18n import _
+from . import (
+    byterange,
+    changelog,
+    error,
+    localrepo,
+    manifest,
+    namespaces,
+    scmutil,
+    store,
+    url,
+    util,
+)
 
 class httprangereader(object):
     def __init__(self, url, opener):
--- a/mercurial/strutil.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/strutil.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,6 +5,8 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 def findall(haystack, needle, start=0, end=None):
     if end is None:
         end = len(haystack)
--- a/mercurial/subrepo.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/subrepo.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,15 +5,34 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import copy
-import errno, os, re, posixpath, sys
+import errno
+import os
+import posixpath
+import re
+import stat
+import subprocess
+import sys
+import tarfile
 import xml.dom.minidom
-import stat, subprocess, tarfile
-from i18n import _
-import config, util, node, error, cmdutil, scmutil, match as matchmod
-import phases
-import pathutil
-import exchange
+
+
+from .i18n import _
+from . import (
+    cmdutil,
+    config,
+    error,
+    exchange,
+    match as matchmod,
+    node,
+    pathutil,
+    phases,
+    scmutil,
+    util,
+)
+
 hg = None
 propertycache = util.propertycache
 
@@ -328,7 +347,7 @@
     # so we manually delay the circular imports to not break
     # scripts that don't use our demand-loading
     global hg
-    import hg as h
+    from . import hg as h
     hg = h
 
     pathutil.pathauditor(ctx.repo().root)(path)
@@ -346,7 +365,7 @@
     # so we manually delay the circular imports to not break
     # scripts that don't use our demand-loading
     global hg
-    import hg as h
+    from . import hg as h
     hg = h
 
     pathutil.pathauditor(ctx.repo().root)(path)
--- a/mercurial/tagmerge.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/tagmerge.py	Thu Sep 03 18:32:17 2015 -0500
@@ -71,11 +71,20 @@
 #         - put blocks whose nodes come all from p2 first
 #     - write the tag blocks in the sorted order
 
-import tags as tagsmod
-import util
-from node import nullid, hex
-from i18n import _
+from __future__ import absolute_import
+
 import operator
+
+from .i18n import _
+from .node import (
+    hex,
+    nullid,
+)
+from .import (
+    tags as tagsmod,
+    util,
+)
+
 hexnullid = hex(nullid)
 
 def readtagsformerge(ui, repo, lines, fn='', keeplinenums=False):
--- a/mercurial/tags.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/tags.py	Thu Sep 03 18:32:17 2015 -0500
@@ -10,15 +10,27 @@
 # Eventually, it could take care of updating (adding/removing/moving)
 # tags too.
 
-from node import nullid, bin, hex, short
-from i18n import _
-import util
-import encoding
-import error
-from array import array
+from __future__ import absolute_import
+
+import array
 import errno
 import time
 
+from .i18n import _
+from .node import (
+    bin,
+    hex,
+    nullid,
+    short,
+)
+from . import (
+    encoding,
+    error,
+    util,
+)
+
+array = array.array
+
 # Tags computation can be expensive and caches exist to make it fast in
 # the common case.
 #
@@ -263,7 +275,7 @@
     If the cache is not up to date, the caller is responsible for reading tag
     info from each returned head. (See findglobaltags().)
     '''
-    import scmutil  # avoid cycle
+    from . import scmutil  # avoid cycle
 
     try:
         cachefile = repo.vfs(_filename(repo), 'r')
--- a/mercurial/templatefilters.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/templatefilters.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,10 +5,21 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import cgi, re, os, time, urllib
-import encoding, node, util
-import hbisect
-import templatekw
+from __future__ import absolute_import
+
+import cgi
+import os
+import re
+import time
+import urllib
+
+from . import (
+    encoding,
+    hbisect,
+    node,
+    templatekw,
+    util,
+)
 
 def addbreaks(text):
     """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
@@ -70,12 +81,6 @@
     """:count: List or text. Returns the length as an integer."""
     return len(i)
 
-def datefilter(text):
-    """:date: Date. Returns a date in a Unix date format, including the
-    timezone: "Mon Sep 04 15:13:13 2006 0700".
-    """
-    return util.datestr(text)
-
 def domain(author):
     """:domain: Any text. Finds the first string that looks like an email
     address, and extracts just the domain component. Example: ``User
@@ -230,10 +235,6 @@
         s = s.replace(k, v)
     return ''.join(_uescape(c) for c in s)
 
-def localdate(text):
-    """:localdate: Date. Converts a date to local date."""
-    return (util.parsedate(text)[0], util.makedate()[1])
-
 def lower(text):
     """:lower: Any text. Converts the text to lowercase."""
     return encoding.lower(text)
@@ -337,10 +338,6 @@
         return ""
     return str(thing)
 
-def strip(text):
-    """:strip: Any text. Strips all leading and trailing whitespace."""
-    return text.strip()
-
 def stripdir(text):
     """:stripdir: Treat the text as path and strip a directory level, if
     possible. For example, "foo" and "foo/bar" becomes "foo".
@@ -390,7 +387,6 @@
     "age": age,
     "basename": basename,
     "count": count,
-    "date": datefilter,
     "domain": domain,
     "email": email,
     "escape": escape,
@@ -403,7 +399,6 @@
     "isodatesec": isodatesec,
     "json": json,
     "jsonescape": jsonescape,
-    "localdate": localdate,
     "lower": lower,
     "nonempty": nonempty,
     "obfuscate": obfuscate,
@@ -418,7 +413,6 @@
     "splitlines": splitlines,
     "stringescape": stringescape,
     "stringify": stringify,
-    "strip": strip,
     "stripdir": stripdir,
     "tabindent": tabindent,
     "upper": upper,
--- a/mercurial/templatekw.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/templatekw.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,9 +5,16 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from node import hex
-import patch, scmutil, util, error
-import hbisect
+from __future__ import absolute_import
+
+from .node import hex
+from . import (
+    error,
+    hbisect,
+    patch,
+    scmutil,
+    util,
+)
 
 # This helper class allows us to handle both:
 #  "{files}" (legacy command-line-specific list hack) and
--- a/mercurial/templater.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/templater.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,12 +5,23 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import os, re
-import util, config, templatefilters, templatekw, parser, error
-import revset as revsetmod
+from __future__ import absolute_import
+
+import os
+import re
 import types
-import minirst
+
+from .i18n import _
+from . import (
+    config,
+    error,
+    minirst,
+    parser,
+    revset as revsetmod,
+    templatefilters,
+    templatekw,
+    util,
+)
 
 # template parsing
 
@@ -194,12 +205,6 @@
         return getlist(x[1]) + [x[2]]
     return [x]
 
-def getfilter(exp, context):
-    f = getsymbol(exp)
-    if f not in context._filters:
-        raise error.ParseError(_("unknown function '%s'") % f)
-    return context._filters[f]
-
 def gettemplate(exp, context):
     if exp[0] == 'template':
         return [compileexp(e, context, methods) for e in exp[1]]
@@ -210,6 +215,15 @@
         return context._load(exp[1])
     raise error.ParseError(_("expected template specifier"))
 
+def evalfuncarg(context, mapping, arg):
+    func, data = arg
+    # func() may return string, generator of strings or arbitrary object such
+    # as date tuple, but filter does not want generator.
+    thing = func(context, mapping, data)
+    if isinstance(thing, types.GeneratorType):
+        thing = stringify(thing)
+    return thing
+
 def runinteger(context, mapping, data):
     return int(data)
 
@@ -242,24 +256,26 @@
         yield func(context, mapping, data)
 
 def buildfilter(exp, context):
-    func, data = compileexp(exp[1], context, methods)
-    filt = getfilter(exp[2], context)
-    return (runfilter, (func, data, filt))
+    arg = compileexp(exp[1], context, methods)
+    n = getsymbol(exp[2])
+    if n in context._filters:
+        filt = context._filters[n]
+        return (runfilter, (arg, filt))
+    if n in funcs:
+        f = funcs[n]
+        return (f, [arg])
+    raise error.ParseError(_("unknown function '%s'") % n)
 
 def runfilter(context, mapping, data):
-    func, data, filt = data
-    # func() may return string, generator of strings or arbitrary object such
-    # as date tuple, but filter does not want generator.
-    thing = func(context, mapping, data)
-    if isinstance(thing, types.GeneratorType):
-        thing = stringify(thing)
+    arg, filt = data
+    thing = evalfuncarg(context, mapping, arg)
     try:
         return filt(thing)
     except (ValueError, AttributeError, TypeError):
-        if isinstance(data, tuple):
-            dt = data[1]
+        if isinstance(arg[1], tuple):
+            dt = arg[1][1]
         else:
-            dt = data
+            dt = arg[1]
         raise util.Abort(_("template filter '%s' is not compatible with "
                            "keyword '%s'") % (filt.func_name, dt))
 
@@ -297,12 +313,13 @@
         if len(args) != 1:
             raise error.ParseError(_("filter %s expects one argument") % n)
         f = context._filters[n]
-        return (runfilter, (args[0][0], args[0][1], f))
+        return (runfilter, (args[0], f))
     raise error.ParseError(_("unknown function '%s'") % n)
 
 def date(context, mapping, args):
     """:date(date[, fmt]): Format a date. See :hg:`help dates` for formatting
-    strings."""
+    strings. The default is a Unix date format, including the timezone:
+    "Mon Sep 04 15:13:13 2006 0700"."""
     if not (1 <= len(args) <= 2):
         # i18n: "date" is a keyword
         raise error.ParseError(_("date expects one or two arguments"))
@@ -499,6 +516,34 @@
     # ignore args[0] (the label string) since this is supposed to be a a no-op
     yield args[1][0](context, mapping, args[1][1])
 
+def localdate(context, mapping, args):
+    """:localdate(date[, tz]): Converts a date to the specified timezone.
+    The default is local date."""
+    if not (1 <= len(args) <= 2):
+        # i18n: "localdate" is a keyword
+        raise error.ParseError(_("localdate expects one or two arguments"))
+
+    date = evalfuncarg(context, mapping, args[0])
+    try:
+        date = util.parsedate(date)
+    except AttributeError:  # not str nor date tuple
+        # i18n: "localdate" is a keyword
+        raise error.ParseError(_("localdate expects a date information"))
+    if len(args) >= 2:
+        tzoffset = None
+        tz = evalfuncarg(context, mapping, args[1])
+        if isinstance(tz, str):
+            tzoffset = util.parsetimezone(tz)
+        if tzoffset is None:
+            try:
+                tzoffset = int(tz)
+            except (TypeError, ValueError):
+                # i18n: "localdate" is a keyword
+                raise error.ParseError(_("localdate expects a timezone"))
+    else:
+        tzoffset = util.makedate()[1]
+    return (date[0], tzoffset)
+
 def revset(context, mapping, args):
     """:revset(query[, formatargs...]): Execute a revision set query. See
     :hg:`help revset`."""
@@ -593,7 +638,8 @@
                 return shortest
 
 def strip(context, mapping, args):
-    """:strip(text[, chars]): Strip characters from a string."""
+    """:strip(text[, chars]): Strip characters from a string. By default,
+    strips all leading and trailing whitespace."""
     if not (1 <= len(args) <= 2):
         # i18n: "strip" is a keyword
         raise error.ParseError(_("strip expects one or two arguments"))
@@ -682,6 +728,7 @@
     "indent": indent,
     "join": join,
     "label": label,
+    "localdate": localdate,
     "pad": pad,
     "revset": revset,
     "rstdoc": rstdoc,
--- a/mercurial/templates/monoblue/footer.tmpl	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/templates/monoblue/footer.tmpl	Thu Sep 03 18:32:17 2015 -0500
@@ -12,11 +12,6 @@
         <p><a href="{logourl}" title="Mercurial"><img src="{staticurl|urlescape}{logoimg}" width=75 height=90 border=0 alt="mercurial" /></a></p>
     </div>
 
-    <div id="corner-top-left"></div>
-    <div id="corner-top-right"></div>
-    <div id="corner-bottom-left"></div>
-    <div id="corner-bottom-right"></div>
-
 </div>
 
 </body>
--- a/mercurial/templates/monoblue/index.tmpl	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/templates/monoblue/index.tmpl	Thu Sep 03 18:32:17 2015 -0500
@@ -29,11 +29,6 @@
         <p><a href="{logourl}" title="Mercurial"><img src="{staticurl|urlescape}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a></p>
     </div>
 
-    <div id="corner-top-left"></div>
-    <div id="corner-top-right"></div>
-    <div id="corner-bottom-left"></div>
-    <div id="corner-bottom-right"></div>
-
 </div>
 <script type="text/javascript">process_dates()</script>
 </body>
--- a/mercurial/templates/monoblue/summary.tmpl	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/templates/monoblue/summary.tmpl	Thu Sep 03 18:32:17 2015 -0500
@@ -26,6 +26,7 @@
             <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
             <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
             <li><a href="{url|urlescape}file{sessionvars%urlparameter}">files</a></li>
+            {archives%archiveentry}
             <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
         </ul>
     </div>
--- a/mercurial/templates/static/style-gitweb.css	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/templates/static/style-gitweb.css	Thu Sep 03 18:32:17 2015 -0500
@@ -194,3 +194,7 @@
 .scroll-loading-error {
     background-color: #FFCCCC !important;
 }
+
+#doc {
+    margin: 0 8px;
+}
--- a/mercurial/templates/static/style-monoblue.css	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/templates/static/style-monoblue.css	Thu Sep 03 18:32:17 2015 -0500
@@ -263,7 +263,6 @@
 
 td.source {
   white-space: pre;
-  font-family: monospace;
   margin: 10px 30px 0;
   font-size: 1.2em;
   font-family: monospace;
@@ -311,44 +310,6 @@
 div#powered-by a:hover {
   text-decoration: underline;
 }
-/*
-div#monoblue-corner-top-left {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 10px;
-  height: 10px;
-  background: url(./monoblue-corner.png) top left no-repeat !important;
-  background: none;
-}
-div#monoblue-corner-top-right {
-  position: absolute;
-  top: 0;
-  right: 0;
-  width: 10px;
-  height: 10px;
-  background: url(./monoblue-corner.png) top right no-repeat !important;
-  background: none;
-}
-div#monoblue-corner-bottom-left {
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  width: 10px;
-  height: 10px;
-  background: url(./monoblue-corner.png) bottom left no-repeat !important;
-  background: none;
-}
-div#monoblue-corner-bottom-right {
-  position: absolute;
-  bottom: 0;
-  right: 0;
-  width: 10px;
-  height: 10px;
-  background: url(./monoblue-corner.png) bottom right no-repeat !important;
-  background: none;
-}
-*/
 /** end of common settings **/
 
 /** summary **/
@@ -553,3 +514,7 @@
 .scroll-loading-error {
     background-color: #FFCCCC !important;
 }
+
+#doc {
+    margin: 0 30px;
+}
--- a/mercurial/transaction.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/transaction.py	Thu Sep 03 18:32:17 2015 -0500
@@ -11,9 +11,15 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
+from __future__ import absolute_import
+
 import errno
-import error, util
+
+from .i18n import _
+from . import (
+    error,
+    util,
+)
 
 version = 2
 
--- a/mercurial/treediscovery.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/treediscovery.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,10 +5,19 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
 import collections
-from node import nullid, short
-from i18n import _
-import util, error
+
+from .i18n import _
+from .node import (
+    nullid,
+    short,
+)
+from . import (
+    error,
+    util,
+)
 
 def findcommonincoming(repo, remote, heads=None, force=False):
     """Return a tuple (common, fetch, heads) used to identify the common
--- a/mercurial/ui.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/ui.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,11 +5,28 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+from __future__ import absolute_import
+
+import errno
+import getpass
 import inspect
-from i18n import _
-import errno, getpass, os, socket, sys, tempfile, traceback
-import config, scmutil, util, error, formatter, progress
-from node import hex
+import os
+import socket
+import sys
+import tempfile
+import traceback
+
+from .i18n import _
+from .node import hex
+
+from . import (
+    config,
+    error,
+    formatter,
+    progress,
+    scmutil,
+    util,
+)
 
 samplehgrcs = {
     'user':
@@ -533,12 +550,9 @@
 
     def expandpath(self, loc, default=None):
         """Return repository location relative to cwd or from [paths]"""
-        if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
-            return loc
-
         p = self.paths.getpath(loc, default=default)
         if p:
-            return p.loc
+            return p.rawloc
         return loc
 
     @util.propertycache
@@ -982,10 +996,23 @@
             # No location is the same as not existing.
             if not loc:
                 continue
+
+            # TODO ignore default-push once all consumers stop referencing it
+            # since it is handled specifically below.
+
             self[name] = path(name, rawloc=loc)
 
+        # Handle default-push, which is a one-off that defines the push URL for
+        # the "default" path.
+        defaultpush = ui.config('paths', 'default-push')
+        if defaultpush and 'default' in self:
+            self['default']._pushloc = defaultpush
+
     def getpath(self, name, default=None):
-        """Return a ``path`` for the specified name, falling back to a default.
+        """Return a ``path`` from a string, falling back to a default.
+
+        ``name`` can be a named path or locations. Locations are filesystem
+        paths or URIs.
 
         Returns the first of ``name`` or ``default`` that is present, or None
         if neither is present.
@@ -993,6 +1020,12 @@
         try:
             return self[name]
         except KeyError:
+            # Try to resolve as a local path or URI.
+            try:
+                return path(None, rawloc=name)
+            except ValueError:
+                pass
+
             if default is not None:
                 try:
                     return self[default]
@@ -1004,15 +1037,51 @@
 class path(object):
     """Represents an individual path and its configuration."""
 
-    def __init__(self, name, rawloc=None):
+    def __init__(self, name, rawloc=None, pushloc=None):
         """Construct a path from its config options.
 
         ``name`` is the symbolic name of the path.
         ``rawloc`` is the raw location, as defined in the config.
+        ``pushloc`` is the raw locations pushes should be made to.
+
+        If ``name`` is not defined, we require that the location be a) a local
+        filesystem path with a .hg directory or b) a URL. If not,
+        ``ValueError`` is raised.
         """
+        if not rawloc:
+            raise ValueError('rawloc must be defined')
+
+        # Locations may define branches via syntax <base>#<branch>.
+        u = util.url(rawloc)
+        branch = None
+        if u.fragment:
+            branch = u.fragment
+            u.fragment = None
+
+        self.url = u
+        self.branch = branch
+
         self.name = name
-        # We'll do more intelligent things with rawloc in the future.
-        self.loc = rawloc
+        self.rawloc = rawloc
+        self.loc = str(u)
+        self._pushloc = pushloc
+
+        # When given a raw location but not a symbolic name, validate the
+        # location is valid.
+        if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
+            raise ValueError('location is not a URL or path to a local '
+                             'repo: %s' % rawloc)
+
+    def _isvalidlocalpath(self, path):
+        """Returns True if the given path is a potentially valid repository.
+        This is its own function so that extensions can change the definition of
+        'valid' in this case (like when pulling from a git repo into a hg
+        one)."""
+        return os.path.isdir(os.path.join(path, '.hg'))
+
+    @property
+    def pushloc(self):
+        return self._pushloc or self.loc
 
 # we instantiate one globally shared progress bar to avoid
 # competing progress bars when multiple UI objects get created
--- a/mercurial/unionrepo.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/unionrepo.py	Thu Sep 03 18:32:17 2015 -0500
@@ -11,11 +11,25 @@
 allowing operations like diff and log with revsets.
 """
 
-from node import nullid
-from i18n import _
+from __future__ import absolute_import
+
 import os
-import util, mdiff, cmdutil, scmutil
-import localrepo, changelog, manifest, filelog, revlog, pathutil
+
+from .i18n import _
+from .node import nullid
+
+from . import (
+    changelog,
+    cmdutil,
+    filelog,
+    localrepo,
+    manifest,
+    mdiff,
+    pathutil,
+    revlog,
+    scmutil,
+    util,
+)
 
 class unionrevlog(revlog.revlog):
     def __init__(self, opener, indexfile, revlog2, linkmapper):
--- a/mercurial/url.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/url.py	Thu Sep 03 18:32:17 2015 -0500
@@ -7,10 +7,23 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import urllib, urllib2, httplib, os, socket, cStringIO, base64
-from i18n import _
-import keepalive, util, sslutil
-import httpconnection as httpconnectionmod
+from __future__ import absolute_import
+
+import base64
+import cStringIO
+import httplib
+import os
+import socket
+import urllib
+import urllib2
+
+from .i18n import _
+from . import (
+    httpconnection as httpconnectionmod,
+    keepalive,
+    sslutil,
+    util,
+)
 
 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
     def __init__(self, ui):
--- a/mercurial/util.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/util.py	Thu Sep 03 18:32:17 2015 -0500
@@ -1371,22 +1371,22 @@
     """turn (timestamp, tzoff) tuple into iso 8631 date."""
     return datestr(date, format='%Y-%m-%d')
 
+def parsetimezone(tz):
+    """parse a timezone string and return an offset integer"""
+    if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
+        sign = (tz[0] == "+") and 1 or -1
+        hours = int(tz[1:3])
+        minutes = int(tz[3:5])
+        return -sign * (hours * 60 + minutes) * 60
+    if tz == "GMT" or tz == "UTC":
+        return 0
+    return None
+
 def strdate(string, format, defaults=[]):
     """parse a localized time string and return a (unixtime, offset) tuple.
     if the string cannot be parsed, ValueError is raised."""
-    def timezone(string):
-        tz = string.split()[-1]
-        if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
-            sign = (tz[0] == "+") and 1 or -1
-            hours = int(tz[1:3])
-            minutes = int(tz[3:5])
-            return -sign * (hours * 60 + minutes) * 60
-        if tz == "GMT" or tz == "UTC":
-            return 0
-        return None
-
     # NOTE: unixtime = localunixtime + offset
-    offset, date = timezone(string), string
+    offset, date = parsetimezone(string.split()[-1]), string
     if offset is not None:
         date = " ".join(string.split()[:-1])
 
@@ -2260,7 +2260,7 @@
 
 class hooks(object):
     '''A collection of hook functions that can be used to extend a
-    function's behaviour. Hooks are called in lexicographic order,
+    function's behavior. Hooks are called in lexicographic order,
     based on the names of their sources.'''
 
     def __init__(self):
--- a/mercurial/verify.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/verify.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,10 +5,21 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from node import nullid, short
-from i18n import _
+from __future__ import absolute_import
+
 import os
-import revlog, util, error
+
+from .i18n import _
+from .node import (
+    nullid,
+    short,
+)
+
+from . import (
+    error,
+    revlog,
+    util,
+)
 
 def verify(repo):
     lock = repo.lock()
--- a/mercurial/win32.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/win32.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,7 +5,14 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import ctypes, errno, msvcrt, os, subprocess, random
+from __future__ import absolute_import
+
+import ctypes
+import errno
+import msvcrt
+import os
+import random
+import subprocess
 
 _kernel32 = ctypes.windll.kernel32
 _advapi32 = ctypes.windll.advapi32
--- a/mercurial/wireproto.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/wireproto.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,12 +5,29 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import urllib, tempfile, os, sys
-from i18n import _
-from node import bin, hex
-import changegroup as changegroupmod, bundle2, pushkey as pushkeymod
-import peer, error, encoding, util, exchange
+from __future__ import absolute_import
+
+import os
+import sys
+import tempfile
+import urllib
 
+from .i18n import _
+from .node import (
+    bin,
+    hex,
+)
+
+from . import (
+    bundle2,
+    changegroup as changegroupmod,
+    encoding,
+    error,
+    exchange,
+    peer,
+    pushkey as pushkeymod,
+    util,
+)
 
 class abstractserverproto(object):
     """abstract class that summarizes the protocol API
@@ -58,48 +75,12 @@
         Some protocols may have compressed the contents."""
         raise NotImplementedError()
 
-# abstract batching support
-
-class future(object):
-    '''placeholder for a value to be set later'''
-    def set(self, value):
-        if util.safehasattr(self, 'value'):
-            raise error.RepoError("future is already set")
-        self.value = value
-
-class batcher(object):
-    '''base class for batches of commands submittable in a single request
-
-    All methods invoked on instances of this class are simply queued and
-    return a a future for the result. Once you call submit(), all the queued
-    calls are performed and the results set in their respective futures.
-    '''
-    def __init__(self):
-        self.calls = []
-    def __getattr__(self, name):
-        def call(*args, **opts):
-            resref = future()
-            self.calls.append((name, args, opts, resref,))
-            return resref
-        return call
-    def submit(self):
-        pass
-
-class localbatch(batcher):
-    '''performs the queued calls directly'''
-    def __init__(self, local):
-        batcher.__init__(self)
-        self.local = local
-    def submit(self):
-        for name, args, opts, resref in self.calls:
-            resref.set(getattr(self.local, name)(*args, **opts))
-
-class remotebatch(batcher):
+class remotebatch(peer.batcher):
     '''batches the queued calls; uses as few roundtrips as possible'''
     def __init__(self, remote):
         '''remote must support _submitbatch(encbatch) and
         _submitone(op, encargs)'''
-        batcher.__init__(self)
+        peer.batcher.__init__(self)
         self.remote = remote
     def submit(self):
         req, rsp = [], []
@@ -128,41 +109,10 @@
             encresref.set(encres)
             resref.set(batchable.next())
 
-def batchable(f):
-    '''annotation for batchable methods
-
-    Such methods must implement a coroutine as follows:
-
-    @batchable
-    def sample(self, one, two=None):
-        # Handle locally computable results first:
-        if not one:
-            yield "a local result", None
-        # Build list of encoded arguments suitable for your wire protocol:
-        encargs = [('one', encode(one),), ('two', encode(two),)]
-        # Create future for injection of encoded result:
-        encresref = future()
-        # Return encoded arguments and future:
-        yield encargs, encresref
-        # Assuming the future to be filled with the result from the batched
-        # request now. Decode it:
-        yield decode(encresref.value)
-
-    The decorator returns a function which wraps this coroutine as a plain
-    method, but adds the original method as an attribute called "batchable",
-    which is used by remotebatch to split the call into separate encoding and
-    decoding phases.
-    '''
-    def plain(*args, **opts):
-        batchable = f(*args, **opts)
-        encargsorres, encresref = batchable.next()
-        if not encresref:
-            return encargsorres # a local result in this case
-        self = args[0]
-        encresref.set(self._submitone(f.func_name, encargsorres))
-        return batchable.next()
-    setattr(plain, 'batchable', f)
-    return plain
+# Forward a couple of names from peer to make wireproto interactions
+# slightly more sensible.
+batchable = peer.batchable
+future = peer.future
 
 # list of nodes encoding / decoding
 
@@ -216,7 +166,10 @@
 class wirepeer(peer.peerrepository):
 
     def batch(self):
-        return remotebatch(self)
+        if self.capable('batch'):
+            return remotebatch(self)
+        else:
+            return peer.localbatch(self)
     def _submitbatch(self, req):
         cmds = []
         for op, argsdict in req:
--- a/mercurial/worker.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/mercurial/worker.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,30 +5,24 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from i18n import _
-import errno, os, signal, sys, threading
-import util
+from __future__ import absolute_import
+
+import errno
+import multiprocessing
+import os
+import signal
+import sys
+import threading
+
+from .i18n import _
+from . import util
 
 def countcpus():
     '''try to count the number of CPUs on the system'''
-
-    # posix
     try:
-        n = int(os.sysconf('SC_NPROCESSORS_ONLN'))
-        if n > 0:
-            return n
-    except (AttributeError, ValueError):
-        pass
-
-    # windows
-    try:
-        n = int(os.environ['NUMBER_OF_PROCESSORS'])
-        if n > 0:
-            return n
-    except (KeyError, ValueError):
-        pass
-
-    return 1
+        return multiprocessing.cpu_count()
+    except NotImplementedError:
+        return 1
 
 def _numworkers(ui):
     s = ui.config('worker', 'numcpus')
--- a/tests/bzr-definitions	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/bzr-definitions	Thu Sep 03 18:32:17 2015 -0500
@@ -1,7 +1,5 @@
 # this file holds the definitions that are used in various bzr tests
 
-"$TESTDIR/hghave" bzr || exit 80
-
 TERM=dumb; export TERM
 echo '[extensions]' >> $HGRCPATH
 echo 'convert = ' >> $HGRCPATH
--- a/tests/filterpyflakes.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/filterpyflakes.py	Thu Sep 03 18:32:17 2015 -0500
@@ -2,7 +2,7 @@
 
 # Filter output by pyflakes to control which warnings we check
 
-import sys, re, os
+import sys, re
 
 def makekey(typeandline):
     """
@@ -42,7 +42,7 @@
     else:
         continue # no pattern matched, next line
     fn = line.split(':', 1)[0]
-    f = open(os.path.join(os.path.dirname(os.path.dirname(__file__)), fn))
+    f = open(fn)
     data = f.read()
     f.close()
     if 'no-' 'check-code' in data:
--- a/tests/hghave	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/hghave	Thu Sep 03 18:32:17 2015 -0500
@@ -30,10 +30,8 @@
                   help="test available features")
 parser.add_option("--list-features", action="store_true",
                   help="list available features")
-parser.add_option("-q", "--quiet", action="store_true",
-                  help="check features silently")
 
-def _loadaddon(quiet):
+def _loadaddon():
     if 'TESTDIR' in os.environ:
         # loading from '.' isn't needed, because `hghave` should be
         # running at TESTTMP in this case
@@ -48,15 +46,14 @@
     try:
         import hghaveaddon
     except BaseException, inst:
-        if not quiet:
-            sys.stderr.write('failed to import hghaveaddon.py from %r: %s\n'
-                             % (path, inst))
+        sys.stderr.write('failed to import hghaveaddon.py from %r: %s\n'
+                         % (path, inst))
         sys.exit(2)
     sys.path.pop(0)
 
 if __name__ == '__main__':
     options, args = parser.parse_args()
-    _loadaddon(options.quiet)
+    _loadaddon()
     if options.list_features:
         list_features()
         sys.exit(0)
@@ -64,36 +61,4 @@
     if options.test_features:
         sys.exit(test_features())
 
-    quiet = options.quiet
-
-    failures = 0
-
-    def error(msg):
-        global failures
-        if not quiet:
-            sys.stderr.write(msg + '\n')
-        failures += 1
-
-    for feature in args:
-        negate = feature.startswith('no-')
-        if negate:
-            feature = feature[3:]
-
-        if feature not in checks:
-            error('skipped: unknown feature: ' + feature)
-            sys.exit(2)
-
-        check, desc = checks[feature]
-        try:
-            available = check()
-        except Exception, e:
-            error('hghave check failed: ' + feature)
-            continue
-
-        if not negate and not available:
-            error('skipped: missing feature: ' + desc)
-        elif negate and available:
-            error('skipped: system supports %s' % desc)
-
-    if failures != 0:
-        sys.exit(1)
+    hghave.require(args)
--- a/tests/hghave.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/hghave.py	Thu Sep 03 18:32:17 2015 -0500
@@ -1,6 +1,9 @@
-import os, stat
+import errno
+import os
 import re
 import socket
+import stat
+import subprocess
 import sys
 import tempfile
 
@@ -17,19 +20,68 @@
         return func
     return decorator
 
+def checkfeatures(features):
+    result = {
+        'error': [],
+        'missing': [],
+        'skipped': [],
+    }
+
+    for feature in features:
+        negate = feature.startswith('no-')
+        if negate:
+            feature = feature[3:]
+
+        if feature not in checks:
+            result['missing'].append(feature)
+            continue
+
+        check, desc = checks[feature]
+        try:
+            available = check()
+        except Exception:
+            result['error'].append('hghave check failed: %s' % feature)
+            continue
+
+        if not negate and not available:
+            result['skipped'].append('missing feature: %s' % desc)
+        elif negate and available:
+            result['skipped'].append('system supports %s' % desc)
+
+    return result
+
+def require(features):
+    """Require that features are available, exiting if not."""
+    result = checkfeatures(features)
+
+    for missing in result['missing']:
+        sys.stderr.write('skipped: unknown feature: %s\n' % missing)
+    for msg in result['skipped']:
+        sys.stderr.write('skipped: %s\n' % msg)
+    for msg in result['error']:
+        sys.stderr.write('%s\n' % msg)
+
+    if result['missing']:
+        sys.exit(2)
+
+    if result['skipped'] or result['error']:
+        sys.exit(1)
+
 def matchoutput(cmd, regexp, ignorestatus=False):
     """Return True if cmd executes successfully and its output
     is matched by the supplied regular expression.
     """
     r = re.compile(regexp)
-    fh = os.popen(cmd)
-    s = fh.read()
     try:
-        ret = fh.close()
-    except IOError:
-        # Happen in Windows test environment
-        ret = 1
-    return (ignorestatus or ret is None) and r.search(s)
+        p = subprocess.Popen(
+            cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    except OSError as e:
+        if e.errno != errno.ENOENT:
+            raise
+        ret = -1
+    ret = p.wait()
+    s = p.stdout.read()
+    return (ignorestatus or not ret) and r.search(s)
 
 @check("baz", "GNU Arch baz client")
 def has_baz():
@@ -367,6 +419,33 @@
 def has_osx():
     return sys.platform == 'darwin'
 
+@check("docker", "docker support")
+def has_docker():
+    pat = r'A self-sufficient runtime for linux containers\.'
+    if matchoutput('docker --help', pat):
+        if 'linux' not in sys.platform:
+            # TODO: in theory we should be able to test docker-based
+            # package creation on non-linux using boot2docker, but in
+            # practice that requires extra coordination to make sure
+            # $TESTTEMP is going to be visible at the same path to the
+            # boot2docker VM. If we figure out how to verify that, we
+            # can use the following instead of just saying False:
+            # return 'DOCKER_HOST' in os.environ
+            return False
+
+        return True
+    return False
+
+@check("debhelper", "debian packaging tools")
+def has_debhelper():
+    dpkg = matchoutput('dpkg --version',
+                       "Debian `dpkg' package management program")
+    dh = matchoutput('dh --help',
+                     'dh is a part of debhelper.', ignorestatus=True)
+    dh_py2 = matchoutput('dh_python2 --help',
+                         'other supported Python versions')
+    return dpkg and dh and dh_py2
+
 @check("absimport", "absolute_import in __future__")
 def has_absimport():
     import __future__
@@ -380,3 +459,7 @@
 @check("pure", "running with pure Python code")
 def has_pure():
     return os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure"
+
+@check("slow", "allow slow tests")
+def has_slow():
+    return os.environ.get('HGTEST_SLOW') == 'slow'
--- a/tests/run-tests.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/run-tests.py	Thu Sep 03 18:32:17 2015 -0500
@@ -35,6 +35,8 @@
 #      ./run-tests.py -j2 -c --local test-s*  # unsupported (and broken)
 #  9) parallel, custom tmp dir:
 #      ./run-tests.py -j2 --tmpdir /tmp/myhgtests
+#  10) parallel, pure, tests that call run-tests:
+#      ./run-tests.py --pure `grep -l run-tests.py *.t`
 #
 # (You could use any subset of the tests: test-s* happens to match
 # enough that it's worth doing parallel runs, few enough that it
@@ -259,6 +261,8 @@
                       help='run tests in random order')
     parser.add_option('--profile-runner', action='store_true',
                       help='run statprof on run-tests')
+    parser.add_option('--allow-slow-tests', action='store_true',
+                      help='allow extremely slow tests')
 
     for option, (envvar, default) in defaults.items():
         defaults[option] = type(default)(os.environ.get(envvar, default))
@@ -1835,6 +1839,11 @@
         if self.options.pure:
             os.environ["HGTEST_RUN_TESTS_PURE"] = "--pure"
 
+        if self.options.allow_slow_tests:
+            os.environ["HGTEST_SLOW"] = "slow"
+        elif 'HGTEST_SLOW' in os.environ:
+            del os.environ['HGTEST_SLOW']
+
         self._coveragefile = os.path.join(self._testdir, b'.coverage')
 
         vlog("# Using TESTDIR", self._testdir)
@@ -2078,7 +2087,11 @@
         vlog("# Running", cmd)
         if os.system(cmd) == 0:
             if not self.options.verbose:
-                os.remove(installerrs)
+                try:
+                    os.remove(installerrs)
+                except OSError as e:
+                    if e.errno != errno.ENOENT:
+                        raise
         else:
             f = open(installerrs, 'rb')
             for line in f:
--- a/tests/test-batching.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-batching.py	Thu Sep 03 18:32:17 2015 -0500
@@ -5,7 +5,8 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from mercurial.wireproto import localbatch, remotebatch, batchable, future
+from mercurial.peer import localbatch, batchable, future
+from mercurial.wireproto import remotebatch
 
 # equivalent of repo.repository
 class thing(object):
--- a/tests/test-bookmarks.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-bookmarks.t	Thu Sep 03 18:32:17 2015 -0500
@@ -511,10 +511,10 @@
 
 test clone with update to a bookmark
 
-  $ hg clone -u Z . cloned-bookmarks-update
+  $ hg clone -u Z . ../cloned-bookmarks-update
   updating to branch default
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg -R cloned-bookmarks-update bookmarks
+  $ hg -R ../cloned-bookmarks-update bookmarks
      X2                        1:925d80f479bb
      Y                         2:db815d6d32e6
    * Z                         2:db815d6d32e6
@@ -569,10 +569,10 @@
 
   $ hg bookmark -r3 Y
   moving bookmark 'Y' forward from db815d6d32e6
-  $ hg -R cloned-bookmarks-update update Y
+  $ hg -R ../cloned-bookmarks-update update Y
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (activating bookmark Y)
-  $ hg -R cloned-bookmarks-update pull --update .
+  $ hg -R ../cloned-bookmarks-update pull --update .
   pulling from .
   searching for changes
   adding changesets
--- a/tests/test-bundle2-format.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-bundle2-format.t	Thu Sep 03 18:32:17 2015 -0500
@@ -78,6 +78,7 @@
   >           ('', 'reply', False, 'produce a reply bundle'),
   >           ('', 'pushrace', False, 'includes a check:head part with unknown nodes'),
   >           ('', 'genraise', False, 'includes a part that raise an exception during generation'),
+  >           ('', 'timeout', False, 'emulate a timeout during bundle generation'),
   >           ('r', 'rev', [], 'includes those changeset in the bundle'),],
   >          '[OUTPUTFILE]')
   > def cmdbundle2(ui, repo, path=None, **opts):
@@ -143,6 +144,18 @@
   >     else:
   >         file = open(path, 'wb')
   > 
+  >     if opts['timeout']:
+  >         bundler.newpart('test:song', data=ELEPHANTSSONG, mandatory=False)
+  >         for idx, junk in enumerate(bundler.getchunks()):
+  >             ui.write('%d chunk\n' % idx)
+  >             if idx > 4:
+  >                 # This throws a GeneratorExit inside the generator, which
+  >                 # can cause problems if the exception-recovery code is
+  >                 # too zealous. It's important for this test that the break
+  >                 # occur while we're in the middle of a part.
+  >                 break
+  >         ui.write('fake timeout complete.\n')
+  >         return
   >     try:
   >         for chunk in bundler.getchunks():
   >             file.write(chunk)
@@ -239,6 +252,26 @@
   $ hg bundle2
   HG20\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
 
+Test timeouts during bundling
+  $ hg bundle2 --timeout --debug --config devel.bundle2.debug=yes
+  bundle2-output-bundle: "HG20", 1 parts total
+  bundle2-output: start emission of HG20 stream
+  0 chunk
+  bundle2-output: bundle parameter: 
+  1 chunk
+  bundle2-output: start of parts
+  bundle2-output: bundle part: "test:song"
+  bundle2-output-part: "test:song" (advisory) 178 bytes payload
+  bundle2-output: part 0: "test:song"
+  bundle2-output: header chunk size: 16
+  2 chunk
+  3 chunk
+  bundle2-output: payload chunk size: 178
+  4 chunk
+  5 chunk
+  bundle2-generatorexit
+  fake timeout complete.
+
 Test unbundling
 
   $ hg bundle2 | hg statbundle2
--- a/tests/test-clone-r.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-clone-r.t	Thu Sep 03 18:32:17 2015 -0500
@@ -218,3 +218,26 @@
   4 files, 9 changesets, 7 total revisions
   $ cd ..
 
+  $ hg clone test test-9
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd test-9
+  $ hg branch foobar
+  marked working directory as branch foobar
+  (branches are permanent and global, did you want a bookmark?)
+  $ echo file2 >> file2
+  $ hg add file2
+  $ hg commit -m "changeset9"
+  $ echo file3 >> file3
+  $ hg add file3
+  $ hg commit -m "changeset10"
+  $ cd ..
+  $ hg clone -r 9 -u foobar test-9 test-10
+  adding changesets
+  adding manifests
+  adding file changes
+  added 6 changesets with 6 changes to 3 files
+  updating to branch foobar
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
--- a/tests/test-clone.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-clone.t	Thu Sep 03 18:32:17 2015 -0500
@@ -1020,7 +1020,7 @@
   $ hg -R a id -r 0
   acb14030fe0a
   $ hg id -R remote -r 0
-  abort: there is no Mercurial repository here (.hg not found)
+  abort: repository remote not found!
   [255]
   $ hg --config share.pool=share -q clone -e "python \"$TESTDIR/dummyssh\"" a ssh://user@dummy/remote
   $ hg -R remote id -r 0
--- a/tests/test-command-template.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-command-template.t	Thu Sep 03 18:32:17 2015 -0500
@@ -2495,10 +2495,14 @@
   abort: template filter 'escape' is not compatible with keyword 'date'
   [255]
 
+  $ hg log -l 3 --template 'line: {extras|localdate}\n'
+  hg: parse error: localdate expects a date information
+  [255]
+
 Behind the scenes, this will throw ValueError
 
   $ hg tip --template '{author|email|date}\n'
-  abort: template filter 'datefilter' is not compatible with keyword 'author'
+  hg: parse error: date expects a date information
   [255]
 
 Error in nested template:
@@ -3105,6 +3109,25 @@
   hg: parse error: get() expects a dict as first argument
   [255]
 
+Test localdate(date, tz) function:
+
+  $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
+  1970-01-01 09:00 +0900
+  $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
+  1970-01-01 00:00 +0000
+  $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
+  1970-01-01 02:00 +0200
+  $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
+  1970-01-01 00:00 +0000
+  $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
+  1970-01-01 00:00 +0000
+  $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
+  hg: parse error: localdate expects a timezone
+  [255]
+  $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
+  hg: parse error: localdate expects a timezone
+  [255]
+
 Test shortest(node) function:
 
   $ echo b > b
@@ -3117,6 +3140,8 @@
   e777603221
   bcc7ff960b
   f7769ec2ab
+  $ hg log --template '{node|shortest}\n' -l1
+  e777
 
 Test pad function
 
--- a/tests/test-commandserver.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-commandserver.t	Thu Sep 03 18:32:17 2015 -0500
@@ -589,6 +589,15 @@
   000000000000 tip
 
 
+don't fall back to cwd if invalid -R path is specified (issue4805):
+
+  $ cd repo
+  $ hg serve --cmdserver pipe -R ../nonexistent
+  abort: repository ../nonexistent not found!
+  [255]
+  $ cd ..
+
+
 unix domain socket:
 
   $ cd repo
--- a/tests/test-completion.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-completion.t	Thu Sep 03 18:32:17 2015 -0500
@@ -254,7 +254,7 @@
   debugpathcomplete: full, normal, added, removed
   debugpushkey: 
   debugpvec: 
-  debugrebuilddirstate: rev
+  debugrebuilddirstate: rev, minimal
   debugrebuildfncache: 
   debugrename: rev
   debugrevlog: changelog, manifest, dir, dump
--- a/tests/test-convert-bzr-114.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-convert-bzr-114.t	Thu Sep 03 18:32:17 2015 -0500
@@ -1,4 +1,4 @@
-#require bzr114
+#require bzr bzr114
 
   $ . "$TESTDIR/bzr-definitions"
 
--- a/tests/test-convert-bzr-directories.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-convert-bzr-directories.t	Thu Sep 03 18:32:17 2015 -0500
@@ -1,3 +1,4 @@
+#require bzr
 
   $ . "$TESTDIR/bzr-definitions"
 
--- a/tests/test-convert-bzr-ghosts.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-convert-bzr-ghosts.t	Thu Sep 03 18:32:17 2015 -0500
@@ -1,3 +1,4 @@
+#require bzr
 
   $ . "$TESTDIR/bzr-definitions"
   $ cat > ghostcreator.py <<EOF
--- a/tests/test-convert-bzr-merges.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-convert-bzr-merges.t	Thu Sep 03 18:32:17 2015 -0500
@@ -1,3 +1,5 @@
+#require bzr
+
 N.B. bzr 1.13 has a bug that breaks this test.  If you see this
 test fail, check your bzr version.  Upgrading to bzr 1.13.1
 should fix it.
--- a/tests/test-convert-bzr-treeroot.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-convert-bzr-treeroot.t	Thu Sep 03 18:32:17 2015 -0500
@@ -1,3 +1,4 @@
+#require bzr
 
   $ . "$TESTDIR/bzr-definitions"
   $ cat > treeset.py <<EOF
--- a/tests/test-convert-bzr.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-convert-bzr.t	Thu Sep 03 18:32:17 2015 -0500
@@ -1,3 +1,5 @@
+#require bzr
+
   $ . "$TESTDIR/bzr-definitions"
 
 create and rename on the same file in the same step
--- a/tests/test-convert-filemap.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-convert-filemap.t	Thu Sep 03 18:32:17 2015 -0500
@@ -671,3 +671,73 @@
   |/
   o  0:c334dc3be0da@default "add" files: a
   
+  $ cd ..
+
+test converting merges into a repo that contains other files
+
+  $ hg init merge-test1
+  $ cd merge-test1
+  $ touch a && hg commit -Aqm 'add a'
+  $ echo a > a && hg commit -Aqm 'edit a'
+  $ hg up -q 0
+  $ touch b && hg commit -Aqm 'add b'
+  $ hg merge -q 1 && hg commit -qm 'merge a & b'
+
+  $ cd ..
+  $ hg init merge-test2
+  $ cd merge-test2
+  $ mkdir converted
+  $ touch converted/a toberemoved && hg commit -Aqm 'add converted/a & toberemoved'
+  $ touch x && rm toberemoved && hg commit -Aqm 'add x & remove tobremoved'
+  $ cd ..
+  $ hg log -G -T '{shortest(node)} {desc}' -R merge-test1
+  @    1191 merge a & b
+  |\
+  | o  9077 add b
+  | |
+  o |  d19f edit a
+  |/
+  o  ac82 add a
+  
+  $ hg log -G -T '{shortest(node)} {desc}' -R merge-test2
+  @  150e add x & remove tobremoved
+  |
+  o  bbac add converted/a & toberemoved
+  
+- Build a shamap where the target converted/a is in on top of an unrelated
+- change to 'x'. This simulates using convert to merge several repositories
+- together.
+  $ cat >> merge-test2/.hg/shamap <<EOF
+  > $(hg -R merge-test1 log -r 0 -T '{node}') $(hg -R merge-test2 log -r 0 -T '{node}')
+  > $(hg -R merge-test1 log -r 1 -T '{node}') $(hg -R merge-test2 log -r 1 -T '{node}')
+  > EOF
+  $ cat >> merge-test-filemap <<EOF
+  > rename . converted/
+  > EOF
+  $ hg convert --filemap merge-test-filemap merge-test1 merge-test2 --traceback
+  scanning source...
+  sorting...
+  converting...
+  1 add b
+  0 merge a & b
+  $ hg -R merge-test2 manifest -r tip
+  converted/a
+  converted/b
+  x
+  $ hg -R merge-test2 log -G -T '{shortest(node)} {desc}\n{files % "- {file}\n"}\n'
+  o    6eaa merge a & b
+  |\   - converted/a
+  | |  - toberemoved
+  | |
+  | o  2995 add b
+  | |  - converted/b
+  | |
+  @ |  150e add x & remove tobremoved
+  |/   - toberemoved
+  |    - x
+  |
+  o  bbac add converted/a & toberemoved
+     - converted/a
+     - toberemoved
+  
+
--- a/tests/test-convert-git.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-convert-git.t	Thu Sep 03 18:32:17 2015 -0500
@@ -652,6 +652,12 @@
   $ hg -R git-repo6-hg tip -T "{file_dels}\n"
   .hgsub .hgsubstate
 
+skip submodules in the conversion
+
+  $ hg convert -q git-repo6 no-submodules --config convert.git.skipsubmodules=True
+  $ hg -R no-submodules manifest --all
+  .gitmodules-renamed
+
 convert using a different remote prefix
   $ git init git-repo7
   Initialized empty Git repository in $TESTTMP/git-repo7/.git/
@@ -678,6 +684,28 @@
      master                    0:03bf38caa4c6
      origin/master             0:03bf38caa4c6
 
+Run convert when the remote branches have changed
+(there was an old bug where the local convert read branches from the server)
+
+  $ cd git-repo7
+  $ echo a >> a
+  $ git commit -am "move master forward"
+  [master 0c81947] move master forward
+   Author: nottest <test@example.org>
+   1 file changed, 1 insertion(+)
+  $ cd ..
+  $ rm -rf hg-repo7
+  $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
+  initializing destination hg-repo7 repository
+  scanning source...
+  sorting...
+  converting...
+  0 commit a
+  updating bookmarks
+  $ hg -R hg-repo7 bookmarks
+     master                    0:03bf38caa4c6
+     origin/master             0:03bf38caa4c6
+
 damaged git repository tests:
 In case the hard-coded hashes change, the following commands can be used to
 list the hashes and their corresponding types in the repository:
--- a/tests/test-convert-hg-startrev.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-convert-hg-startrev.t	Thu Sep 03 18:32:17 2015 -0500
@@ -201,4 +201,23 @@
   |
   o  0 "0: add a b f" files: a b f
   
-  $ cd ..
+Convert from specified revs
+
+  $ hg convert --rev 3 --rev 2 source multiplerevs
+  initializing destination multiplerevs repository
+  scanning source...
+  sorting...
+  converting...
+  3 0: add a b f
+  2 1: add c, move f to d
+  1 2: copy e from a, change b
+  0 3: change a
+  $ glog multiplerevs
+  o  3 "3: change a" files: a
+  |
+  | o  2 "2: copy e from a, change b" files: b e
+  | |
+  | o  1 "1: add c, move f to d" files: c d f
+  |/
+  o  0 "0: add a b f" files: a b f
+  
--- a/tests/test-convert.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-convert.t	Thu Sep 03 18:32:17 2015 -0500
@@ -265,6 +265,9 @@
                     remote refs are converted as bookmarks with
                     "convert.git.remoteprefix" as a prefix followed by a /. The
                     default is 'remote'.
+      convert.git.skipsubmodules
+                    does not convert root level .gitmodules files or files with
+                    160000 mode indicating a submodule. Default is False.
   
       Perforce Source
       ###############
--- a/tests/test-copy-move-merge.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-copy-move-merge.t	Thu Sep 03 18:32:17 2015 -0500
@@ -59,4 +59,107 @@
   1
   2
 
+Test disabling copy tracing
+
+- first verify copy metadata was kept
+
+  $ hg up -qC 2
+  $ hg rebase --keep -d 1 -b 2 --config extensions.rebase=
+  rebasing 2:add3f11052fa "other" (tip)
+  merging b and a to b
+  merging c and a to c
+
+  $ cat b
+  0
+  1
+  2
+
+- next verify copy metadata is lost when disabled
+
+  $ hg strip -r . --config extensions.strip=
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/t/.hg/strip-backup/550bd84c0cd3-fc575957-backup.hg (glob)
+  $ hg up -qC 2
+  $ hg rebase --keep -d 1 -b 2 --config extensions.rebase= --config experimental.disablecopytrace=True
+  rebasing 2:add3f11052fa "other" (tip)
+  remote changed a which local deleted
+  use (c)hanged version or leave (d)eleted? c
+
+  $ cat b
+  1
+  2
+
   $ cd ..
+
+Verify disabling copy tracing still keeps copies from rebase source
+
+  $ hg init copydisable
+  $ cd copydisable
+  $ touch a
+  $ hg ci -Aqm 'add a'
+  $ touch b
+  $ hg ci -Aqm 'add b, c'
+  $ hg cp b x
+  $ echo x >> x
+  $ hg ci -qm 'copy b->x'
+  $ hg up -q 1
+  $ touch z
+  $ hg ci -Aqm 'add z'
+  $ hg log -G -T '{rev} {desc}\n'
+  @  3 add z
+  |
+  | o  2 copy b->x
+  |/
+  o  1 add b, c
+  |
+  o  0 add a
+  
+  $ hg rebase -d . -b 2 --config extensions.rebase= --config experimental.disablecopytrace=True
+  rebasing 2:6adcf8c12e7d "copy b->x"
+  saved backup bundle to $TESTTMP/copydisable/.hg/strip-backup/6adcf8c12e7d-ce4b3e75-backup.hg (glob)
+  $ hg up -q 3
+  $ hg log -f x -T '{rev} {desc}\n'
+  3 copy b->x
+  1 add b, c
+
+  $ cd ../
+
+Verify we duplicate existing copies, instead of detecting them
+
+  $ hg init copydisable3
+  $ cd copydisable3
+  $ touch a
+  $ hg ci -Aqm 'add a'
+  $ hg cp a b
+  $ hg ci -Aqm 'copy a->b'
+  $ hg mv b c
+  $ hg ci -Aqm 'move b->c'
+  $ hg up -q 0
+  $ hg cp a b
+  $ echo b >> b
+  $ hg ci -Aqm 'copy a->b (2)'
+  $ hg log -G -T '{rev} {desc}\n'
+  @  3 copy a->b (2)
+  |
+  | o  2 move b->c
+  | |
+  | o  1 copy a->b
+  |/
+  o  0 add a
+  
+  $ hg rebase -d 2 -s 3 --config extensions.rebase= --config experimental.disablecopytrace=True
+  rebasing 3:47e1a9e6273b "copy a->b (2)" (tip)
+  saved backup bundle to $TESTTMP/copydisable3/.hg/strip-backup/47e1a9e6273b-2d099c59-backup.hg (glob)
+
+  $ hg log -G -f b
+  @  changeset:   3:76024fb4b05b
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     copy a->b (2)
+  |
+  o  changeset:   0:ac82d8b1f7c4
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     add a
+  
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-debian-packages.t	Thu Sep 03 18:32:17 2015 -0500
@@ -0,0 +1,20 @@
+#require test-repo slow debhelper
+
+Ensure debuild doesn't run the testsuite, as that could get silly.
+  $ DEB_BUILD_OPTIONS=nocheck
+  $ export DEB_BUILD_OPTIONS
+  $ OUTPUTDIR=`pwd`
+  $ export OUTPUTDIR
+
+  $ cd "$TESTDIR"/..
+  $ make deb > $OUTPUTDIR/build.log 2>&1
+  $ cd $OUTPUTDIR
+  $ ls *.deb
+  mercurial-common_*.deb (glob)
+  mercurial_*.deb (glob)
+main deb should have .so but no .py
+  $ dpkg --contents mercurial_*.deb | egrep '(localrepo|parsers)'
+  * ./usr/lib/python2.7/dist-packages/mercurial/parsers*.so (glob)
+mercurial-common should have py but no .so or pyc
+  $ dpkg --contents mercurial-common_*.deb | egrep '(localrepo|parsers)'
+  * ./usr/lib/python2.7/dist-packages/mercurial/localrepo.py (glob)
--- a/tests/test-default-push.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-default-push.t	Thu Sep 03 18:32:17 2015 -0500
@@ -18,7 +18,6 @@
 Push should provide a hint when both 'default' and 'default-push' not set:
   $ cd c
   $ hg push --config paths.default=
-  pushing to default-push
   abort: default repository not configured!
   (see the "path" section in "hg help config")
   [255]
--- a/tests/test-diff-change.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-diff-change.t	Thu Sep 03 18:32:17 2015 -0500
@@ -29,15 +29,59 @@
   -first
   +second
 
-Test dumb revspecs (issue3474)
+  $ cd ..
+
+Test dumb revspecs: top-level "x:y", "x:", ":y" and ":" ranges should be handled
+as pairs even if x == y, but not for "f(x:y)" nor "x::y" (issue3474, issue4774)
+
+  $ hg clone -q a dumbspec
+  $ cd dumbspec
+  $ echo "wdir" > file.txt
 
   $ hg diff -r 2:2
+  $ hg diff -r 2:.
+  $ hg diff -r 2:
+  $ hg diff -r :0
+  $ hg diff -r '2:first(2:2)'
+  $ hg diff -r 'first(2:2)' --nodates
+  diff -r bf5ff72eb7e0 file.txt
+  --- a/file.txt
+  +++ b/file.txt
+  @@ -1,1 +1,1 @@
+  -third
+  +wdir
+  $ hg diff -r 2::2 --nodates
+  diff -r bf5ff72eb7e0 file.txt
+  --- a/file.txt
+  +++ b/file.txt
+  @@ -1,1 +1,1 @@
+  -third
+  +wdir
   $ hg diff -r "2 and 1"
   abort: empty revision range
   [255]
 
+  $ cd ..
+
+  $ hg clone -qr0 a dumbspec-rev0
+  $ cd dumbspec-rev0
+  $ echo "wdir" > file.txt
+
+  $ hg diff -r :
+  $ hg diff -r 'first(:)' --nodates
+  diff -r 4bb65dda5db4 file.txt
+  --- a/file.txt
+  +++ b/file.txt
+  @@ -1,1 +1,1 @@
+  -first
+  +wdir
+
+  $ cd ..
+
 Testing diff --change when merge:
 
+  $ cd a
+
   $ for i in 1 2 3 4 5 6 7 8 9 10; do
   >    echo $i >> file.txt
   > done
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-docker-packaging.t	Thu Sep 03 18:32:17 2015 -0500
@@ -0,0 +1,27 @@
+#require test-repo slow docker
+
+Ensure debuild doesn't run the testsuite, as that could get silly.
+  $ DEB_BUILD_OPTIONS=nocheck
+  $ export DEB_BUILD_OPTIONS
+  $ OUTPUTDIR=`pwd`
+  $ export OUTPUTDIR
+
+  $ cd "$TESTDIR"/..
+  $ make docker-debian-jessie > $OUTPUTDIR/build.log 2>&1
+  $ cd $OUTPUTDIR
+  $ ls *.deb
+  mercurial-common_*.deb (glob)
+  mercurial_*.deb (glob)
+
+We check debian package contents with portable tools so that when
+we're on non-debian machines we can still test the packages that are
+built using docker.
+
+main deb should have .so but no .py
+  $ ar x mercurial_*.deb
+  $ tar tf data.tar* | egrep '(localrepo|parsers)'
+  ./usr/lib/python2.7/dist-packages/mercurial/parsers*.so (glob)
+mercurial-common should have .py but no .so or .pyc
+  $ ar x mercurial-common_*.deb
+  $ tar tf data.tar* | egrep '(localrepo|parsers)'
+  ./usr/lib/python2.7/dist-packages/mercurial/localrepo.py
--- a/tests/test-filecache.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-filecache.py	Thu Sep 03 18:32:17 2015 -0500
@@ -130,7 +130,7 @@
     util.cachestat.__init__ = originit
 
 def test_filecache_synced():
-    # test old behaviour that caused filecached properties to go out of sync
+    # test old behavior that caused filecached properties to go out of sync
     os.system('hg init && echo a >> a && hg ci -qAm.')
     repo = hg.repository(ui.ui())
     # first rollback clears the filecache, but changelog to stays in __dict__
--- a/tests/test-filelog.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-filelog.py	Thu Sep 03 18:32:17 2015 -0500
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 """
-Tests the behaviour of filelog w.r.t. data starting with '\1\n'
+Tests the behavior of filelog w.r.t. data starting with '\1\n'
 """
 from mercurial import ui, hg
 from mercurial.node import nullid, hex
--- a/tests/test-generaldelta.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-generaldelta.t	Thu Sep 03 18:32:17 2015 -0500
@@ -69,3 +69,37 @@
      rev    offset  length   base linkrev nodeid       p1           p2
        0         0       3      0       1 1406e7411862 000000000000 000000000000
 
+  $ cd ..
+
+Test format.aggressivemergedeltas
+
+  $ hg init --config format.generaldelta=1 aggressive
+  $ cd aggressive
+  $ touch a b c d e
+  $ hg commit -Aqm side1
+  $ hg up -q null
+  $ touch x y
+  $ hg commit -Aqm side2
+
+- Verify non-aggressive merge uses p1 (commit 1) as delta parent
+  $ hg merge -q 0
+  $ hg commit -q -m merge
+  $ hg debugindex -m
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0      59     -1       0 8dde941edb6e 000000000000 000000000000
+       1        59      59     -1       1 315c023f341d 000000000000 000000000000
+       2       118      65      1       2 2ab389a983eb 315c023f341d 8dde941edb6e
+
+  $ hg strip -q -r . --config extensions.strip=
+
+- Verify aggressive merge uses p2 (commit 0) as delta parent
+  $ hg up -q -C 1
+  $ hg merge -q 0
+  $ hg commit -q -m merge --config format.aggressivemergedeltas=True
+  $ hg debugindex -m
+     rev    offset  length  delta linkrev nodeid       p1           p2
+       0         0      59     -1       0 8dde941edb6e 000000000000 000000000000
+       1        59      59     -1       1 315c023f341d 000000000000 000000000000
+       2       118      62      0       2 2ab389a983eb 315c023f341d 8dde941edb6e
+
+  $ cd ..
--- a/tests/test-help.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-help.t	Thu Sep 03 18:32:17 2015 -0500
@@ -912,6 +912,36 @@
       working directory is checked out, it is equivalent to null. If an
       uncommitted merge is in progress, "." is the revision of the first parent.
 
+Test repeated config section name
+
+  $ hg help config.host
+      "http_proxy.host"
+          Host name and (optional) port of the proxy server, for example
+          "myproxy:8000".
+  
+      "smtp.host"
+          Host name of mail server, e.g. "mail.example.com".
+  
+Test capitalized section name
+
+  $ hg help scripting.HGPLAIN > /dev/null
+
+Help subsection:
+
+  $ hg help config.charsets |grep "Email example:" > /dev/null
+  [1]
+
+Last item in help config.*:
+
+  $ hg help config.`hg help config|grep '^    "'| \
+  >       tail -1|sed 's![ "]*!!g'`| \
+  >   grep "hg help -c config" > /dev/null
+  [1]
+
+note to use help -c for general hg help config:
+
+  $ hg help config |grep "hg help -c config" > /dev/null
+
 Test templating help
 
   $ hg help templating | egrep '(desc|diffstat|firstline|nonempty)  '
@@ -1202,6 +1232,11 @@
       ":tagmerge"
         Uses the internal tag merge algorithm (experimental).
   
+      ":union"
+        Uses the internal non-interactive simple merge algorithm for merging
+        files. It will use both left and right sides for conflict regions. No
+        markers are inserted.
+  
       Internal tools are always available and do not require a GUI but will by
       default not handle symlinks or binary files.
   
--- a/tests/test-hghave.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-hghave.t	Thu Sep 03 18:32:17 2015 -0500
@@ -18,7 +18,7 @@
   >   $ echo foo
   >   foo
   > EOF
-  $ run-tests.py test-hghaveaddon.t
+  $ run-tests.py $HGTEST_RUN_TESTS_PURE test-hghaveaddon.t
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
--- a/tests/test-hgweb-commands.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-hgweb-commands.t	Thu Sep 03 18:32:17 2015 -0500
@@ -1653,7 +1653,7 @@
   <tr class="parity0">
   <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td>
   <td><a class="list" href="/shortlog/cad8025a2e87?style=gitweb"><b>cad8025a2e87</b></a></td>
-  <td class="">unstable</td>
+  <td class="open">unstable</td>
   <td class="link">
   <a href="/changeset/cad8025a2e87?style=gitweb">changeset</a> |
   <a href="/log/cad8025a2e87?style=gitweb">changelog</a> |
@@ -1663,7 +1663,7 @@
   <tr class="parity1">
   <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td>
   <td><a class="list" href="/shortlog/1d22e65f027e?style=gitweb"><b>1d22e65f027e</b></a></td>
-  <td class="">stable</td>
+  <td class="inactive">stable</td>
   <td class="link">
   <a href="/changeset/1d22e65f027e?style=gitweb">changeset</a> |
   <a href="/log/1d22e65f027e?style=gitweb">changelog</a> |
@@ -1673,7 +1673,7 @@
   <tr class="parity0">
   <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td>
   <td><a class="list" href="/shortlog/a4f92ed23982?style=gitweb"><b>a4f92ed23982</b></a></td>
-  <td class="">default</td>
+  <td class="inactive">default</td>
   <td class="link">
   <a href="/changeset/a4f92ed23982?style=gitweb">changeset</a> |
   <a href="/log/a4f92ed23982?style=gitweb">changelog</a> |
--- a/tests/test-hgweb-descend-empties.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-hgweb-descend-empties.t	Thu Sep 03 18:32:17 2015 -0500
@@ -373,11 +373,6 @@
           <p><a href="http://mercurial.selenic.com/" title="Mercurial"><img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a></p>
       </div>
   
-      <div id="corner-top-left"></div>
-      <div id="corner-top-right"></div>
-      <div id="corner-bottom-left"></div>
-      <div id="corner-bottom-right"></div>
-  
   </div>
   
   </body>
--- a/tests/test-hgweb-symrev.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-hgweb-symrev.t	Thu Sep 03 18:32:17 2015 -0500
@@ -639,6 +639,7 @@
 (De)referencing symbolic revisions (monoblue)
 
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'summary?style=monoblue' | egrep $REVLINKS
+              <li><a href="/archive/tip.zip">zip</a></li>
   <a href="/rev/9d8c40cba617?style=monoblue">
   <a href="/rev/9d8c40cba617?style=monoblue">changeset</a> |
   <a href="/file/9d8c40cba617?style=monoblue">files</a>
--- a/tests/test-hgwebdir.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-hgwebdir.t	Thu Sep 03 18:32:17 2015 -0500
@@ -1245,6 +1245,67 @@
   $ get-with-headers.py localhost:$HGPORT2 'a/rss-log' | grep '<guid'
       <guid isPermaLink="true">http://hg.example.com:8080/foo/a/rev/8580ff50825a</guid>
 
+Path refreshing works as expected
+
+  $ killdaemons.py
+  $ mkdir $root/refreshtest
+  $ hg init $root/refreshtest/a
+  $ cat > paths.conf << EOF
+  > [paths]
+  > / = $root/refreshtest/*
+  > EOF
+  $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf
+  $ cat hg.pid >> $DAEMON_PIDS
+
+  $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
+  200 Script output follows
+  
+  
+  /a/
+  
+
+By default refreshing occurs every 20s and a new repo won't be listed
+immediately.
+
+  $ hg init $root/refreshtest/b
+  $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
+  200 Script output follows
+  
+  
+  /a/
+  
+
+Restart the server with no refresh interval. New repo should appear
+immediately.
+
+  $ killdaemons.py
+  $ cat > paths.conf << EOF
+  > [web]
+  > refreshinterval = -1
+  > [paths]
+  > / = $root/refreshtest/*
+  > EOF
+  $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf
+  $ cat hg.pid >> $DAEMON_PIDS
+
+  $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
+  200 Script output follows
+  
+  
+  /a/
+  /b/
+  
+
+  $ hg init $root/refreshtest/c
+  $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
+  200 Script output follows
+  
+  
+  /a/
+  /b/
+  /c/
+  
+
 paths errors 1
 
   $ cat error-paths-1.log
--- a/tests/test-histedit-arguments.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-histedit-arguments.t	Thu Sep 03 18:32:17 2015 -0500
@@ -69,7 +69,7 @@
   #  f, fold = use commit, but combine it with the one above
   #  r, roll = like fold, but discard this commit's description
   #  d, drop = remove commit from history
-  #  m, mess = edit message without changing commit content
+  #  m, mess = edit commit message without changing commit content
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
@@ -292,7 +292,7 @@
   #  f, fold = use commit, but combine it with the one above
   #  r, roll = like fold, but discard this commit's description
   #  d, drop = remove commit from history
-  #  m, mess = edit message without changing commit content
+  #  m, mess = edit commit message without changing commit content
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
--- a/tests/test-histedit-bookmark-motion.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-histedit-bookmark-motion.t	Thu Sep 03 18:32:17 2015 -0500
@@ -75,7 +75,7 @@
   #  f, fold = use commit, but combine it with the one above
   #  r, roll = like fold, but discard this commit's description
   #  d, drop = remove commit from history
-  #  m, mess = edit message without changing commit content
+  #  m, mess = edit commit message without changing commit content
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg histedit 1 --commands - --verbose << EOF | grep histedit
@@ -136,7 +136,7 @@
   #  f, fold = use commit, but combine it with the one above
   #  r, roll = like fold, but discard this commit's description
   #  d, drop = remove commit from history
-  #  m, mess = edit message without changing commit content
+  #  m, mess = edit commit message without changing commit content
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg histedit 1 --commands - --verbose << EOF | grep histedit
--- a/tests/test-histedit-commute.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-histedit-commute.t	Thu Sep 03 18:32:17 2015 -0500
@@ -69,7 +69,7 @@
   #  f, fold = use commit, but combine it with the one above
   #  r, roll = like fold, but discard this commit's description
   #  d, drop = remove commit from history
-  #  m, mess = edit message without changing commit content
+  #  m, mess = edit commit message without changing commit content
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
@@ -347,7 +347,7 @@
   #  f, fold = use commit, but combine it with the one above
   #  r, roll = like fold, but discard this commit's description
   #  d, drop = remove commit from history
-  #  m, mess = edit message without changing commit content
+  #  m, mess = edit commit message without changing commit content
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
--- a/tests/test-histedit-obsolete.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-histedit-obsolete.t	Thu Sep 03 18:32:17 2015 -0500
@@ -54,7 +54,7 @@
   #  f, fold = use commit, but combine it with the one above
   #  r, roll = like fold, but discard this commit's description
   #  d, drop = remove commit from history
-  #  m, mess = edit message without changing commit content
+  #  m, mess = edit commit message without changing commit content
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
--- a/tests/test-histedit-outgoing.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-histedit-outgoing.t	Thu Sep 03 18:32:17 2015 -0500
@@ -51,7 +51,7 @@
   #  f, fold = use commit, but combine it with the one above
   #  r, roll = like fold, but discard this commit's description
   #  d, drop = remove commit from history
-  #  m, mess = edit message without changing commit content
+  #  m, mess = edit commit message without changing commit content
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd ..
@@ -83,7 +83,7 @@
   #  f, fold = use commit, but combine it with the one above
   #  r, roll = like fold, but discard this commit's description
   #  d, drop = remove commit from history
-  #  m, mess = edit message without changing commit content
+  #  m, mess = edit commit message without changing commit content
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd ..
@@ -107,7 +107,7 @@
   #  f, fold = use commit, but combine it with the one above
   #  r, roll = like fold, but discard this commit's description
   #  d, drop = remove commit from history
-  #  m, mess = edit message without changing commit content
+  #  m, mess = edit commit message without changing commit content
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
--- a/tests/test-log.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-log.t	Thu Sep 03 18:32:17 2015 -0500
@@ -620,6 +620,21 @@
   $ hg up -C 1
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo b1 > b1
+
+log -r "follow('set:clean()')"
+
+  $ hg log -r "follow('set:clean()')"
+  changeset:   0:67e992f2c4f3
+  user:        test
+  date:        Thu Jan 01 00:00:01 1970 +0000
+  summary:     base
+  
+  changeset:   1:3d5bf5654eda
+  user:        test
+  date:        Thu Jan 01 00:00:01 1970 +0000
+  summary:     r1
+  
+
   $ hg ci -Amb1 -d '1 0'
   adding b1
   created new head
@@ -646,7 +661,26 @@
   summary:     base
   
 
+log -r follow('glob:b*')
 
+  $ hg log -r "follow('glob:b*')"
+  changeset:   0:67e992f2c4f3
+  user:        test
+  date:        Thu Jan 01 00:00:01 1970 +0000
+  summary:     base
+  
+  changeset:   1:3d5bf5654eda
+  user:        test
+  date:        Thu Jan 01 00:00:01 1970 +0000
+  summary:     r1
+  
+  changeset:   3:e62f78d544b4
+  tag:         tip
+  parent:      1:3d5bf5654eda
+  user:        test
+  date:        Thu Jan 01 00:00:01 1970 +0000
+  summary:     b1
+  
 log -f -r '1 + 4'
 
   $ hg up -C 0
@@ -673,6 +707,16 @@
   date:        Thu Jan 01 00:00:01 1970 +0000
   summary:     base
   
+log -r "follow('set:grep(b2)')"
+
+  $ hg log -r "follow('set:grep(b2)')"
+  changeset:   4:ddb82e70d1a1
+  tag:         tip
+  parent:      0:67e992f2c4f3
+  user:        test
+  date:        Thu Jan 01 00:00:01 1970 +0000
+  summary:     b2
+  
 log -f -r null
 
   $ hg log -f -r null
--- a/tests/test-merge-internal-tools-pattern.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-merge-internal-tools-pattern.t	Thu Sep 03 18:32:17 2015 -0500
@@ -1,5 +1,6 @@
-Make sure that the internal merge tools (internal:fail, internal:local, and
-internal:other) are used when matched by a merge-pattern in hgrc
+Make sure that the internal merge tools (internal:fail, internal:local,
+internal:union and internal:other) are used when matched by a
+merge-pattern in hgrc
 
 Make sure HGMERGE doesn't interfere with the test:
 
@@ -110,3 +111,31 @@
   $ hg stat
   M f
 
+Merge using internal:union tool:
+
+  $ hg update -C 2
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ echo "line 4a" >>f
+  $ hg ci -Am "Adding fourth line (commit 4)"
+  $ hg update 2
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ echo "line 4b" >>f
+  $ hg ci -Am "Adding fourth line v2 (commit 5)"
+  created new head
+
+  $ echo "[merge-patterns]" > .hg/hgrc
+  $ echo "* = internal:union" >> .hg/hgrc
+
+  $ hg merge 3
+  merging f
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+
+  $ cat f
+  line 1
+  line 2
+  third line
+  line 4b
+  line 4a
--- a/tests/test-module-imports.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-module-imports.t	Thu Sep 03 18:32:17 2015 -0500
@@ -112,22 +112,5 @@
 these may expose other cycles.
 
   $ 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
-  mercurial/fileset.py mixed imports
-     stdlib:    parser
-     relative:  error, merge, util
-  mercurial/revset.py mixed imports
-     stdlib:    parser
-     relative:  error, hbisect, phases, util
-  mercurial/templater.py mixed imports
-     stdlib:    parser
-     relative:  config, error, templatefilters, templatekw, util
-  mercurial/ui.py mixed imports
-     stdlib:    formatter
-     relative:  config, error, progress, 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
   [1]
--- a/tests/test-mq.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-mq.t	Thu Sep 03 18:32:17 2015 -0500
@@ -39,7 +39,7 @@
   
   By default, mq will automatically use git patches when required to avoid
   losing file mode changes, copy records, binary files or empty files creations
-  or deletions. This behaviour can be configured with:
+  or deletions. This behavior can be configured with:
   
     [mq]
     git = auto/keep/yes/no
--- a/tests/test-parseindex.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-parseindex.t	Thu Sep 03 18:32:17 2015 -0500
@@ -60,9 +60,66 @@
 
   $ cd ..
 
-Test corrupted p1/p2 fields that could cause SEGV at parsers.c:
+#if no-pure
+
+Test SEGV caused by bad revision passed to reachableroots() (issue4775):
+
+  $ cd a
 
-#if no-pure
+  $ python <<EOF
+  > from mercurial import changelog, scmutil
+  > cl = changelog.changelog(scmutil.vfs('.hg/store'))
+  > print 'good heads:'
+  > for head in [0, len(cl) - 1, -1]:
+  >     print'%s: %r' % (head, cl.reachableroots(0, [head], [0]))
+  > print 'bad heads:'
+  > for head in [len(cl), 10000, -2, -10000, None]:
+  >     print '%s:' % head,
+  >     try:
+  >         cl.reachableroots(0, [head], [0])
+  >         print 'uncaught buffer overflow?'
+  >     except (IndexError, TypeError) as inst:
+  >         print inst
+  > print 'good roots:'
+  > for root in [0, len(cl) - 1, -1]:
+  >     print '%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root]))
+  > print 'out-of-range roots are ignored:'
+  > for root in [len(cl), 10000, -2, -10000]:
+  >     print '%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root]))
+  > print 'bad roots:'
+  > for root in [None]:
+  >     print '%s:' % root,
+  >     try:
+  >         cl.reachableroots(root, [len(cl) - 1], [root])
+  >         print 'uncaught error?'
+  >     except TypeError as inst:
+  >         print inst
+  > EOF
+  good heads:
+  0: [0]
+  1: [0]
+  -1: []
+  bad heads:
+  2: head out of range
+  10000: head out of range
+  -2: head out of range
+  -10000: head out of range
+  None: an integer is required
+  good roots:
+  0: [0]
+  1: [1]
+  -1: [-1]
+  out-of-range roots are ignored:
+  2: []
+  10000: []
+  -2: []
+  -10000: []
+  bad roots:
+  None: an integer is required
+
+  $ cd ..
+
+Test corrupted p1/p2 fields that could cause SEGV at parsers.c:
 
   $ mkdir invalidparent
   $ cd invalidparent
@@ -94,6 +151,8 @@
   > cl = changelog.changelog(scmutil.vfs(sys.argv[1]))
   > n0, n1 = cl.node(0), cl.node(1)
   > ops = [
+  >     ('reachableroots',
+  >      lambda: cl.index.reachableroots2(0, [1], [0], False)),
   >     ('compute_phases_map_sets', lambda: cl.computephases([[0], []])),
   >     ('index_headrevs', lambda: cl.headrevs()),
   >     ('find_gca_candidates', lambda: cl.commonancestorsheads(n0, n1)),
@@ -109,11 +168,13 @@
   > EOF
 
   $ python test.py limit/.hg/store
+  reachableroots: parent out of range
   compute_phases_map_sets: parent out of range
   index_headrevs: parent out of range
   find_gca_candidates: parent out of range
   find_deepest: parent out of range
   $ python test.py segv/.hg/store
+  reachableroots: parent out of range
   compute_phases_map_sets: parent out of range
   index_headrevs: parent out of range
   find_gca_candidates: parent out of range
--- a/tests/test-pathencode.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-pathencode.py	Thu Sep 03 18:32:17 2015 -0500
@@ -2,7 +2,7 @@
 # time it is invoked, and tests the encoding of those pathnames.
 #
 # It uses a simple probabilistic model to generate valid pathnames
-# that have proven likely to expose bugs and divergent behaviour in
+# that have proven likely to expose bugs and divergent behavior in
 # different encoding implementations.
 
 from mercurial import store
--- a/tests/test-rebase-parameters.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-rebase-parameters.t	Thu Sep 03 18:32:17 2015 -0500
@@ -485,7 +485,6 @@
   $ hg resolve -m c2
   (no more unresolved files)
   $ hg rebase -c --tool internal:fail
-  tool option will be ignored
   rebasing 2:e4e3f3546619 "c2b" (tip)
   note: rebase of 2:e4e3f3546619 created no changes to commit
   saved backup bundle to $TESTTMP/b3/.hg/strip-backup/e4e3f3546619-b0841178-backup.hg (glob)
--- a/tests/test-revset.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-revset.t	Thu Sep 03 18:32:17 2015 -0500
@@ -141,7 +141,7 @@
     ('symbol', '3')
     ('symbol', '6'))
   * set:
-  <baseset [3, 5, 6]>
+  <baseset+ [3, 5, 6]>
   3
   5
   6
@@ -197,11 +197,53 @@
   <filteredset
     <baseset [7]>>
   7
-  $ try -- '-a-b-c-' # complains
-  hg: parse error at 7: not a prefix: end
-  [255]
-  $ log -a-b-c- # succeeds with fallback
+
+names that should be caught by fallback mechanism
+
+  $ try -- '-a-b-c-'
+  ('symbol', '-a-b-c-')
+  * set:
+  <baseset [4]>
+  4
+  $ log -a-b-c-
+  4
+  $ try '+a+b+c+'
+  ('symbol', '+a+b+c+')
+  * set:
+  <baseset [3]>
+  3
+  $ try '+a+b+c+:'
+  (rangepost
+    ('symbol', '+a+b+c+'))
+  * set:
+  <spanset+ 3:9>
+  3
   4
+  5
+  6
+  7
+  8
+  9
+  $ try ':+a+b+c+'
+  (rangepre
+    ('symbol', '+a+b+c+'))
+  * set:
+  <spanset+ 0:3>
+  0
+  1
+  2
+  3
+  $ try -- '-a-b-c-:+a+b+c+'
+  (range
+    ('symbol', '-a-b-c-')
+    ('symbol', '+a+b+c+'))
+  * set:
+  <spanset- 3:4>
+  4
+  3
+  $ log '-a-b-c-:+a+b+c+'
+  4
+  3
 
   $ try -- -a-b-c--a # complains
   (minus
@@ -949,7 +991,7 @@
       ('symbol', '4')))
   * set:
   <addset
-    <baseset [5, 3, 1]>,
+    <baseset- [1, 3, 5]>,
     <generatorset+>>
   5
   3
@@ -972,7 +1014,7 @@
   * set:
   <addset+
     <generatorset+>,
-    <baseset [5, 3, 1]>>
+    <baseset- [1, 3, 5]>>
   0
   1
   2
@@ -1473,10 +1515,16 @@
 (single rev)
 
   $ hg diff -r 'tip^' -r 'tip^'
-  $ hg diff -r 'tip^::tip^ or tip^'
+  $ hg diff -r 'tip^:tip^'
 
 (single rev that does not looks like a range)
 
+  $ hg diff -r 'tip^::tip^ or tip^'
+  diff -r d5d0dcbdc4d9 .hgtags
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/.hgtags	* (glob)
+  @@ -0,0 +1,1 @@
+  +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
   $ hg diff -r 'tip^ or tip^'
   diff -r d5d0dcbdc4d9 .hgtags
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
--- a/tests/test-run-tests.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-run-tests.t	Thu Sep 03 18:32:17 2015 -0500
@@ -580,7 +580,7 @@
   >   $ echo foo
   >   foo
   > EOF
-  $ run-tests.py test-hghave.t
+  $ run-tests.py $HGTEST_RUN_TESTS_PURE test-hghave.t
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
@@ -599,7 +599,7 @@
   >   #
   >   # check-code - a style and portability checker for Mercurial
   > EOF
-  $ run-tests.py test-runtestdir.t
+  $ run-tests.py $HGTEST_RUN_TESTS_PURE test-runtestdir.t
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
@@ -616,8 +616,22 @@
   >   $ custom-command.sh
   >   hello world
   > EOF
-  $ run-tests.py test-testdir-path.t
+  $ run-tests.py $HGTEST_RUN_TESTS_PURE test-testdir-path.t
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
 #endif
+
+test support for --allow-slow-tests
+  $ cat > test-very-slow-test.t <<EOF
+  > #require slow
+  >   $ echo pass
+  >   pass
+  > EOF
+  $ run-tests.py $HGTEST_RUN_TESTS_PURE test-very-slow-test.t
+  s
+  Skipped test-very-slow-test.t: skipped
+  # Ran 0 tests, 1 skipped, 0 warned, 0 failed.
+  $ run-tests.py $HGTEST_RUN_TESTS_PURE --allow-slow-tests test-very-slow-test.t
+  .
+  # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
--- a/tests/test-ssh-bundle1.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-ssh-bundle1.t	Thu Sep 03 18:32:17 2015 -0500
@@ -43,14 +43,14 @@
 repo not found error
 
   $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
-  remote: abort: there is no Mercurial repository here (.hg not found)!
+  remote: abort: repository nonexistent not found!
   abort: no suitable response from remote hg!
   [255]
 
 non-existent absolute path
 
   $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
-  remote: abort: there is no Mercurial repository here (.hg not found)!
+  remote: abort: repository /$TESTTMP/nonexistent not found!
   abort: no suitable response from remote hg!
   [255]
 
@@ -128,7 +128,7 @@
 
   $ hg pull -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
   pulling from ssh://user@dummy/doesnotexist
-  remote: abort: there is no Mercurial repository here (.hg not found)!
+  remote: abort: repository doesnotexist not found!
   abort: no suitable response from remote hg!
   [255]
 
--- a/tests/test-ssh.t	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-ssh.t	Thu Sep 03 18:32:17 2015 -0500
@@ -34,14 +34,14 @@
 repo not found error
 
   $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
-  remote: abort: there is no Mercurial repository here (.hg not found)!
+  remote: abort: repository nonexistent not found!
   abort: no suitable response from remote hg!
   [255]
 
 non-existent absolute path
 
-  $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
-  remote: abort: there is no Mercurial repository here (.hg not found)!
+  $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/`pwd`/nonexistent local
+  remote: abort: repository $TESTTMP/nonexistent not found!
   abort: no suitable response from remote hg!
   [255]
 
@@ -119,7 +119,7 @@
 
   $ hg pull -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
   pulling from ssh://user@dummy/doesnotexist
-  remote: abort: there is no Mercurial repository here (.hg not found)!
+  remote: abort: repository doesnotexist not found!
   abort: no suitable response from remote hg!
   [255]
 
@@ -471,7 +471,7 @@
 
   $ cat dummylog
   Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
-  Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
+  Got arguments 1:user@dummy 2:hg -R $TESTTMP/nonexistent serve --stdio
   Got arguments 1:user@dummy 2:hg -R remote serve --stdio
   Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
   Got arguments 1:user@dummy 2:hg -R remote serve --stdio
--- a/tests/test-wireproto.py	Thu Sep 03 22:25:16 2015 +0800
+++ b/tests/test-wireproto.py	Thu Sep 03 18:32:17 2015 -0500
@@ -12,6 +12,10 @@
 class clientpeer(wireproto.wirepeer):
     def __init__(self, serverrepo):
         self.serverrepo = serverrepo
+
+    def _capabilities(self):
+        return ['batch']
+
     def _call(self, cmd, **args):
         return wireproto.dispatch(self.serverrepo, proto(args), cmd)