# HG changeset patch # User Gregory Szorc # Date 1525568805 25200 # Node ID 86e7a57449fa4f854d730cafd505ab089c25df76 # Parent 92213f6745ed6f2c50feca9a2261b6f33a9a32fa# Parent 1f65d7d46545a2845c1e258d73d73b2a0c0bb20f merge with stable diff -r 92213f6745ed -r 86e7a57449fa .hgsigs --- a/.hgsigs Sun Mar 04 15:29:41 2018 -0500 +++ b/.hgsigs Sat May 05 18:06:45 2018 -0700 @@ -160,3 +160,7 @@ d334afc585e29577f271c5eda03378736a16ca6b 0 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 diff -r 92213f6745ed -r 86e7a57449fa .hgtags --- a/.hgtags Sun Mar 04 15:29:41 2018 -0500 +++ b/.hgtags Sat May 05 18:06:45 2018 -0700 @@ -173,3 +173,7 @@ d334afc585e29577f271c5eda03378736a16ca6b 4.5 369aadf7a3264b03c8b09efce715bc41e6ab4a9b 4.5.1 8bba684efde7f45add05f737952093bb2aa07155 4.5.2 +7de7bd407251af2bc98e5b809c8598ee95830daf 4.5.3 +ed5448edcbfa747b9154099e18630e49024fd47b 4.6rc0 +1ec874717d8a93b19e0d50628443e0ee5efab3a9 4.6rc1 +6614cac550aea66d19c601e45efd1b7bd08d7c40 4.6 diff -r 92213f6745ed -r 86e7a57449fa contrib/phabricator.py --- a/contrib/phabricator.py Sun Mar 04 15:29:41 2018 -0500 +++ b/contrib/phabricator.py Sat May 05 18:06:45 2018 -0700 @@ -68,6 +68,7 @@ ) from mercurial.utils import ( procutil, + stringutil, ) cmdtable = {} @@ -328,6 +329,19 @@ } callconduit(ctx.repo(), 'differential.setdiffproperty', params) + params = { + 'diff_id': diff[r'id'], + 'name': 'local:commits', + 'data': json.dumps({ + ctx.hex(): { + 'author': stringutil.person(ctx.user()), + 'authorEmail': stringutil.email(ctx.user()), + 'time': ctx.date()[0], + }, + }), + } + callconduit(ctx.repo(), 'differential.setdiffproperty', params) + def createdifferentialrevision(ctx, revid=None, parentrevid=None, oldnode=None, olddiff=None, actions=None): """create or update a Differential Revision diff -r 92213f6745ed -r 86e7a57449fa hgext/infinitepush/__init__.py --- a/hgext/infinitepush/__init__.py Sun Mar 04 15:29:41 2018 -0500 +++ b/hgext/infinitepush/__init__.py Sat May 05 18:06:45 2018 -0700 @@ -1067,7 +1067,7 @@ bookmarknode = nodesctx[-1].hex() if nodesctx else None key = None if newheadscount: - with open(bundlefile, 'r') as f: + with open(bundlefile, 'rb') as f: bundledata = f.read() with logservicecall(log, 'bundlestore', bundlesize=len(bundledata)): diff -r 92213f6745ed -r 86e7a57449fa hgext/infinitepush/fileindexapi.py --- a/hgext/infinitepush/fileindexapi.py Sun Mar 04 15:29:41 2018 -0500 +++ b/hgext/infinitepush/fileindexapi.py Sat May 05 18:06:45 2018 -0700 @@ -15,6 +15,8 @@ import os +from mercurial import util + from mercurial.utils import stringutil from . import indexapi @@ -82,6 +84,7 @@ for dirpath, _, books in self._repo.vfs.walk(self._bookmarkmap): for book in books: bookmark = os.path.join(dirpath, book)[prefixlen:] + bookmark = util.pconvert(bookmark) if not matcher(bookmark): continue yield bookmark, self._read(os.path.join(dirpath, book)) diff -r 92213f6745ed -r 86e7a57449fa hgext/infinitepush/store.py --- a/hgext/infinitepush/store.py Sun Mar 04 15:29:41 2018 -0500 +++ b/hgext/infinitepush/store.py Sat May 05 18:06:45 2018 -0700 @@ -79,19 +79,18 @@ if not os.path.exists(dirpath): os.makedirs(dirpath) - with open(self._filepath(filename), 'w') as f: + with open(self._filepath(filename), 'wb') as f: f.write(data) return filename def read(self, key): try: - f = open(self._filepath(key), 'r') + with open(self._filepath(key), 'rb') as f: + return f.read() except IOError: return None - return f.read() - class externalbundlestore(abstractbundlestore): def __init__(self, put_binary, put_args, get_binary, get_args): """ diff -r 92213f6745ed -r 86e7a57449fa hgext/notify.py --- a/hgext/notify.py Sun Mar 04 15:29:41 2018 -0500 +++ b/hgext/notify.py Sat May 05 18:06:45 2018 -0700 @@ -455,7 +455,7 @@ changegroup. else send one email per changeset.''' n = notifier(ui, repo, hooktype) - ctx = repo[node] + ctx = repo.unfiltered()[node] if not n.subs: ui.debug('notify: no subscribers to repository %s\n' % n.root) @@ -469,8 +469,7 @@ count = 0 author = '' if hooktype == 'changegroup' or hooktype == 'outgoing': - start, end = ctx.rev(), len(repo) - for rev in xrange(start, end): + for rev in repo.changelog.revs(start=ctx.rev()): if n.node(repo[rev]): count += 1 if not author: @@ -482,7 +481,7 @@ ui.pushbuffer() if count: n.diff(ctx, repo['tip']) - else: + elif ctx.rev() in repo: if not n.node(ctx): ui.popbuffer() ui.note(_('notify: suppressing notification for merge %d:%s\n') % diff -r 92213f6745ed -r 86e7a57449fa hgext/remotenames.py --- a/hgext/remotenames.py Sun Mar 04 15:29:41 2018 -0500 +++ b/hgext/remotenames.py Sat May 05 18:06:45 2018 -0700 @@ -6,7 +6,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -""" showing remotebookmarks and remotebranches in UI +""" showing remotebookmarks and remotebranches in UI (EXPERIMENTAL) By default both remotebookmarks and remotebranches are turned on. Config knob to control the individually are as follows. diff -r 92213f6745ed -r 86e7a57449fa mercurial/cmdutil.py --- a/mercurial/cmdutil.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/cmdutil.py Sat May 05 18:06:45 2018 -0700 @@ -1537,7 +1537,7 @@ # --exact with --no-commit is still useful in that it does merge # and branch bits ui.warn(_("warning: can't check exact import with --no-commit\n")) - elif opts.get('exact') and hex(n) != nodeid: + elif opts.get('exact') and (not n or hex(n) != nodeid): raise error.Abort(_('patch is damaged or loses information')) msg = _('applied to working directory') if n: diff -r 92213f6745ed -r 86e7a57449fa mercurial/color.py --- a/mercurial/color.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/color.py Sat May 05 18:06:45 2018 -0700 @@ -90,16 +90,16 @@ 'branches.inactive': 'none', 'diff.changed': 'white', 'diff.deleted': 'red', - 'diff.deleted.changed': 'red', - 'diff.deleted.unchanged': 'red dim', + 'diff.deleted.changed': 'red bold underline', + 'diff.deleted.unchanged': 'red', 'diff.diffline': 'bold', 'diff.extended': 'cyan bold', 'diff.file_a': 'red bold', 'diff.file_b': 'green bold', 'diff.hunk': 'magenta', 'diff.inserted': 'green', - 'diff.inserted.changed': 'green', - 'diff.inserted.unchanged': 'green dim', + 'diff.inserted.changed': 'green bold underline', + 'diff.inserted.unchanged': 'green', 'diff.tab': '', 'diff.trailingwhitespace': 'bold red_background', 'changeset.public': '', diff -r 92213f6745ed -r 86e7a57449fa mercurial/context.py --- a/mercurial/context.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/context.py Sat May 05 18:06:45 2018 -0700 @@ -497,8 +497,10 @@ changeid = hex(changeid) except TypeError: pass - except (error.FilteredIndexError, error.FilteredLookupError, - error.FilteredRepoLookupError): + except (error.FilteredIndexError, error.FilteredLookupError): + raise error.FilteredRepoLookupError(_("filtered revision '%s'") + % changeid) + except error.FilteredRepoLookupError: raise except IndexError: pass diff -r 92213f6745ed -r 86e7a57449fa mercurial/debugcommands.py --- a/mercurial/debugcommands.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/debugcommands.py Sat May 05 18:06:45 2018 -0700 @@ -462,6 +462,8 @@ def _debugdisplaystyle(ui): ui.write(_('available style:\n')) + if not ui._styles: + return width = max(len(s) for s in ui._styles) for label, effects in sorted(ui._styles.items()): ui.write('%s' % label, label=label) diff -r 92213f6745ed -r 86e7a57449fa mercurial/diffhelper.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/diffhelper.py Sat May 05 18:06:45 2018 -0700 @@ -0,0 +1,77 @@ +# diffhelper.py - helper routines for patch +# +# Copyright 2009 Matt Mackall and others +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +from .i18n import _ + +from . import ( + error, +) + +def addlines(fp, hunk, lena, lenb, a, b): + """Read lines from fp into the hunk + + The hunk is parsed into two arrays, a and b. a gets the old state of + the text, b gets the new state. The control char from the hunk is saved + when inserting into a, but not b (for performance while deleting files.) + """ + while True: + todoa = lena - len(a) + todob = lenb - len(b) + num = max(todoa, todob) + if num == 0: + break + for i in xrange(num): + s = fp.readline() + if not s: + raise error.ParseError(_('incomplete hunk')) + if s == "\\ No newline at end of file\n": + fixnewline(hunk, a, b) + continue + if s == '\n' or s == '\r\n': + # Some patches may be missing the control char + # on empty lines. Supply a leading space. + s = ' ' + s + hunk.append(s) + if s.startswith('+'): + b.append(s[1:]) + elif s.startswith('-'): + a.append(s) + else: + b.append(s[1:]) + a.append(s) + +def fixnewline(hunk, a, b): + """Fix up the last lines of a and b when the patch has no newline at EOF""" + l = hunk[-1] + # tolerate CRLF in last line + if l.endswith('\r\n'): + hline = l[:-2] + else: + hline = l[:-1] + + if hline.startswith((' ', '+')): + b[-1] = hline[1:] + if hline.startswith((' ', '-')): + a[-1] = hline + hunk[-1] = hline + +def testhunk(a, b, bstart): + """Compare the lines in a with the lines in b + + a is assumed to have a control char at the start of each line, this char + is ignored in the compare. + """ + alen = len(a) + blen = len(b) + if alen > blen - bstart or bstart < 0: + return False + for i in xrange(alen): + if a[i][1:] != b[i + bstart]: + return False + return True diff -r 92213f6745ed -r 86e7a57449fa mercurial/diffhelpers.py --- a/mercurial/diffhelpers.py Sun Mar 04 15:29:41 2018 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -# diffhelpers.py - helper routines for patch -# -# Copyright 2009 Matt Mackall and others -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -from __future__ import absolute_import - -from .i18n import _ - -from . import ( - error, -) - -def addlines(fp, hunk, lena, lenb, a, b): - """Read lines from fp into the hunk - - The hunk is parsed into two arrays, a and b. a gets the old state of - the text, b gets the new state. The control char from the hunk is saved - when inserting into a, but not b (for performance while deleting files.) - """ - while True: - todoa = lena - len(a) - todob = lenb - len(b) - num = max(todoa, todob) - if num == 0: - break - for i in xrange(num): - s = fp.readline() - if not s: - raise error.ParseError(_('incomplete hunk')) - if s == "\\ No newline at end of file\n": - fixnewline(hunk, a, b) - continue - if s == '\n' or s == '\r\n': - # Some patches may be missing the control char - # on empty lines. Supply a leading space. - s = ' ' + s - hunk.append(s) - if s.startswith('+'): - b.append(s[1:]) - elif s.startswith('-'): - a.append(s) - else: - b.append(s[1:]) - a.append(s) - -def fixnewline(hunk, a, b): - """Fix up the last lines of a and b when the patch has no newline at EOF""" - l = hunk[-1] - # tolerate CRLF in last line - if l.endswith('\r\n'): - hline = l[:-2] - else: - hline = l[:-1] - - if hline.startswith((' ', '+')): - b[-1] = hline[1:] - if hline.startswith((' ', '-')): - a[-1] = hline - hunk[-1] = hline - -def testhunk(a, b, bstart): - """Compare the lines in a with the lines in b - - a is assumed to have a control char at the start of each line, this char - is ignored in the compare. - """ - alen = len(a) - blen = len(b) - if alen > blen - bstart: - return False - for i in xrange(alen): - if a[i][1:] != b[i + bstart]: - return False - return True diff -r 92213f6745ed -r 86e7a57449fa mercurial/filelog.py --- a/mercurial/filelog.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/filelog.py Sat May 05 18:06:45 2018 -0700 @@ -7,16 +7,16 @@ from __future__ import absolute_import -from .thirdparty.zope import ( - interface as zi, -) from . import ( error, repository, revlog, ) +from .utils import ( + interfaceutil, +) -@zi.implementer(repository.ifilestorage) +@interfaceutil.implementer(repository.ifilestorage) class filelog(object): def __init__(self, opener, path): self._revlog = revlog.revlog(opener, @@ -135,7 +135,9 @@ return False t = self.revision(node) m = revlog.parsemeta(t)[0] - if m and "copy" in m: + # copy and copyrev occur in pairs. In rare cases due to bugs, + # one can occur without the other. + if m and "copy" in m and "copyrev" in m: return (m["copy"], revlog.bin(m["copyrev"])) return False diff -r 92213f6745ed -r 86e7a57449fa mercurial/help/internals/bundle2.txt --- a/mercurial/help/internals/bundle2.txt Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/help/internals/bundle2.txt Sat May 05 18:06:45 2018 -0700 @@ -61,7 +61,7 @@ The following stream level parameters are defined: -compression +Compression Compression format of payload data. ``GZ`` denotes zlib. ``BZ`` denotes bzip2. ``ZS`` denotes zstandard. diff -r 92213f6745ed -r 86e7a57449fa mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/hgweb/hgweb_mod.py Sat May 05 18:06:45 2018 -0700 @@ -399,6 +399,12 @@ tag = 'W/"%d"' % self.mtime if req.headers.get('If-None-Match') == tag: res.status = '304 Not Modified' + # Content-Type may be defined globally. It isn't valid on a + # 304, so discard it. + try: + del res.headers[b'Content-Type'] + except KeyError: + pass # Response body not allowed on 304. res.setbodybytes('') return res.sendresponse() diff -r 92213f6745ed -r 86e7a57449fa mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/hgweb/hgwebdir_mod.py Sat May 05 18:06:45 2018 -0700 @@ -428,7 +428,10 @@ uenv.iteritems()} req = requestmod.parserequestfromenv( uenv, reponame=virtualrepo, - altbaseurl=self.ui.config('web', 'baseurl')) + altbaseurl=self.ui.config('web', 'baseurl'), + # Reuse wrapped body file object otherwise state + # tracking can get confused. + bodyfh=req.bodyfh) try: # ensure caller gets private copy of ui repo = hg.repository(self.ui.copy(), real) diff -r 92213f6745ed -r 86e7a57449fa mercurial/hgweb/request.py --- a/mercurial/hgweb/request.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/hgweb/request.py Sat May 05 18:06:45 2018 -0700 @@ -124,7 +124,7 @@ # WSGI environment dict, unmodified. rawenv = attr.ib() -def parserequestfromenv(env, reponame=None, altbaseurl=None): +def parserequestfromenv(env, reponame=None, altbaseurl=None, bodyfh=None): """Parse URL components from environment variables. WSGI defines request attributes via environment variables. This function @@ -144,6 +144,9 @@ if the request were to ``http://myserver:9000/prefix/rev/@``. In other words, ``wsgi.url_scheme``, ``SERVER_NAME``, ``SERVER_PORT``, and ``SCRIPT_NAME`` are all effectively replaced by components from this URL. + + ``bodyfh`` can be used to specify a file object to read the request body + from. If not defined, ``wsgi.input`` from the environment dict is used. """ # PEP 3333 defines the WSGI spec and is a useful reference for this code. @@ -307,9 +310,11 @@ if 'CONTENT_TYPE' in env and 'HTTP_CONTENT_TYPE' not in env: headers['Content-Type'] = env['CONTENT_TYPE'] - bodyfh = env['wsgi.input'] - if 'Content-Length' in headers: - bodyfh = util.cappedreader(bodyfh, int(headers['Content-Length'])) + if bodyfh is None: + bodyfh = env['wsgi.input'] + if 'Content-Length' in headers: + bodyfh = util.cappedreader(bodyfh, + int(headers['Content-Length'] or '0')) return parsedrequest(method=env['REQUEST_METHOD'], url=fullurl, baseurl=baseurl, @@ -468,6 +473,7 @@ if k.lower() not in ('date', 'etag', 'expires', 'cache-control', 'content-location', + 'content-security-policy', 'vary')} if badheaders: raise error.ProgrammingError( diff -r 92213f6745ed -r 86e7a57449fa mercurial/hgweb/server.py --- a/mercurial/hgweb/server.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/hgweb/server.py Sat May 05 18:06:45 2018 -0700 @@ -122,6 +122,14 @@ self.sent_headers = False path, query = _splitURI(self.path) + # Ensure the slicing of path below is valid + if (path != self.server.prefix + and not path.startswith(self.server.prefix + b'/')): + self._start_response(common.statusmessage(404), []) + self._write("Not Found") + self._done() + return + env = {} env[r'GATEWAY_INTERFACE'] = r'CGI/1.1' env[r'REQUEST_METHOD'] = self.command diff -r 92213f6745ed -r 86e7a57449fa mercurial/httppeer.py --- a/mercurial/httppeer.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/httppeer.py Sat May 05 18:06:45 2018 -0700 @@ -20,9 +20,6 @@ from .thirdparty import ( cbor, ) -from .thirdparty.zope import ( - interface as zi, -) from . import ( bundle2, error, @@ -38,6 +35,9 @@ wireprotov2peer, wireprotov2server, ) +from .utils import ( + interfaceutil, +) httplib = util.httplib urlerr = util.urlerr @@ -328,13 +328,24 @@ return res +class RedirectedRepoError(error.RepoError): + def __init__(self, msg, respurl): + super(RedirectedRepoError, self).__init__(msg) + self.respurl = respurl + def parsev1commandresponse(ui, baseurl, requrl, qs, resp, compressible, allowcbor=False): # record the url we got redirected to + redirected = False respurl = pycompat.bytesurl(resp.geturl()) if respurl.endswith(qs): respurl = respurl[:-len(qs)] + qsdropped = False + else: + qsdropped = True + if baseurl.rstrip('/') != respurl.rstrip('/'): + redirected = True if not ui.quiet: ui.warn(_('real URL is %s\n') % respurl) @@ -351,10 +362,16 @@ # application/hg-changegroup. We don't support such old servers. if not proto.startswith('application/mercurial-'): ui.debug("requested URL: '%s'\n" % util.hidepassword(requrl)) - raise error.RepoError( - _("'%s' does not appear to be an hg repository:\n" - "---%%<--- (%s)\n%s\n---%%<---\n") - % (safeurl, proto or 'no content-type', resp.read(1024))) + msg = _("'%s' does not appear to be an hg repository:\n" + "---%%<--- (%s)\n%s\n---%%<---\n") % ( + safeurl, proto or 'no content-type', resp.read(1024)) + + # Some servers may strip the query string from the redirect. We + # raise a special error type so callers can react to this specially. + if redirected and qsdropped: + raise RedirectedRepoError(msg, respurl) + else: + raise error.RepoError(msg) try: subtype = proto.split('-', 1)[1] @@ -434,8 +451,6 @@ # End of ipeercommands interface. - # look up capabilities only when needed - def _callstream(self, cmd, _compressible=False, **args): args = pycompat.byteskwargs(args) @@ -582,7 +597,7 @@ # will resolve to Future.result. return self.result(timeout) -@zi.implementer(repository.ipeercommandexecutor) +@interfaceutil.implementer(repository.ipeercommandexecutor) class httpv2executor(object): def __init__(self, ui, opener, requestbuilder, apiurl, descriptor): self._ui = ui @@ -731,8 +746,9 @@ pass # TODO implement interface for version 2 peers -@zi.implementer(repository.ipeerconnection, repository.ipeercapabilities, - repository.ipeerrequests) +@interfaceutil.implementer(repository.ipeerconnection, + repository.ipeercapabilities, + repository.ipeerrequests) class httpv2peer(object): def __init__(self, ui, repourl, apipath, opener, requestbuilder, apidescriptor): @@ -852,12 +868,32 @@ req, requrl, qs = makev1commandrequest(ui, requestbuilder, caps, capable, url, 'capabilities', args) - resp = sendrequest(ui, opener, req) - respurl, ct, resp = parsev1commandresponse(ui, url, requrl, qs, resp, - compressible=False, - allowcbor=advertisev2) + # The server may redirect us to the repo root, stripping the + # ?cmd=capabilities query string from the URL. The server would likely + # return HTML in this case and ``parsev1commandresponse()`` would raise. + # We catch this special case and re-issue the capabilities request against + # the new URL. + # + # We should ideally not do this, as a redirect that drops the query + # string from the URL is arguably a server bug. (Garbage in, garbage out). + # However, Mercurial clients for several years appeared to handle this + # issue without behavior degradation. And according to issue 5860, it may + # be a longstanding bug in some server implementations. So we allow a + # redirect that drops the query string to "just work." + try: + respurl, ct, resp = parsev1commandresponse(ui, url, requrl, qs, resp, + compressible=False, + allowcbor=advertisev2) + except RedirectedRepoError as e: + req, requrl, qs = makev1commandrequest(ui, requestbuilder, caps, + capable, e.respurl, + 'capabilities', args) + resp = sendrequest(ui, opener, req) + respurl, ct, resp = parsev1commandresponse(ui, url, requrl, qs, resp, + compressible=False, + allowcbor=advertisev2) try: rawdata = resp.read() diff -r 92213f6745ed -r 86e7a57449fa mercurial/localrepo.py --- a/mercurial/localrepo.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/localrepo.py Sat May 05 18:06:45 2018 -0700 @@ -21,9 +21,6 @@ nullid, short, ) -from .thirdparty.zope import ( - interface as zi, -) from . import ( bookmarks, branchmap, @@ -68,6 +65,7 @@ vfs as vfsmod, ) from .utils import ( + interfaceutil, procutil, stringutil, ) @@ -153,7 +151,7 @@ 'unbundle'} legacycaps = moderncaps.union({'changegroupsubset'}) -@zi.implementer(repository.ipeercommandexecutor) +@interfaceutil.implementer(repository.ipeercommandexecutor) class localcommandexecutor(object): def __init__(self, peer): self._peer = peer @@ -196,7 +194,7 @@ def close(self): self._closed = True -@zi.implementer(repository.ipeercommands) +@interfaceutil.implementer(repository.ipeercommands) class localpeer(repository.peer): '''peer for a local repo; reflects only the most recent API''' @@ -324,7 +322,7 @@ # End of peer interface. -@zi.implementer(repository.ipeerlegacycommands) +@interfaceutil.implementer(repository.ipeerlegacycommands) class locallegacypeer(localpeer): '''peer extension which implements legacy methods too; used for tests with restricted capabilities''' @@ -365,7 +363,7 @@ # set to reflect that the extension knows how to handle that requirements. featuresetupfuncs = set() -@zi.implementer(repository.completelocalrepository) +@interfaceutil.implementer(repository.completelocalrepository) class localrepository(object): # obsolete experimental requirements: @@ -850,8 +848,7 @@ try: self[changeid] return True - except (error.RepoLookupError, error.FilteredIndexError, - error.FilteredLookupError): + except error.RepoLookupError: return False def __nonzero__(self): diff -r 92213f6745ed -r 86e7a57449fa mercurial/patch.py --- a/mercurial/patch.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/patch.py Sat May 05 18:06:45 2018 -0700 @@ -28,7 +28,7 @@ ) from . import ( copies, - diffhelpers, + diffhelper, encoding, error, mail, @@ -800,7 +800,7 @@ # if there's skew we want to emit the "(offset %d lines)" even # when the hunk cleanly applies at start + skew, so skip the # fast case code - if self.skew == 0 and diffhelpers.testhunk(old, self.lines, oldstart): + if self.skew == 0 and diffhelper.testhunk(old, self.lines, oldstart): if self.remove: self.backend.unlink(self.fname) else: @@ -827,7 +827,7 @@ cand = [oldstart] for l in cand: - if not old or diffhelpers.testhunk(old, self.lines, l): + if not old or diffhelper.testhunk(old, self.lines, l): self.lines[l : l + len(old)] = new self.offset += len(new) - len(old) self.skew = l - orig_start @@ -1259,8 +1259,8 @@ self.starta = int(self.starta) self.startb = int(self.startb) try: - diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, - self.a, self.b) + diffhelper.addlines(lr, self.hunk, self.lena, self.lenb, + self.a, self.b) except error.ParseError as e: raise PatchError(_("bad hunk #%d: %s") % (self.number, e)) # if we hit eof before finishing out the hunk, the last line will @@ -1379,7 +1379,7 @@ def _fixnewline(self, lr): l = lr.readline() if l.startswith('\ '): - diffhelpers.fixnewline(self.hunk, self.a, self.b) + diffhelper.fixnewline(self.hunk, self.a, self.b) else: lr.push(l) diff -r 92213f6745ed -r 86e7a57449fa mercurial/repository.py --- a/mercurial/repository.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/repository.py Sat May 05 18:06:45 2018 -0700 @@ -8,14 +8,14 @@ from __future__ import absolute_import from .i18n import _ -from .thirdparty.zope import ( - interface as zi, -) from . import ( error, ) +from .utils import ( + interfaceutil, +) -class ipeerconnection(zi.Interface): +class ipeerconnection(interfaceutil.Interface): """Represents a "connection" to a repository. This is the base interface for representing a connection to a repository. @@ -24,7 +24,7 @@ This is not a complete interface definition and should not be used outside of this module. """ - ui = zi.Attribute("""ui.ui instance""") + ui = interfaceutil.Attribute("""ui.ui instance""") def url(): """Returns a URL string representing this peer. @@ -61,7 +61,7 @@ associated with the peer should be cleaned up. """ -class ipeercapabilities(zi.Interface): +class ipeercapabilities(interfaceutil.Interface): """Peer sub-interface related to capabilities.""" def capable(name): @@ -81,7 +81,7 @@ Raises a ``CapabilityError`` if the capability isn't present. """ -class ipeercommands(zi.Interface): +class ipeercommands(interfaceutil.Interface): """Client-side interface for communicating over the wire protocol. This interface is used as a gateway to the Mercurial wire protocol. @@ -170,7 +170,7 @@ Returns the integer number of heads added to the peer. """ -class ipeerlegacycommands(zi.Interface): +class ipeerlegacycommands(interfaceutil.Interface): """Interface for implementing support for legacy wire protocol commands. Wire protocol commands transition to legacy status when they are no longer @@ -202,7 +202,7 @@ def changegroupsubset(bases, heads, source): pass -class ipeercommandexecutor(zi.Interface): +class ipeercommandexecutor(interfaceutil.Interface): """Represents a mechanism to execute remote commands. This is the primary interface for requesting that wire protocol commands @@ -259,7 +259,7 @@ This method may call ``sendcommands()`` if there are buffered commands. """ -class ipeerrequests(zi.Interface): +class ipeerrequests(interfaceutil.Interface): """Interface for executing commands on a peer.""" def commandexecutor(): @@ -290,7 +290,7 @@ All peer instances must conform to this interface. """ -@zi.implementer(ipeerbase) +@interfaceutil.implementer(ipeerbase) class peer(object): """Base class for peer repositories.""" @@ -314,7 +314,7 @@ _('cannot %s; remote repository does not support the %r ' 'capability') % (purpose, name)) -class ifilerevisionssequence(zi.Interface): +class ifilerevisionssequence(interfaceutil.Interface): """Contains index data for all revisions of a file. Types implementing this behave like lists of tuples. The index @@ -365,7 +365,7 @@ def insert(self, i, entry): """Add an item to the index at specific revision.""" -class ifileindex(zi.Interface): +class ifileindex(interfaceutil.Interface): """Storage interface for index data of a single file. File storage data is divided into index metadata and data storage. @@ -377,7 +377,7 @@ * DAG data (storing and querying the relationship between nodes). * Metadata to facilitate storage. """ - index = zi.Attribute( + index = interfaceutil.Attribute( """An ``ifilerevisionssequence`` instance.""") def __len__(): @@ -470,7 +470,7 @@ def candelta(baserev, rev): """"Whether a delta can be generated between two revisions.""" -class ifiledata(zi.Interface): +class ifiledata(interfaceutil.Interface): """Storage interface for data storage of a specific file. This complements ``ifileindex`` and provides an interface for accessing @@ -536,7 +536,7 @@ revision data. """ -class ifilemutation(zi.Interface): +class ifilemutation(interfaceutil.Interface): """Storage interface for mutation events of a tracked file.""" def add(filedata, meta, transaction, linkrev, p1, p2): @@ -608,13 +608,13 @@ class ifilestorage(ifileindex, ifiledata, ifilemutation): """Complete storage interface for a single tracked file.""" - version = zi.Attribute( + version = interfaceutil.Attribute( """Version number of storage. TODO this feels revlog centric and could likely be removed. """) - storedeltachains = zi.Attribute( + storedeltachains = interfaceutil.Attribute( """Whether the store stores deltas. TODO deltachains are revlog centric. This can probably removed @@ -622,7 +622,7 @@ data. """) - _generaldelta = zi.Attribute( + _generaldelta = interfaceutil.Attribute( """Whether deltas can be against any parent revision. TODO this is used by changegroup code and it could probably be @@ -642,59 +642,59 @@ TODO this is used by verify and it should not be part of the interface. """ -class completelocalrepository(zi.Interface): +class completelocalrepository(interfaceutil.Interface): """Monolithic interface for local repositories. This currently captures the reality of things - not how things should be. """ - supportedformats = zi.Attribute( + supportedformats = interfaceutil.Attribute( """Set of requirements that apply to stream clone. This is actually a class attribute and is shared among all instances. """) - openerreqs = zi.Attribute( + openerreqs = interfaceutil.Attribute( """Set of requirements that are passed to the opener. This is actually a class attribute and is shared among all instances. """) - supported = zi.Attribute( + supported = interfaceutil.Attribute( """Set of requirements that this repo is capable of opening.""") - requirements = zi.Attribute( + requirements = interfaceutil.Attribute( """Set of requirements this repo uses.""") - filtername = zi.Attribute( + filtername = interfaceutil.Attribute( """Name of the repoview that is active on this repo.""") - wvfs = zi.Attribute( + wvfs = interfaceutil.Attribute( """VFS used to access the working directory.""") - vfs = zi.Attribute( + vfs = interfaceutil.Attribute( """VFS rooted at the .hg directory. Used to access repository data not in the store. """) - svfs = zi.Attribute( + svfs = interfaceutil.Attribute( """VFS rooted at the store. Used to access repository data in the store. Typically .hg/store. But can point elsewhere if the store is shared. """) - root = zi.Attribute( + root = interfaceutil.Attribute( """Path to the root of the working directory.""") - path = zi.Attribute( + path = interfaceutil.Attribute( """Path to the .hg directory.""") - origroot = zi.Attribute( + origroot = interfaceutil.Attribute( """The filesystem path that was used to construct the repo.""") - auditor = zi.Attribute( + auditor = interfaceutil.Attribute( """A pathauditor for the working directory. This checks if a path refers to a nested repository. @@ -702,40 +702,40 @@ Operates on the filesystem. """) - nofsauditor = zi.Attribute( + nofsauditor = interfaceutil.Attribute( """A pathauditor for the working directory. This is like ``auditor`` except it doesn't do filesystem checks. """) - baseui = zi.Attribute( + baseui = interfaceutil.Attribute( """Original ui instance passed into constructor.""") - ui = zi.Attribute( + ui = interfaceutil.Attribute( """Main ui instance for this instance.""") - sharedpath = zi.Attribute( + sharedpath = interfaceutil.Attribute( """Path to the .hg directory of the repo this repo was shared from.""") - store = zi.Attribute( + store = interfaceutil.Attribute( """A store instance.""") - spath = zi.Attribute( + spath = interfaceutil.Attribute( """Path to the store.""") - sjoin = zi.Attribute( + sjoin = interfaceutil.Attribute( """Alias to self.store.join.""") - cachevfs = zi.Attribute( + cachevfs = interfaceutil.Attribute( """A VFS used to access the cache directory. Typically .hg/cache. """) - filteredrevcache = zi.Attribute( + filteredrevcache = interfaceutil.Attribute( """Holds sets of revisions to be filtered.""") - names = zi.Attribute( + names = interfaceutil.Attribute( """A ``namespaces`` instance.""") def close(): @@ -750,19 +750,19 @@ def filtered(name, visibilityexceptions=None): """Obtain a named view of this repository.""" - obsstore = zi.Attribute( + obsstore = interfaceutil.Attribute( """A store of obsolescence data.""") - changelog = zi.Attribute( + changelog = interfaceutil.Attribute( """A handle on the changelog revlog.""") - manifestlog = zi.Attribute( + manifestlog = interfaceutil.Attribute( """A handle on the root manifest revlog.""") - dirstate = zi.Attribute( + dirstate = interfaceutil.Attribute( """Working directory state.""") - narrowpats = zi.Attribute( + narrowpats = interfaceutil.Attribute( """Matcher patterns for this repository's narrowspec.""") def narrowmatch(): @@ -978,7 +978,7 @@ def checkpush(pushop): pass - prepushoutgoinghooks = zi.Attribute( + prepushoutgoinghooks = interfaceutil.Attribute( """util.hooks instance.""") def pushkey(namespace, key, old, new): diff -r 92213f6745ed -r 86e7a57449fa mercurial/revlog.py --- a/mercurial/revlog.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/revlog.py Sat May 05 18:06:45 2018 -0700 @@ -1466,7 +1466,7 @@ if id in self._pcache: return self._pcache[id] - if len(id) < 40: + if len(id) <= 40: try: # hex(node)[:...] l = len(id) // 2 # grab an even number of digits diff -r 92213f6745ed -r 86e7a57449fa mercurial/sshpeer.py --- a/mercurial/sshpeer.py Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/sshpeer.py Sat May 05 18:06:45 2018 -0700 @@ -261,11 +261,9 @@ handshake.insert(0, 'upgrade %s %s\n' % (token, upgradecaps)) if requestlog: - ui.debug('devel-peer-request: hello\n') + ui.debug('devel-peer-request: hello+between\n') + ui.debug('devel-peer-request: pairs: %d bytes\n' % len(pairsarg)) ui.debug('sending hello command\n') - if requestlog: - ui.debug('devel-peer-request: between\n') - ui.debug('devel-peer-request: pairs: %d bytes\n' % len(pairsarg)) ui.debug('sending between command\n') stdin.write(''.join(handshake)) diff -r 92213f6745ed -r 86e7a57449fa mercurial/templates/paper/changeset.tmpl --- a/mercurial/templates/paper/changeset.tmpl Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/templates/paper/changeset.tmpl Sat May 05 18:06:45 2018 -0700 @@ -73,9 +73,9 @@ diffstat {diffsummary} - [+] + [+] @@ -83,7 +83,7 @@
-
line wrap: on
+
line wrap: on
line diff
{diff} diff -r 92213f6745ed -r 86e7a57449fa mercurial/templates/paper/filediff.tmpl --- a/mercurial/templates/paper/filediff.tmpl Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/templates/paper/filediff.tmpl Sat May 05 18:06:45 2018 -0700 @@ -65,7 +65,7 @@
-
line wrap: on
+
line wrap: on
line diff
{diff} diff -r 92213f6745ed -r 86e7a57449fa mercurial/templates/paper/filerevision.tmpl --- a/mercurial/templates/paper/filerevision.tmpl Sun Mar 04 15:29:41 2018 -0500 +++ b/mercurial/templates/paper/filerevision.tmpl Sat May 05 18:06:45 2018 -0700 @@ -65,7 +65,7 @@
-
line wrap: on
+
line wrap: on
line source
>> versiontuple(v, 4)
     (3, 9, None, 'rc+2-02a8fea4289b')
+
+    >>> versiontuple(b'4.6rc0')
+    (4, 6, None, 'rc0')
+    >>> versiontuple(b'4.6rc0+12-425d55e54f98')
+    (4, 6, None, 'rc0+12-425d55e54f98')
+    >>> versiontuple(b'.1.2.3')
+    (None, None, None, '.1.2.3')
+    >>> versiontuple(b'12.34..5')
+    (12, 34, None, '..5')
+    >>> versiontuple(b'1.2.3.4.5.6')
+    (1, 2, 3, '.4.5.6')
     """
     if not v:
         v = version()
-    parts = remod.split('[\+-]', v, 1)
-    if len(parts) == 1:
-        vparts, extra = parts[0], None
+    m = remod.match(br'(\d+(?:\.\d+){,2})[\+-]?(.*)', v)
+    if not m:
+        vparts, extra = '', v
+    elif m.group(2):
+        vparts, extra = m.groups()
     else:
-        vparts, extra = parts
+        vparts, extra = m.group(1), None
 
     vints = []
     for i in vparts.split('.'):
diff -r 92213f6745ed -r 86e7a57449fa mercurial/utils/interfaceutil.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/utils/interfaceutil.py	Sat May 05 18:06:45 2018 -0700
@@ -0,0 +1,40 @@
+# interfaceutil.py - Utilities for declaring interfaces.
+#
+# Copyright 2018 Gregory Szorc 
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+# zope.interface imposes a run-time cost due to module import overhead and
+# bookkeeping for declaring interfaces. So, we use stubs for various
+# zope.interface primitives unless instructed otherwise.
+
+from __future__ import absolute_import
+
+from .. import (
+    encoding,
+)
+
+if encoding.environ.get('HGREALINTERFACES'):
+    from ..thirdparty.zope import (
+        interface as zi,
+    )
+
+    Attribute = zi.Attribute
+    Interface = zi.Interface
+    implementer = zi.implementer
+else:
+    class Attribute(object):
+        def __init__(self, __name__, __doc__=''):
+            pass
+
+    class Interface(object):
+        def __init__(self, name, bases=(), attrs=None, __doc__=None,
+                 __module__=None):
+            pass
+
+    def implementer(*ifaces):
+        def wrapper(cls):
+            return cls
+
+        return wrapper
diff -r 92213f6745ed -r 86e7a57449fa mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py	Sun Mar 04 15:29:41 2018 -0500
+++ b/mercurial/wireprotoserver.py	Sat May 05 18:06:45 2018 -0700
@@ -15,9 +15,6 @@
 from .thirdparty import (
     cbor,
 )
-from .thirdparty.zope import (
-    interface as zi,
-)
 from . import (
     encoding,
     error,
@@ -29,6 +26,7 @@
     wireprotov2server,
 )
 from .utils import (
+    interfaceutil,
     procutil,
 )
 
@@ -62,7 +60,7 @@
 
     return ''.join(chunks)
 
-@zi.implementer(wireprototypes.baseprotocolhandler)
+@interfaceutil.implementer(wireprototypes.baseprotocolhandler)
 class httpv1protocolhandler(object):
     def __init__(self, req, ui, checkperm):
         self._req = req
@@ -489,7 +487,7 @@
     fout.write(b'\n')
     fout.flush()
 
-@zi.implementer(wireprototypes.baseprotocolhandler)
+@interfaceutil.implementer(wireprototypes.baseprotocolhandler)
 class sshv1protocolhandler(object):
     """Handler for requests services via version 1 of SSH protocol."""
     def __init__(self, ui, fin, fout):
diff -r 92213f6745ed -r 86e7a57449fa mercurial/wireprototypes.py
--- a/mercurial/wireprototypes.py	Sun Mar 04 15:29:41 2018 -0500
+++ b/mercurial/wireprototypes.py	Sat May 05 18:06:45 2018 -0700
@@ -9,14 +9,14 @@
     bin,
     hex,
 )
-from .thirdparty.zope import (
-    interface as zi,
-)
 from .i18n import _
 from . import (
     error,
     util,
 )
+from .utils import (
+    interfaceutil,
+)
 
 # Names of the SSH protocol implementations.
 SSHV1 = 'ssh-v1'
@@ -179,7 +179,7 @@
     'stream': 'boolean',
 }
 
-class baseprotocolhandler(zi.Interface):
+class baseprotocolhandler(interfaceutil.Interface):
     """Abstract base class for wire protocol handlers.
 
     A wire protocol handler serves as an interface between protocol command
@@ -188,7 +188,7 @@
     the request, handle response types, etc.
     """
 
-    name = zi.Attribute(
+    name = interfaceutil.Attribute(
         """The name of the protocol implementation.
 
         Used for uniquely identifying the transport type.
diff -r 92213f6745ed -r 86e7a57449fa mercurial/wireprotov1peer.py
--- a/mercurial/wireprotov1peer.py	Sun Mar 04 15:29:41 2018 -0500
+++ b/mercurial/wireprotov1peer.py	Sat May 05 18:06:45 2018 -0700
@@ -15,9 +15,6 @@
 from .node import (
     bin,
 )
-from .thirdparty.zope import (
-    interface as zi,
-)
 from . import (
     bundle2,
     changegroup as changegroupmod,
@@ -29,6 +26,9 @@
     util,
     wireprototypes,
 )
+from .utils import (
+    interfaceutil,
+)
 
 urlreq = util.urlreq
 
@@ -110,7 +110,7 @@
         # on that.
         return self.result(timeout)
 
-@zi.implementer(repository.ipeercommandexecutor)
+@interfaceutil.implementer(repository.ipeercommandexecutor)
 class peerexecutor(object):
     def __init__(self, peer):
         self._peer = peer
@@ -308,7 +308,8 @@
             else:
                 f.set_result(result)
 
-@zi.implementer(repository.ipeercommands, repository.ipeerlegacycommands)
+@interfaceutil.implementer(repository.ipeercommands,
+                           repository.ipeerlegacycommands)
 class wirepeer(repository.peer):
     """Client-side interface for communicating with a peer repository.
 
diff -r 92213f6745ed -r 86e7a57449fa mercurial/wireprotov2server.py
--- a/mercurial/wireprotov2server.py	Sun Mar 04 15:29:41 2018 -0500
+++ b/mercurial/wireprotov2server.py	Sat May 05 18:06:45 2018 -0700
@@ -12,9 +12,6 @@
 from .thirdparty import (
     cbor,
 )
-from .thirdparty.zope import (
-    interface as zi,
-)
 from . import (
     encoding,
     error,
@@ -24,6 +21,9 @@
     wireprotoframing,
     wireprototypes,
 )
+from .utils import (
+    interfaceutil,
+)
 
 FRAMINGTYPE = b'application/mercurial-exp-framing-0005'
 
@@ -340,7 +340,7 @@
 
     return func(repo, proto, **args)
 
-@zi.implementer(wireprototypes.baseprotocolhandler)
+@interfaceutil.implementer(wireprototypes.baseprotocolhandler)
 class httpv2protocolhandler(object):
     def __init__(self, req, ui, args=None):
         self._req = req
diff -r 92213f6745ed -r 86e7a57449fa tests/test-check-code.t
--- a/tests/test-check-code.t	Sun Mar 04 15:29:41 2018 -0500
+++ b/tests/test-check-code.t	Sat May 05 18:06:45 2018 -0700
@@ -51,3 +51,15 @@
   hgeditor
   hgweb.cgi
   setup.py
+
+Prevent adding modules which could be shadowed by ancient .so/.dylib.
+
+  $ testrepohg files \
+  > mercurial/base85.py \
+  > mercurial/bdiff.py \
+  > mercurial/diffhelpers.py \
+  > mercurial/mpatch.py \
+  > mercurial/osutil.py \
+  > mercurial/parsers.py \
+  > mercurial/zstd.py
+  [1]
diff -r 92213f6745ed -r 86e7a57449fa tests/test-check-commit.t
--- a/tests/test-check-commit.t	Sun Mar 04 15:29:41 2018 -0500
+++ b/tests/test-check-commit.t	Sat May 05 18:06:45 2018 -0700
@@ -8,16 +8,20 @@
 
   $ cd $TESTDIR/..
 
+  $ REVSET='not public() and ::. and not desc("# no-check-commit")'
+
   $ mkdir "$TESTTMP/p"
-  $ testrepohg export --git -o "$TESTTMP/p/%n-%h" \
-  > -r 'not public() and ::. and not desc("# no-check-commit")'
-  $ for f in `ls "$TESTTMP/p"`; do
-  >    contrib/check-commit < "$TESTTMP/p/$f" > "$TESTTMP/check-commit.out"
-  >    if [ $? -ne 0 ]; then
-  >        node="${f##*-}"
-  >        echo "Revision $node does not comply with rules"
-  >        echo '------------------------------------------------------'
-  >        cat ${TESTTMP}/check-commit.out
-  >        echo
-  >   fi
-  > done
+  $ REVS=`testrepohg log -r "$REVSET" -T.`
+  $ if [ -n "$REVS" ] ; then
+  >   testrepohg export --git -o "$TESTTMP/p/%n-%h" -r "$REVSET"
+  >   for f in `ls "$TESTTMP/p"`; do
+  >      contrib/check-commit < "$TESTTMP/p/$f" > "$TESTTMP/check-commit.out"
+  >      if [ $? -ne 0 ]; then
+  >          node="${f##*-}"
+  >          echo "Revision $node does not comply with rules"
+  >          echo '------------------------------------------------------'
+  >          cat ${TESTTMP}/check-commit.out
+  >          echo
+  >     fi
+  >   done
+  > fi
diff -r 92213f6745ed -r 86e7a57449fa tests/test-check-interfaces.py
--- a/tests/test-check-interfaces.py	Sun Mar 04 15:29:41 2018 -0500
+++ b/tests/test-check-interfaces.py	Sat May 05 18:06:45 2018 -0700
@@ -2,7 +2,17 @@
 
 from __future__ import absolute_import, print_function
 
+from mercurial import encoding
+encoding.environ[b'HGREALINTERFACES'] = b'1'
+
 import os
+import subprocess
+import sys
+
+# Only run if tests are run in a repo
+if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'],
+                    'test-repo']):
+    sys.exit(80)
 
 from mercurial.thirdparty.zope import (
     interface as zi,
diff -r 92213f6745ed -r 86e7a57449fa tests/test-check-module-imports.t
--- a/tests/test-check-module-imports.t	Sun Mar 04 15:29:41 2018 -0500
+++ b/tests/test-check-module-imports.t	Sat May 05 18:06:45 2018 -0700
@@ -27,6 +27,7 @@
   > -X i18n/posplit \
   > -X mercurial/thirdparty \
   > -X tests/hypothesishelpers.py \
+  > -X tests/test-check-interfaces.py \
   > -X tests/test-commit-interactive.t \
   > -X tests/test-contrib-check-code.t \
   > -X tests/test-demandimport.py \
diff -r 92213f6745ed -r 86e7a57449fa tests/test-debugcommands.t
--- a/tests/test-debugcommands.t	Sun Mar 04 15:29:41 2018 -0500
+++ b/tests/test-debugcommands.t	Sat May 05 18:06:45 2018 -0700
@@ -333,6 +333,19 @@
   .hg/cache/rbc-names-v1
   .hg/cache/branch2-served
 
+Test debugcolor
+
+#if no-windows
+  $ hg debugcolor --style --color always | egrep 'mode|style|log\.'
+  color mode: ansi
+  available style:
+  \x1b[0;33mlog.changeset\x1b[0m:                      \x1b[0;33myellow\x1b[0m (esc)
+#endif
+
+  $ hg debugcolor --style --color never
+  color mode: None
+  available style:
+
   $ cd ..
 
 Test internal debugstacktrace command
@@ -411,10 +424,9 @@
   $ hg --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" --debug debugpeer ssh://user@dummy/debugrevlog
   running "*" "*/tests/dummyssh" 'user@dummy' 'hg -R debugrevlog serve --stdio' (glob) (no-windows !)
   running "*" "*\tests/dummyssh" "user@dummy" "hg -R debugrevlog serve --stdio" (glob) (windows !)
-  devel-peer-request: hello
+  devel-peer-request: hello+between
+  devel-peer-request:   pairs: 81 bytes
   sending hello command
-  devel-peer-request: between
-  devel-peer-request:   pairs: 81 bytes
   sending between command
   remote: 413
   remote: capabilities: batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset getbundle known lookup protocaps pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
diff -r 92213f6745ed -r 86e7a57449fa tests/test-fix.t
--- a/tests/test-fix.t	Sun Mar 04 15:29:41 2018 -0500
+++ b/tests/test-fix.t	Sat May 05 18:06:45 2018 -0700
@@ -493,11 +493,12 @@
   $ printf "NO FIX NEEDED\n" > foo.whole
   $ hg add
   adding foo.whole
-  $ cp foo.whole foo.whole.orig
+  $ cp -p foo.whole foo.whole.orig
+  $ cp -p foo.whole.orig foo.whole
   $ sleep 2 # mtime has a resolution of one or two seconds.
   $ hg fix --working-dir
-  $ f foo.whole --newer foo.whole.orig
-  foo.whole: older than foo.whole.orig
+  $ f foo.whole.orig --newer foo.whole
+  foo.whole.orig: newer than foo.whole
 
   $ cd ..
 
@@ -514,8 +515,11 @@
   $ printf "hello\n" > hello.txt
   $ hg add
   adding hello.txt
-  $ hg --config "fix.fail:command=printf 'HELLO\n' ; \
-  >                               printf '{rootpath}: some\nerror' >&2" \
+  $ cat >> $TESTTMP/cmd.sh <<'EOF'
+  > printf 'HELLO\n'
+  > printf "$@: some\nerror" >&2
+  > EOF
+  $ hg --config "fix.fail:command=sh $TESTTMP/cmd.sh {rootpath}" \
   >    --config "fix.fail:fileset=hello.txt" \
   >    fix --working-dir
   [wdir] fail: hello.txt: some
diff -r 92213f6745ed -r 86e7a57449fa tests/test-help.t
--- a/tests/test-help.t	Sun Mar 04 15:29:41 2018 -0500
+++ b/tests/test-help.t	Sat May 05 18:06:45 2018 -0700
@@ -278,7 +278,6 @@
        purge         command to delete untracked files from the working
                      directory
        relink        recreates hardlinks between repository clones
-       remotenames   showing remotebookmarks and remotebranches in UI
        schemes       extend schemes with shortcuts to repository swarms
        share         share a common history between several working directories
        shelve        save and restore changes to the working directory
diff -r 92213f6745ed -r 86e7a57449fa tests/test-hgweb-commands.t
--- a/tests/test-hgweb-commands.t	Sun Mar 04 15:29:41 2018 -0500
+++ b/tests/test-hgweb-commands.t	Sat May 05 18:06:45 2018 -0700
@@ -916,9 +916,9 @@
     
        2 files changed, 2 insertions(+), 0 deletions(-)
   
-      [+]
+      [+]