changeset 32245:3a755652ce3a

merge with stable
author Martin von Zweigbergk <martinvonz@google.com>
date Fri, 12 May 2017 11:20:25 -0700
parents 3de4c61b5087 (diff) 176ed32dc159 (current diff)
children 7e79373263ab
files hgext/win32mbcs.py mercurial/commands.py tests/hghave.py tests/test-graft.t tests/test-https.t
diffstat 86 files changed, 1007 insertions(+), 748 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Fri May 12 21:46:14 2017 +0900
+++ b/.hgignore	Fri May 12 11:20:25 2017 -0700
@@ -54,14 +54,6 @@
 locale/*/LC_MESSAGES/hg.mo
 hgext/__index__.py
 
-# files installed with a local --pure build
-mercurial/base85.py
-mercurial/bdiff.py
-mercurial/diffhelpers.py
-mercurial/mpatch.py
-mercurial/osutil.py
-mercurial/parsers.py
-
 # Generated wheels
 wheelhouse/
 
--- a/Makefile	Fri May 12 21:46:14 2017 +0900
+++ b/Makefile	Fri May 12 11:20:25 2017 -0700
@@ -64,7 +64,6 @@
 	-$(PYTHON) setup.py clean --all # ignore errors from this command
 	find contrib doc hgext hgext3rd i18n mercurial tests \
 		\( -name '*.py[cdo]' -o -name '*.so' \) -exec rm -f '{}' ';'
-	rm -f $(addprefix mercurial/,$(notdir $(wildcard mercurial/pure/[a-z]*.py)))
 	rm -f MANIFEST MANIFEST.in hgext/__index__.py tests/*.err
 	rm -f mercurial/__modulepolicy__.py
 	if test -d .hg; then rm -f mercurial/__version__.py; fi
--- a/contrib/bdiff-torture.py	Fri May 12 21:46:14 2017 +0900
+++ b/contrib/bdiff-torture.py	Fri May 12 11:20:25 2017 -0700
@@ -5,8 +5,7 @@
 import sys
 
 from mercurial import (
-    bdiff,
-    mpatch,
+    mdiff,
 )
 
 def reducetest(a, b):
@@ -42,10 +41,10 @@
     sys.exit(0)
 
 def test1(a, b):
-    d = bdiff.bdiff(a, b)
+    d = mdiff.textdiff(a, b)
     if not d:
         raise ValueError("empty")
-    c = mpatch.patches(a, [d])
+    c = mdiff.patches(a, [d])
     if c != b:
         raise ValueError("bad")
 
--- a/contrib/check-code.py	Fri May 12 21:46:14 2017 +0900
+++ b/contrib/check-code.py	Fri May 12 11:20:25 2017 -0700
@@ -474,7 +474,7 @@
 
 py3pats = [
   [
-    (r'os\.environ', "use encoding.environ instead (py3)"),
+    (r'os\.environ', "use encoding.environ instead (py3)", r'#.*re-exports'),
     (r'os\.name', "use pycompat.osname instead (py3)"),
     (r'os\.getcwd', "use pycompat.getcwd instead (py3)"),
     (r'os\.sep', "use pycompat.ossep instead (py3)"),
@@ -492,8 +492,8 @@
 checks = [
     ('python', r'.*\.(py|cgi)$', r'^#!.*python', pyfilters, pypats),
     ('python', r'.*hgext.*\.py$', '', [], pyextnfpats),
-    ('python 3', r'.*(hgext|mercurial).*(?<!pycompat)\.py', '',
-            pyfilters, py3pats),
+    ('python 3', r'.*(hgext|mercurial)/(?!demandimport|policy|pycompat).*\.py',
+     '', pyfilters, py3pats),
     ('test script', r'(.*/)?test-[^.~]*$', '', testfilters, testpats),
     ('c', r'.*\.[ch]$', '', cfilters, cpats),
     ('unified test', r'.*\.t$', '', utestfilters, utestpats),
--- a/contrib/check-py3-compat.py	Fri May 12 21:46:14 2017 +0900
+++ b/contrib/check-py3-compat.py	Fri May 12 11:20:25 2017 -0700
@@ -14,6 +14,16 @@
 import sys
 import traceback
 
+# Modules that have both Python and C implementations.
+_dualmodules = (
+    'base85.py',
+    'bdiff.py',
+    'diffhelpers.py',
+    'mpatch.py',
+    'osutil.py',
+    'parsers.py',
+)
+
 def check_compat_py2(f):
     """Check Python 3 compatibility for a file with Python 2"""
     with open(f, 'rb') as fh:
@@ -55,7 +65,9 @@
     # out module paths for things not in a package can be confusing.
     if f.startswith(('hgext/', 'mercurial/')) and not f.endswith('__init__.py'):
         assert f.endswith('.py')
-        name = f.replace('/', '.')[:-3].replace('.pure.', '.')
+        name = f.replace('/', '.')[:-3]
+        if f.endswith(_dualmodules):
+            name = name.replace('.pure.', '.')
         try:
             importlib.import_module(name)
         except Exception as e:
--- a/contrib/debian/control	Fri May 12 21:46:14 2017 +0900
+++ b/contrib/debian/control	Fri May 12 11:20:25 2017 -0700
@@ -12,7 +12,7 @@
  unzip,
  zip
 Standards-Version: 3.9.4
-X-Python-Version: >= 2.6
+X-Python-Version: >= 2.7
 
 Package: mercurial
 Depends:
--- a/contrib/import-checker.py	Fri May 12 21:46:14 2017 +0900
+++ b/contrib/import-checker.py	Fri May 12 11:20:25 2017 -0700
@@ -24,6 +24,16 @@
     'mercurial.node',
 )
 
+# Modules that have both Python and C implementations.
+_dualmodules = (
+    'base85.py',
+    'bdiff.py',
+    'diffhelpers.py',
+    'mpatch.py',
+    'osutil.py',
+    'parsers.py',
+)
+
 # Modules that must be aliased because they are commonly confused with
 # common variables and can create aliasing and readability issues.
 requirealias = {
@@ -691,7 +701,8 @@
     used_imports = {}
     any_errors = False
     for source_path in argv[1:]:
-        modname = dotted_name_of_path(source_path, trimpure=True)
+        trimpure = source_path.endswith(_dualmodules)
+        modname = dotted_name_of_path(source_path, trimpure=trimpure)
         localmods[modname] = source_path
     for localmodname, source_path in sorted(localmods.items()):
         for src, modname, name, line in sources(source_path, localmodname):
--- a/contrib/mercurial.spec	Fri May 12 21:46:14 2017 +0900
+++ b/contrib/mercurial.spec	Fri May 12 11:20:25 2017 -0700
@@ -37,8 +37,8 @@
 %if "%{?withpython}"
 BuildRequires: readline-devel, openssl-devel, ncurses-devel, zlib-devel, bzip2-devel
 %else
-BuildRequires: python >= 2.6, python-devel, python-docutils >= 0.5
-Requires: python >= 2.6
+BuildRequires: python >= 2.7, python-devel, python-docutils >= 0.5
+Requires: python >= 2.7
 %endif
 # The hgk extension uses the wish tcl interpreter, but we don't enforce it
 #Requires: tk
--- a/contrib/perf.py	Fri May 12 21:46:14 2017 +0900
+++ b/contrib/perf.py	Fri May 12 11:20:25 2017 -0700
@@ -26,7 +26,6 @@
 import sys
 import time
 from mercurial import (
-    bdiff,
     changegroup,
     cmdutil,
     commands,
@@ -812,7 +811,7 @@
 
     def d():
         for pair in textpairs:
-            bdiff.bdiff(*pair)
+            mdiff.textdiff(*pair)
 
     timer, fm = gettimer(ui, opts)
     timer(d)
@@ -851,23 +850,24 @@
 
     The start revision can be defined via ``-s/--startrev``.
     """
-    timer, fm = gettimer(ui, opts)
-    _len = getlen(ui)
+    rl = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
+    rllen = getlen(ui)(rl)
 
     def d():
-        r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
+        rl.clearcaches()
 
-        startrev = 0
-        endrev = _len(r)
+        beginrev = startrev
+        endrev = rllen
         dist = opts['dist']
 
         if reverse:
-            startrev, endrev = endrev, startrev
+            beginrev, endrev = endrev, beginrev
             dist = -1 * dist
 
-        for x in xrange(startrev, endrev, dist):
-            r.revision(r.node(x))
+        for x in xrange(beginrev, endrev, dist):
+            rl.revision(x)
 
+    timer, fm = gettimer(ui, opts)
     timer(d)
     fm.end()
 
@@ -889,6 +889,12 @@
     """
     rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
 
+    # _chunkraw was renamed to _getsegmentforrevs.
+    try:
+        segmentforrevs = rl._getsegmentforrevs
+    except AttributeError:
+        segmentforrevs = rl._chunkraw
+
     # Verify engines argument.
     if engines:
         engines = set(e.strip() for e in engines.split(','))
@@ -919,22 +925,22 @@
     def doread():
         rl.clearcaches()
         for rev in revs:
-            rl._chunkraw(rev, rev)
+            segmentforrevs(rev, rev)
 
     def doreadcachedfh():
         rl.clearcaches()
         fh = rlfh(rl)
         for rev in revs:
-            rl._chunkraw(rev, rev, df=fh)
+            segmentforrevs(rev, rev, df=fh)
 
     def doreadbatch():
         rl.clearcaches()
-        rl._chunkraw(revs[0], revs[-1])
+        segmentforrevs(revs[0], revs[-1])
 
     def doreadbatchcachedfh():
         rl.clearcaches()
         fh = rlfh(rl)
-        rl._chunkraw(revs[0], revs[-1], df=fh)
+        segmentforrevs(revs[0], revs[-1], df=fh)
 
     def dochunk():
         rl.clearcaches()
@@ -1003,6 +1009,13 @@
         raise error.CommandError('perfrevlogrevision', 'invalid arguments')
 
     r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
+
+    # _chunkraw was renamed to _getsegmentforrevs.
+    try:
+        segmentforrevs = r._getsegmentforrevs
+    except AttributeError:
+        segmentforrevs = r._chunkraw
+
     node = r.lookup(rev)
     rev = r.rev(node)
 
@@ -1034,7 +1047,7 @@
     def doread(chain):
         if not cache:
             r.clearcaches()
-        r._chunkraw(chain[0], chain[-1])
+        segmentforrevs(chain[0], chain[-1])
 
     def dorawchunks(data, chain):
         if not cache:
@@ -1062,7 +1075,7 @@
         r.revision(node)
 
     chain = r._deltachain(rev)[0]
-    data = r._chunkraw(chain[0], chain[-1])[1]
+    data = segmentforrevs(chain[0], chain[-1])[1]
     rawchunks = getrawchunks(data, chain)
     bins = r._chunks(chain)
     text = str(bins[0])
--- a/hgext/blackbox.py	Fri May 12 21:46:14 2017 +0900
+++ b/hgext/blackbox.py	Fri May 12 11:20:25 2017 -0700
@@ -173,7 +173,7 @@
                 ui._bbinlog = True
                 date = util.datestr(None, '%Y/%m/%d %H:%M:%S')
                 user = util.getuser()
-                pid = str(util.getpid())
+                pid = '%d' % util.getpid()
                 formattedmsg = msg[0] % msg[1:]
                 rev = '(unknown)'
                 changed = ''
--- a/hgext/extdiff.py	Fri May 12 21:46:14 2017 +0900
+++ b/hgext/extdiff.py	Fri May 12 11:20:25 2017 -0700
@@ -101,7 +101,7 @@
         dirname = '%s.%s' % (dirname, short(node))
     base = os.path.join(tmproot, dirname)
     os.mkdir(base)
-    fns_and_mtime = []
+    fnsandstat = []
 
     if node is not None:
         ui.note(_('making snapshot of %d files from rev %s\n') %
@@ -124,9 +124,8 @@
             if node is None:
                 dest = os.path.join(base, wfn)
 
-                fns_and_mtime.append((dest, repo.wjoin(fn),
-                                      os.lstat(dest).st_mtime))
-    return dirname, fns_and_mtime
+                fnsandstat.append((dest, repo.wjoin(fn), os.lstat(dest)))
+    return dirname, fnsandstat
 
 def dodiff(ui, repo, cmdline, pats, opts):
     '''Do the actual diff:
@@ -199,7 +198,7 @@
                 dir1b = None
                 rev1b = ''
 
-            fns_and_mtime = []
+            fnsandstat = []
 
             # If node2 in not the wc or there is >1 change, copy it
             dir2root = ''
@@ -212,8 +211,8 @@
                 #the working dir in this case (because the other cases
                 #are: diffing 2 revisions or single file -- in which case
                 #the file is already directly passed to the diff tool).
-                dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot,
-                                               subrepos)
+                dir2, fnsandstat = snapshot(ui, repo, modadd, None, tmproot,
+                                            subrepos)
             else:
                 # This lets the diff tool open the changed file directly
                 dir2 = ''
@@ -249,7 +248,7 @@
             dir2 = repo.vfs.reljoin(tmproot, label2)
             dir1b = None
             label1b = None
-            fns_and_mtime = []
+            fnsandstat = []
 
         # Function to quote file/dir names in the argument string.
         # When not operating in 3-way mode, an empty string is
@@ -275,8 +274,13 @@
         ui.debug('running %r in %s\n' % (cmdline, tmproot))
         ui.system(cmdline, cwd=tmproot, blockedtag='extdiff')
 
-        for copy_fn, working_fn, mtime in fns_and_mtime:
-            if os.lstat(copy_fn).st_mtime != mtime:
+        for copy_fn, working_fn, st in fnsandstat:
+            cpstat = os.lstat(copy_fn)
+            # Some tools copy the file and attributes, so mtime may not detect
+            # all changes.  A size check will detect more cases, but not all.
+            # The only certain way to detect every case is to diff all files,
+            # which could be expensive.
+            if cpstat.st_mtime != st.st_mtime or cpstat.st_size != st.st_size:
                 ui.debug('file changed while diffing. '
                          'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
                 util.copyfile(copy_fn, working_fn)
--- a/hgext/fsmonitor/__init__.py	Fri May 12 21:46:14 2017 +0900
+++ b/hgext/fsmonitor/__init__.py	Fri May 12 11:20:25 2017 -0700
@@ -692,11 +692,13 @@
 
         # at this point since fsmonitorstate wasn't present, repo.dirstate is
         # not a fsmonitordirstate
-        repo.dirstate.__class__ = makedirstate(repo.dirstate.__class__)
-        # nuke the dirstate so that _fsmonitorinit and subsequent configuration
-        # changes take effect on it
-        del repo._filecache['dirstate']
-        delattr(repo.unfiltered(), 'dirstate')
+        dirstate = repo.dirstate
+        dirstate.__class__ = makedirstate(dirstate.__class__)
+        dirstate._fsmonitorinit(fsmonitorstate, client)
+        # invalidate property cache, but keep filecache which contains the
+        # wrapped dirstate object
+        del repo.unfiltered().__dict__['dirstate']
+        assert dirstate is repo._filecache['dirstate'].obj
 
         class fsmonitorrepo(repo.__class__):
             def status(self, *args, **kwargs):
--- a/hgext/largefiles/overrides.py	Fri May 12 21:46:14 2017 +0900
+++ b/hgext/largefiles/overrides.py	Fri May 12 11:20:25 2017 -0700
@@ -105,9 +105,9 @@
             scmutil.matchandpats)
 
 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
-    large = opts.get('large')
+    large = opts.get(r'large')
     lfsize = lfutil.getminsize(
-        ui, lfutil.islfilesrepo(repo), opts.get('lfsize'))
+        ui, lfutil.islfilesrepo(repo), opts.get(r'lfsize'))
 
     lfmatcher = None
     if lfutil.islfilesrepo(repo):
@@ -258,7 +258,7 @@
 
 def cmdutiladd(orig, ui, repo, matcher, prefix, explicitonly, **opts):
     # The --normal flag short circuits this override
-    if opts.get('normal'):
+    if opts.get(r'normal'):
         return orig(ui, repo, matcher, prefix, explicitonly, **opts)
 
     ladded, lbad = addlargefiles(ui, repo, False, matcher, **opts)
--- a/hgext/mq.py	Fri May 12 21:46:14 2017 +0900
+++ b/hgext/mq.py	Fri May 12 11:20:25 2017 -0700
@@ -3540,7 +3540,7 @@
     """Add --mq option to operate on patch repository instead of main"""
 
     # some commands do not like getting unknown options
-    mq = kwargs.pop('mq', None)
+    mq = kwargs.pop(r'mq', None)
 
     if not mq:
         return orig(ui, repo, *args, **kwargs)
--- a/hgext/rebase.py	Fri May 12 21:46:14 2017 +0900
+++ b/hgext/rebase.py	Fri May 12 11:20:25 2017 -0700
@@ -1246,7 +1246,7 @@
     if not roots:
         raise error.Abort(_('no matching revisions'))
     roots.sort()
-    state = {}
+    state = dict.fromkeys(rebaseset, revtodo)
     detachset = set()
     for root in roots:
         commonbase = root.ancestor(dest)
@@ -1264,7 +1264,6 @@
                 return None
 
         repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
-        state.update(dict.fromkeys(rebaseset, revtodo))
         # Rebase tries to turn <dest> into a parent of <root> while
         # preserving the number of parents of rebased changesets:
         #
--- a/hgext/win32mbcs.py	Fri May 12 21:46:14 2017 +0900
+++ b/hgext/win32mbcs.py	Fri May 12 11:20:25 2017 -0700
@@ -183,7 +183,8 @@
         if pycompat.osname == 'nt':
             for f in winfuncs.split():
                 wrapname(f, wrapper)
-        wrapname("mercurial.osutil.listdir", wrapperforlistdir)
+        wrapname("mercurial.util.listdir", wrapperforlistdir)
+        wrapname("mercurial.windows.listdir", wrapperforlistdir)
         # wrap functions to be called with local byte string arguments
         for f in rfuncs.split():
             wrapname(f, reversewrapper)
--- a/mercurial/__init__.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/__init__.py	Fri May 12 11:20:25 2017 -0700
@@ -165,7 +165,8 @@
             if not spec:
                 return None
 
-            if fullname.startswith('mercurial.pure.'):
+            if (fullname.startswith('mercurial.pure.')
+                and fullname.replace('.pure.', '.') in _dualmodules):
                 spec.name = spec.name.replace('.pure.', '.')
 
             # TODO need to support loaders from alternate specs, like zip
--- a/mercurial/branchmap.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/branchmap.py	Fri May 12 11:20:25 2017 -0700
@@ -67,8 +67,6 @@
             partial.setdefault(label, []).append(node)
             if state == 'c':
                 partial._closednodes.add(node)
-    except KeyboardInterrupt:
-        raise
     except Exception as inst:
         if repo.ui.debugflag:
             msg = 'invalid branchheads cache'
--- a/mercurial/bundle2.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/bundle2.py	Fri May 12 11:20:25 2017 -0700
@@ -1005,7 +1005,7 @@
             # backup exception data for later
             ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
                      % exc)
-            exc_info = sys.exc_info()
+            tb = sys.exc_info()[2]
             msg = 'unexpected error: %s' % exc
             interpart = bundlepart('error:abort', [('message', msg)],
                                    mandatory=False)
@@ -1016,10 +1016,7 @@
             outdebug(ui, 'closing payload chunk')
             # abort current part payload
             yield _pack(_fpayloadsize, 0)
-            if pycompat.ispy3:
-                raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
-            else:
-                exec("""raise exc_info[0], exc_info[1], exc_info[2]""")
+            pycompat.raisewithtb(exc, tb)
         # end of payload
         outdebug(ui, 'closing payload chunk')
         yield _pack(_fpayloadsize, 0)
@@ -1342,6 +1339,68 @@
     obscaps = caps.get('obsmarkers', ())
     return [int(c[1:]) for c in obscaps if c.startswith('V')]
 
+def writenewbundle(ui, repo, source, filename, bundletype, outgoing, opts,
+                   vfs=None, compression=None, compopts=None):
+    if bundletype.startswith('HG10'):
+        cg = changegroup.getchangegroup(repo, source, outgoing, version='01')
+        return writebundle(ui, cg, filename, bundletype, vfs=vfs,
+                           compression=compression, compopts=compopts)
+    elif not bundletype.startswith('HG20'):
+        raise error.ProgrammingError('unknown bundle type: %s' % bundletype)
+
+    bundle = bundle20(ui)
+    bundle.setcompression(compression, compopts)
+    _addpartsfromopts(ui, repo, bundle, source, outgoing, opts)
+    chunkiter = bundle.getchunks()
+
+    return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
+
+def _addpartsfromopts(ui, repo, bundler, source, outgoing, opts):
+    # We should eventually reconcile this logic with the one behind
+    # 'exchange.getbundle2partsgenerator'.
+    #
+    # The type of input from 'getbundle' and 'writenewbundle' are a bit
+    # different right now. So we keep them separated for now for the sake of
+    # simplicity.
+
+    # we always want a changegroup in such bundle
+    cgversion = opts.get('cg.version')
+    if cgversion is None:
+        cgversion = changegroup.safeversion(repo)
+    cg = changegroup.getchangegroup(repo, source, outgoing,
+                                    version=cgversion)
+    part = bundler.newpart('changegroup', data=cg.getchunks())
+    part.addparam('version', cg.version)
+    if 'clcount' in cg.extras:
+        part.addparam('nbchanges', str(cg.extras['clcount']),
+                      mandatory=False)
+
+    addparttagsfnodescache(repo, bundler, outgoing)
+
+def addparttagsfnodescache(repo, bundler, outgoing):
+    # we include the tags fnode cache for the bundle changeset
+    # (as an optional parts)
+    cache = tags.hgtagsfnodescache(repo.unfiltered())
+    chunks = []
+
+    # .hgtags fnodes are only relevant for head changesets. While we could
+    # transfer values for all known nodes, there will likely be little to
+    # no benefit.
+    #
+    # We don't bother using a generator to produce output data because
+    # a) we only have 40 bytes per head and even esoteric numbers of heads
+    # consume little memory (1M heads is 40MB) b) we don't want to send the
+    # part if we don't have entries and knowing if we have entries requires
+    # cache lookups.
+    for node in outgoing.missingheads:
+        # Don't compute missing, as this may slow down serving.
+        fnode = cache.getfnode(node, computemissing=False)
+        if fnode is not None:
+            chunks.extend([node, fnode])
+
+    if chunks:
+        bundler.newpart('hgtagsfnodes', data=''.join(chunks))
+
 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None,
                 compopts=None):
     """Write a bundle file and return its filename.
--- a/mercurial/changegroup.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/changegroup.py	Fri May 12 11:20:25 2017 -0700
@@ -502,16 +502,9 @@
 class cg1packer(object):
     deltaheader = _CHANGEGROUPV1_DELTA_HEADER
     version = '01'
-    def __init__(self, repo, bundlecaps=None):
+    def __init__(self, repo):
         """Given a source repo, construct a bundler.
-
-        bundlecaps is optional and can be used to specify the set of
-        capabilities which can be used to build the bundle.
         """
-        # Set of capabilities we can use to build the bundle.
-        if bundlecaps is None:
-            bundlecaps = set()
-        self._bundlecaps = bundlecaps
         # experimental config: bundle.reorder
         reorder = repo.ui.config('bundle', 'reorder', 'auto')
         if reorder == 'auto':
@@ -815,8 +808,8 @@
     version = '02'
     deltaheader = _CHANGEGROUPV2_DELTA_HEADER
 
-    def __init__(self, repo, bundlecaps=None):
-        super(cg2packer, self).__init__(repo, bundlecaps)
+    def __init__(self, repo):
+        super(cg2packer, self).__init__(repo)
         if self._reorder is None:
             # Since generaldelta is directly supported by cg2, reordering
             # generally doesn't help, so we disable it by default (treating
@@ -910,9 +903,9 @@
     assert versions
     return min(versions)
 
-def getbundler(version, repo, bundlecaps=None):
+def getbundler(version, repo):
     assert version in supportedoutgoingversions(repo)
-    return _packermap[version][0](repo, bundlecaps)
+    return _packermap[version][0](repo)
 
 def getunbundler(version, fh, alg, extras=None):
     return _packermap[version][1](fh, alg, extras=extras)
@@ -963,40 +956,30 @@
     bundler = getbundler(version, repo)
     return getsubset(repo, outgoing, bundler, source)
 
-def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None,
-                           version='01'):
+def getlocalchangegroupraw(repo, source, outgoing, version='01'):
     """Like getbundle, but taking a discovery.outgoing as an argument.
 
     This is only implemented for local repos and reuses potentially
     precomputed sets in outgoing. Returns a raw changegroup generator."""
     if not outgoing.missing:
         return None
-    bundler = getbundler(version, repo, bundlecaps)
+    bundler = getbundler(version, repo)
     return getsubsetraw(repo, outgoing, bundler, source)
 
-def getlocalchangegroup(repo, source, outgoing, bundlecaps=None,
-                        version='01'):
+def getchangegroup(repo, source, outgoing, version='01'):
     """Like getbundle, but taking a discovery.outgoing as an argument.
 
     This is only implemented for local repos and reuses potentially
     precomputed sets in outgoing."""
     if not outgoing.missing:
         return None
-    bundler = getbundler(version, repo, bundlecaps)
+    bundler = getbundler(version, repo)
     return getsubset(repo, outgoing, bundler, source)
 
-def getchangegroup(repo, source, outgoing, bundlecaps=None,
-                   version='01'):
-    """Like changegroupsubset, but returns the set difference between the
-    ancestors of heads and the ancestors common.
-
-    If heads is None, use the local heads. If common is None, use [nullid].
-
-    The nodes in common might not all be known locally due to the way the
-    current discovery protocol works.
-    """
-    return getlocalchangegroup(repo, source, outgoing, bundlecaps=bundlecaps,
-                               version=version)
+def getlocalchangegroup(repo, *args, **kwargs):
+    repo.ui.deprecwarn('getlocalchangegroup is deprecated, use getchangegroup',
+                       '4.3')
+    return getchangegroup(repo, *args, **kwargs)
 
 def changegroup(repo, basenodes, source):
     # to avoid a race we use changegroupsubset() (issue1320)
--- a/mercurial/changelog.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/changelog.py	Fri May 12 11:20:25 2017 -0700
@@ -190,7 +190,7 @@
 
         # The list of files may be empty. Which means nl3 is the first of the
         # double newline that precedes the description.
-        if text[nl3 + 1] == '\n':
+        if text[nl3 + 1:nl3 + 2] == '\n':
             doublenl = nl3
         else:
             doublenl = text.index('\n\n', nl3 + 1)
--- a/mercurial/chgserver.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/chgserver.py	Fri May 12 11:20:25 2017 -0700
@@ -44,6 +44,7 @@
 import inspect
 import os
 import re
+import socket
 import struct
 import time
 
@@ -54,7 +55,6 @@
     encoding,
     error,
     extensions,
-    osutil,
     pycompat,
     util,
 )
@@ -313,7 +313,7 @@
         # tell client to sendmsg() with 1-byte payload, which makes it
         # distinctive from "attachio\n" command consumed by client.read()
         self.clientsock.sendall(struct.pack('>cI', 'I', 1))
-        clientfds = osutil.recvfds(self.clientsock.fileno())
+        clientfds = util.recvfds(self.clientsock.fileno())
         _log('received fds: %r\n' % clientfds)
 
         ui = self.ui
@@ -458,12 +458,12 @@
                          'setenv': setenv,
                          'setumask': setumask})
 
-    if util.safehasattr(osutil, 'setprocname'):
+    if util.safehasattr(util, 'setprocname'):
         def setprocname(self):
             """Change process title"""
             name = self._readstr()
             _log('setprocname: %r\n' % name)
-            osutil.setprocname(name)
+            util.setprocname(name)
         capabilities['setprocname'] = setprocname
 
 def _tempaddress(address):
@@ -492,6 +492,7 @@
         self._checkextensions()
         self._bind(sock)
         self._createsymlink()
+        # no "listening at" message should be printed to simulate hg behavior
 
     def _inithashstate(self, address):
         self._baseaddress = address
@@ -517,6 +518,7 @@
         tempaddress = _tempaddress(self._realaddress)
         util.bindunixsocket(sock, tempaddress)
         self._socketstat = os.stat(tempaddress)
+        sock.listen(socket.SOMAXCONN)
         # rename will replace the old socket file if exists atomically. the
         # old server will detect ownership change and exit.
         util.rename(tempaddress, self._realaddress)
@@ -545,10 +547,6 @@
         # the client will start a new server on demand.
         util.tryunlink(self._realaddress)
 
-    def printbanner(self, address):
-        # no "listening at" message should be printed to simulate hg behavior
-        pass
-
     def shouldexit(self):
         if not self._issocketowner():
             self.ui.debug('%s is not owned, exiting.\n' % self._realaddress)
--- a/mercurial/cmdutil.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/cmdutil.py	Fri May 12 11:20:25 2017 -0700
@@ -115,6 +115,7 @@
 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
             filterfn, *pats, **opts):
     from . import merge as mergemod
+    opts = pycompat.byteskwargs(opts)
     if not ui.interactive():
         if cmdsuggest:
             msg = _('running non-interactively, use %s instead') % cmdsuggest
@@ -490,10 +491,10 @@
         patlen = len(pat)
         i = 0
         while i < patlen:
-            c = pat[i]
+            c = pat[i:i + 1]
             if c == '%':
                 i += 1
-                c = pat[i]
+                c = pat[i:i + 1]
                 c = expander[c]()
             newname.append(c)
             i += 1
@@ -1360,7 +1361,7 @@
         if rev is None:
             jrev = jnode = 'null'
         else:
-            jrev = str(rev)
+            jrev = '%d' % rev
             jnode = '"%s"' % hex(ctx.node())
         j = encoding.jsonescape
 
@@ -1607,7 +1608,7 @@
         if rev in results:
             ui.status(_("found revision %s from %s\n") %
                       (rev, util.datestr(results[rev])))
-            return str(rev)
+            return '%d' % rev
 
     raise error.Abort(_("revision matching date not found"))
 
@@ -2278,7 +2279,7 @@
         sub = wctx.sub(subpath)
         try:
             submatch = matchmod.subdirmatcher(subpath, match)
-            if opts.get('subrepos'):
+            if opts.get(r'subrepos'):
                 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
             else:
                 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
@@ -2286,7 +2287,7 @@
             ui.status(_("skipping missing subrepository: %s\n")
                            % join(subpath))
 
-    if not opts.get('dry_run'):
+    if not opts.get(r'dry_run'):
         rejected = wctx.add(names, prefix)
         bad.extend(f for f in rejected if f in match.files())
     return bad
@@ -2308,7 +2309,7 @@
     forgot = []
 
     s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
-    forget = sorted(s[0] + s[1] + s[3] + s[6])
+    forget = sorted(s.modified + s.added + s.deleted + s.clean)
     if explicitonly:
         forget = [f for f in forget if match.exact(f)]
 
--- a/mercurial/commands.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/commands.py	Fri May 12 11:20:25 2017 -0700
@@ -255,7 +255,7 @@
     Returns 0 if all files are successfully added.
     """
 
-    m = scmutil.match(repo[None], pats, opts)
+    m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
     rejected = cmdutil.add(ui, repo, m, "", False, **opts)
     return rejected and 1 or 0
 
@@ -325,6 +325,7 @@
 
     Returns 0 if all files are successfully added.
     """
+    opts = pycompat.byteskwargs(opts)
     try:
         sim = float(opts.get('similarity') or 100)
     except ValueError:
@@ -368,6 +369,7 @@
 
     Returns 0 on success.
     """
+    opts = pycompat.byteskwargs(opts)
     if not pats:
         raise error.Abort(_('at least one filename or pattern is required'))
 
@@ -452,7 +454,7 @@
                                      whitespace=True)
     for abs in ctx.walk(m):
         fctx = ctx[abs]
-        if not opts.get('text') and util.binary(fctx.data()):
+        if not opts.get('text') and fctx.isbinary():
             fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
             continue
 
@@ -532,6 +534,7 @@
     Returns 0 on success.
     '''
 
+    opts = pycompat.byteskwargs(opts)
     ctx = scmutil.revsingle(repo, opts.get('rev'))
     if not ctx:
         raise error.Abort(_('no working directory: please specify a revision'))
@@ -627,6 +630,7 @@
         release(lock, wlock)
 
 def _dobackout(ui, repo, node=None, rev=None, **opts):
+    opts = pycompat.byteskwargs(opts)
     if opts.get('commit') and opts.get('no_commit'):
         raise error.Abort(_("cannot use --commit with --no-commit"))
     if opts.get('merge') and opts.get('no_commit'):
@@ -702,7 +706,8 @@
 
     def commitfunc(ui, repo, message, match, opts):
         editform = 'backout'
-        e = cmdutil.getcommiteditor(editform=editform, **opts)
+        e = cmdutil.getcommiteditor(editform=editform,
+                                    **pycompat.strkwargs(opts))
         if not message:
             # we don't translate commit messages
             message = "Backed out changeset %s" % short(node)
@@ -837,8 +842,6 @@
     elif extra or good + bad + skip + reset + extend + bool(command) > 1:
         raise error.Abort(_('incompatible arguments'))
 
-    cmdutil.checkunfinished(repo)
-
     if reset:
         hbisect.resetstate(repo)
         return
@@ -865,6 +868,7 @@
         """common used update sequence"""
         if noupdate:
             return
+        cmdutil.checkunfinished(repo)
         cmdutil.bailifchanged(repo)
         return hg.clean(repo, node, show_stats=show_stats)
 
@@ -1002,6 +1006,7 @@
 
           hg book -f @
     '''
+    opts = pycompat.byteskwargs(opts)
     force = opts.get('force')
     rev = opts.get('rev')
     delete = opts.get('delete')
@@ -1178,6 +1183,7 @@
 
     Returns 0 on success.
     """
+    opts = pycompat.byteskwargs(opts)
     if label:
         label = label.strip()
 
@@ -1226,6 +1232,7 @@
     Returns 0.
     """
 
+    opts = pycompat.byteskwargs(opts)
     ui.pager('branches')
     fm = ui.formatter('branches', opts)
     hexfunc = fm.hexfunc
@@ -1309,6 +1316,7 @@
 
     Returns 0 on success, 1 if no changes found.
     """
+    opts = pycompat.byteskwargs(opts)
     revs = None
     if 'rev' in opts:
         revstrings = opts['rev']
@@ -1339,8 +1347,6 @@
         base = ['null']
     else:
         base = scmutil.revrange(repo, opts.get('base'))
-    # TODO: get desired bundlecaps from command line.
-    bundlecaps = None
     if cgversion not in changegroup.supportedoutgoingversions(repo):
         raise error.Abort(_("repository does not support bundle version %s") %
                           cgversion)
@@ -1352,10 +1358,6 @@
         common = [repo.lookup(rev) for rev in base]
         heads = revs and map(repo.lookup, revs) or None
         outgoing = discovery.outgoing(repo, common, heads)
-        cg = changegroup.getchangegroup(repo, 'bundle', outgoing,
-                                        bundlecaps=bundlecaps,
-                                        version=cgversion)
-        outgoing = None
     else:
         dest = ui.expandpath(dest or 'default-push', dest or 'default')
         dest, branches = hg.parseurl(dest, opts.get('branch'))
@@ -1366,10 +1368,9 @@
                                                 onlyheads=heads,
                                                 force=opts.get('force'),
                                                 portable=True)
-        cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
-                                                bundlecaps, version=cgversion)
-    if not cg:
-        scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
+
+    if not outgoing.missing:
+        scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
         return 1
 
     if cgversion == '01': #bundle1
@@ -1392,8 +1393,11 @@
     if complevel is not None:
         compopts['level'] = complevel
 
-    bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression,
-                        compopts=compopts)
+
+    contentopts = {'cg.version': cgversion}
+    bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
+                           contentopts, compression=bcompression,
+                           compopts=compopts)
 
 @command('cat',
     [('o', 'output', '',
@@ -1549,6 +1553,7 @@
 
     Returns 0 on success.
     """
+    opts = pycompat.byteskwargs(opts)
     if opts.get('noupdate') and opts.get('updaterev'):
         raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
 
@@ -1639,16 +1644,16 @@
         release(lock, wlock)
 
 def _docommit(ui, repo, *pats, **opts):
-    opts = pycompat.byteskwargs(opts)
-    if opts.get('interactive'):
-        opts.pop('interactive')
+    if opts.get(r'interactive'):
+        opts.pop(r'interactive')
         ret = cmdutil.dorecord(ui, repo, commit, None, False,
                                cmdutil.recordfilter, *pats,
-                               **pycompat.strkwargs(opts))
+                               **opts)
         # ret can be 0 (no changes to record) or the value returned by
         # commit(), 1 if nothing changed or None on success.
         return 1 if ret == 0 else ret
 
+    opts = pycompat.byteskwargs(opts)
     if opts.get('subrepos'):
         if opts.get('amend'):
             raise error.Abort(_('cannot amend with --subrepos'))
@@ -1769,6 +1774,7 @@
 
     """
 
+    opts = pycompat.byteskwargs(opts)
     if opts.get('edit') or opts.get('local') or opts.get('global'):
         if opts.get('local') and opts.get('global'):
             raise error.Abort(_("can't use --local and --global together"))
@@ -1871,6 +1877,7 @@
 
     Returns 0 on success, 1 if errors are encountered.
     """
+    opts = pycompat.byteskwargs(opts)
     with repo.wlock(False):
         return cmdutil.copy(ui, repo, pats, opts)
 
@@ -1938,6 +1945,7 @@
     Returns 0 on success.
     """
 
+    opts = pycompat.byteskwargs(opts)
     revs = opts.get('rev')
     change = opts.get('change')
     stat = opts.get('stat')
@@ -2094,7 +2102,9 @@
     Returns 0 if a match is found, 1 otherwise.
 
     """
-    ctx = scmutil.revsingle(repo, opts.get(r'rev'), None)
+
+    opts = pycompat.byteskwargs(opts)
+    ctx = scmutil.revsingle(repo, opts.get('rev'), None)
 
     end = '\n'
     if opts.get('print0'):
@@ -2136,6 +2146,7 @@
     Returns 0 on success.
     """
 
+    opts = pycompat.byteskwargs(opts)
     if not pats:
         raise error.Abort(_('no files specified'))
 
@@ -2220,6 +2231,7 @@
         return _dograft(ui, repo, *revs, **opts)
 
 def _dograft(ui, repo, *revs, **opts):
+    opts = pycompat.byteskwargs(opts)
     if revs and opts.get('rev'):
         ui.warn(_('warning: inconsistent use of --rev might give unexpected '
                   'revision ordering!\n'))
@@ -2232,7 +2244,8 @@
     if not opts.get('date') and opts.get('currentdate'):
         opts['date'] = "%d %d" % util.makedate()
 
-    editor = cmdutil.getcommiteditor(editform='graft', **opts)
+    editor = cmdutil.getcommiteditor(editform='graft',
+                                     **pycompat.strkwargs(opts))
 
     cont = False
     if opts.get('continue'):
@@ -2439,6 +2452,7 @@
 
     Returns 0 if a match is found, 1 otherwise.
     """
+    opts = pycompat.byteskwargs(opts)
     reflags = re.M
     if opts.get('ignore_case'):
         reflags |= re.I
@@ -2685,6 +2699,7 @@
     Returns 0 if matching heads are found, 1 if not.
     """
 
+    opts = pycompat.byteskwargs(opts)
     start = None
     if 'rev' in opts:
         start = scmutil.revsingle(repo, opts['rev'], None).node()
@@ -2743,7 +2758,7 @@
     Returns 0 if successful.
     """
 
-    keep = opts.get('system') or []
+    keep = opts.get(r'system') or []
     if len(keep) == 0:
         if pycompat.sysplatform.startswith('win'):
             keep.append('windows')
@@ -2810,6 +2825,7 @@
     Returns 0 if successful.
     """
 
+    opts = pycompat.byteskwargs(opts)
     if not repo and not source:
         raise error.Abort(_("there is no Mercurial repository here "
                            "(.hg not found)"))
@@ -3033,6 +3049,7 @@
     Returns 0 on success, 1 on partial success (see --partial).
     """
 
+    opts = pycompat.byteskwargs(opts)
     if not patch1:
         raise error.Abort(_('need at least one patch to import'))
 
@@ -3237,6 +3254,7 @@
 
     Returns 0 on success.
     """
+    opts = pycompat.byteskwargs(opts)
     hg.peer(ui, opts, ui.expandpath(dest), create=True)
 
 @command('locate',
@@ -3267,6 +3285,7 @@
 
     Returns 0 if a match is found, 1 otherwise.
     """
+    opts = pycompat.byteskwargs(opts)
     if opts.get('print0'):
         end = '\0'
     else:
@@ -3473,6 +3492,7 @@
 
     Returns 0 on success.
     """
+    opts = pycompat.byteskwargs(opts)
     fm = ui.formatter('manifest', opts)
 
     if opts.get('all'):
@@ -3551,6 +3571,7 @@
     Returns 0 on success, 1 if there are unresolved files.
     """
 
+    opts = pycompat.byteskwargs(opts)
     if opts.get('rev') and node:
         raise error.Abort(_("please specify just one revision"))
     if not node:
@@ -3630,6 +3651,7 @@
 
     Returns 0 if there are outgoing changes, 1 otherwise.
     """
+    opts = pycompat.byteskwargs(opts)
     if opts.get('graph'):
         cmdutil.checkunsupportedgraphflags([], opts)
         o, other = hg._outgoing(ui, repo, dest, opts)
@@ -3687,6 +3709,7 @@
     Returns 0 on success.
     """
 
+    opts = pycompat.byteskwargs(opts)
     ctx = scmutil.revsingle(repo, opts.get('rev'), None)
 
     if file_:
@@ -3749,6 +3772,8 @@
 
     Returns 0 on success.
     """
+
+    opts = pycompat.byteskwargs(opts)
     ui.pager('paths')
     if search:
         pathitems = [(name, path) for name, path in ui.paths.iteritems()
@@ -3811,6 +3836,7 @@
 
     (For more information about the phases concept, see :hg:`help phases`.)
     """
+    opts = pycompat.byteskwargs(opts)
     # search for a unique phase argument
     targetphase = None
     for idx, name in enumerate(phases.phasenames):
@@ -3943,6 +3969,7 @@
     Returns 0 on success, 1 if an update had unresolved files.
     """
 
+    opts = pycompat.byteskwargs(opts)
     if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
         msg = _('update destination required by configuration')
         hint = _('use hg pull followed by hg update DEST')
@@ -4073,6 +4100,7 @@
     Returns 0 if push was successful, 1 if nothing to push.
     """
 
+    opts = pycompat.byteskwargs(opts)
     if opts.get('bookmark'):
         ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
         for b in opts['bookmark']:
@@ -4198,6 +4226,7 @@
     Returns 0 on success, 1 if any warnings encountered.
     """
 
+    opts = pycompat.byteskwargs(opts)
     after, force = opts.get('after'), opts.get('force')
     if not pats and not after:
         raise error.Abort(_('no files specified'))
@@ -4227,6 +4256,7 @@
 
     Returns 0 on success, 1 if errors are encountered.
     """
+    opts = pycompat.byteskwargs(opts)
     with repo.wlock(False):
         return cmdutil.copy(ui, repo, pats, opts, rename=True)
 
@@ -4281,6 +4311,7 @@
     Returns 0 on success, 1 if any files fail a resolve attempt.
     """
 
+    opts = pycompat.byteskwargs(opts)
     flaglist = 'all mark unmark list no_status'.split()
     all, mark, unmark, show, nostatus = \
         [opts.get(o) for o in flaglist]
@@ -4586,8 +4617,8 @@
     if not ui.configbool('ui', 'rollback', True):
         raise error.Abort(_('rollback is disabled because it is unsafe'),
                           hint=('see `hg help -v rollback` for information'))
-    return repo.rollback(dryrun=opts.get('dry_run'),
-                         force=opts.get('force'))
+    return repo.rollback(dryrun=opts.get(r'dry_run'),
+                         force=opts.get(r'force'))
 
 @command('root', [])
 def root(ui, repo):
@@ -4652,6 +4683,7 @@
     Returns 0 on success.
     """
 
+    opts = pycompat.byteskwargs(opts)
     if opts["stdio"] and opts["cmdserver"]:
         raise error.Abort(_("cannot use --stdio with --cmdserver"))
 
@@ -4817,6 +4849,7 @@
     Returns 0 on success.
     """
 
+    opts = pycompat.byteskwargs(opts)
     ui.pager('summary')
     ctx = repo[None]
     parents = ctx.parents()
@@ -5125,6 +5158,7 @@
 
     Returns 0 on success.
     """
+    opts = pycompat.byteskwargs(opts)
     wlock = lock = None
     try:
         wlock = repo.wlock()
@@ -5189,7 +5223,8 @@
             editform = 'tag.remove'
         else:
             editform = 'tag.add'
-        editor = cmdutil.getcommiteditor(editform=editform, **opts)
+        editor = cmdutil.getcommiteditor(editform=editform,
+                                         **pycompat.strkwargs(opts))
 
         # don't allow tagging the null rev
         if (not opts.get('remove') and
@@ -5212,6 +5247,7 @@
     Returns 0 on success.
     """
 
+    opts = pycompat.byteskwargs(opts)
     ui.pager('tags')
     fm = ui.formatter('tags', opts)
     hexfunc = fm.hexfunc
@@ -5256,6 +5292,7 @@
 
     Returns 0 on success.
     """
+    opts = pycompat.byteskwargs(opts)
     displayer = cmdutil.show_changeset(ui, repo, opts)
     displayer.show(repo['tip'])
     displayer.close()
@@ -5303,7 +5340,7 @@
             else:
                 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
 
-    return postincoming(ui, repo, modheads, opts.get('update'), None, None)
+    return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
 
 @command('^update|up|checkout|co',
     [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
@@ -5430,6 +5467,7 @@
 @command('version', [] + formatteropts, norepo=True)
 def version_(ui, **opts):
     """output version and copyright information"""
+    opts = pycompat.byteskwargs(opts)
     if ui.verbose:
         ui.pager('version')
     fm = ui.formatter("version", opts)
--- a/mercurial/commandserver.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/commandserver.py	Fri May 12 11:20:25 2017 -0700
@@ -409,14 +409,13 @@
 
     def bindsocket(self, sock, address):
         util.bindunixsocket(sock, address)
+        sock.listen(socket.SOMAXCONN)
+        self.ui.status(_('listening at %s\n') % address)
+        self.ui.flush()  # avoid buffering of status message
 
     def unlinksocket(self, address):
         os.unlink(address)
 
-    def printbanner(self, address):
-        self.ui.status(_('listening at %s\n') % address)
-        self.ui.flush()  # avoid buffering of status message
-
     def shouldexit(self):
         """True if server should shut down; checked per pollinterval"""
         return False
@@ -452,10 +451,8 @@
     def init(self):
         self._sock = socket.socket(socket.AF_UNIX)
         self._servicehandler.bindsocket(self._sock, self.address)
-        self._sock.listen(socket.SOMAXCONN)
         o = signal.signal(signal.SIGCHLD, self._sigchldhandler)
         self._oldsigchldhandler = o
-        self._servicehandler.printbanner(self.address)
         self._socketunlinked = False
 
     def _unlinksocket(self):
--- a/mercurial/context.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/context.py	Fri May 12 11:20:25 2017 -0700
@@ -257,13 +257,13 @@
         return changectx(self._repo, nullrev)
 
     def _fileinfo(self, path):
-        if '_manifest' in self.__dict__:
+        if r'_manifest' in self.__dict__:
             try:
                 return self._manifest[path], self._manifest.flags(path)
             except KeyError:
                 raise error.ManifestLookupError(self._node, path,
                                                 _('not found in manifest'))
-        if '_manifestdelta' in self.__dict__ or path in self.files():
+        if r'_manifestdelta' in self.__dict__ or path in self.files():
             if path in self._manifestdelta:
                 return (self._manifestdelta[path],
                         self._manifestdelta.flags(path))
@@ -687,21 +687,20 @@
              in the repo,
     workingfilectx: a filecontext that represents files from the working
                     directory,
-    memfilectx: a filecontext that represents files in-memory."""
-    def __new__(cls, repo, path, *args, **kwargs):
-        return super(basefilectx, cls).__new__(cls)
-
+    memfilectx: a filecontext that represents files in-memory,
+    overlayfilectx: duplicate another filecontext with some fields overridden.
+    """
     @propertycache
     def _filelog(self):
         return self._repo.file(self._path)
 
     @propertycache
     def _changeid(self):
-        if '_changeid' in self.__dict__:
+        if r'_changeid' in self.__dict__:
             return self._changeid
-        elif '_changectx' in self.__dict__:
+        elif r'_changectx' in self.__dict__:
             return self._changectx.rev()
-        elif '_descendantrev' in self.__dict__:
+        elif r'_descendantrev' in self.__dict__:
             # this file context was created from a revision with a known
             # descendant, we can (lazily) correct for linkrev aliases
             return self._adjustlinkrev(self._descendantrev)
@@ -710,7 +709,7 @@
 
     @propertycache
     def _filenode(self):
-        if '_fileid' in self.__dict__:
+        if r'_fileid' in self.__dict__:
             return self._filelog.lookup(self._fileid)
         else:
             return self._changectx.filenode(self._path)
@@ -762,8 +761,11 @@
         return self._filerev
     def filenode(self):
         return self._filenode
+    @propertycache
+    def _flags(self):
+        return self._changectx.flags(self._path)
     def flags(self):
-        return self._changectx.flags(self._path)
+        return self._flags
     def filelog(self):
         return self._filelog
     def rev(self):
@@ -794,8 +796,12 @@
         return self._changectx.manifest()
     def changectx(self):
         return self._changectx
+    def renamed(self):
+        return self._copied
     def repo(self):
         return self._repo
+    def size(self):
+        return len(self.data())
 
     def path(self):
         return self._path
@@ -1134,6 +1140,10 @@
     def rawdata(self):
         return self._filelog.revision(self._filenode, raw=True)
 
+    def rawflags(self):
+        """low-level revlog flags"""
+        return self._filelog.flags(self._filerev)
+
     def data(self):
         try:
             return self._filelog.read(self._filenode)
@@ -1146,7 +1156,8 @@
     def size(self):
         return self._filelog.size(self._filerev)
 
-    def renamed(self):
+    @propertycache
+    def _copied(self):
         """check if file was actually renamed in this changeset revision
 
         If rename logged in file revision, we report copy for changeset only
@@ -1396,7 +1407,7 @@
         return []
 
     def flags(self, path):
-        if '_manifest' in self.__dict__:
+        if r'_manifest' in self.__dict__:
             try:
                 return self._manifest.flags(path)
             except KeyError:
@@ -2059,12 +2070,6 @@
 
     def data(self):
         return self._data
-    def size(self):
-        return len(self.data())
-    def flags(self):
-        return self._flags
-    def renamed(self):
-        return self._copied
 
     def remove(self, ignoremissing=False):
         """wraps unlink for a repo's working directory"""
@@ -2075,6 +2080,77 @@
         """wraps repo.wwrite"""
         self._data = data
 
+class overlayfilectx(committablefilectx):
+    """Like memfilectx but take an original filectx and optional parameters to
+    override parts of it. This is useful when fctx.data() is expensive (i.e.
+    flag processor is expensive) and raw data, flags, and filenode could be
+    reused (ex. rebase or mode-only amend a REVIDX_EXTSTORED file).
+    """
+
+    def __init__(self, originalfctx, datafunc=None, path=None, flags=None,
+                 copied=None, ctx=None):
+        """originalfctx: filecontext to duplicate
+
+        datafunc: None or a function to override data (file content). It is a
+        function to be lazy. path, flags, copied, ctx: None or overridden value
+
+        copied could be (path, rev), or False. copied could also be just path,
+        and will be converted to (path, nullid). This simplifies some callers.
+        """
+
+        if path is None:
+            path = originalfctx.path()
+        if ctx is None:
+            ctx = originalfctx.changectx()
+            ctxmatch = lambda: True
+        else:
+            ctxmatch = lambda: ctx == originalfctx.changectx()
+
+        repo = originalfctx.repo()
+        flog = originalfctx.filelog()
+        super(overlayfilectx, self).__init__(repo, path, flog, ctx)
+
+        if copied is None:
+            copied = originalfctx.renamed()
+            copiedmatch = lambda: True
+        else:
+            if copied and not isinstance(copied, tuple):
+                # repo._filecommit will recalculate copyrev so nullid is okay
+                copied = (copied, nullid)
+            copiedmatch = lambda: copied == originalfctx.renamed()
+
+        # When data, copied (could affect data), ctx (could affect filelog
+        # parents) are not overridden, rawdata, rawflags, and filenode may be
+        # reused (repo._filecommit should double check filelog parents).
+        #
+        # path, flags are not hashed in filelog (but in manifestlog) so they do
+        # not affect reusable here.
+        #
+        # If ctx or copied is overridden to a same value with originalfctx,
+        # still consider it's reusable. originalfctx.renamed() may be a bit
+        # expensive so it's not called unless necessary. Assuming datafunc is
+        # always expensive, do not call it for this "reusable" test.
+        reusable = datafunc is None and ctxmatch() and copiedmatch()
+
+        if datafunc is None:
+            datafunc = originalfctx.data
+        if flags is None:
+            flags = originalfctx.flags()
+
+        self._datafunc = datafunc
+        self._flags = flags
+        self._copied = copied
+
+        if reusable:
+            # copy extra fields from originalfctx
+            attrs = ['rawdata', 'rawflags', '_filenode', '_filerev']
+            for attr in attrs:
+                if util.safehasattr(originalfctx, attr):
+                    setattr(self, attr, getattr(originalfctx, attr))
+
+    def data(self):
+        return self._datafunc()
+
 class metadataonlyctx(committablectx):
     """Like memctx but it's reusing the manifest of different commit.
     Intended to be used by lightweight operations that are creating
--- a/mercurial/debugcommands.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/debugcommands.py	Fri May 12 11:20:25 2017 -0700
@@ -1020,19 +1020,20 @@
     fm.write('hgmodules', _("checking installed modules (%s)...\n"),
              os.path.dirname(pycompat.fsencode(__file__)))
 
-    err = None
-    try:
-        from . import (
-            base85,
-            bdiff,
-            mpatch,
-            osutil,
-        )
-        dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
-    except Exception as inst:
-        err = inst
-        problems += 1
-    fm.condwrite(err, 'extensionserror', " %s\n", err)
+    if policy.policy in ('c', 'allow'):
+        err = None
+        try:
+            from . import (
+                base85,
+                bdiff,
+                mpatch,
+                osutil,
+            )
+            dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
+        except Exception as inst:
+            err = inst
+            problems += 1
+        fm.condwrite(err, 'extensionserror', " %s\n", err)
 
     compengines = util.compengines._engines.values()
     fm.write('compengines', _('checking registered compression engines (%s)\n'),
@@ -1706,9 +1707,9 @@
                 numother += 1
 
         # Obtain data on the raw chunks in the revlog.
-        chunk = r._chunkraw(rev, rev)[1]
-        if chunk:
-            chunktype = chunk[0]
+        segment = r._getsegmentforrevs(rev, rev)[1]
+        if segment:
+            chunktype = segment[0]
         else:
             chunktype = 'empty'
 
--- a/mercurial/demandimport.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/demandimport.py	Fri May 12 11:20:25 2017 -0700
@@ -297,6 +297,7 @@
     'distutils.msvc9compiler',
     '__builtin__',
     'builtins',
+    'urwid.command_map', # for pudb
     ]
 
 if _pypy:
--- a/mercurial/dirstate.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/dirstate.py	Fri May 12 11:20:25 2017 -0700
@@ -18,7 +18,6 @@
     encoding,
     error,
     match as matchmod,
-    osutil,
     parsers,
     pathutil,
     pycompat,
@@ -988,7 +987,7 @@
         matchalways = match.always()
         matchtdir = match.traversedir
         dmap = self._map
-        listdir = osutil.listdir
+        listdir = util.listdir
         lstat = os.lstat
         dirkind = stat.S_IFDIR
         regkind = stat.S_IFREG
@@ -1021,6 +1020,8 @@
             wadd = work.append
             while work:
                 nd = work.pop()
+                if not match.visitdir(nd):
+                    continue
                 skip = None
                 if nd == '.':
                     nd = ''
--- a/mercurial/dispatch.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/dispatch.py	Fri May 12 11:20:25 2017 -0700
@@ -162,9 +162,13 @@
     ret = None
     try:
         ret = _runcatch(req)
-    except KeyboardInterrupt:
+    except KeyboardInterrupt as inst:
         try:
-            req.ui.warn(_("interrupted!\n"))
+            if isinstance(inst, error.SignalInterrupt):
+                msg = _("killed!\n")
+            else:
+                msg = _("interrupted!\n")
+            req.ui.warn(msg)
         except error.SignalInterrupt:
             # maybe pager would quit without consuming all the output, and
             # SIGPIPE was raised. we cannot print anything in this case.
@@ -179,7 +183,7 @@
         if req.ui.logblockedtimes:
             req.ui._blockedtimes['command_duration'] = duration * 1000
             req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes)
-        req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
+        req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n",
                    msg, ret or 0, duration)
         try:
             req._runexithandlers()
@@ -475,7 +479,8 @@
         return aliasargs(self.fn, args)
 
     def __getattr__(self, name):
-        adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
+        adefaults = {r'norepo': True,
+                     r'optionalrepo': False, r'inferrepo': False}
         if name not in adefaults:
             raise AttributeError(name)
         if self.badalias or util.safehasattr(self, 'shell'):
--- a/mercurial/encoding.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/encoding.py	Fri May 12 11:20:25 2017 -0700
@@ -51,14 +51,14 @@
 # the process environment
 _nativeenviron = (not pycompat.ispy3 or os.supports_bytes_environ)
 if not pycompat.ispy3:
-    environ = os.environ
+    environ = os.environ  # re-exports
 elif _nativeenviron:
-    environ = os.environb
+    environ = os.environb  # re-exports
 else:
     # preferred encoding isn't known yet; use utf-8 to avoid unicode error
     # and recreate it once encoding is settled
     environ = dict((k.encode(u'utf-8'), v.encode(u'utf-8'))
-                   for k, v in os.environ.items())
+                   for k, v in os.environ.items())  # re-exports
 
 def _getpreferredencoding():
     '''
@@ -218,7 +218,7 @@
     # now encoding and helper functions are available, recreate the environ
     # dict to be exported to other modules
     environ = dict((tolocal(k.encode(u'utf-8')), tolocal(v.encode(u'utf-8')))
-                   for k, v in os.environ.items())
+                   for k, v in os.environ.items())  # re-exports
 
 # How to treat ambiguous-width characters. Set to 'wide' to treat as wide.
 wide = (environ.get("HGENCODINGAMBIGUOUS", "narrow") == "wide"
@@ -429,7 +429,7 @@
 
 _jsonmap = []
 _jsonmap.extend("\\u%04x" % x for x in range(32))
-_jsonmap.extend(chr(x) for x in range(32, 127))
+_jsonmap.extend(pycompat.bytechr(x) for x in range(32, 127))
 _jsonmap.append('\\u007f')
 _jsonmap[0x09] = '\\t'
 _jsonmap[0x0a] = '\\n'
@@ -441,7 +441,7 @@
 _paranoidjsonmap = _jsonmap[:]
 _paranoidjsonmap[0x3c] = '\\u003c'  # '<' (e.g. escape "</script>")
 _paranoidjsonmap[0x3e] = '\\u003e'  # '>'
-_jsonmap.extend(chr(x) for x in range(128, 256))
+_jsonmap.extend(pycompat.bytechr(x) for x in range(128, 256))
 
 def jsonescape(s, paranoid=False):
     '''returns a string suitable for JSON
--- a/mercurial/exchange.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/exchange.py	Fri May 12 11:20:25 2017 -0700
@@ -16,7 +16,6 @@
     nullid,
 )
 from . import (
-    base85,
     bookmarks as bookmod,
     bundle2,
     changegroup,
@@ -29,7 +28,6 @@
     scmutil,
     sslutil,
     streamclone,
-    tags,
     url as urlmod,
     util,
 )
@@ -938,22 +936,19 @@
     pushop.repo.prepushoutgoinghooks(pushop)
     outgoing = pushop.outgoing
     unbundle = pushop.remote.capable('unbundle')
-    # TODO: get bundlecaps from remote
-    bundlecaps = None
     # create a changegroup from local
     if pushop.revs is None and not (outgoing.excluded
                             or pushop.repo.changelog.filteredrevs):
         # push everything,
         # use the fast path, no race possible on push
-        bundler = changegroup.cg1packer(pushop.repo, bundlecaps)
+        bundler = changegroup.cg1packer(pushop.repo)
         cg = changegroup.getsubset(pushop.repo,
                                    outgoing,
                                    bundler,
                                    'push',
                                    fastpath=True)
     else:
-        cg = changegroup.getlocalchangegroup(pushop.repo, 'push', outgoing,
-                                        bundlecaps)
+        cg = changegroup.getchangegroup(pushop.repo, 'push', outgoing)
 
     # apply changegroup to remote
     if unbundle:
@@ -1512,7 +1507,7 @@
             markers = []
             for key in sorted(remoteobs, reverse=True):
                 if key.startswith('dump'):
-                    data = base85.b85decode(remoteobs[key])
+                    data = util.b85decode(remoteobs[key])
                     version, newmarks = obsolete._readmarkers(data)
                     markers += newmarks
             if markers:
@@ -1578,7 +1573,7 @@
             raise ValueError(_('unsupported getbundle arguments: %s')
                              % ', '.join(sorted(kwargs.keys())))
         outgoing = _computeoutgoing(repo, heads, common)
-        bundler = changegroup.getbundler('01', repo, bundlecaps)
+        bundler = changegroup.getbundler('01', repo)
         return changegroup.getsubsetraw(repo, outgoing, bundler, source)
 
     # bundle20 case
@@ -1616,7 +1611,6 @@
             version = max(cgversions)
         outgoing = _computeoutgoing(repo, heads, common)
         cg = changegroup.getlocalchangegroupraw(repo, source, outgoing,
-                                                bundlecaps=bundlecaps,
                                                 version=version)
 
     if cg:
@@ -1668,30 +1662,7 @@
         return
 
     outgoing = _computeoutgoing(repo, heads, common)
-
-    if not outgoing.missingheads:
-        return
-
-    cache = tags.hgtagsfnodescache(repo.unfiltered())
-    chunks = []
-
-    # .hgtags fnodes are only relevant for head changesets. While we could
-    # transfer values for all known nodes, there will likely be little to
-    # no benefit.
-    #
-    # We don't bother using a generator to produce output data because
-    # a) we only have 40 bytes per head and even esoteric numbers of heads
-    # consume little memory (1M heads is 40MB) b) we don't want to send the
-    # part if we don't have entries and knowing if we have entries requires
-    # cache lookups.
-    for node in outgoing.missingheads:
-        # Don't compute missing, as this may slow down serving.
-        fnode = cache.getfnode(node, computemissing=False)
-        if fnode is not None:
-            chunks.extend([node, fnode])
-
-    if chunks:
-        bundler.newpart('hgtagsfnodes', data=''.join(chunks))
+    bundle2.addparttagsfnodescache(repo, bundler, outgoing)
 
 def _getbookmarks(repo, **kwargs):
     """Returns bookmark to node mapping.
--- a/mercurial/extensions.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/extensions.py	Fri May 12 11:20:25 2017 -0700
@@ -171,8 +171,6 @@
                 continue
         try:
             load(ui, name, path)
-        except KeyboardInterrupt:
-            raise
         except Exception as inst:
             inst = _forbytes(inst)
             if path:
--- a/mercurial/filelog.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/filelog.py	Fri May 12 11:20:25 2017 -0700
@@ -18,7 +18,7 @@
 
 _mdre = re.compile('\1\n')
 def parsemeta(text):
-    """return (metadatadict, keylist, metadatasize)"""
+    """return (metadatadict, metadatasize)"""
     # text can be buffer, so we can't use .startswith or .index
     if text[:2] != '\1\n':
         return None, None
--- a/mercurial/fileset.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/fileset.py	Fri May 12 11:20:25 2017 -0700
@@ -256,7 +256,7 @@
     """
     # i18n: "binary" is a keyword
     getargs(x, 0, 0, _("binary takes no arguments"))
-    return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())]
+    return [f for f in mctx.existing() if mctx.ctx[f].isbinary()]
 
 @predicate('exec()', callexisting=True)
 def exec_(mctx, x):
--- a/mercurial/formatter.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/formatter.py	Fri May 12 11:20:25 2017 -0700
@@ -114,6 +114,7 @@
 
 from . import (
     error,
+    pycompat,
     templatefilters,
     templatekw,
     templater,
@@ -178,6 +179,7 @@
         pass
     def data(self, **data):
         '''insert data into item that's not shown in default output'''
+        data = pycompat.byteskwargs(data)
         self._item.update(data)
     def write(self, fields, deftext, *fielddata, **opts):
         '''do default text output while assigning data to item'''
--- a/mercurial/graphmod.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/graphmod.py	Fri May 12 11:20:25 2017 -0700
@@ -273,7 +273,7 @@
         # | | |             | | |
         line.extend(echars[idx * 2:(idx + 1) * 2])
     else:
-        line.extend('  ')
+        line.extend([' ', ' '])
     # all edges to the right of the current node
     remainder = ncols - idx - 1
     if remainder > 0:
@@ -410,14 +410,17 @@
     # shift_interline is the line containing the non-vertical
     # edges between this entry and the next
     shift_interline = echars[:idx * 2]
-    shift_interline.extend(' ' * (2 + coldiff))
+    for i in xrange(2 + coldiff):
+        shift_interline.append(' ')
     count = ncols - idx - 1
     if coldiff == -1:
-        shift_interline.extend('/ ' * count)
+        for i in xrange(count):
+            shift_interline.extend(['/', ' '])
     elif coldiff == 0:
         shift_interline.extend(echars[(idx + 1) * 2:ncols * 2])
     else:
-        shift_interline.extend(r'\ ' * count)
+        for i in xrange(count):
+            shift_interline.extend(['\\', ' '])
 
     # draw edges from the current node to its parents
     _drawedges(echars, edges, nodeline, shift_interline)
--- a/mercurial/help.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/help.py	Fri May 12 11:20:25 2017 -0700
@@ -23,6 +23,7 @@
     filemerge,
     fileset,
     minirst,
+    pycompat,
     revset,
     templatefilters,
     templatekw,
@@ -304,6 +305,7 @@
     '''
 
     from . import commands # avoid cycle
+    opts = pycompat.byteskwargs(opts)
 
     def helpcmd(name, subtopic=None):
         try:
--- a/mercurial/help/internals/wireprotocol.txt	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/help/internals/wireprotocol.txt	Fri May 12 11:20:25 2017 -0700
@@ -632,6 +632,9 @@
 branches
 --------
 
+(Legacy command used for discovery in old clients. Clients with ``getbundle``
+use the ``known`` and ``heads`` commands instead.)
+
 Obtain ancestor changesets of specific nodes back to a branch point.
 
 Despite the name, this command has nothing to do with Mercurial named branches.
--- a/mercurial/hg.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/hg.py	Fri May 12 11:20:25 2017 -0700
@@ -869,7 +869,7 @@
         revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
 
     other = peer(repo, opts, dest)
-    outgoing = discovery.findcommonoutgoing(repo.unfiltered(), other, revs,
+    outgoing = discovery.findcommonoutgoing(repo, other, revs,
                                             force=opts.get('force'))
     o = outgoing.missing
     if not o:
--- a/mercurial/hgweb/webcommands.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/hgweb/webcommands.py	Fri May 12 11:20:25 2017 -0700
@@ -808,7 +808,7 @@
         context = parsecontext(web.config('web', 'comparisoncontext', '5'))
 
     def filelines(f):
-        if util.binary(f.data()):
+        if f.isbinary():
             mt = mimetypes.guess_type(f.path())[0]
             if not mt:
                 mt = 'application/octet-stream'
@@ -886,7 +886,7 @@
             yield p
 
     def annotate(**map):
-        if util.binary(fctx.data()):
+        if fctx.isbinary():
             mt = (mimetypes.guess_type(fctx.path())[0]
                   or 'application/octet-stream')
             lines = [((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]
--- a/mercurial/localrepo.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/localrepo.py	Fri May 12 11:20:25 2017 -0700
@@ -164,7 +164,7 @@
                                           **kwargs)
         cb = util.chunkbuffer(chunks)
 
-        if bundlecaps is not None and 'HG20' in bundlecaps:
+        if exchange.bundle2requested(bundlecaps):
             # When requesting a bundle2, getbundle returns a stream to make the
             # wire level function happier. We need to build a proper object
             # from it in local peer.
@@ -385,16 +385,6 @@
         # generic mapping between names and nodes
         self.names = namespaces.namespaces()
 
-    @property
-    def wopener(self):
-        self.ui.deprecwarn("use 'repo.wvfs' instead of 'repo.wopener'", '4.2')
-        return self.wvfs
-
-    @property
-    def opener(self):
-        self.ui.deprecwarn("use 'repo.vfs' instead of 'repo.opener'", '4.2')
-        return self.vfs
-
     def close(self):
         self._writecaches()
 
@@ -649,11 +639,6 @@
         """
         return hook.hook(self.ui, self, name, throw, **args)
 
-    def tag(self, names, node, message, local, user, date, editor=False):
-        self.ui.deprecwarn("use 'tagsmod.tag' instead of 'repo.tag'", '4.2')
-        tagsmod.tag(self, names, node, message, local, user, date,
-                    editor=editor)
-
     @filteredpropertycache
     def _tagscache(self):
         '''Returns a tagscache object that contains various tags related
@@ -841,10 +826,6 @@
             return 'store'
         return None
 
-    def join(self, f, *insidef):
-        self.ui.deprecwarn("use 'repo.vfs.join' instead of 'repo.join'", '4.2')
-        return self.vfs.join(os.path.join(f, *insidef))
-
     def wjoin(self, f, *insidef):
         return self.vfs.reljoin(self.root, f, *insidef)
 
@@ -884,15 +865,6 @@
     def pathto(self, f, cwd=None):
         return self.dirstate.pathto(f, cwd)
 
-    def wfile(self, f, mode='r'):
-        self.ui.deprecwarn("use 'repo.wvfs' instead of 'repo.wfile'", '4.2')
-        return self.wvfs(f, mode)
-
-    def _link(self, f):
-        self.ui.deprecwarn("use 'repo.wvfs.islink' instead of 'repo._link'",
-                           '4.2')
-        return self.wvfs.islink(f)
-
     def _loadfilter(self, filter):
         if filter not in self.filterpats:
             l = []
--- a/mercurial/manifest.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/manifest.py	Fri May 12 11:20:25 2017 -0700
@@ -1317,8 +1317,7 @@
             cachesize = opts.get('manifestcachesize', cachesize)
         self._treeinmem = usetreemanifest
 
-        self._oldmanifest = repo._constructmanifest()
-        self._revlog = self._oldmanifest
+        self._revlog = repo._constructmanifest()
 
         # A cache of the manifestctx or treemanifestctx for each directory
         self._dirmancache = {}
@@ -1340,12 +1339,7 @@
                    the revlog
         """
         if node in self._dirmancache.get(dir, ()):
-            cachemf = self._dirmancache[dir][node]
-            # The old manifest may put non-ctx manifests in the cache, so
-            # skip those since they don't implement the full api.
-            if (isinstance(cachemf, manifestctx) or
-                isinstance(cachemf, treemanifestctx)):
-                return cachemf
+            return self._dirmancache[dir][node]
 
         if dir:
             if self._revlog._treeondisk:
--- a/mercurial/match.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/match.py	Fri May 12 11:20:25 2017 -0700
@@ -52,7 +52,7 @@
     return fset, other
 
 def _expandsubinclude(kindpats, root):
-    '''Returns the list of subinclude matchers and the kindpats without the
+    '''Returns the list of subinclude matcher args and the kindpats without the
     subincludes in it.'''
     relmatchers = []
     other = []
@@ -64,12 +64,12 @@
             path = pathutil.join(sourceroot, pat)
 
             newroot = pathutil.dirname(path)
-            relmatcher = match(newroot, '', [], ['include:%s' % path])
+            matcherargs = (newroot, '', [], ['include:%s' % path])
 
             prefix = pathutil.canonpath(root, root, newroot)
             if prefix:
                 prefix += '/'
-            relmatchers.append((prefix, relmatcher))
+            relmatchers.append((prefix, matcherargs))
         else:
             other.append((kind, pat, source))
 
@@ -134,7 +134,7 @@
         self._includeroots = set()
         self._excluderoots = set()
         # dirs are directories which are non-recursively included.
-        self._includedirs = set(['.'])
+        self._includedirs = set()
 
         if badfn is not None:
             self.bad = badfn
@@ -254,7 +254,7 @@
             return 'all'
         if dir in self._excluderoots:
             return False
-        if ((self._includeroots or self._includedirs != set(['.'])) and
+        if ((self._includeroots or self._includedirs) and
             '.' not in self._includeroots and
             dir not in self._includeroots and
             dir not in self._includedirs and
@@ -584,10 +584,17 @@
 
     subincludes, kindpats = _expandsubinclude(kindpats, root)
     if subincludes:
+        submatchers = {}
         def matchsubinclude(f):
-            for prefix, mf in subincludes:
-                if f.startswith(prefix) and mf(f[len(prefix):]):
-                    return True
+            for prefix, matcherargs in subincludes:
+                if f.startswith(prefix):
+                    mf = submatchers.get(prefix)
+                    if mf is None:
+                        mf = match(*matcherargs)
+                        submatchers[prefix] = mf
+
+                    if mf(f[len(prefix):]):
+                        return True
             return False
         matchfuncs.append(matchsubinclude)
 
@@ -677,16 +684,16 @@
 
     >>> _rootsanddirs(\
         [('glob', 'g/h/*', ''), ('glob', 'g/h', ''), ('glob', 'g*', '')])
-    (['g/h', 'g/h', '.'], ['g'])
+    (['g/h', 'g/h', '.'], ['g', '.'])
     >>> _rootsanddirs(\
         [('rootfilesin', 'g/h', ''), ('rootfilesin', '', '')])
-    ([], ['g/h', '.', 'g'])
+    ([], ['g/h', '.', 'g', '.'])
     >>> _rootsanddirs(\
         [('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
-    (['r', 'p/p', '.'], ['p'])
+    (['r', 'p/p', '.'], ['p', '.'])
     >>> _rootsanddirs(\
         [('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
-    (['.', '.', '.'], [])
+    (['.', '.', '.'], ['.'])
     '''
     r, d = _patternrootsanddirs(kindpats)
 
@@ -694,6 +701,8 @@
     # scanned to get to either the roots or the other exact directories.
     d.extend(util.dirs(d))
     d.extend(util.dirs(r))
+    # util.dirs() does not include the root directory, so add it manually
+    d.append('.')
 
     return r, d
 
--- a/mercurial/mdiff.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/mdiff.py	Fri May 12 11:20:25 2017 -0700
@@ -13,7 +13,6 @@
 
 from .i18n import _
 from . import (
-    base85,
     bdiff,
     error,
     mpatch,
@@ -21,6 +20,12 @@
     util,
 )
 
+blocks = bdiff.blocks
+fixws = bdiff.fixws
+patches = mpatch.patches
+patchedsize = mpatch.patchedsize
+textdiff = bdiff.bdiff
+
 def splitnewlines(text):
     '''like str.splitlines, but only split on newlines.'''
     lines = [l + '\n' for l in text.split('\n')]
@@ -426,7 +431,7 @@
             l = chr(ord('A') + l - 1)
         else:
             l = chr(l - 26 + ord('a') - 1)
-        return '%c%s\n' % (l, base85.b85encode(line, True))
+        return '%c%s\n' % (l, util.b85encode(line, True))
 
     def chunk(text, csize=52):
         l = len(text)
@@ -478,7 +483,3 @@
 
 def replacediffheader(oldlen, newlen):
     return struct.pack(">lll", 0, oldlen, newlen)
-
-patches = mpatch.patches
-patchedsize = mpatch.patchedsize
-textdiff = bdiff.bdiff
--- a/mercurial/merge.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/merge.py	Fri May 12 11:20:25 2017 -0700
@@ -786,7 +786,7 @@
     return True
 
 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
-                  acceptremote, followcopies):
+                  acceptremote, followcopies, forcefulldiff=False):
     """
     Merge wctx and p2 with ancestor pa and generate merge action list
 
@@ -821,6 +821,26 @@
         if any(wctx.sub(s).dirty() for s in wctx.substate):
             m1['.hgsubstate'] = modifiednodeid
 
+    # Don't use m2-vs-ma optimization if:
+    # - ma is the same as m1 or m2, which we're just going to diff again later
+    # - The matcher is set already, so we can't override it
+    # - The caller specifically asks for a full diff, which is useful during bid
+    #   merge.
+    if (pa not in ([wctx, p2] + wctx.parents()) and
+        matcher is None and not forcefulldiff):
+        # Identify which files are relevant to the merge, so we can limit the
+        # total m1-vs-m2 diff to just those files. This has significant
+        # performance benefits in large repositories.
+        relevantfiles = set(ma.diff(m2).keys())
+
+        # For copied and moved files, we need to add the source file too.
+        for copykey, copyvalue in copy.iteritems():
+            if copyvalue in relevantfiles:
+                relevantfiles.add(copykey)
+        for movedirkey in movewithdir.iterkeys():
+            relevantfiles.add(movedirkey)
+        matcher = scmutil.matchfiles(repo, relevantfiles)
+
     diff = m1.diff(m2, match=matcher)
 
     if matcher is None:
@@ -974,7 +994,7 @@
             repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
             actions, diverge1, renamedelete1 = manifestmerge(
                 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
-                acceptremote, followcopies)
+                acceptremote, followcopies, forcefulldiff=True)
             _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
 
             # Track the shortest set of warning on the theory that bid
--- a/mercurial/obsolete.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/obsolete.py	Fri May 12 11:20:25 2017 -0700
@@ -74,7 +74,6 @@
 
 from .i18n import _
 from . import (
-    base85,
     error,
     node,
     parsers,
@@ -744,7 +743,7 @@
         currentlen += len(nextdata)
     for idx, part in enumerate(reversed(parts)):
         data = ''.join([_pack('>B', _fm0version)] + part)
-        keys['dump%i' % idx] = base85.b85encode(data)
+        keys['dump%i' % idx] = util.b85encode(data)
     return keys
 
 def listmarkers(repo):
@@ -761,7 +760,7 @@
     if old:
         repo.ui.warn(_('unexpected old value for %r') % key)
         return 0
-    data = base85.b85decode(new)
+    data = util.b85decode(new)
     lock = repo.lock()
     try:
         tr = repo.transaction('pushkey: obsolete markers')
--- a/mercurial/patch.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/patch.py	Fri May 12 11:20:25 2017 -0700
@@ -26,7 +26,6 @@
     short,
 )
 from . import (
-    base85,
     copies,
     diffhelpers,
     encoding,
@@ -1430,7 +1429,7 @@
             else:
                 l = ord(l) - ord('a') + 27
             try:
-                dec.append(base85.b85decode(line[1:])[:l])
+                dec.append(util.b85decode(line[1:])[:l])
             except ValueError as e:
                 raise PatchError(_('could not decode "%s" binary patch: %s')
                                  % (self._fname, str(e)))
@@ -2508,6 +2507,9 @@
         revinfo = ' '.join(["-r %s" % rev for rev in revs])
         return 'diff %s %s' % (revinfo, f)
 
+    def isempty(fctx):
+        return fctx is None or fctx.size() == 0
+
     date1 = util.datestr(ctx1.date())
     date2 = util.datestr(ctx2.date())
 
@@ -2523,28 +2525,30 @@
     for f1, f2, copyop in _filepairs(modified, added, removed, copy, opts):
         content1 = None
         content2 = None
+        fctx1 = None
+        fctx2 = None
         flag1 = None
         flag2 = None
         if f1:
-            content1 = getfilectx(f1, ctx1).data()
+            fctx1 = getfilectx(f1, ctx1)
             if opts.git or losedatafn:
                 flag1 = ctx1.flags(f1)
         if f2:
-            content2 = getfilectx(f2, ctx2).data()
+            fctx2 = getfilectx(f2, ctx2)
             if opts.git or losedatafn:
                 flag2 = ctx2.flags(f2)
-        binary = False
-        if opts.git or losedatafn:
-            binary = util.binary(content1) or util.binary(content2)
+        # if binary is True, output "summary" or "base85", but not "text diff"
+        binary = not opts.text and any(f.isbinary()
+                                       for f in [fctx1, fctx2] if f is not None)
 
         if losedatafn and not opts.git:
             if (binary or
                 # copy/rename
                 f2 in copy or
                 # empty file creation
-                (not f1 and not content2) or
+                (not f1 and isempty(fctx2)) or
                 # empty file deletion
-                (not content1 and not f2) or
+                (isempty(fctx1) and not f2) or
                 # create with flags
                 (not f1 and flag2) or
                 # change flags
@@ -2577,7 +2581,37 @@
         elif revs and not repo.ui.quiet:
             header.append(diffline(path1, revs))
 
-        if binary and opts.git and not opts.nobinary and not opts.text:
+        #  fctx.is  | diffopts                | what to   | is fctx.data()
+        #  binary() | text nobinary git index | output?   | outputted?
+        # ------------------------------------|----------------------------
+        #  yes      | no   no       no  *     | summary   | no
+        #  yes      | no   no       yes *     | base85    | yes
+        #  yes      | no   yes      no  *     | summary   | no
+        #  yes      | no   yes      yes 0     | summary   | no
+        #  yes      | no   yes      yes >0    | summary   | semi [1]
+        #  yes      | yes  *        *   *     | text diff | yes
+        #  no       | *    *        *   *     | text diff | yes
+        # [1]: hash(fctx.data()) is outputted. so fctx.data() cannot be faked
+        if binary and (not opts.git or (opts.git and opts.nobinary and not
+                                        opts.index)):
+            # fast path: no binary content will be displayed, content1 and
+            # content2 are only used for equivalent test. cmp() could have a
+            # fast path.
+            if fctx1 is not None:
+                content1 = b'\0'
+            if fctx2 is not None:
+                if fctx1 is not None and not fctx1.cmp(fctx2):
+                    content2 = b'\0' # not different
+                else:
+                    content2 = b'\0\0'
+        else:
+            # normal path: load contents
+            if fctx1 is not None:
+                content1 = fctx1.data()
+            if fctx2 is not None:
+                content2 = fctx2.data()
+
+        if binary and opts.git and not opts.nobinary:
             text = mdiff.b85diff(content1, content2)
             if text:
                 header.append('index %s..%s' %
--- a/mercurial/policy.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/policy.py	Fri May 12 11:20:25 2017 -0700
@@ -33,8 +33,8 @@
 #
 # The canonical way to do this is to test platform.python_implementation().
 # But we don't import platform and don't bloat for it here.
-if '__pypy__' in sys.builtin_module_names:
-    policy = 'cffi'
+if r'__pypy__' in sys.builtin_module_names:
+    policy = b'cffi'
 
 # Our C extensions aren't yet compatible with Python 3. So use pure Python
 # on Python 3 for now.
@@ -43,7 +43,7 @@
 
 # Environment variable can always force settings.
 if sys.version_info[0] >= 3:
-    if 'HGMODULEPOLICY' in os.environ:
-        policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
+    if r'HGMODULEPOLICY' in os.environ:
+        policy = os.environ[r'HGMODULEPOLICY'].encode(r'utf-8')
 else:
-    policy = os.environ.get('HGMODULEPOLICY', policy)
+    policy = os.environ.get(r'HGMODULEPOLICY', policy)
--- a/mercurial/posix.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/posix.py	Fri May 12 11:20:25 2017 -0700
@@ -494,7 +494,7 @@
 
 def getuser():
     '''return name of current user'''
-    return getpass.getuser()
+    return pycompat.fsencode(getpass.getuser())
 
 def username(uid=None):
     """Return the name of the user with the given uid.
--- a/mercurial/pvec.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/pvec.py	Fri May 12 11:20:25 2017 -0700
@@ -52,7 +52,6 @@
 
 from .node import nullrev
 from . import (
-    base85,
     util,
 )
 
@@ -166,13 +165,13 @@
                 else:
                     pvc[n] = _mergevec(pvc[p1], pvc[p2], node)
     bs = _join(*pvc[ctx.rev()])
-    return pvec(base85.b85encode(bs))
+    return pvec(util.b85encode(bs))
 
 class pvec(object):
     def __init__(self, hashorctx):
         if isinstance(hashorctx, str):
             self._bs = hashorctx
-            self._depth, self._vec = _split(base85.b85decode(hashorctx))
+            self._depth, self._vec = _split(util.b85decode(hashorctx))
         else:
             self._vec = ctxpvec(hashorctx)
 
--- a/mercurial/pycompat.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/pycompat.py	Fri May 12 11:20:25 2017 -0700
@@ -164,6 +164,10 @@
             return s
         return s.decode(u'latin-1')
 
+    def raisewithtb(exc, tb):
+        """Raise exception with the given traceback"""
+        raise exc.with_traceback(tb)
+
     def _wrapattrfunc(f):
         @functools.wraps(f)
         def w(object, name, *args):
@@ -224,6 +228,10 @@
     sysbytes = identity
     sysstr = identity
 
+    # this can't be parsed on Python 3
+    exec('def raisewithtb(exc, tb):\n'
+         '    raise exc, None, tb\n')
+
     # Partial backport from os.py in Python 3, which only accepts bytes.
     # In Python 2, our paths should only ever be bytes, a unicode path
     # indicates a bug.
--- a/mercurial/rcutil.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/rcutil.py	Fri May 12 11:20:25 2017 -0700
@@ -11,7 +11,6 @@
 
 from . import (
     encoding,
-    osutil,
     pycompat,
     util,
 )
@@ -30,7 +29,7 @@
     p = util.expandpath(path)
     if os.path.isdir(p):
         join = os.path.join
-        return [join(p, f) for f, k in osutil.listdir(p) if f.endswith('.rc')]
+        return [join(p, f) for f, k in util.listdir(p) if f.endswith('.rc')]
     return [p]
 
 def envrcitems(env=None):
--- a/mercurial/repair.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/repair.py	Fri May 12 11:20:25 2017 -0700
@@ -165,13 +165,8 @@
             tr.startgroup()
             cl.strip(striprev, tr)
             mfst.strip(striprev, tr)
-            if 'treemanifest' in repo.requirements: # safe but unnecessary
-                                                    # otherwise
-                for unencoded, encoded, size in repo.store.datafiles():
-                    if (unencoded.startswith('meta/') and
-                        unencoded.endswith('00manifest.i')):
-                        dir = unencoded[5:-12]
-                        repo.manifestlog._revlog.dirlog(dir).strip(striprev, tr)
+            striptrees(repo, tr, striprev, files)
+
             for fn in files:
                 repo.file(fn).strip(striprev, tr)
             tr.endgroup()
@@ -240,6 +235,16 @@
     # extensions can use it
     return backupfile
 
+def striptrees(repo, tr, striprev, files):
+    if 'treemanifest' in repo.requirements: # safe but unnecessary
+                                            # otherwise
+        treerevlog = repo.manifestlog._revlog
+        for dir in util.dirs(files):
+            # If the revlog doesn't exist, this returns an empty revlog and is a
+            # no-op.
+            rl = treerevlog.dirlog(dir)
+            rl.strip(striprev, tr)
+
 def rebuildfncache(ui, repo):
     """Rebuilds the fncache file from repo history.
 
--- a/mercurial/revlog.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/revlog.py	Fri May 12 11:20:25 2017 -0700
@@ -1075,7 +1075,7 @@
         p1, p2 = self.parents(node)
         return hash(text, p1, p2) != node
 
-    def _addchunk(self, offset, data):
+    def _cachesegment(self, offset, data):
         """Add a segment to the revlog cache.
 
         Accepts an absolute offset and the data that is at that location.
@@ -1087,7 +1087,7 @@
         else:
             self._chunkcache = offset, data
 
-    def _loadchunk(self, offset, length, df=None):
+    def _readsegment(self, offset, length, df=None):
         """Load a segment of raw data from the revlog.
 
         Accepts an absolute offset, length to read, and an optional existing
@@ -1118,12 +1118,12 @@
         d = df.read(reallength)
         if closehandle:
             df.close()
-        self._addchunk(realoffset, d)
+        self._cachesegment(realoffset, d)
         if offset != realoffset or reallength != length:
             return util.buffer(d, offset - realoffset, length)
         return d
 
-    def _getchunk(self, offset, length, df=None):
+    def _getsegment(self, offset, length, df=None):
         """Obtain a segment of raw data from the revlog.
 
         Accepts an absolute offset, length of bytes to obtain, and an
@@ -1145,9 +1145,9 @@
                 return d # avoid a copy
             return util.buffer(d, cachestart, cacheend - cachestart)
 
-        return self._loadchunk(offset, length, df=df)
+        return self._readsegment(offset, length, df=df)
 
-    def _chunkraw(self, startrev, endrev, df=None):
+    def _getsegmentforrevs(self, startrev, endrev, df=None):
         """Obtain a segment of raw data corresponding to a range of revisions.
 
         Accepts the start and end revisions and an optional already-open
@@ -1179,7 +1179,7 @@
             end += (endrev + 1) * self._io.size
         length = end - start
 
-        return start, self._getchunk(start, length, df=df)
+        return start, self._getsegment(start, length, df=df)
 
     def _chunk(self, rev, df=None):
         """Obtain a single decompressed chunk for a revision.
@@ -1190,7 +1190,7 @@
 
         Returns a str holding uncompressed data for the requested revision.
         """
-        return self.decompress(self._chunkraw(rev, rev, df=df)[1])
+        return self.decompress(self._getsegmentforrevs(rev, rev, df=df)[1])
 
     def _chunks(self, revs, df=None):
         """Obtain decompressed chunks for the specified revisions.
@@ -1217,7 +1217,7 @@
         ladd = l.append
 
         try:
-            offset, data = self._chunkraw(revs[0], revs[-1], df=df)
+            offset, data = self._getsegmentforrevs(revs[0], revs[-1], df=df)
         except OverflowError:
             # issue4215 - we can't cache a run of chunks greater than
             # 2G on Windows
@@ -1443,7 +1443,7 @@
         df = self.opener(self.datafile, 'w')
         try:
             for r in self:
-                df.write(self._chunkraw(r, r)[1])
+                df.write(self._getsegmentforrevs(r, r)[1])
         finally:
             df.close()
 
@@ -1502,6 +1502,15 @@
         if validatehash:
             self.checkhash(rawtext, node, p1=p1, p2=p2)
 
+        return self.addrawrevision(rawtext, transaction, link, p1, p2, node,
+                                   flags, cachedelta=cachedelta)
+
+    def addrawrevision(self, rawtext, transaction, link, p1, p2, node, flags,
+                       cachedelta=None):
+        """add a raw revision with known flags, node and parents
+        useful when reusing a revision not stored in this revlog (ex: received
+        over wire, or read from an external bundle).
+        """
         dfh = None
         if not self._inline:
             dfh = self.opener(self.datafile, "a+")
--- a/mercurial/scmposix.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/scmposix.py	Fri May 12 11:20:25 2017 -0700
@@ -8,8 +8,8 @@
 
 from . import (
     encoding,
-    osutil,
     pycompat,
+    util,
 )
 
 # BSD 'more' escapes ANSI color sequences by default. This can be disabled by
@@ -23,7 +23,7 @@
     rcdir = os.path.join(path, 'hgrc.d')
     try:
         rcs.extend([os.path.join(rcdir, f)
-                    for f, kind in osutil.listdir(rcdir)
+                    for f, kind in util.listdir(rcdir)
                     if f.endswith(".rc")])
     except OSError:
         pass
--- a/mercurial/scmutil.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/scmutil.py	Fri May 12 11:20:25 2017 -0700
@@ -26,7 +26,6 @@
     revsetlang,
     similar,
     util,
-    vfs as vfsmod,
 )
 
 if pycompat.osname == 'nt':
@@ -121,10 +120,6 @@
     secretlist = []
     if excluded:
         for n in excluded:
-            if n not in repo:
-                # discovery should not have included the filtered revision,
-                # we have to explicitly exclude it until discovery is cleanup.
-                continue
             ctx = repo[n]
             if ctx.phase() >= phases.secret and not ctx.extinct():
                 secretlist.append(n)
@@ -186,8 +181,6 @@
         ui.warn(_("abort: file censored %s!\n") % inst)
     except error.RevlogError as inst:
         ui.warn(_("abort: %s!\n") % inst)
-    except error.SignalInterrupt:
-        ui.warn(_("killed!\n"))
     except error.InterventionRequired as inst:
         ui.warn("%s\n" % inst)
         if inst.hint:
@@ -215,7 +208,7 @@
                 reason = inst.reason
             if isinstance(reason, unicode):
                 # SSLError of Python 2.7.9 contains a unicode
-                reason = reason.encode(encoding.encoding, 'replace')
+                reason = encoding.unitolocal(reason)
             ui.warn(_("abort: error: %s\n") % reason)
         elif (util.safehasattr(inst, "args")
               and inst.args and inst.args[0] == errno.EPIPE):
@@ -335,27 +328,6 @@
         key = s.digest()
     return key
 
-def _deprecated(old, new, func):
-    msg = ('class at mercurial.scmutil.%s moved to mercurial.vfs.%s'
-           % (old, new))
-    def wrapper(*args, **kwargs):
-        util.nouideprecwarn(msg, '4.2')
-        return func(*args, **kwargs)
-    return wrapper
-
-# compatibility layer since all 'vfs' code moved to 'mercurial.vfs'
-#
-# This is hard to instal deprecation warning to this since we do not have
-# access to a 'ui' object.
-opener = _deprecated('opener', 'vfs', vfsmod.vfs)
-vfs = _deprecated('vfs', 'vfs', vfsmod.vfs)
-filteropener = _deprecated('filteropener', 'filtervfs', vfsmod.filtervfs)
-filtervfs = _deprecated('filtervfs', 'filtervfs', vfsmod.filtervfs)
-abstractvfs = _deprecated('abstractvfs', 'abstractvfs', vfsmod.abstractvfs)
-readonlyvfs = _deprecated('readonlyvfs', 'readonlyvfs', vfsmod.readonlyvfs)
-auditvfs = _deprecated('auditvfs', 'auditvfs', vfsmod.auditvfs)
-checkambigatclosing = vfsmod.checkambigatclosing
-
 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
     '''yield every hg repository under path, always recursively.
     The recurse flag will only control recursion into repo working dirs'''
--- a/mercurial/scmwindows.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/scmwindows.py	Fri May 12 11:20:25 2017 -0700
@@ -4,7 +4,6 @@
 
 from . import (
     encoding,
-    osutil,
     pycompat,
     util,
     win32,
@@ -29,7 +28,7 @@
     # Use hgrc.d found in directory with hg.exe
     progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
     if os.path.isdir(progrcd):
-        for f, kind in osutil.listdir(progrcd):
+        for f, kind in util.listdir(progrcd):
             if f.endswith('.rc'):
                 rcpath.append(os.path.join(progrcd, f))
     # else look for a system rcpath in the registry
@@ -42,7 +41,7 @@
         if p.lower().endswith('mercurial.ini'):
             rcpath.append(p)
         elif os.path.isdir(p):
-            for f, kind in osutil.listdir(p):
+            for f, kind in util.listdir(p):
                 if f.endswith('.rc'):
                     rcpath.append(os.path.join(p, f))
     return rcpath
--- a/mercurial/similar.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/similar.py	Fri May 12 11:20:25 2017 -0700
@@ -9,7 +9,6 @@
 
 from .i18n import _
 from . import (
-    bdiff,
     mdiff,
 )
 
@@ -56,10 +55,10 @@
 def _score(fctx, otherdata):
     orig, lines = otherdata
     text = fctx.data()
-    # bdiff.blocks() returns blocks of matching lines
+    # mdiff.blocks() returns blocks of matching lines
     # count the number of bytes in each
     equal = 0
-    matches = bdiff.blocks(text, orig)
+    matches = mdiff.blocks(text, orig)
     for x1, x2, y1, y2 in matches:
         for line in lines[y1:y2]:
             equal += len(line)
--- a/mercurial/smartset.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/smartset.py	Fri May 12 11:20:25 2017 -0700
@@ -245,7 +245,7 @@
     @util.propertycache
     def _list(self):
         # _list is only lazily constructed if we have _set
-        assert '_set' in self.__dict__
+        assert r'_set' in self.__dict__
         return list(self._set)
 
     def __iter__(self):
--- a/mercurial/subrepo.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/subrepo.py	Fri May 12 11:20:25 2017 -0700
@@ -1771,7 +1771,7 @@
                 if exact:
                     rejected.append(f)
                 continue
-            if not opts.get('dry_run'):
+            if not opts.get(r'dry_run'):
                 self._gitcommand(command + [f])
 
         for f in rejected:
@@ -1878,9 +1878,9 @@
         deleted, unknown, ignored, clean = [], [], [], []
 
         command = ['status', '--porcelain', '-z']
-        if opts.get('unknown'):
+        if opts.get(r'unknown'):
             command += ['--untracked-files=all']
-        if opts.get('ignored'):
+        if opts.get(r'ignored'):
             command += ['--ignored']
         out = self._gitcommand(command)
 
@@ -1908,7 +1908,7 @@
             elif st == '!!':
                 ignored.append(filename1)
 
-        if opts.get('clean'):
+        if opts.get(r'clean'):
             out = self._gitcommand(['ls-files'])
             for f in out.split('\n'):
                 if not f in changedfiles:
@@ -1921,7 +1921,7 @@
     def diff(self, ui, diffopts, node2, match, prefix, **opts):
         node1 = self._state[1]
         cmd = ['diff', '--no-renames']
-        if opts['stat']:
+        if opts[r'stat']:
             cmd.append('--stat')
         else:
             # for Git, this also implies '-p'
@@ -1964,7 +1964,7 @@
     @annotatesubrepoerror
     def revert(self, substate, *pats, **opts):
         self.ui.status(_('reverting subrepo %s\n') % substate[0])
-        if not opts.get('no_backup'):
+        if not opts.get(r'no_backup'):
             status = self.status(None)
             names = status.modified
             for name in names:
@@ -1973,7 +1973,7 @@
                         (name, bakname))
                 self.wvfs.rename(name, bakname)
 
-        if not opts.get('dry_run'):
+        if not opts.get(r'dry_run'):
             self.get(substate, overwrite=True)
         return []
 
--- a/mercurial/templatefilters.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/templatefilters.py	Fri May 12 11:20:25 2017 -0700
@@ -16,6 +16,7 @@
     encoding,
     hbisect,
     node,
+    pycompat,
     registrar,
     templatekw,
     util,
@@ -24,6 +25,9 @@
 urlerr = util.urlerr
 urlreq = util.urlreq
 
+if pycompat.ispy3:
+    long = int
+
 # filters are callables like:
 #   fn(obj)
 # with:
@@ -226,8 +230,8 @@
     elif obj is True:
         return 'true'
     elif isinstance(obj, (int, long, float)):
-        return str(obj)
-    elif isinstance(obj, str):
+        return pycompat.bytestr(obj)
+    elif isinstance(obj, bytes):
         return '"%s"' % encoding.jsonescape(obj, paranoid=paranoid)
     elif util.safehasattr(obj, 'keys'):
         out = ['%s: %s' % (json(k), json(v))
@@ -351,11 +355,11 @@
     text and concatenating them.
     """
     thing = templatekw.unwraphybrid(thing)
-    if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
+    if util.safehasattr(thing, '__iter__') and not isinstance(thing, bytes):
         return "".join([stringify(t) for t in thing if t is not None])
     if thing is None:
         return ""
-    return str(thing)
+    return pycompat.bytestr(thing)
 
 @templatefilter('stripdir')
 def stripdir(text):
--- a/mercurial/templater.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/templater.py	Fri May 12 11:20:25 2017 -0700
@@ -53,6 +53,7 @@
     """Parse a template expression into a stream of tokens, which must end
     with term if specified"""
     pos = start
+    program = pycompat.bytestr(program)
     while pos < end:
         c = program[pos]
         if c.isspace(): # skip inter-token whitespace
--- a/mercurial/util.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/util.py	Fri May 12 11:20:25 2017 -0700
@@ -42,6 +42,7 @@
 import zlib
 
 from . import (
+    base85,
     encoding,
     error,
     i18n,
@@ -50,6 +51,9 @@
     pycompat,
 )
 
+b85decode = base85.b85decode
+b85encode = base85.b85encode
+
 cookielib = pycompat.cookielib
 empty = pycompat.empty
 httplib = pycompat.httplib
@@ -102,6 +106,7 @@
 hidewindow = platform.hidewindow
 isexec = platform.isexec
 isowner = platform.isowner
+listdir = osutil.listdir
 localpath = platform.localpath
 lookupreg = platform.lookupreg
 makedir = platform.makedir
@@ -139,6 +144,15 @@
 unlink = platform.unlink
 username = platform.username
 
+try:
+    recvfds = osutil.recvfds
+except AttributeError:
+    pass
+try:
+    setprocname = osutil.setprocname
+except AttributeError:
+    pass
+
 # Python compatibility
 
 _notset = object()
@@ -1161,7 +1175,7 @@
                         os.stat(os.path.dirname(dst)).st_dev)
         topic = gettopic()
         os.mkdir(dst)
-        for name, kind in osutil.listdir(src):
+        for name, kind in listdir(src):
             srcname = os.path.join(src, name)
             dstname = os.path.join(dst, name)
             def nprog(t, pos):
@@ -1724,8 +1738,7 @@
     iterator over chunks of arbitrary size."""
 
     def __init__(self, in_iter):
-        """in_iter is the iterator that's iterating over the input chunks.
-        targetsize is how big a buffer to try to maintain."""
+        """in_iter is the iterator that's iterating over the input chunks."""
         def splitbig(chunks):
             for chunk in chunks:
                 if len(chunk) > 2**20:
@@ -1914,6 +1927,7 @@
     # add missing elements from defaults
     usenow = False # default to using biased defaults
     for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
+        part = pycompat.bytestr(part)
         found = [True for p in part if ("%"+p) in format]
         if not found:
             date += "@" + defaults[part][usenow]
@@ -1981,13 +1995,13 @@
             # this piece is for rounding the specific end of unknowns
             b = bias.get(part)
             if b is None:
-                if part[0] in "HMS":
+                if part[0:1] in "HMS":
                     b = "00"
                 else:
                     b = "0"
 
             # this piece is for matching the generic end to today's date
-            n = datestr(now, "%" + part[0])
+            n = datestr(now, "%" + part[0:1])
 
             defaults[part] = (b, n)
 
--- a/mercurial/vfs.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/vfs.py	Fri May 12 11:20:25 2017 -0700
@@ -17,7 +17,6 @@
 from .i18n import _
 from . import (
     error,
-    osutil,
     pathutil,
     pycompat,
     util,
@@ -163,7 +162,7 @@
             return fd, fname
 
     def readdir(self, path=None, stat=None, skip=None):
-        return osutil.listdir(self.join(path), stat, skip)
+        return util.listdir(self.join(path), stat, skip)
 
     def readlock(self, path):
         return util.readlock(self.join(path))
--- a/mercurial/windows.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/windows.py	Fri May 12 11:20:25 2017 -0700
@@ -136,6 +136,9 @@
         # convert to a friendlier exception
         raise IOError(err.errno, '%s: %s' % (name, err.strerror))
 
+# may be wrapped by win32mbcs extension
+listdir = osutil.listdir
+
 class winstdout(object):
     '''stdout on windows misbehaves if sent through a pipe'''
 
@@ -349,7 +352,7 @@
         if cache is None:
             try:
                 dmap = dict([(normcase(n), s)
-                             for n, k, s in osutil.listdir(dir, True)
+                             for n, k, s in listdir(dir, True)
                              if getkind(s.st_mode) in _wantedkinds])
             except OSError as err:
                 # Python >= 2.5 returns ENOENT and adds winerror field
@@ -376,7 +379,7 @@
 def removedirs(name):
     """special version of os.removedirs that does not remove symlinked
     directories or junction points if they actually contain files"""
-    if osutil.listdir(name):
+    if listdir(name):
         return
     os.rmdir(name)
     head, tail = os.path.split(name)
@@ -384,7 +387,7 @@
         head, tail = os.path.split(head)
     while head and tail:
         try:
-            if osutil.listdir(head):
+            if listdir(head):
                 return
             os.rmdir(head)
         except (ValueError, OSError):
--- a/mercurial/worker.py	Fri May 12 21:46:14 2017 +0900
+++ b/mercurial/worker.py	Fri May 12 11:20:25 2017 -0700
@@ -134,37 +134,43 @@
             killworkers()
     oldchldhandler = signal.signal(signal.SIGCHLD, sigchldhandler)
     ui.flush()
+    parentpid = os.getpid()
     for pargs in partition(args, workers):
-        pid = os.fork()
-        if pid == 0:
-            signal.signal(signal.SIGINT, oldhandler)
-            signal.signal(signal.SIGCHLD, oldchldhandler)
-
-            def workerfunc():
-                os.close(rfd)
-                for i, item in func(*(staticargs + (pargs,))):
-                    os.write(wfd, '%d %s\n' % (i, item))
-                return 0
+        # make sure we use os._exit in all worker code paths. otherwise the
+        # worker may do some clean-ups which could cause surprises like
+        # deadlock. see sshpeer.cleanup for example.
+        # override error handling *before* fork. this is necessary because
+        # exception (signal) may arrive after fork, before "pid =" assignment
+        # completes, and other exception handler (dispatch.py) can lead to
+        # unexpected code path without os._exit.
+        ret = -1
+        try:
+            pid = os.fork()
+            if pid == 0:
+                signal.signal(signal.SIGINT, oldhandler)
+                signal.signal(signal.SIGCHLD, oldchldhandler)
 
-            # make sure we use os._exit in all code paths. otherwise the worker
-            # may do some clean-ups which could cause surprises like deadlock.
-            # see sshpeer.cleanup for example.
-            ret = 0
-            try:
+                def workerfunc():
+                    os.close(rfd)
+                    for i, item in func(*(staticargs + (pargs,))):
+                        os.write(wfd, '%d %s\n' % (i, item))
+                    return 0
+
+                ret = scmutil.callcatch(ui, workerfunc)
+        except: # parent re-raises, child never returns
+            if os.getpid() == parentpid:
+                raise
+            exctype = sys.exc_info()[0]
+            force = not issubclass(exctype, KeyboardInterrupt)
+            ui.traceback(force=force)
+        finally:
+            if os.getpid() != parentpid:
                 try:
-                    ret = scmutil.callcatch(ui, workerfunc)
-                finally:
                     ui.flush()
-            except KeyboardInterrupt:
-                os._exit(255)
-            except: # never return, therefore no re-raises
-                try:
-                    ui.traceback(force=True)
-                    ui.flush()
+                except: # never returns, no re-raises
+                    pass
                 finally:
-                    os._exit(255)
-            else:
-                os._exit(ret & 255)
+                    os._exit(ret & 255)
         pids.add(pid)
     os.close(wfd)
     fp = os.fdopen(rfd, pycompat.sysstr('rb'), 0)
--- a/setup.py	Fri May 12 21:46:14 2017 +0900
+++ b/setup.py	Fri May 12 11:20:25 2017 -0700
@@ -5,8 +5,8 @@
 # 'python setup.py --help' for more options
 
 import sys, platform
-if getattr(sys, 'version_info', (0, 0, 0)) < (2, 6, 0, 'final'):
-    raise SystemExit("Mercurial requires Python 2.6 or later.")
+if sys.version_info < (2, 7, 0, 'final'):
+    raise SystemExit('Mercurial requires Python 2.7 or later.')
 
 if sys.version_info[0] >= 3:
     printf = eval('print')
@@ -577,7 +577,10 @@
             'build_hgexe': buildhgexe,
             }
 
-packages = ['mercurial', 'mercurial.hgweb', 'mercurial.httpclient',
+packages = ['mercurial',
+            'mercurial.cext',
+            'mercurial.hgweb',
+            'mercurial.httpclient',
             'mercurial.pure',
             'hgext', 'hgext.convert', 'hgext.fsmonitor',
             'hgext.fsmonitor.pywatchman', 'hgext.highlight',
@@ -586,6 +589,7 @@
 common_depends = ['mercurial/bitmanipulation.h',
                   'mercurial/compat.h',
                   'mercurial/util.h']
+common_include_dirs = ['mercurial']
 
 osutil_cflags = []
 osutil_ldflags = []
@@ -615,21 +619,27 @@
 
 extmodules = [
     Extension('mercurial.base85', ['mercurial/base85.c'],
+              include_dirs=common_include_dirs,
               depends=common_depends),
     Extension('mercurial.bdiff', ['mercurial/bdiff.c',
                                   'mercurial/bdiff_module.c'],
+              include_dirs=common_include_dirs,
               depends=common_depends + ['mercurial/bdiff.h']),
     Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'],
+              include_dirs=common_include_dirs,
               depends=common_depends),
     Extension('mercurial.mpatch', ['mercurial/mpatch.c',
                                    'mercurial/mpatch_module.c'],
+              include_dirs=common_include_dirs,
               depends=common_depends),
     Extension('mercurial.parsers', ['mercurial/dirs.c',
                                     'mercurial/manifest.c',
                                     'mercurial/parsers.c',
                                     'mercurial/pathencode.c'],
+              include_dirs=common_include_dirs,
               depends=common_depends),
     Extension('mercurial.osutil', ['mercurial/osutil.c'],
+              include_dirs=common_include_dirs,
               extra_compile_args=osutil_cflags,
               extra_link_args=osutil_ldflags,
               depends=common_depends),
--- a/tests/hghave.py	Fri May 12 21:46:14 2017 +0900
+++ b/tests/hghave.py	Fri May 12 11:20:25 2017 -0700
@@ -580,10 +580,6 @@
     from mercurial import util
     return util.safehasattr(__future__, "absolute_import")
 
-@check("py27+", "running with Python 2.7+")
-def has_python27ornewer():
-    return sys.version_info[0:2] >= (2, 7)
-
 @check("py3k", "running with Python 3.x")
 def has_py3k():
     return 3 == sys.version_info[0]
--- a/tests/test-bdiff.py	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-bdiff.py	Fri May 12 11:20:25 2017 -0700
@@ -4,8 +4,7 @@
 import unittest
 
 from mercurial import (
-    bdiff,
-    mpatch,
+    mdiff,
 )
 
 class diffreplace(
@@ -16,10 +15,10 @@
 class BdiffTests(unittest.TestCase):
 
     def assert_bdiff_applies(self, a, b):
-        d = bdiff.bdiff(a, b)
+        d = mdiff.textdiff(a, b)
         c = a
         if d:
-            c = mpatch.patches(a, [d])
+            c = mdiff.patches(a, [d])
         self.assertEqual(
             c, b, ("bad diff+patch result from\n  %r to\n  "
                    "%r: \nbdiff: %r\npatched: %r" % (a, b, d, c[:200])))
@@ -54,7 +53,7 @@
             self.assert_bdiff(a, b)
 
     def showdiff(self, a, b):
-        bin = bdiff.bdiff(a, b)
+        bin = mdiff.textdiff(a, b)
         pos = 0
         q = 0
         actions = []
@@ -110,7 +109,7 @@
             ("", "", 0),
         ]
         for a, b, allws in cases:
-            c = bdiff.fixws(a, allws)
+            c = mdiff.fixws(a, allws)
             self.assertEqual(
                 c, b, 'fixws(%r) want %r got %r (allws=%r)' % (a, b, c, allws))
 
--- a/tests/test-bisect.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-bisect.t	Fri May 12 11:20:25 2017 -0700
@@ -551,7 +551,14 @@
   date:        Thu Jan 01 00:00:06 1970 +0000
   summary:     msg 6
   
-
+  $ hg graft -q 15
+  warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
+  abort: unresolved conflicts, can't continue
+  (use 'hg resolve' and 'hg graft --continue')
+  [255]
+  $ hg bisect --reset
+  $ hg up -C .
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Check that bisect does not break on obsolete changesets
 =========================================================
--- a/tests/test-bundle2-format.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-bundle2-format.t	Fri May 12 11:20:25 2017 -0700
@@ -113,7 +113,7 @@
   >             headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
   >             headcommon  = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
   >             outgoing = discovery.outgoing(repo, headcommon, headmissing)
-  >             cg = changegroup.getlocalchangegroup(repo, 'test:bundle2', outgoing, None)
+  >             cg = changegroup.getchangegroup(repo, 'test:bundle2', outgoing)
   >             bundler.newpart('changegroup', data=cg.getchunks(),
   >                             mandatory=False)
   > 
--- a/tests/test-bundle2-multiple-changegroups.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-bundle2-multiple-changegroups.t	Fri May 12 11:20:25 2017 -0700
@@ -13,13 +13,11 @@
   >     # in 'heads' as intermediate heads for the first changegroup.
   >     intermediates = [repo[r].p1().node() for r in heads]
   >     outgoing = discovery.outgoing(repo, common, intermediates)
-  >     cg = changegroup.getchangegroup(repo, source, outgoing,
-  >                                     bundlecaps=bundlecaps)
+  >     cg = changegroup.getchangegroup(repo, source, outgoing)
   >     bundler.newpart('output', data='changegroup1')
   >     bundler.newpart('changegroup', data=cg.getchunks())
   >     outgoing = discovery.outgoing(repo, common + intermediates, heads)
-  >     cg = changegroup.getchangegroup(repo, source, outgoing,
-  >                                     bundlecaps=bundlecaps)
+  >     cg = changegroup.getchangegroup(repo, source, outgoing)
   >     bundler.newpart('output', data='changegroup2')
   >     bundler.newpart('changegroup', data=cg.getchunks())
   > 
--- a/tests/test-check-code.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-check-code.t	Fri May 12 11:20:25 2017 -0700
@@ -9,39 +9,11 @@
 
   $ hg locate -X contrib/python-zstandard -X hgext/fsmonitor/pywatchman |
   > sed 's-\\-/-g' | "$check_code" --warnings --per-file=0 - || false
-  contrib/perf.py:869:
-   >             r.revision(r.node(x))
-   don't convert rev to node before passing to revision(nodeorrev)
   Skipping i18n/polib.py it has no-che?k-code (glob)
-  mercurial/demandimport.py:313:
-   >     if os.environ.get('HGDEMANDIMPORT') != 'disable':
-   use encoding.environ instead (py3)
-  mercurial/encoding.py:54:
-   >     environ = os.environ
-   use encoding.environ instead (py3)
-  mercurial/encoding.py:56:
-   >     environ = os.environb
-   use encoding.environ instead (py3)
-  mercurial/encoding.py:61:
-   >                    for k, v in os.environ.items())
-   use encoding.environ instead (py3)
-  mercurial/encoding.py:221:
-   >                    for k, v in os.environ.items())
-   use encoding.environ instead (py3)
   Skipping mercurial/httpclient/__init__.py it has no-che?k-code (glob)
   Skipping mercurial/httpclient/_readers.py it has no-che?k-code (glob)
-  mercurial/policy.py:46:
-   >     if 'HGMODULEPOLICY' in os.environ:
-   use encoding.environ instead (py3)
-  mercurial/policy.py:47:
-   >         policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
-   use encoding.environ instead (py3)
-  mercurial/policy.py:49:
-   >     policy = os.environ.get('HGMODULEPOLICY', policy)
-   use encoding.environ instead (py3)
   Skipping mercurial/statprof.py it has no-che?k-code (glob)
   Skipping tests/badserverext.py it has no-che?k-code (glob)
-  [1]
 
 @commands in debugcommands.py should be in alphabetical order.
 
--- a/tests/test-check-py3-commands.t	Fri May 12 21:46:14 2017 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-#require py3exe
-
-This test helps in keeping a track on which commands we can run on
-Python 3 and see what kind of errors are coming up.
-The full traceback is hidden to have a stable output.
-  $ HGBIN=`which hg`
-
-  $ for cmd in version debuginstall ; do
-  >   echo $cmd
-  >   $PYTHON3 $HGBIN $cmd 2>&1 2>&1 | tail -1
-  > done
-  version
-  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-  debuginstall
-  no problems detected
-
-#if test-repo
-Make a clone so that any features in the developer's .hg/hgrc that
-might confuse Python 3 don't break this test. When we can do commit in
-Python 3, we'll stop doing this. We use e76ed1e480ef for the clone
-because it has different files than 273ce12ad8f1, so we can test both
-`files` from dirstate and `files` loaded from a specific revision.
-
-  $ hg clone -r e76ed1e480ef "`dirname "$TESTDIR"`" testrepo 2>&1 | tail -1
-  15 files updated, 0 files merged, 0 files removed, 0 files unresolved
-
-Test using -R, which exercises some URL code:
-  $ $PYTHON3 $HGBIN -R testrepo files -r 273ce12ad8f1 | tail -1
-  testrepo/tkmerge
-
-Now prove `hg files` is reading the whole manifest. We have to grep
-out some potential warnings that come from hgrc as yet.
-  $ cd testrepo
-  $ $PYTHON3 $HGBIN files -r 273ce12ad8f1
-  .hgignore
-  PKG-INFO
-  README
-  hg
-  mercurial/__init__.py
-  mercurial/byterange.py
-  mercurial/fancyopts.py
-  mercurial/hg.py
-  mercurial/mdiff.py
-  mercurial/revlog.py
-  mercurial/transaction.py
-  notes.txt
-  setup.py
-  tkmerge
-
-  $ $PYTHON3 $HGBIN files -r 273ce12ad8f1 | wc -l
-  \s*14 (re)
-  $ $PYTHON3 $HGBIN files | wc -l
-  \s*15 (re)
-
-Test if log-like commands work:
-
-  $ $PYTHON3 $HGBIN tip
-  changeset:   10:e76ed1e480ef
-  tag:         tip
-  user:        oxymoron@cinder.waste.org
-  date:        Tue May 03 23:37:43 2005 -0800
-  summary:     Fix linking of changeset revs when merging
-  
-
-  $ $PYTHON3 $HGBIN log -r0
-  changeset:   0:9117c6561b0b
-  user:        mpm@selenic.com
-  date:        Tue May 03 13:16:10 2005 -0800
-  summary:     Add back links from file revisions to changeset revisions
-  
-
-  $ cd ..
-#endif
-
-Test if `hg config` works:
-
-  $ $PYTHON3 $HGBIN config
-  defaults.backout=-d "0 0"
-  defaults.commit=-d "0 0"
-  defaults.shelve=--date "0 0"
-  defaults.tag=-d "0 0"
-  devel.all-warnings=true
-  largefiles.usercache=$TESTTMP/.cache/largefiles
-  ui.slash=True
-  ui.interactive=False
-  ui.mergemarkers=detailed
-  ui.promptecho=True
-  web.address=localhost
-  web.ipv6=False
-
-  $ cat > included-hgrc <<EOF
-  > [extensions]
-  > babar = imaginary_elephant
-  > EOF
-  $ cat >> $HGRCPATH <<EOF
-  > %include $TESTTMP/included-hgrc
-  > EOF
-  $ $PYTHON3 $HGBIN version | tail -1
-  *** failed to import extension babar from imaginary_elephant: *: 'imaginary_elephant' (glob)
-  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-  $ rm included-hgrc
-  $ touch included-hgrc
-
-Test bytes-ness of policy.policy with HGMODULEPOLICY
-
-  $ HGMODULEPOLICY=py
-  $ export HGMODULEPOLICY
-  $ $PYTHON3 `which hg` debuginstall 2>&1 2>&1 | tail -1
-  no problems detected
-
-`hg init` can create empty repos
-`hg status works fine`
-`hg summary` also works!
-
-  $ $PYTHON3 `which hg` init py3repo
-  $ cd py3repo
-  $ echo "This is the file 'iota'." > iota
-  $ $PYTHON3 $HGBIN status
-  ? iota
-  $ $PYTHON3 $HGBIN add iota
-  $ $PYTHON3 $HGBIN status
-  A iota
-  $ $PYTHON3 $HGBIN commit --message 'commit performed in Python 3'
-  $ $PYTHON3 $HGBIN status
-
-  $ mkdir A
-  $ echo "This is the file 'mu'." > A/mu
-  $ $PYTHON3 $HGBIN addremove
-  adding A/mu
-  $ $PYTHON3 $HGBIN status
-  A A/mu
-  $ HGEDITOR='echo message > ' $PYTHON3 $HGBIN commit
-  $ $PYTHON3 $HGBIN status
-  $ $PYHON3 $HGBIN summary
-  parent: 1:e1e9167203d4 tip
-   message
-  branch: default
-  commit: (clean)
-  update: (current)
-  phases: 2 draft
-
-Prove the repo is valid using the Python 2 `hg`:
-  $ hg verify
-  checking changesets
-  checking manifests
-  crosschecking files in changesets and manifests
-  checking files
-  2 files, 2 changesets, 2 total revisions
-  $ hg log
-  changeset:   1:e1e9167203d4
-  tag:         tip
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     message
-  
-  changeset:   0:71c96e924262
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     commit performed in Python 3
-  
--- a/tests/test-commit-interactive-curses.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-commit-interactive-curses.t	Fri May 12 11:20:25 2017 -0700
@@ -343,7 +343,7 @@
   $ cp $HGRCPATH.pretest $HGRCPATH
   $ chunkselectorinterface() {
   > python <<EOF
-  > from mercurial import hg, ui, parsers;\
+  > from mercurial import hg, ui;\
   > repo = hg.repository(ui.ui.load(), ".");\
   > print repo.ui.interface("chunkselector")
   > EOF
--- a/tests/test-contrib-perf.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-contrib-perf.t	Fri May 12 11:20:25 2017 -0700
@@ -165,7 +165,3 @@
   $ (hg files -r 1.2 glob:mercurial/*.c glob:mercurial/*.py;
   >  hg files -r tip glob:mercurial/*.c glob:mercurial/*.py) |
   > "$TESTDIR"/check-perf-code.py contrib/perf.py
-  contrib/perf.py:869:
-   >             r.revision(r.node(x))
-   don't convert rev to node before passing to revision(nodeorrev)
-  [1]
--- a/tests/test-extdiff.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-extdiff.t	Fri May 12 11:20:25 2017 -0700
@@ -62,15 +62,10 @@
 
 Should diff cloned files directly:
 
-#if windows
   $ hg falabala -r 0:1
-  diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob)
+  diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
+  diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
   [1]
-#else
-  $ hg falabala -r 0:1
-  diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
-  [1]
-#endif
 
 Specifying an empty revision should abort.
 
@@ -92,41 +87,27 @@
 
 Should diff cloned file against wc file:
 
-#if windows
   $ hg falabala
-  diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "*\\a\\a" (glob)
+  diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "*\\a\\a" (glob) (windows !)
+  diffing */extdiff.*/a.2a13a4d2da36/a */a/a (glob) (no-windows !)
   [1]
-#else
-  $ hg falabala
-  diffing */extdiff.*/a.2a13a4d2da36/a */a/a (glob)
-  [1]
-#endif
 
 
 Test --change option:
 
   $ hg ci -d '2 0' -mtest3
-#if windows
+
   $ hg falabala -c 1
-  diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob)
+  diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
+  diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
   [1]
-#else
-  $ hg falabala -c 1
-  diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
-  [1]
-#endif
 
 Check diff are made from the first parent:
 
-#if windows
   $ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
-  diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "a.46c0e4daeb72\\a" (glob)
+  diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "a.46c0e4daeb72\\a" (glob) (windows !)
+  diffing */extdiff.*/a.2a13a4d2da36/a a.46c0e4daeb72/a (glob) (no-windows !)
   diff-like tools yield a non-zero exit code
-#else
-  $ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
-  diffing */extdiff.*/a.2a13a4d2da36/a a.46c0e4daeb72/a (glob)
-  diff-like tools yield a non-zero exit code
-#endif
 
 issue3153: ensure using extdiff with removed subrepos doesn't crash:
 
@@ -158,21 +139,16 @@
   > EOF
   $ hg update -q -C 0
   $ echo a >> a
-#if windows
+
   $ hg --debug 4463a | grep '^running'
-  running 'echo a-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
+  running 'echo a-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
+  running 'echo a-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
   $ hg --debug 4463b | grep '^running'
-  running 'echo b-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
+  running 'echo b-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
+  running 'echo b-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
   $ hg --debug echo | grep '^running'
-  running '*echo* "*\\a" "*\\a"' in */extdiff.* (glob)
-#else
-  $ hg --debug 4463a | grep '^running'
-  running 'echo a-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
-  $ hg --debug 4463b | grep '^running'
-  running 'echo b-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
-  $ hg --debug echo | grep '^running'
-  running '*echo */a $TESTTMP/a/a' in */extdiff.* (glob)
-#endif
+  running '*echo* "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
+  running '*echo */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
 
 (getting options from other than extdiff section)
 
@@ -189,29 +165,22 @@
   > [merge-tools]
   > 4463b3.diffargs = b3-naked 'single quoted' "double quoted"
   > EOF
-#if windows
+
   $ hg --debug 4463b2 | grep '^running'
-  running 'echo b2-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
+  running 'echo b2-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
+  running 'echo b2-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
   $ hg --debug 4463b3 | grep '^running'
-  running 'echo b3-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
+  running 'echo b3-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
+  running 'echo b3-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
   $ hg --debug 4463b4 | grep '^running'
-  running 'echo "*\\a" "*\\a"' in */extdiff.* (glob)
+  running 'echo "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
+  running 'echo */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
   $ hg --debug 4463b4 --option b4-naked --option 'being quoted' | grep '^running'
-  running 'echo b4-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
+  running 'echo b4-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
+  running "echo b4-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob) (no-windows !)
   $ hg --debug extdiff -p echo --option echo-naked --option 'being quoted' | grep '^running'
-  running 'echo echo-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
-#else
-  $ hg --debug 4463b2 | grep '^running'
-  running 'echo b2-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
-  $ hg --debug 4463b3 | grep '^running'
-  running 'echo b3-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
-  $ hg --debug 4463b4 | grep '^running'
-  running 'echo */a $TESTTMP/a/a' in */extdiff.* (glob)
-  $ hg --debug 4463b4 --option b4-naked --option 'being quoted' | grep '^running'
-  running "echo b4-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob)
-  $ hg --debug extdiff -p echo --option echo-naked --option 'being quoted' | grep '^running'
-  running "echo echo-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob)
-#endif
+  running 'echo echo-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
+  running "echo echo-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob) (no-windows !)
 
   $ touch 'sp ace'
   $ hg add 'sp ace'
@@ -228,12 +197,10 @@
   > odd.diffargs = --foo='\$clabel' '\$clabel' "--bar=\$clabel" "\$clabel"
   > odd.executable = echo
   > EOF
-#if windows
-TODO
-#else
+
   $ hg --debug odd | grep '^running'
-  running "*/echo --foo='sp ace' 'sp ace' --bar='sp ace' 'sp ace'" in * (glob)
-#endif
+  running '"*\\echo.exe" --foo="sp ace" "sp ace" --bar="sp ace" "sp ace"' in * (glob) (windows !)
+  running "*/echo --foo='sp ace' 'sp ace' --bar='sp ace' 'sp ace'" in * (glob) (no-windows !)
 
 Empty argument must be quoted
 
@@ -243,22 +210,20 @@
   > [merge-tools]
   > kdiff3.diffargs=--L1 \$plabel1 --L2 \$clabel \$parent \$child
   > EOF
-#if windows
-  $ hg --debug kdiff3 -r0 | grep '^running'
-  running 'echo --L1 "@0" --L2 "" a.8a5febb7f867 a' in * (glob)
-#else
+
   $ hg --debug kdiff3 -r0 | grep '^running'
-  running "echo --L1 '@0' --L2 '' a.8a5febb7f867 a" in * (glob)
-#endif
+  running 'echo --L1 "@0" --L2 "" a.8a5febb7f867 a' in * (glob) (windows !)
+  running "echo --L1 '@0' --L2 '' a.8a5febb7f867 a" in * (glob) (no-windows !)
 
-#if execbit
 
 Test extdiff of multiple files in tmp dir:
 
   $ hg update -C 0 > /dev/null
   $ echo changed > a
   $ echo changed > b
+#if execbit
   $ chmod +x b
+#endif
 
 Diff in working directory, before:
 
@@ -270,8 +235,8 @@
   -a
   +changed
   diff --git a/b b/b
-  old mode 100644
-  new mode 100755
+  old mode 100644 (execbit !)
+  new mode 100755 (execbit !)
   --- a/b
   +++ b/b
   @@ -1,1 +1,1 @@
@@ -291,7 +256,9 @@
   > file('a/b', 'ab').write('edited\n')
   > EOT
 
+#if execbit
   $ chmod +x 'diff tool.py'
+#endif
 
 will change to /tmp/extdiff.TMP and populate directories a.TMP and a
 and start tool
@@ -310,8 +277,8 @@
   +changed
   +edited
   diff --git a/b b/b
-  old mode 100644
-  new mode 100755
+  old mode 100644 (execbit !)
+  new mode 100755 (execbit !)
   --- a/b
   +++ b/b
   @@ -1,1 +1,2 @@
@@ -322,41 +289,67 @@
 Test extdiff with --option:
 
   $ hg extdiff -p echo -o this -c 1
-  this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
+  this "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
+  this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
   [1]
 
   $ hg falabala -o this -c 1
-  diffing this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
+  diffing this "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
+  diffing this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
   [1]
 
 Test extdiff's handling of options with spaces in them:
 
   $ hg edspace -c 1
-  name  <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
+  "name  <user@example.com>" "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
+  name  <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
   [1]
 
   $ hg extdiff -p echo -o "name  <user@example.com>" -c 1
-  name  <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
+  "name  <user@example.com>" "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
+  name  <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
   [1]
 
 Test with revsets:
 
   $ hg extdif -p echo -c "rev(1)"
-  */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
+  "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
+  */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
   [1]
 
   $ hg extdif -p echo -r "0::1"
-  */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
+  "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
+  */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
   [1]
 
 Fallback to merge-tools.tool.executable|regkey
   $ mkdir dir
-  $ cat > 'dir/tool.sh' << EOF
+  $ cat > 'dir/tool.sh' << 'EOF'
   > #!/bin/sh
+  > # Mimic a tool that syncs all attrs, including mtime
+  > cp $1/a $2/a
+  > touch -r $1/a $2/a
   > echo "** custom diff **"
   > EOF
+#if execbit
   $ chmod +x dir/tool.sh
+#endif
+
+Windows can't run *.sh directly, so create a shim executable that can be.
+Without something executable, the next hg command will try to run `tl` instead
+of $tool (and fail).
+#if windows
+  $ cat > dir/tool.bat <<EOF
+  > @sh -c "`pwd`/dir/tool.sh %1 %2"
+  > EOF
+  $ tool=`pwd`/dir/tool.bat
+#else
   $ tool=`pwd`/dir/tool.sh
+#endif
+
+  $ cat a
+  changed
+  edited
   $ hg --debug tl --config extdiff.tl= --config merge-tools.tl.executable=$tool
   making snapshot of 2 files from rev * (glob)
     a
@@ -364,15 +357,17 @@
   making snapshot of 2 files from working directory
     a
     b
-  running '$TESTTMP/a/dir/tool.sh a.* a' in */extdiff.* (glob)
+  running '$TESTTMP/a/dir/tool.bat a.* a' in */extdiff.* (glob) (windows !)
+  running '$TESTTMP/a/dir/tool.sh a.* a' in */extdiff.* (glob) (no-windows !)
   ** custom diff **
+  file changed while diffing. Overwriting: $TESTTMP/a/a (src: */extdiff.*/a/a) (glob)
   cleaning up temp directory
   [1]
+  $ cat a
+  a
 
   $ cd ..
 
-#endif
-
 #if symlink
 
 Test symlinks handling (issue1909)
--- a/tests/test-graft.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-graft.t	Fri May 12 11:20:25 2017 -0700
@@ -186,7 +186,6 @@
    ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
    e: remote is newer -> g
   getting e
-   b: remote unchanged -> k
   committing files:
   e
   committing manifest
@@ -203,7 +202,6 @@
    preserving e for resolve of e
    d: remote is newer -> g
   getting d
-   b: remote unchanged -> k
    e: versions differ -> m (premerge)
   picked tool ':merge' for e (binary False symlink False changedelete False)
   merging e
--- a/tests/test-https.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-https.t	Fri May 12 11:20:25 2017 -0700
@@ -333,20 +333,9 @@
   > --config hostsecurity.disabletls10warning=true
   5fed3813f7f5
 
-#if no-sslcontext no-py27+
-Setting ciphers doesn't work in Python 2.6
-  $ P="$CERTSDIR" hg --config hostsecurity.ciphers=HIGH -R copy-pull id https://localhost:$HGPORT/
-  warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
-  abort: setting ciphers in [hostsecurity] is not supported by this version of Python
-  (remove the config option or run Mercurial with a modern Python version (preferred))
-  [255]
-#endif
+Error message for setting ciphers is different depending on SSLContext support
 
-Setting ciphers works in Python 2.7+ but the error message is different on
-legacy ssl. We test legacy once and do more feature checking on modern
-configs.
-
-#if py27+ no-sslcontext
+#if no-sslcontext
   $ P="$CERTSDIR" hg --config hostsecurity.ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
   warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
   abort: *No cipher can be selected. (glob)
--- a/tests/test-install.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-install.t	Fri May 12 11:20:25 2017 -0700
@@ -34,7 +34,7 @@
     "editornotfound": false,
     "encoding": "ascii",
     "encodingerror": null,
-    "extensionserror": null,
+    "extensionserror": null, (no-pure !)
     "hgmodulepolicy": "*", (glob)
     "hgmodules": "*mercurial", (glob)
     "hgver": "*", (glob)
--- a/tests/test-issue672.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-issue672.t	Fri May 12 11:20:25 2017 -0700
@@ -38,7 +38,6 @@
   removing 1
    1a: remote created -> g
   getting 1a
-   2: remote unchanged -> k
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-py3-commands.t	Fri May 12 11:20:25 2017 -0700
@@ -0,0 +1,201 @@
+#require py3exe
+
+This test helps in keeping a track on which commands we can run on
+Python 3 and see what kind of errors are coming up.
+The full traceback is hidden to have a stable output.
+  $ HGBIN=`which hg`
+
+  $ for cmd in version debuginstall ; do
+  >   echo $cmd
+  >   $PYTHON3 $HGBIN $cmd 2>&1 2>&1 | tail -1
+  > done
+  version
+  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  debuginstall
+  no problems detected
+
+#if test-repo
+Make a clone so that any features in the developer's .hg/hgrc that
+might confuse Python 3 don't break this test. When we can do commit in
+Python 3, we'll stop doing this. We use e76ed1e480ef for the clone
+because it has different files than 273ce12ad8f1, so we can test both
+`files` from dirstate and `files` loaded from a specific revision.
+
+  $ hg clone -r e76ed1e480ef "`dirname "$TESTDIR"`" testrepo 2>&1 | tail -1
+  15 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Test using -R, which exercises some URL code:
+  $ $PYTHON3 $HGBIN -R testrepo files -r 273ce12ad8f1 | tail -1
+  testrepo/tkmerge
+
+Now prove `hg files` is reading the whole manifest. We have to grep
+out some potential warnings that come from hgrc as yet.
+  $ cd testrepo
+  $ $PYTHON3 $HGBIN files -r 273ce12ad8f1
+  .hgignore
+  PKG-INFO
+  README
+  hg
+  mercurial/__init__.py
+  mercurial/byterange.py
+  mercurial/fancyopts.py
+  mercurial/hg.py
+  mercurial/mdiff.py
+  mercurial/revlog.py
+  mercurial/transaction.py
+  notes.txt
+  setup.py
+  tkmerge
+
+  $ $PYTHON3 $HGBIN files -r 273ce12ad8f1 | wc -l
+  \s*14 (re)
+  $ $PYTHON3 $HGBIN files | wc -l
+  \s*15 (re)
+
+Test if log-like commands work:
+
+  $ $PYTHON3 $HGBIN tip
+  changeset:   10:e76ed1e480ef
+  tag:         tip
+  user:        oxymoron@cinder.waste.org
+  date:        Tue May 03 23:37:43 2005 -0800
+  summary:     Fix linking of changeset revs when merging
+  
+
+  $ $PYTHON3 $HGBIN log -r0
+  changeset:   0:9117c6561b0b
+  user:        mpm@selenic.com
+  date:        Tue May 03 13:16:10 2005 -0800
+  summary:     Add back links from file revisions to changeset revisions
+  
+
+  $ cd ..
+#endif
+
+Test if `hg config` works:
+
+  $ $PYTHON3 $HGBIN config
+  defaults.backout=-d "0 0"
+  defaults.commit=-d "0 0"
+  defaults.shelve=--date "0 0"
+  defaults.tag=-d "0 0"
+  devel.all-warnings=true
+  largefiles.usercache=$TESTTMP/.cache/largefiles
+  ui.slash=True
+  ui.interactive=False
+  ui.mergemarkers=detailed
+  ui.promptecho=True
+  web.address=localhost
+  web.ipv6=False
+
+  $ cat > included-hgrc <<EOF
+  > [extensions]
+  > babar = imaginary_elephant
+  > EOF
+  $ cat >> $HGRCPATH <<EOF
+  > %include $TESTTMP/included-hgrc
+  > EOF
+  $ $PYTHON3 $HGBIN version | tail -1
+  *** failed to import extension babar from imaginary_elephant: *: 'imaginary_elephant' (glob)
+  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+  $ rm included-hgrc
+  $ touch included-hgrc
+
+Test bytes-ness of policy.policy with HGMODULEPOLICY
+
+  $ HGMODULEPOLICY=py
+  $ export HGMODULEPOLICY
+  $ $PYTHON3 `which hg` debuginstall 2>&1 2>&1 | tail -1
+  no problems detected
+
+`hg init` can create empty repos
+`hg status works fine`
+`hg summary` also works!
+
+  $ $PYTHON3 `which hg` init py3repo
+  $ cd py3repo
+  $ echo "This is the file 'iota'." > iota
+  $ $PYTHON3 $HGBIN status
+  ? iota
+  $ $PYTHON3 $HGBIN add iota
+  $ $PYTHON3 $HGBIN status
+  A iota
+  $ $PYTHON3 $HGBIN commit --message 'commit performed in Python 3'
+  $ $PYTHON3 $HGBIN status
+
+  $ mkdir A
+  $ echo "This is the file 'mu'." > A/mu
+  $ $PYTHON3 $HGBIN addremove
+  adding A/mu
+  $ $PYTHON3 $HGBIN status
+  A A/mu
+  $ HGEDITOR='echo message > ' $PYTHON3 $HGBIN commit
+  $ $PYTHON3 $HGBIN status
+  $ $PYHON3 $HGBIN summary
+  parent: 1:e1e9167203d4 tip
+   message
+  branch: default
+  commit: (clean)
+  update: (current)
+  phases: 2 draft
+
+Prove the repo is valid using the Python 2 `hg`:
+  $ hg verify
+  checking changesets
+  checking manifests
+  crosschecking files in changesets and manifests
+  checking files
+  2 files, 2 changesets, 2 total revisions
+  $ hg log
+  changeset:   1:e1e9167203d4
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     message
+  
+  changeset:   0:71c96e924262
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     commit performed in Python 3
+  
+
+  $ hg log -G
+  @  changeset:   1:e1e9167203d4
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     message
+  |
+  o  changeset:   0:71c96e924262
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     commit performed in Python 3
+  
+  $ hg log -Tjson
+  [
+   {
+    "rev": 1,
+    "node": "e1e9167203d450ca2f558af628955b5f5afd4489",
+    "branch": "default",
+    "phase": "draft",
+    "user": "test",
+    "date": [0, 0],
+    "desc": "message",
+    "bookmarks": [],
+    "tags": ["tip"],
+    "parents": ["71c96e924262969ff0d8d3d695b0f75412ccc3d8"]
+   },
+   {
+    "rev": 0,
+    "node": "71c96e924262969ff0d8d3d695b0f75412ccc3d8",
+    "branch": "default",
+    "phase": "draft",
+    "user": "test",
+    "date": [0, 0],
+    "desc": "commit performed in Python 3",
+    "bookmarks": [],
+    "tags": [],
+    "parents": ["0000000000000000000000000000000000000000"]
+   }
+  ]
--- a/tests/test-rename-merge2.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-rename-merge2.t	Fri May 12 11:20:25 2017 -0700
@@ -89,7 +89,6 @@
    preserving a for resolve of b
    preserving rev for resolve of rev
   starting 4 threads for background file closing (?)
-   a: remote unchanged -> k
    b: remote copied from a -> m (premerge)
   picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging a and b to b
@@ -652,7 +651,6 @@
    preserving b for resolve of b
    preserving rev for resolve of rev
   starting 4 threads for background file closing (?)
-   a: remote unchanged -> k
    b: both created -> m (premerge)
   picked tool 'python ../merge' for b (binary False symlink False changedelete False)
   merging b
--- a/tests/test-tags.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-tags.t	Fri May 12 11:20:25 2017 -0700
@@ -716,3 +716,15 @@
   0040: ff ff ff ff ff ff ff ff 40 f0 35 8c 19 e0 a7 d3 |........@.5.....|
   0050: 8a 5c 6a 82 4d cf fb a5 87 d0 2f a3 1e 4f 2f 8a |.\j.M...../..O/.|
 
+Check that the bundle includes cache data
+
+  $ hg -R tagsclient bundle --all ./test-cache-in-bundle-all-rev.hg
+  4 changesets found
+  $ hg debugbundle ./test-cache-in-bundle-all-rev.hg
+  Stream params: sortdict([('Compression', 'BZ')])
+  changegroup -- "sortdict([('version', '02'), ('nbchanges', '4')])"
+      96ee1d7354c4ad7372047672c36a1f561e3a6a4c
+      c4dab0c2fd337eb9191f80c3024830a4889a8f34
+      f63cc8fe54e4d326f8d692805d70e092f851ddb1
+      40f0358cb314c824a5929ee527308d90e023bc10
+  hgtagsfnodes -- 'sortdict()'
--- a/tests/test-worker.t	Fri May 12 21:46:14 2017 +0900
+++ b/tests/test-worker.t	Fri May 12 11:20:25 2017 -0700
@@ -2,6 +2,7 @@
 
   $ cat > t.py <<EOF
   > from __future__ import absolute_import, print_function
+  > import time
   > from mercurial import (
   >     cmdutil,
   >     error,
@@ -22,6 +23,7 @@
   >     for arg in args:
   >         ui.status('run\n')
   >         yield 1, arg
+  >     time.sleep(0.1) # easier to trigger killworkers code path
   > functable = {
   >     'abort': abort,
   >     'exc': exc,
@@ -74,21 +76,53 @@
 
 Known exception should be caught, but printed if --traceback is enabled
 
-  $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=2' \
-  > test 100000.0 abort
+  $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=8' \
+  > test 100000.0 abort 2>&1
   start
   abort: known exception
   [255]
 
-  $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=2' \
-  > test 100000.0 abort --traceback 2>&1 | grep '^Traceback'
-  Traceback (most recent call last):
-  Traceback (most recent call last):
+  $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=8' \
+  > test 100000.0 abort --traceback 2>&1 | egrep '^(SystemExit|Abort)'
+  Abort: known exception
+  SystemExit: 255
 
 Traceback must be printed for unknown exceptions
 
-  $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=2' \
-  > test 100000.0 exc 2>&1 | grep '^Traceback'
-  Traceback (most recent call last):
+  $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=8' \
+  > test 100000.0 exc 2>&1 | grep '^Exception'
+  Exception: unknown exception
+
+Workers should not do cleanups in all cases
+
+  $ cat > $TESTTMP/detectcleanup.py <<EOF
+  > from __future__ import absolute_import
+  > import atexit
+  > import os
+  > import time
+  > oldfork = os.fork
+  > count = 0
+  > parentpid = os.getpid()
+  > def delayedfork():
+  >     global count
+  >     count += 1
+  >     pid = oldfork()
+  >     # make it easier to test SIGTERM hitting other workers when they have
+  >     # not set up error handling yet.
+  >     if count > 1 and pid == 0:
+  >         time.sleep(0.1)
+  >     return pid
+  > os.fork = delayedfork
+  > def cleanup():
+  >     if os.getpid() != parentpid:
+  >         os.write(1, 'should never happen\n')
+  > atexit.register(cleanup)
+  > EOF
+
+  $ hg --config "extensions.t=$abspath" --config worker.numcpus=8 --config \
+  > "extensions.d=$TESTTMP/detectcleanup.py" test 100000 abort
+  start
+  abort: known exception
+  [255]
 
 #endif