Mercurial > hg
changeset 38739:7acec9408e1c stable
release: merge default into stable for 4.7 release freeze
author | Augie Fackler <augie@google.com> |
---|---|
date | Thu, 19 Jul 2018 13:55:54 -0400 |
parents | 443029011990 (current diff) faea9b1980d9 (diff) |
children | c08ea1e219c0 |
files | contrib/build-linux-wheels.sh contrib/builddeb contrib/buildrpm contrib/debian/cacerts.rc contrib/debian/changelog contrib/debian/compat contrib/debian/control contrib/debian/copyright contrib/debian/default-tools.rc contrib/debian/hgkpath.rc contrib/debian/rules contrib/docker/centos5 contrib/docker/centos6 contrib/docker/centos7 contrib/docker/debian.template contrib/docker/fedora20 contrib/docker/fedora21 contrib/docker/ubuntu.template contrib/dockerdeb contrib/dockerlib.sh contrib/dockerrpm contrib/fixpax.py contrib/linux-wheel-centos5-blacklist contrib/macosx/Readme.html contrib/macosx/Welcome.html contrib/macosx/distribution.xml contrib/mercurial.spec contrib/packagelib.sh hgext/narrow/narrowmerge.py mercurial/encoding.py tests/test-command-template.t tests/test-hybridencode.py.out tests/test-template-engine.t |
diffstat | 471 files changed, 23651 insertions(+), 12167 deletions(-) [+] |
line wrap: on
line diff
--- a/.editorconfig Sun Jul 01 23:36:53 2018 +0900 +++ b/.editorconfig Thu Jul 19 13:55:54 2018 -0400 @@ -11,3 +11,8 @@ indent_size = 8 indent_style = tab trim_trailing_whitespace = true + +[*.t] +indent_size = 2 +indent_style = space +trim_trailing_whitespace = false
--- a/.hgignore Sun Jul 01 23:36:53 2018 +0900 +++ b/.hgignore Thu Jul 19 13:55:54 2018 -0400 @@ -31,8 +31,6 @@ contrib/chg/chg contrib/hgsh/hgsh contrib/vagrant/.vagrant -contrib/docker/debian-* -contrib/docker/ubuntu-* dist packages doc/common.txt
--- a/Makefile Sun Jul 01 23:36:53 2018 +0900 +++ b/Makefile Thu Jul 19 13:55:54 2018 -0400 @@ -164,6 +164,39 @@ # Packaging targets +packaging_targets := \ + centos5 \ + centos6 \ + centos7 \ + deb \ + docker-centos5 \ + docker-centos6 \ + docker-centos7 \ + docker-debian-jessie \ + docker-debian-stretch \ + docker-fedora20 \ + docker-fedora21 \ + docker-fedora28 \ + docker-ubuntu-trusty \ + docker-ubuntu-trusty-ppa \ + docker-ubuntu-xenial \ + docker-ubuntu-xenial-ppa \ + docker-ubuntu-artful \ + docker-ubuntu-artful-ppa \ + docker-ubuntu-bionic \ + docker-ubuntu-bionic-ppa \ + fedora20 \ + fedora21 \ + fedora28 \ + linux-wheels \ + linux-wheels-x86_64 \ + linux-wheels-i686 \ + ppa + +# Forward packaging targets for convenience. +$(packaging_targets): + $(MAKE) -C contrib/packaging $@ + osx: rm -rf build/mercurial /usr/bin/python2.7 setup.py install --optimize=1 \ @@ -197,127 +230,14 @@ --identifier org.mercurial-scm.mercurial \ --version "$${HGVER}" \ build/mercurial.pkg && \ - productbuild --distribution contrib/macosx/distribution.xml \ + productbuild --distribution contrib/packaging/macosx/distribution.xml \ --package-path build/ \ --version "$${HGVER}" \ - --resources contrib/macosx/ \ + --resources contrib/packaging/macosx/ \ "$${OUTPUTDIR:-dist/}"/Mercurial-"$${HGVER}"-macosx"$${OSXVER}".pkg -deb: - contrib/builddeb - -ppa: - contrib/builddeb --source-only - -contrib/docker/debian-%: contrib/docker/debian.template - sed "s/__CODENAME__/$*/" $< > $@ - -docker-debian-jessie: contrib/docker/debian-jessie - contrib/dockerdeb debian jessie - -docker-debian-stretch: contrib/docker/debian-stretch - contrib/dockerdeb debian stretch - -contrib/docker/ubuntu-%: contrib/docker/ubuntu.template - sed "s/__CODENAME__/$*/" $< > $@ - -docker-ubuntu-trusty: contrib/docker/ubuntu-trusty - contrib/dockerdeb ubuntu trusty - -docker-ubuntu-trusty-ppa: contrib/docker/ubuntu-trusty - contrib/dockerdeb ubuntu trusty --source-only - -docker-ubuntu-xenial: contrib/docker/ubuntu-xenial - contrib/dockerdeb ubuntu xenial - -docker-ubuntu-xenial-ppa: contrib/docker/ubuntu-xenial - contrib/dockerdeb ubuntu xenial --source-only - -docker-ubuntu-artful: contrib/docker/ubuntu-artful - contrib/dockerdeb ubuntu artful - -docker-ubuntu-artful-ppa: contrib/docker/ubuntu-artful - contrib/dockerdeb ubuntu artful --source-only - -docker-ubuntu-bionic: contrib/docker/ubuntu-bionic - contrib/dockerdeb ubuntu bionic - -docker-ubuntu-bionic-ppa: contrib/docker/ubuntu-bionic - contrib/dockerdeb ubuntu bionic --source-only - -fedora20: - mkdir -p packages/fedora20 - contrib/buildrpm - cp rpmbuild/RPMS/*/* packages/fedora20 - cp rpmbuild/SRPMS/* packages/fedora20 - rm -rf rpmbuild - -docker-fedora20: - mkdir -p packages/fedora20 - contrib/dockerrpm fedora20 - -fedora21: - mkdir -p packages/fedora21 - contrib/buildrpm - cp rpmbuild/RPMS/*/* packages/fedora21 - cp rpmbuild/SRPMS/* packages/fedora21 - rm -rf rpmbuild - -docker-fedora21: - mkdir -p packages/fedora21 - contrib/dockerrpm fedora21 - -centos5: - mkdir -p packages/centos5 - contrib/buildrpm --withpython - cp rpmbuild/RPMS/*/* packages/centos5 - cp rpmbuild/SRPMS/* packages/centos5 - -docker-centos5: - mkdir -p packages/centos5 - contrib/dockerrpm centos5 --withpython - -centos6: - mkdir -p packages/centos6 - contrib/buildrpm --withpython - cp rpmbuild/RPMS/*/* packages/centos6 - cp rpmbuild/SRPMS/* packages/centos6 - -docker-centos6: - mkdir -p packages/centos6 - contrib/dockerrpm centos6 --withpython - -centos7: - mkdir -p packages/centos7 - contrib/buildrpm - cp rpmbuild/RPMS/*/* packages/centos7 - cp rpmbuild/SRPMS/* packages/centos7 - -docker-centos7: - mkdir -p packages/centos7 - contrib/dockerrpm centos7 - -linux-wheels: linux-wheels-x86_64 linux-wheels-i686 - -linux-wheels-x86_64: - docker run -e "HGTEST_JOBS=$(shell nproc)" --rm -ti -v `pwd`:/src quay.io/pypa/manylinux1_x86_64 /src/contrib/build-linux-wheels.sh - -linux-wheels-i686: - docker run -e "HGTEST_JOBS=$(shell nproc)" --rm -ti -v `pwd`:/src quay.io/pypa/manylinux1_i686 linux32 /src/contrib/build-linux-wheels.sh - .PHONY: help all local build doc cleanbutpackages clean install install-bin \ install-doc install-home install-home-bin install-home-doc \ dist dist-notests check tests check-code format-c update-pot \ - osx deb ppa \ - docker-debian-jessie \ - docker-debian-stretch \ - docker-ubuntu-trusty docker-ubuntu-trusty-ppa \ - docker-ubuntu-xenial docker-ubuntu-xenial-ppa \ - docker-ubuntu-artful docker-ubuntu-artful-ppa \ - docker-ubuntu-bionic docker-ubuntu-bionic-ppa \ - fedora20 docker-fedora20 \ - fedora21 docker-fedora21 \ - centos5 docker-centos5 \ - centos6 docker-centos6 \ - centos7 docker-centos7 \ - linux-wheels + $(packaging_targets) \ + osx
--- a/contrib/all-revsets.txt Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/all-revsets.txt Thu Jul 19 13:55:54 2018 -0400 @@ -135,3 +135,7 @@ # testing the mutable phases set draft() secret() + +# test finding common ancestors +heads(commonancestors(last(head(), 2))) +heads(commonancestors(head()))
--- a/contrib/base-revsets.txt Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/base-revsets.txt Thu Jul 19 13:55:54 2018 -0400 @@ -46,3 +46,4 @@ (20000::) - (20000) # The one below is used by rebase (children(ancestor(tip~5, tip)) and ::(tip~5)):: +heads(commonancestors(last(head(), 2)))
--- a/contrib/build-linux-wheels.sh Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -#!/bin/bash -# This file is directly inspired by -# https://github.com/pypa/python-manylinux-demo/blob/master/travis/build-wheels.sh -set -e -x - -PYTHON_TARGETS=$(ls -d /opt/python/cp27*/bin) - -# Create an user for the tests -useradd hgbuilder - -# Bypass uid/gid problems -cp -R /src /io && chown -R hgbuilder:hgbuilder /io - -# Compile wheels for Python 2.X -for PYBIN in $PYTHON_TARGETS; do - "${PYBIN}/pip" wheel /io/ -w wheelhouse/ -done - -# Bundle external shared libraries into the wheels with -# auditwheel (https://github.com/pypa/auditwheel) repair. -# It also fix the ABI tag on the wheel making it pip installable. -for whl in wheelhouse/*.whl; do - auditwheel repair "$whl" -w /src/wheelhouse/ -done - -# Install packages and run the tests for all Python versions -cd /io/tests/ - -for PYBIN in $PYTHON_TARGETS; do - # Install mercurial wheel as root - "${PYBIN}/pip" install mercurial --no-index -f /src/wheelhouse - # But run tests as hgbuilder user (non-root) - su hgbuilder -c "\"${PYBIN}/python\" /io/tests/run-tests.py --with-hg=\"${PYBIN}/hg\" --blacklist=/io/contrib/linux-wheel-centos5-blacklist" -done
--- a/contrib/builddeb Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -#!/bin/sh -e -# -# Build a Mercurial debian package from the current repo -# -# Tested on Jessie (stable as of original script authoring.) - -. $(dirname $0)/packagelib.sh - -BUILD=1 -CLEANUP=1 -DISTID=`(lsb_release -is 2> /dev/null | tr '[:upper:]' '[:lower:]') || echo debian` -CODENAME=`lsb_release -cs 2> /dev/null || echo unknown` -DEBFLAGS=-b -while [ "$1" ]; do - case "$1" in - --distid ) - shift - DISTID="$1" - shift - ;; - --codename ) - shift - CODENAME="$1" - shift - ;; - --cleanup ) - shift - BUILD= - ;; - --build ) - shift - CLEANUP= - ;; - --source-only ) - shift - DEBFLAGS=-S - ;; - * ) - echo "Invalid parameter $1!" 1>&2 - exit 1 - ;; - esac -done - -trap "if [ '$CLEANUP' ] ; then rm -r '$PWD/debian' ; fi" EXIT - -set -u - -if [ ! -d .hg ]; then - echo 'You are not inside a Mercurial repository!' 1>&2 - exit 1 -fi - -gethgversion -debver="$version" -if [ -n "$type" ] ; then - debver="$debver~$type" -fi -if [ -n "$distance" ] ; then - debver="$debver+$distance-$CODENAME-$node" -elif [ "$DEBFLAGS" = "-S" ] ; then - # for building a ppa (--source-only) for a release (distance == 0), we need - # to version the distroseries so that we can upload to launchpad - debver="$debver~${CODENAME}1" -fi - -control=debian/control -changelog=debian/changelog - -if [ "$BUILD" ]; then - if [ -d debian ] ; then - echo "Error! debian control directory already exists!" - exit 1 - fi - - cp -r "$PWD"/contrib/debian debian - - sed -i.tmp "s/__VERSION__/$debver/" $changelog - sed -i.tmp "s/__DATE__/$(date --rfc-2822)/" $changelog - sed -i.tmp "s/__CODENAME__/$CODENAME/" $changelog - rm $changelog.tmp - - # remove the node from the version string - SRCFILE="mercurial_$(echo $debver | sed "s,-$node,,").orig.tar.gz" - "$PWD/hg" archive $SRCFILE - mv $SRCFILE .. - debuild -us -uc -i -I $DEBFLAGS - if [ $? != 0 ]; then - echo 'debuild failed!' - exit 1 - fi - -fi -if [ "$CLEANUP" ] ; then - echo - OUTPUTDIR=${OUTPUTDIR:=packages/$DISTID-$CODENAME} - mkdir -p "$OUTPUTDIR" - find ../mercurial*.deb ../mercurial_*.build ../mercurial_*.changes \ - ../mercurial*.dsc ../mercurial*.gz \ - -type f -newer $control -print0 2>/dev/null | \ - xargs -Inarf -0 mv narf "$OUTPUTDIR" - echo "Built packages for $debver:" - find "$OUTPUTDIR" -type f -newer $control -name '*.deb' -fi
--- a/contrib/buildrpm Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,162 +0,0 @@ -#!/bin/bash -e -# -# Build a Mercurial RPM from the current repo -# -# Tested on -# - Fedora 20 -# - CentOS 5 -# - centOS 6 - -. $(dirname $0)/packagelib.sh - -BUILD=1 -RPMBUILDDIR="$PWD/rpmbuild" - -while [ "$1" ]; do - case "$1" in - --prepare ) - shift - BUILD= - ;; - --withpython | --with-python) - shift - PYTHONVER=2.7.14 - PYTHONMD5=cee2e4b33ad3750da77b2e85f2f8b724 - ;; - --rpmbuilddir ) - shift - RPMBUILDDIR="$1" - shift - ;; - * ) - echo "Invalid parameter $1!" 1>&2 - exit 1 - ;; - esac -done - -cd "`dirname $0`/.." - -specfile=$PWD/contrib/mercurial.spec -if [ ! -f $specfile ]; then - echo "Cannot find $specfile!" 1>&2 - exit 1 -fi - -if [ ! -d .hg ]; then - echo 'You are not inside a Mercurial repository!' 1>&2 - exit 1 -fi - -gethgversion - -# TODO: handle distance/node set, and type set - -if [ -z "$type" ] ; then - release=1 -else - release=0.9_$type -fi - -if [ -n "$distance" ] ; then - release=$release+$distance_$node -fi - -if [ "$PYTHONVER" ]; then - release=$release+$PYTHONVER - RPMPYTHONVER=$PYTHONVER -else - RPMPYTHONVER=%{nil} -fi - -mkdir -p $RPMBUILDDIR/{SOURCES,BUILD,SRPMS,RPMS} -$HG archive -t tgz $RPMBUILDDIR/SOURCES/mercurial-$version-$release.tar.gz -if [ "$PYTHONVER" ]; then -( - mkdir -p build - cd build - PYTHON_SRCFILE=Python-$PYTHONVER.tgz - [ -f $PYTHON_SRCFILE ] || curl -Lo $PYTHON_SRCFILE http://www.python.org/ftp/python/$PYTHONVER/$PYTHON_SRCFILE - if [ "$PYTHONMD5" ]; then - echo "$PYTHONMD5 $PYTHON_SRCFILE" | md5sum -w -c - fi - ln -f $PYTHON_SRCFILE $RPMBUILDDIR/SOURCES/$PYTHON_SRCFILE - - DOCUTILSVER=`sed -ne "s/^%global docutilsname docutils-//p" $specfile` - DOCUTILS_SRCFILE=docutils-$DOCUTILSVER.tar.gz - [ -f $DOCUTILS_SRCFILE ] || curl -Lo $DOCUTILS_SRCFILE http://downloads.sourceforge.net/project/docutils/docutils/$DOCUTILSVER/$DOCUTILS_SRCFILE - DOCUTILSMD5=`sed -ne "s/^%global docutilsmd5 //p" $specfile` - if [ "$DOCUTILSMD5" ]; then - echo "$DOCUTILSMD5 $DOCUTILS_SRCFILE" | md5sum -w -c - fi - ln -f $DOCUTILS_SRCFILE $RPMBUILDDIR/SOURCES/$DOCUTILS_SRCFILE -) -fi - -mkdir -p $RPMBUILDDIR/SPECS -rpmspec=$RPMBUILDDIR/SPECS/mercurial.spec - -sed -e "s,^Version:.*,Version: $version," \ - -e "s,^Release:.*,Release: $release," \ - $specfile > $rpmspec - -echo >> $rpmspec -echo "%changelog" >> $rpmspec - -if echo $version | grep '+' > /dev/null 2>&1; then - latesttag="`echo $version | sed -e 's/+.*//'`" - $HG log -r .:"$latesttag" -fM \ - --template '{date|hgdate}\t{author}\t{desc|firstline}\n' | python -c ' -import sys, time - -def datestr(date, format): - return time.strftime(format, time.gmtime(float(date[0]) - date[1])) - -changelog = [] -for l in sys.stdin.readlines(): - tok = l.split("\t") - hgdate = tuple(int(v) for v in tok[0].split()) - changelog.append((datestr(hgdate, "%F"), tok[1], hgdate, tok[2])) -prevtitle = "" -for l in sorted(changelog, reverse=True): - title = "* %s %s" % (datestr(l[2], "%a %b %d %Y"), l[1]) - if prevtitle != title: - prevtitle = title - print - print title - print "- %s" % l[3].strip() -' >> $rpmspec - -else - - $HG log \ - --template '{date|hgdate}\t{author}\t{desc|firstline}\n' \ - .hgtags | python -c ' -import sys, time - -def datestr(date, format): - return time.strftime(format, time.gmtime(float(date[0]) - date[1])) - -for l in sys.stdin.readlines(): - tok = l.split("\t") - hgdate = tuple(int(v) for v in tok[0].split()) - print "* %s %s\n- %s" % (datestr(hgdate, "%a %b %d %Y"), tok[1], tok[2]) -' >> $rpmspec - -fi - -sed -i \ - -e "s/^%define withpython.*$/%define withpython $RPMPYTHONVER/" \ - $rpmspec - -if [ "$BUILD" ]; then - rpmbuild --define "_topdir $RPMBUILDDIR" -ba $rpmspec --clean - if [ $? = 0 ]; then - echo - echo "Built packages for $version-$release:" - find $RPMBUILDDIR/*RPMS/ -type f -newer $rpmspec - fi -else - echo "Prepared sources for $version-$release $rpmspec are in $RPMBUILDDIR/SOURCES/ - use like:" - echo "rpmbuild --define '_topdir $RPMBUILDDIR' -ba $rpmspec --clean" -fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/byteify-strings.py Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +# +# byteify-strings.py - transform string literals to be Python 3 safe +# +# Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +import argparse +import contextlib +import errno +import os +import sys +import tempfile +import token +import tokenize + +def adjusttokenpos(t, ofs): + """Adjust start/end column of the given token""" + return t._replace(start=(t.start[0], t.start[1] + ofs), + end=(t.end[0], t.end[1] + ofs)) + +def replacetokens(tokens, opts): + """Transform a stream of tokens from raw to Python 3. + + Returns a generator of possibly rewritten tokens. + + The input token list may be mutated as part of processing. However, + its changes do not necessarily match the output token stream. + """ + sysstrtokens = set() + + # The following utility functions access the tokens list and i index of + # the for i, t enumerate(tokens) loop below + def _isop(j, *o): + """Assert that tokens[j] is an OP with one of the given values""" + try: + return tokens[j].type == token.OP and tokens[j].string in o + except IndexError: + return False + + def _findargnofcall(n): + """Find arg n of a call expression (start at 0) + + Returns index of the first token of that argument, or None if + there is not that many arguments. + + Assumes that token[i + 1] is '('. + + """ + nested = 0 + for j in range(i + 2, len(tokens)): + if _isop(j, ')', ']', '}'): + # end of call, tuple, subscription or dict / set + nested -= 1 + if nested < 0: + return None + elif n == 0: + # this is the starting position of arg + return j + elif _isop(j, '(', '[', '{'): + nested += 1 + elif _isop(j, ',') and nested == 0: + n -= 1 + + return None + + def _ensuresysstr(j): + """Make sure the token at j is a system string + + Remember the given token so the string transformer won't add + the byte prefix. + + Ignores tokens that are not strings. Assumes bounds checking has + already been done. + + """ + st = tokens[j] + if st.type == token.STRING and st.string.startswith(("'", '"')): + sysstrtokens.add(st) + + coldelta = 0 # column increment for new opening parens + coloffset = -1 # column offset for the current line (-1: TBD) + parens = [(0, 0, 0)] # stack of (line, end-column, column-offset) + for i, t in enumerate(tokens): + # Compute the column offset for the current line, such that + # the current line will be aligned to the last opening paren + # as before. + if coloffset < 0: + if t.start[1] == parens[-1][1]: + coloffset = parens[-1][2] + elif t.start[1] + 1 == parens[-1][1]: + # fix misaligned indent of s/util.Abort/error.Abort/ + coloffset = parens[-1][2] + (parens[-1][1] - t.start[1]) + else: + coloffset = 0 + + # Reset per-line attributes at EOL. + if t.type in (token.NEWLINE, tokenize.NL): + yield adjusttokenpos(t, coloffset) + coldelta = 0 + coloffset = -1 + continue + + # Remember the last paren position. + if _isop(i, '(', '[', '{'): + parens.append(t.end + (coloffset + coldelta,)) + elif _isop(i, ')', ']', '}'): + parens.pop() + + # Convert most string literals to byte literals. String literals + # in Python 2 are bytes. String literals in Python 3 are unicode. + # Most strings in Mercurial are bytes and unicode strings are rare. + # Rather than rewrite all string literals to use ``b''`` to indicate + # byte strings, we apply this token transformer to insert the ``b`` + # prefix nearly everywhere. + if t.type == token.STRING and t not in sysstrtokens: + s = t.string + + # Preserve docstrings as string literals. This is inconsistent + # with regular unprefixed strings. However, the + # "from __future__" parsing (which allows a module docstring to + # exist before it) doesn't properly handle the docstring if it + # is b''' prefixed, leading to a SyntaxError. We leave all + # docstrings as unprefixed to avoid this. This means Mercurial + # components touching docstrings need to handle unicode, + # unfortunately. + if s[0:3] in ("'''", '"""'): + yield adjusttokenpos(t, coloffset) + continue + + # If the first character isn't a quote, it is likely a string + # prefixing character (such as 'b', 'u', or 'r'. Ignore. + if s[0] not in ("'", '"'): + yield adjusttokenpos(t, coloffset) + continue + + # String literal. Prefix to make a b'' string. + yield adjusttokenpos(t._replace(string='b%s' % t.string), + coloffset) + coldelta += 1 + continue + + # This looks like a function call. + if t.type == token.NAME and _isop(i + 1, '('): + fn = t.string + + # *attr() builtins don't accept byte strings to 2nd argument. + if (fn in ('getattr', 'setattr', 'hasattr', 'safehasattr') and + not _isop(i - 1, '.')): + arg1idx = _findargnofcall(1) + if arg1idx is not None: + _ensuresysstr(arg1idx) + + # .encode() and .decode() on str/bytes/unicode don't accept + # byte strings on Python 3. + elif fn in ('encode', 'decode') and _isop(i - 1, '.'): + for argn in range(2): + argidx = _findargnofcall(argn) + if argidx is not None: + _ensuresysstr(argidx) + + # It changes iteritems/values to items/values as they are not + # present in Python 3 world. + elif opts['dictiter'] and fn in ('iteritems', 'itervalues'): + yield adjusttokenpos(t._replace(string=fn[4:]), coloffset) + continue + + # Emit unmodified token. + yield adjusttokenpos(t, coloffset) + +def process(fin, fout, opts): + tokens = tokenize.tokenize(fin.readline) + tokens = replacetokens(list(tokens), opts) + fout.write(tokenize.untokenize(tokens)) + +def tryunlink(fname): + try: + os.unlink(fname) + except OSError as err: + if err.errno != errno.ENOENT: + raise + +@contextlib.contextmanager +def editinplace(fname): + n = os.path.basename(fname) + d = os.path.dirname(fname) + fp = tempfile.NamedTemporaryFile(prefix='.%s-' % n, suffix='~', dir=d, + delete=False) + try: + yield fp + fp.close() + if os.name == 'nt': + tryunlink(fname) + os.rename(fp.name, fname) + finally: + fp.close() + tryunlink(fp.name) + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument('-i', '--inplace', action='store_true', default=False, + help='edit files in place') + ap.add_argument('--dictiter', action='store_true', default=False, + help='rewrite iteritems() and itervalues()'), + ap.add_argument('files', metavar='FILE', nargs='+', help='source file') + args = ap.parse_args() + opts = { + 'dictiter': args.dictiter, + } + for fname in args.files: + if args.inplace: + with editinplace(fname) as fout: + with open(fname, 'rb') as fin: + process(fin, fout, opts) + else: + with open(fname, 'rb') as fin: + fout = sys.stdout.buffer + process(fin, fout, opts) + +if __name__ == '__main__': + main()
--- a/contrib/check-code.py Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/check-code.py Thu Jul 19 13:55:54 2018 -0400 @@ -340,7 +340,8 @@ (r'\butil\.Abort\b', "directly use error.Abort"), (r'^@(\w*\.)?cachefunc', "module-level @cachefunc is risky, please avoid"), (r'^import atexit', "don't use atexit, use ui.atexit"), - (r'^import Queue', "don't use Queue, use util.queue + util.empty"), + (r'^import Queue', "don't use Queue, use pycompat.queue.Queue + " + "pycompat.queue.Empty"), (r'^import cStringIO', "don't use cStringIO.StringIO, use util.stringio"), (r'^import urllib', "don't use urllib, use util.urlreq/util.urlerr"), (r'^import SocketServer', "don't use SockerServer, use util.socketserver"),
--- a/contrib/debian/cacerts.rc Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -# This config file points Mercurial at the system-wide certificate -# store from the ca-certificates package. - -[web] -cacerts = /etc/ssl/certs/ca-certificates.crt
--- a/contrib/debian/changelog Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -mercurial (__VERSION__) __CODENAME__; urgency=medium - - * Automated build performed by upstream. - - -- Mercurial Devel <mercurial-devel@mercurial-scm.org> __DATE__
--- a/contrib/debian/compat Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -9
--- a/contrib/debian/control Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -Source: mercurial -Section: vcs -Priority: optional -Maintainer: Mercurial Developers <mercurial-devel@mercurial-scm.org> -Build-Depends: - debhelper (>= 9), - dh-python, - less, - netbase, - python-all, - python-all-dev, - python-docutils, - unzip, - zip -Standards-Version: 3.9.4 -X-Python-Version: >= 2.7 - -Package: mercurial -Depends: - python, - ${shlibs:Depends}, - ${misc:Depends}, - ${python:Depends}, - mercurial-common (= ${source:Version}) -Architecture: any -Description: fast, easy to use, distributed revision control tool. - Mercurial is a fast, lightweight Source Control Management system designed - for efficient handling of very large distributed projects. - . - Its features include: - * O(1) delta-compressed file storage and retrieval scheme - * Complete cross-indexing of files and changesets for efficient exploration - of project history - * Robust SHA1-based integrity checking and append-only storage model - * Decentralized development model with arbitrary merging between trees - * Easy-to-use command-line interface - * Integrated stand-alone web interface - * Small Python codebase - -Package: mercurial-common -Architecture: all -Depends: - ${misc:Depends}, - ${python:Depends}, -Recommends: mercurial (= ${source:Version}), ca-certificates -Suggests: wish -Breaks: mercurial (<< ${source:Version}) -Replaces: mercurial (<< 2.6.3) -Description: easy-to-use, scalable distributed version control system (common files) - Mercurial is a fast, lightweight Source Control Management system designed - for efficient handling of very large distributed projects. - . - This package contains the architecture independent components of Mercurial, - and is generally useless without the mercurial package.
--- a/contrib/debian/copyright Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: mercurial -Source: https://www.mercurial-scm.org/ - -Files: * -Copyright: 2005-2018, Matt Mackall <mpm@selenic.com> and others. -License: GPL-2+ - This program is free software; you can redistribute it - and/or modify it under the terms of the GNU General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later - version. - . - This program is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied - warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. See the GNU General Public License for more - details. - . - You should have received a copy of the GNU General Public - License along with this package; if not, write to the Free - Software Foundation, Inc., 51 Franklin St, Fifth Floor, - Boston, MA 02110-1301 USA - . - On Debian systems, the full text of the GNU General Public - License version 2 can be found in the file - `/usr/share/common-licenses/GPL-2'.
--- a/contrib/debian/default-tools.rc Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -[ui] -editor = sensible-editor - -[pager] -pager = sensible-pager
--- a/contrib/debian/hgkpath.rc Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -[hgk] -path = /usr/share/mercurial/hgk
--- a/contrib/debian/rules Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -#!/usr/bin/make -f -# Uncomment this to turn on verbose mode. -# export DH_VERBOSE=1 - -CPUS=$(shell cat /proc/cpuinfo | grep -E ^processor | wc -l) - -%: - dh $@ --with python2 - -override_dh_auto_test: - http_proxy='' dh_auto_test -- TESTFLAGS="-j$(CPUS)" - -override_dh_python2: - dh_python2 - find debian/mercurial/usr/share -type d -empty -delete - -override_dh_install: - python$(PYVERS) setup.py install --root "$(CURDIR)"/debian/mercurial --install-layout=deb - # chg - make -C contrib/chg \ - DESTDIR="$(CURDIR)"/debian/mercurial \ - PREFIX=/usr \ - clean install - # remove arch-independent python stuff - find "$(CURDIR)"/debian/mercurial/usr/lib \ - ! -name '*.so' ! -type d -delete , \ - -type d -empty -delete - python$(PYVERS) setup.py install --root "$(CURDIR)/debian/mercurial-common" --install-layout=deb - make install-doc PREFIX="$(CURDIR)"/debian/mercurial-common/usr - # remove arch-dependent python stuff - find "$(CURDIR)"/debian/mercurial-common/usr/lib \ - -name '*.so' ! -type d -delete , \ - -type d -empty -delete - cp contrib/hg-ssh "$(CURDIR)"/debian/mercurial-common/usr/bin - mkdir -p "$(CURDIR)"/debian/mercurial-common/usr/share/mercurial - cp contrib/hgk "$(CURDIR)"/debian/mercurial-common/usr/share/mercurial - mkdir -p "$(CURDIR)"/debian/mercurial-common/etc/mercurial/hgrc.d/ - cp contrib/debian/*.rc "$(CURDIR)"/debian/mercurial-common/etc/mercurial/hgrc.d/ - # completions - mkdir -p "$(CURDIR)"/debian/mercurial-common/usr/share/bash-completion/completions - cp contrib/bash_completion "$(CURDIR)"/debian/mercurial-common/usr/share/bash-completion/completions/hg - mkdir -p "$(CURDIR)"/debian/mercurial-common/usr/share/zsh/vendor-completions - cp contrib/zsh_completion "$(CURDIR)"/debian/mercurial-common/usr/share/zsh/vendor-completions/_hg - rm "$(CURDIR)"/debian/mercurial-common/usr/bin/hg
--- a/contrib/docker/centos5 Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -FROM centos:centos5 -RUN \ - sed -i 's/^mirrorlist/#mirrorlist/' /etc/yum.repos.d/*.repo && \ - sed -i 's/^#\(baseurl=\)http:\/\/mirror.centos.org\/centos/\1http:\/\/vault.centos.org/' /etc/yum.repos.d/*.repo && \ - sed -i 's/\$releasever/5.11/' /etc/yum.repos.d/*.repo - -RUN yum install -y \ - gcc \ - gettext \ - make \ - python-devel \ - python-docutils \ - rpm-build \ - tar - -# For creating repo meta data -RUN yum install -y \ - bzip2-devel \ - createrepo \ - ncurses-devel \ - openssl-devel \ - readline-devel \ - zlib-devel
--- a/contrib/docker/centos6 Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -FROM centos:centos6 -RUN yum install -y \ - gcc \ - gettext \ - make \ - python-devel \ - python-docutils \ - rpm-build \ - tar - -# For creating repo meta data -RUN yum install -y createrepo - -# For python -RUN yum install -y \ - bzip2-devel \ - ncurses-devel \ - openssl-devel \ - readline-devel \ - zlib-devel
--- a/contrib/docker/centos7 Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -FROM centos:centos7 -RUN yum install -y \ - gcc \ - gettext \ - make \ - python-devel \ - python-docutils \ - rpm-build \ - tar - -# For creating repo meta data -RUN yum install -y createrepo
--- a/contrib/docker/debian.template Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -FROM debian:__CODENAME__ -RUN apt-get update && apt-get install -y \ - build-essential \ - debhelper \ - devscripts \ - dh-python \ - less \ - python \ - python-all-dev \ - python-docutils \ - unzip \ - zip
--- a/contrib/docker/fedora20 Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -FROM fedora:20 -RUN yum install -y \ - gcc \ - gettext \ - make \ - python-devel \ - python-docutils \ - rpm-build - -# For creating repo meta data -RUN yum install -y createrepo
--- a/contrib/docker/fedora21 Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -FROM fedora:21 -RUN yum install -y \ - gcc \ - gettext \ - make \ - python-devel \ - python-docutils \ - rpm-build - -# For creating repo meta data -RUN yum install -y createrepo
--- a/contrib/docker/ubuntu.template Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -FROM ubuntu:__CODENAME__ -RUN apt-get update && apt-get install -y \ - build-essential \ - debhelper \ - devscripts \ - dh-python \ - less \ - python \ - python-all-dev \ - python-docutils \ - unzip \ - zip
--- a/contrib/dockerdeb Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -#!/bin/bash -eu - -. $(dirname $0)/dockerlib.sh -. $(dirname $0)/packagelib.sh - -BUILDDIR=$(dirname $0) -export ROOTDIR=$(cd $BUILDDIR/.. > /dev/null; pwd) - -checkdocker - -DISTID="$1" -CODENAME="$2" -PLATFORM="$1-$2" -shift; shift # extra params are passed to build process - -OUTPUTDIR=${OUTPUTDIR:=$ROOTDIR/packages/$PLATFORM} - -initcontainer $PLATFORM - -# debuild only appears to be able to save built debs etc to .., so we -# have to share the .. of the current directory with the docker -# container and hope it's writable. Whee. -dn=$(basename $PWD) - -if [ $(uname) = "Darwin" ] ; then - $DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \ - sh -c "cd /mnt/$dn && make clean && make local" -fi -$DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \ - sh -c "cd /mnt/$dn && DEB_BUILD_OPTIONS='${DEB_BUILD_OPTIONS:=}' contrib/builddeb --build --distid $DISTID --codename $CODENAME $@" -contrib/builddeb --cleanup --distid $DISTID --codename $CODENAME -if [ $(uname) = "Darwin" ] ; then - $DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \ - sh -c "cd /mnt/$dn && make clean" -fi
--- a/contrib/dockerlib.sh Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -#!/bin/sh -eu - -# This function exists to set up the DOCKER variable and verify that -# it's the binary we expect. It also verifies that the docker service -# is running on the system and we can talk to it. -function checkdocker() { - if which docker.io >> /dev/null 2>&1 ; then - DOCKER=docker.io - elif which docker >> /dev/null 2>&1 ; then - DOCKER=docker - else - echo "Error: docker must be installed" - exit 1 - fi - - $DOCKER -h 2> /dev/null | grep -q Jansens && { echo "Error: $DOCKER is the Docking System Tray - install docker.io instead"; exit 1; } - $DOCKER version | grep -Eq "^Client( version)?:" || { echo "Error: unexpected output from \"$DOCKER version\""; exit 1; } - $DOCKER version | grep -Eq "^Server( version)?:" || { echo "Error: could not get docker server version - check it is running and your permissions"; exit 1; } -} - -# Construct a container and leave its name in $CONTAINER for future use. -function initcontainer() { - [ "$1" ] || { echo "Error: platform name must be specified"; exit 1; } - - DFILE="$ROOTDIR/contrib/docker/$1" - [ -f "$DFILE" ] || { echo "Error: docker file $DFILE not found"; exit 1; } - - CONTAINER="hg-dockerrpm-$1" - DBUILDUSER=build - ( - cat $DFILE - if [ $(uname) = "Darwin" ] ; then - # The builder is using boot2docker on OS X, so we're going to - # *guess* the uid of the user inside the VM that is actually - # running docker. This is *very likely* to fail at some point. - echo RUN useradd $DBUILDUSER -u 1000 - else - echo RUN groupadd $DBUILDUSER -g `id -g` -o - echo RUN useradd $DBUILDUSER -u `id -u` -g $DBUILDUSER -o - fi - ) | $DOCKER build --build-arg http_proxy --build-arg https_proxy --tag $CONTAINER - -}
--- a/contrib/dockerrpm Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -#!/bin/bash -e - -. $(dirname $0)/dockerlib.sh - -BUILDDIR=$(dirname $0) -export ROOTDIR=$(cd $BUILDDIR/..; pwd) - -checkdocker - -PLATFORM="$1" -shift # extra params are passed to buildrpm - -initcontainer $PLATFORM - -RPMBUILDDIR=$ROOTDIR/packages/$PLATFORM -contrib/buildrpm --rpmbuilddir $RPMBUILDDIR --prepare $* - -DSHARED=/mnt/shared -$DOCKER run -e http_proxy -e https_proxy -u $DBUILDUSER --rm -v $RPMBUILDDIR:$DSHARED $CONTAINER \ - rpmbuild --define "_topdir $DSHARED" -ba $DSHARED/SPECS/mercurial.spec --clean - -$DOCKER run -e http_proxy -e https_proxy -u $DBUILDUSER --rm -v $RPMBUILDDIR:$DSHARED $CONTAINER \ - createrepo $DSHARED - -cat << EOF > $RPMBUILDDIR/mercurial.repo -# Place this file in /etc/yum.repos.d/mercurial.repo -[mercurial] -name=Mercurial packages for $PLATFORM -# baseurl=file://$RPMBUILDDIR/ -baseurl=http://hg.example.com/build/$PLATFORM/ -skip_if_unavailable=True -gpgcheck=0 -enabled=1 -EOF - -echo -echo "Build complete - results can be found in $RPMBUILDDIR"
--- a/contrib/fixpax.py Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -#!/usr/bin/env python -# fixpax - fix ownership in bdist_mpkg output -# -# Copyright 2015 Matt Mackall <mpm@selenic.com> -# -# This software may be used and distributed according to the terms of the -# MIT license (http://opensource.org/licenses/MIT) - -"""Set file ownership to 0 in an Archive.pax.gz. -Suitable for fixing files bdist_mpkg output: -*.mpkg/Contents/Packages/*.pkg/Contents/Archive.pax.gz -""" - -from __future__ import absolute_import, print_function -import gzip -import os -import sys - -def fixpax(iname, oname): - i = gzip.GzipFile(iname) - o = gzip.GzipFile(oname, "w") - - while True: - magic = i.read(6) - dev = i.read(6) - ino = i.read(6) - mode = i.read(6) - i.read(6) # uid - i.read(6) # gid - nlink = i.read(6) - rdev = i.read(6) - mtime = i.read(11) - namesize = i.read(6) - filesize = i.read(11) - name = i.read(int(namesize, 8)) - data = i.read(int(filesize, 8)) - - o.write(magic) - o.write(dev) - o.write(ino) - o.write(mode) - o.write("000000") - o.write("000000") - o.write(nlink) - o.write(rdev) - o.write(mtime) - o.write(namesize) - o.write(filesize) - o.write(name) - o.write(data) - - if name.startswith("TRAILER!!!"): - o.write(i.read()) - break - - o.close() - i.close() - -if __name__ == '__main__': - for iname in sys.argv[1:]: - print('fixing file ownership in %s' % iname) - oname = sys.argv[1] + '.tmp' - fixpax(iname, oname) - os.rename(oname, iname)
--- a/contrib/fuzz/Makefile Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/fuzz/Makefile Thu Jul 19 13:55:54 2018 -0400 @@ -1,40 +1,81 @@ +CC = clang +CXX = clang++ + +all: bdiff mpatch xdiff + +fuzzutil.o: fuzzutil.cc fuzzutil.h + $(CXX) $(CXXFLAGS) -g -O1 -fsanitize=fuzzer-no-link,address \ + -std=c++17 \ + -I../../mercurial -c -o fuzzutil.o fuzzutil.cc + +fuzzutil-oss-fuzz.o: fuzzutil.cc fuzzutil.h + $(CXX) $(CXXFLAGS) -std=c++17 \ + -I../../mercurial -c -o fuzzutil-oss-fuzz.o fuzzutil.cc + bdiff.o: ../../mercurial/bdiff.c - clang -g -O1 -fsanitize=fuzzer-no-link,address -c -o bdiff.o \ + $(CC) $(CFLAGS) -fsanitize=fuzzer-no-link,address -c -o bdiff.o \ ../../mercurial/bdiff.c -bdiff: bdiff.cc bdiff.o - clang -DHG_FUZZER_INCLUDE_MAIN=1 -g -O1 -fsanitize=fuzzer-no-link,address \ - -I../../mercurial bdiff.cc bdiff.o -o bdiff +bdiff: bdiff.cc bdiff.o fuzzutil.o + $(CXX) $(CXXFLAGS) -DHG_FUZZER_INCLUDE_MAIN=1 -g -O1 -fsanitize=fuzzer-no-link,address \ + -std=c++17 \ + -I../../mercurial bdiff.cc bdiff.o fuzzutil.o -o bdiff bdiff-oss-fuzz.o: ../../mercurial/bdiff.c - $$CC $$CFLAGS -c -o bdiff-oss-fuzz.o ../../mercurial/bdiff.c + $(CC) $(CFLAGS) -c -o bdiff-oss-fuzz.o ../../mercurial/bdiff.c + +bdiff_fuzzer: bdiff.cc bdiff-oss-fuzz.o fuzzutil-oss-fuzz.o + $(CXX) $(CXXFLAGS) -std=c++17 -I../../mercurial bdiff.cc \ + bdiff-oss-fuzz.o fuzzutil-oss-fuzz.o -lFuzzingEngine -o \ + $$OUT/bdiff_fuzzer + +mpatch.o: ../../mercurial/mpatch.c + $(CC) -g -O1 -fsanitize=fuzzer-no-link,address -c -o mpatch.o \ + ../../mercurial/mpatch.c -bdiff_fuzzer: bdiff.cc bdiff-oss-fuzz.o - $$CXX $$CXXFLAGS -std=c++11 -I../../mercurial bdiff.cc \ - bdiff-oss-fuzz.o -lFuzzingEngine -o $$OUT/bdiff_fuzzer +mpatch: CXXFLAGS += -std=c++17 +mpatch: mpatch.cc mpatch.o fuzzutil.o + $(CXX) $(CXXFLAGS) -DHG_FUZZER_INCLUDE_MAIN=1 -g -O1 -fsanitize=fuzzer-no-link,address \ + -I../../mercurial mpatch.cc mpatch.o fuzzutil.o -o mpatch + +mpatch-oss-fuzz.o: ../../mercurial/mpatch.c + $(CC) $(CFLAGS) -c -o mpatch-oss-fuzz.o ../../mercurial/mpatch.c + +mpatch_fuzzer: mpatch.cc mpatch-oss-fuzz.o fuzzutil-oss-fuzz.o + $(CXX) $(CXXFLAGS) -std=c++17 -I../../mercurial mpatch.cc \ + mpatch-oss-fuzz.o fuzzutil-oss-fuzz.o -lFuzzingEngine -o \ + $$OUT/mpatch_fuzzer + +mpatch_corpus.zip: + python mpatch_corpus.py $$OUT/mpatch_fuzzer_seed_corpus.zip x%.o: ../../mercurial/thirdparty/xdiff/x%.c ../../mercurial/thirdparty/xdiff/*.h - clang -g -O1 -fsanitize=fuzzer-no-link,address -c \ + $(CC) -g -O1 -fsanitize=fuzzer-no-link,address -c \ -o $@ \ $< -xdiff: xdiff.cc xdiffi.o xprepare.o xutils.o - clang -DHG_FUZZER_INCLUDE_MAIN=1 -g -O1 -fsanitize=fuzzer-no-link,address \ +xdiff: CXXFLAGS += -std=c++17 +xdiff: xdiff.cc xdiffi.o xprepare.o xutils.o fuzzutil.o + $(CXX) $(CXXFLAGS) -DHG_FUZZER_INCLUDE_MAIN=1 -g -O1 -fsanitize=fuzzer-no-link,address \ -I../../mercurial xdiff.cc \ - xdiffi.o xprepare.o xutils.o -o xdiff + xdiffi.o xprepare.o xutils.o fuzzutil.o -o xdiff fuzz-x%.o: ../../mercurial/thirdparty/xdiff/x%.c ../../mercurial/thirdparty/xdiff/*.h - $$CC $$CFLAGS -c \ + $(CC) $(CFLAGS) -c \ -o $@ \ $< -xdiff_fuzzer: xdiff.cc fuzz-xdiffi.o fuzz-xprepare.o fuzz-xutils.o - $$CXX $$CXXFLAGS -std=c++11 -I../../mercurial xdiff.cc \ - fuzz-xdiffi.o fuzz-xprepare.o fuzz-xutils.o \ +xdiff_fuzzer: xdiff.cc fuzz-xdiffi.o fuzz-xprepare.o fuzz-xutils.o fuzzutil-oss-fuzz.o + $(CXX) $(CXXFLAGS) -std=c++17 -I../../mercurial xdiff.cc \ + fuzz-xdiffi.o fuzz-xprepare.o fuzz-xutils.o fuzzutil-oss-fuzz.o \ -lFuzzingEngine -o $$OUT/xdiff_fuzzer -all: bdiff xdiff +clean: + $(RM) *.o *_fuzzer \ + bdiff \ + mpatch \ + xdiff -oss-fuzz: bdiff_fuzzer xdiff_fuzzer +oss-fuzz: bdiff_fuzzer mpatch_fuzzer mpatch_corpus.zip xdiff_fuzzer -.PHONY: all oss-fuzz +.PHONY: all clean oss-fuzz
--- a/contrib/fuzz/bdiff.cc Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/fuzz/bdiff.cc Thu Jul 19 13:55:54 2018 -0400 @@ -6,30 +6,25 @@ * This software may be used and distributed according to the terms of * the GNU General Public License, incorporated herein by reference. */ +#include <memory> #include <stdlib.h> +#include "fuzzutil.h" + extern "C" { #include "bdiff.h" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (!Size) { + auto maybe_inputs = SplitInputs(Data, Size); + if (!maybe_inputs) { return 0; } - // figure out a random point in [0, Size] to split our input. - size_t split = Data[0] / 255.0 * Size; - - // left input to diff is data[1:split] - const uint8_t *left = Data + 1; - // which has len split-1 - size_t left_size = split - 1; - // right starts at the next byte after left ends - const uint8_t *right = left + left_size; - size_t right_size = Size - split; + auto inputs = std::move(maybe_inputs.value()); struct bdiff_line *a, *b; - int an = bdiff_splitlines((const char *)left, split - 1, &a); - int bn = bdiff_splitlines((const char *)right, right_size, &b); + int an = bdiff_splitlines(inputs.left.get(), inputs.left_size, &a); + int bn = bdiff_splitlines(inputs.right.get(), inputs.right_size, &b); struct bdiff_hunk l; bdiff_diff(a, an, b, bn, &l); free(a);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/fuzz/fuzzutil.cc Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,27 @@ +#include "fuzzutil.h" + +#include <cstring> +#include <utility> + +contrib::optional<two_inputs> SplitInputs(const uint8_t *Data, size_t Size) +{ + if (!Size) { + return contrib::nullopt; + } + // figure out a random point in [0, Size] to split our input. + size_t left_size = (Data[0] / 255.0) * (Size - 1); + + // Copy inputs to new allocations so if bdiff over-reads + // AddressSanitizer can detect it. + std::unique_ptr<char[]> left(new char[left_size]); + std::memcpy(left.get(), Data + 1, left_size); + // right starts at the next byte after left ends + size_t right_size = Size - (left_size + 1); + std::unique_ptr<char[]> right(new char[right_size]); + std::memcpy(right.get(), Data + 1 + left_size, right_size); + LOG(2) << "inputs are " << left_size << " and " << right_size + << " bytes" << std::endl; + two_inputs result = {std::move(right), right_size, std::move(left), + left_size}; + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/fuzz/fuzzutil.h Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,47 @@ +#ifndef CONTRIB_FUZZ_FUZZUTIL_H +#define CONTRIB_FUZZ_FUZZUTIL_H +#include <iostream> +#include <memory> +#include <stdint.h> + +/* Try and use std::optional, but failing that assume we'll have a + * workable https://abseil.io/ install on the include path to get + * their backport of std::optional. */ +#ifdef __has_include +#if __has_include(<optional>) && __cplusplus >= 201703L +#include <optional> +#define CONTRIB_FUZZ_HAVE_STD_OPTIONAL +#endif +#endif +#ifdef CONTRIB_FUZZ_HAVE_STD_OPTIONAL +namespace contrib +{ +using std::nullopt; +using std::optional; +} /* namespace contrib */ +#else +#include "third_party/absl/types/optional.h" +namespace contrib +{ +using absl::nullopt; +using absl::optional; +} /* namespace contrib */ +#endif + +/* set DEBUG to 1 for a few debugging prints, or 2 for a lot */ +#define DEBUG 0 +#define LOG(level) \ + if (level <= DEBUG) \ + std::cout + +struct two_inputs { + std::unique_ptr<char[]> right; + size_t right_size; + std::unique_ptr<char[]> left; + size_t left_size; +}; + +/* Split a non-zero-length input into two inputs. */ +contrib::optional<two_inputs> SplitInputs(const uint8_t *Data, size_t Size); + +#endif /* CONTRIB_FUZZ_FUZZUTIL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/fuzz/mpatch.cc Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,122 @@ +/* + * mpatch.cc - fuzzer harness for mpatch.c + * + * Copyright 2018, Google Inc. + * + * This software may be used and distributed according to the terms of + * the GNU General Public License, incorporated herein by reference. + */ +#include <iostream> +#include <memory> +#include <stdint.h> +#include <stdlib.h> +#include <vector> + +#include "fuzzutil.h" + +// To avoid having too many OOMs from the fuzzer infrastructure, we'll +// skip patch application if the resulting fulltext would be bigger +// than 10MiB. +#define MAX_OUTPUT_SIZE 10485760 + +extern "C" { +#include "bitmanipulation.h" +#include "mpatch.h" + +struct mpatchbin { + std::unique_ptr<char[]> data; + size_t len; +}; + +static mpatch_flist *getitem(void *vbins, ssize_t pos) +{ + std::vector<mpatchbin> *bins = (std::vector<mpatchbin> *)vbins; + const mpatchbin &bin = bins->at(pos + 1); + struct mpatch_flist *res; + LOG(2) << "mpatch_decode " << bin.len << std::endl; + if (mpatch_decode(bin.data.get(), bin.len, &res) < 0) + return NULL; + return res; +} + +// input format: +// u8 number of inputs +// one u16 for each input, its length +// the inputs +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + if (!Size) { + return 0; + } + // First byte of data is how many texts we expect, first text + // being the base the rest being the deltas. + ssize_t numtexts = Data[0]; + if (numtexts < 2) { + // No point if we don't have at least a base text and a delta... + return 0; + } + // Each text will be described by a byte for how long it + // should be, so give up if we don't have enough. + if ((Size - 1) < (numtexts * 2)) { + return 0; + } + size_t consumed = 1 + (numtexts * 2); + LOG(2) << "input contains " << Size << std::endl; + LOG(2) << numtexts << " texts, consuming " << consumed << std::endl; + std::vector<mpatchbin> bins; + bins.reserve(numtexts); + for (int i = 0; i < numtexts; ++i) { + mpatchbin bin; + size_t nthsize = getbeuint16((char *)Data + 1 + (2 * i)); + LOG(2) << "text " << i << " is " << nthsize << std::endl; + char *start = (char *)Data + consumed; + consumed += nthsize; + if (consumed > Size) { + LOG(2) << "ran out of data, consumed " << consumed + << " of " << Size << std::endl; + return 0; + } + bin.len = nthsize; + bin.data.reset(new char[nthsize]); + memcpy(bin.data.get(), start, nthsize); + bins.push_back(std::move(bin)); + } + LOG(2) << "mpatch_flist" << std::endl; + struct mpatch_flist *patch = + mpatch_fold(&bins, getitem, 0, numtexts - 1); + if (!patch) { + return 0; + } + LOG(2) << "mpatch_calcsize" << std::endl; + ssize_t outlen = mpatch_calcsize(bins[0].len, patch); + LOG(2) << "outlen " << outlen << std::endl; + if (outlen < 0 || outlen > MAX_OUTPUT_SIZE) { + goto cleanup; + } + { + char *dest = (char *)malloc(outlen); + LOG(2) << "expecting " << outlen << " total bytes at " + << (void *)dest << std::endl; + mpatch_apply(dest, bins[0].data.get(), bins[0].len, patch); + free(dest); + LOG(1) << "applied a complete patch" << std::endl; + } +cleanup: + mpatch_lfree(patch); + return 0; +} + +#ifdef HG_FUZZER_INCLUDE_MAIN +int main(int argc, char **argv) +{ + // One text, one patch. + const char data[] = "\x02\x00\0x1\x00\x0d" + // base text + "a" + // binary delta that will append a single b + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01b"; + return LLVMFuzzerTestOneInput((const uint8_t *)data, 19); +} +#endif + +} // extern "C"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/fuzz/mpatch_corpus.py Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,345 @@ +from __future__ import absolute_import, print_function + +import argparse +import struct +import zipfile + +from mercurial import ( + hg, + ui as uimod, +) + +ap = argparse.ArgumentParser() +ap.add_argument("out", metavar="some.zip", type=str, nargs=1) +args = ap.parse_args() + +class deltafrag(object): + def __init__(self, start, end, data): + self.start = start + self.end = end + self.data = data + + def __str__(self): + return struct.pack( + ">lll", self.start, self.end, len(self.data)) + self.data + +class delta(object): + def __init__(self, frags): + self.frags = frags + + def __str__(self): + return ''.join(str(f) for f in self.frags) + +class corpus(object): + + def __init__(self, base, deltas): + self.base = base + self.deltas = deltas + + def __str__(self): + deltas = [str(d) for d in self.deltas] + parts = ( + [ + struct.pack(">B", len(deltas) + 1), + struct.pack(">H", len(self.base)), + ] + + [struct.pack(">H", len(d)) for d in deltas] + + [self.base] + + deltas + ) + return "".join(parts) + +with zipfile.ZipFile(args.out[0], "w", zipfile.ZIP_STORED) as zf: + # Manually constructed entries + zf.writestr( + "one_delta_applies", + str(corpus('a', [delta([deltafrag(0, 1, 'b')])])) + ) + zf.writestr( + "one_delta_starts_late", + str(corpus('a', [delta([deltafrag(3, 1, 'b')])])) + ) + zf.writestr( + "one_delta_ends_late", + str(corpus('a', [delta([deltafrag(0, 20, 'b')])])) + ) + + try: + # Generated from repo data + r = hg.repository(uimod.ui(), '../..') + fl = r.file('mercurial/manifest.py') + rl = getattr(fl, '_revlog', fl) + bins = rl._chunks(rl._deltachain(10)[0]) + zf.writestr('manifest_py_rev_10', + str(corpus(bins[0], bins[1:]))) + except: # skip this, so no re-raises + print('skipping seed file from repo data') + # Automatically discovered by running the fuzzer + zf.writestr( + "mpatch_decode_old_overread", "\x02\x00\x00\x00\x02\x00\x00\x00" + ) + # https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=8876 + zf.writestr( + "mpatch_ossfuzz_getbe32_ubsan", + "\x02\x00\x00\x00\x0c \xff\xff\xff\xff ") + zf.writestr( + "mpatch_apply_over_memcpy", + '\x13\x01\x00\x05\xd0\x00\x00\x00\x00\x00\x00\x00\x00\n \x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8c\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00A\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\x18' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfa\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x94\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfa\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00se\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00')
--- a/contrib/fuzz/xdiff.cc Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/fuzz/xdiff.cc Thu Jul 19 13:55:54 2018 -0400 @@ -10,6 +10,8 @@ #include <inttypes.h> #include <stdlib.h> +#include "fuzzutil.h" + extern "C" { int hunk_consumer(long a1, long a2, long b1, long b2, void *priv) @@ -20,21 +22,17 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (!Size) { + auto maybe_inputs = SplitInputs(Data, Size); + if (!maybe_inputs) { return 0; } - // figure out a random point in [0, Size] to split our input. - size_t split = Data[0] / 255.0 * Size; - + auto inputs = std::move(maybe_inputs.value()); mmfile_t a, b; - // `a` input to diff is data[1:split] - a.ptr = (char *)Data + 1; - // which has len split-1 - a.size = split - 1; - // `b` starts at the next byte after `a` ends - b.ptr = a.ptr + a.size; - b.size = Size - split; + a.ptr = inputs.left.get(); + a.size = inputs.left_size; + b.ptr = inputs.right.get(); + b.size = inputs.right_size; xpparam_t xpp = { XDF_INDENT_HEURISTIC, /* flags */ };
--- a/contrib/genosxversion.py Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/genosxversion.py Thu Jul 19 13:55:54 2018 -0400 @@ -117,9 +117,9 @@ return with open(opts.versionfile) as f: for l in f: - if l.startswith('version = '): + if l.startswith('version = b'): # version number is entire line minus the quotes - ver = l[len('version = ') + 1:-2] + ver = l[len('version = b') + 1:-2] break if opts.paranoid: print(paranoidver(ver))
--- a/contrib/hg-ssh Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/hg-ssh Thu Jul 19 13:55:54 2018 -0400 @@ -39,10 +39,14 @@ from mercurial import ( dispatch, + pycompat, ui as uimod, ) def main(): + # Prevent insertion/deletion of CRs + dispatch.initstdio() + cwd = os.getcwd() readonly = False args = sys.argv[1:] @@ -66,15 +70,15 @@ path = cmdargv[2] repo = os.path.normpath(os.path.join(cwd, os.path.expanduser(path))) if repo in allowed_paths: - cmd = ['-R', repo, 'serve', '--stdio'] + cmd = [b'-R', pycompat.fsencode(repo), b'serve', b'--stdio'] req = dispatch.request(cmd) if readonly: if not req.ui: req.ui = uimod.ui.load() - req.ui.setconfig('hooks', 'pretxnopen.hg-ssh', - 'python:__main__.rejectpush', 'hg-ssh') - req.ui.setconfig('hooks', 'prepushkey.hg-ssh', - 'python:__main__.rejectpush', 'hg-ssh') + req.ui.setconfig(b'hooks', b'pretxnopen.hg-ssh', + b'python:__main__.rejectpush', b'hg-ssh') + req.ui.setconfig(b'hooks', b'prepushkey.hg-ssh', + b'python:__main__.rejectpush', b'hg-ssh') dispatch.dispatch(req) else: sys.stderr.write('Illegal repository "%s"\n' % repo) @@ -84,7 +88,7 @@ sys.exit(255) def rejectpush(ui, **kwargs): - ui.warn(("Permission denied\n")) + ui.warn((b"Permission denied\n")) # mercurial hooks use unix process conventions for hook return values # so a truthy return means failure return True
--- a/contrib/linux-wheel-centos5-blacklist Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -test-convert-git.t -test-subrepo-git.t -test-patchbomb-tls.t
--- a/contrib/macosx/Readme.html Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> -<!-- This is the second screen displayed during the install. --> -<html> -<head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> - <meta http-equiv="Content-Style-Type" content="text/css"> - <title>Read Me - Important Information</title> - <style type="text/css"> - p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Helvetica} - p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px} - p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica} - p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; color: #000fed} - span.s1 {text-decoration: underline} - span.s2 {font: 12.0px Courier} - </style> -</head> -<body> -<p class="p1"><b>Before you install</b></p> -<p class="p2"><br></p> -<p class="p3">This is an OS X version of Mercurial that depends on the default Python installation.</p> -<p class="p2"><br></p> -<p class="p1"><b>After you install</b></p> -<p class="p2"><br></p> -<p class="p3">This package installs the <span class="s2">hg</span> executable as <span class="s2">/usr/local/bin/hg</span>. See <span class="s2">hg debuginstall</span> for more info on file locations.</p> -<p class="p2"><br></p> -<p class="p1"><b>Documentation</b></p> -<p class="p2"><br></p> -<p class="p3">Visit the <a href="https://mercurial-scm.org/">Mercurial web site and wiki</a></p> -<p class="p2"><br></p> -<p class="p3">There's also a free book, <a href="https://book.mercurial-scm.org/">Distributed revision control with Mercurial</a></p> -<p class="p2"><br></p> -<p class="p1"><b>Reporting problems</b></p> -<p class="p2"><br></p> -<p class="p3">If you run into any problems, please file a bug online:</p> -<p class="p3"><a href="https://bz.mercurial-scm.org/">https://bz.mercurial-scm.org/</a></p> -</body> -</html>
--- a/contrib/macosx/Welcome.html Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> -<!-- This is the first screen displayed during the install. --> -<html> -<head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> - <meta http-equiv="Content-Style-Type" content="text/css"> - <title></title> - <style type="text/css"> - p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Helvetica} - p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px} - </style> -</head> -<body> -<p class="p1">This is a prepackaged release of <a href="https://mercurial-scm.org/">Mercurial</a> for Mac OS X.</p> -<p class="p2"><br></p> -<br> -<p> -Please be sure to read the latest <a href="https://mercurial-scm.org/wiki/WhatsNew">release notes</a>.</p> -</body> -</html>
--- a/contrib/macosx/distribution.xml Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?> -<installer-gui-script minSpecVersion="1"> - <title>Mercurial SCM</title> - <organization>org.mercurial-scm</organization> - <options customize="never" require-scripts="false" rootVolumeOnly="true" /> - <welcome file="Welcome.html" mime-type="text/html" /> - <license file="../../COPYING" mime-type="text/plain" /> - <readme file="Readme.html" mime-type="text/html" /> - <pkg-ref id="org.mercurial-scm.mercurial" - version="0" - auth="root" - onConclusion="none">mercurial.pkg</pkg-ref> - <choices-outline> - <line choice="org.mercurial-scm.mercurial"/> - </choices-outline> - <choice id="org.mercurial-scm.mercurial" visible="false"> - <pkg-ref id="org.mercurial-scm.mercurial"/> - </choice> -</installer-gui-script>
--- a/contrib/mercurial.spec Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,164 +0,0 @@ -%global emacs_lispdir %{_datadir}/emacs/site-lisp - -%define withpython %{nil} - -%if "%{?withpython}" - -%global pythonver %{withpython} -%global pythonname Python-%{withpython} -%global docutilsname docutils-0.14 -%global docutilsmd5 c53768d63db3873b7d452833553469de -%global pythonhg python-hg -%global hgpyprefix /opt/%{pythonhg} -# byte compilation will fail on some some Python /test/ files -%global _python_bytecompile_errors_terminate_build 0 - -%else - -%global pythonver %(python -c 'import sys;print ".".join(map(str, sys.version_info[:2]))') - -%endif - -Summary: A fast, lightweight Source Control Management system -Name: mercurial -Version: snapshot -Release: 0 -License: GPLv2+ -Group: Development/Tools -URL: https://mercurial-scm.org/ -Source0: %{name}-%{version}-%{release}.tar.gz -%if "%{?withpython}" -Source1: %{pythonname}.tgz -Source2: %{docutilsname}.tar.gz -%endif -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root - -BuildRequires: make, gcc, gettext -%if "%{?withpython}" -BuildRequires: readline-devel, openssl-devel, ncurses-devel, zlib-devel, bzip2-devel -%else -BuildRequires: python >= 2.7, python-devel, python-docutils >= 0.5 -Requires: python >= 2.7 -%endif -# The hgk extension uses the wish tcl interpreter, but we don't enforce it -#Requires: tk - -%description -Mercurial is a fast, lightweight source control management system designed -for efficient handling of very large distributed projects. - -%prep - -%if "%{?withpython}" -%setup -q -n mercurial-%{version}-%{release} -a1 -a2 -# despite the comments in cgi.py, we do this to prevent rpmdeps from picking /usr/local/bin/python up -sed -i '1c#! /usr/bin/env python' %{pythonname}/Lib/cgi.py -%else -%setup -q -n mercurial-%{version}-%{release} -%endif - -%build - -%if "%{?withpython}" - -PYPATH=$PWD/%{pythonname} -cd $PYPATH -./configure --prefix=%{hgpyprefix} -make all %{?_smp_mflags} -cd - - -cd %{docutilsname} -LD_LIBRARY_PATH=$PYPATH $PYPATH/python setup.py build -cd - - -# verify Python environment -LD_LIBRARY_PATH=$PYPATH PYTHONPATH=$PWD/%{docutilsname} $PYPATH/python -c 'import sys, zlib, bz2, ssl, curses, readline' - -# set environment for make -export PATH=$PYPATH:$PATH -export LD_LIBRARY_PATH=$PYPATH -export CFLAGS="-L $PYPATH" -export PYTHONPATH=$PWD/%{docutilsname} - -%endif - -make all -make -C contrib/chg - -%install -rm -rf $RPM_BUILD_ROOT - -%if "%{?withpython}" - -PYPATH=$PWD/%{pythonname} -cd $PYPATH -make install DESTDIR=$RPM_BUILD_ROOT -# these .a are not necessary and they are readonly and strip fails - kill them! -rm -f %{buildroot}%{hgpyprefix}/lib/{,python2.*/config}/libpython2.*.a -cd - - -cd %{docutilsname} -LD_LIBRARY_PATH=$PYPATH $PYPATH/python setup.py install --root="$RPM_BUILD_ROOT" -cd - - -PATH=$PYPATH:$PATH LD_LIBRARY_PATH=$PYPATH make install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{hgpyprefix} MANDIR=%{_mandir} -mkdir -p $RPM_BUILD_ROOT%{_bindir} -( cd $RPM_BUILD_ROOT%{_bindir}/ && ln -s ../..%{hgpyprefix}/bin/hg . ) -( cd $RPM_BUILD_ROOT%{_bindir}/ && ln -s ../..%{hgpyprefix}/bin/python2.? %{pythonhg} ) - -%else - -make install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{_prefix} MANDIR=%{_mandir} - -%endif - -install -m 755 contrib/chg/chg $RPM_BUILD_ROOT%{_bindir}/ -install -m 755 contrib/hgk $RPM_BUILD_ROOT%{_bindir}/ -install -m 755 contrib/hg-ssh $RPM_BUILD_ROOT%{_bindir}/ - -bash_completion_dir=$RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d -mkdir -p $bash_completion_dir -install -m 644 contrib/bash_completion $bash_completion_dir/mercurial.sh - -zsh_completion_dir=$RPM_BUILD_ROOT%{_datadir}/zsh/site-functions -mkdir -p $zsh_completion_dir -install -m 644 contrib/zsh_completion $zsh_completion_dir/_mercurial - -mkdir -p $RPM_BUILD_ROOT%{emacs_lispdir} -install -m 644 contrib/mercurial.el $RPM_BUILD_ROOT%{emacs_lispdir}/ -install -m 644 contrib/mq.el $RPM_BUILD_ROOT%{emacs_lispdir}/ - -mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/mercurial/hgrc.d - -%clean -rm -rf $RPM_BUILD_ROOT - -%files -%defattr(-,root,root,-) -%doc CONTRIBUTORS COPYING doc/README doc/hg*.txt doc/hg*.html *.cgi contrib/*.fcgi -%doc %attr(644,root,root) %{_mandir}/man?/hg* -%doc %attr(644,root,root) contrib/*.svg -%dir %{_datadir}/zsh/ -%dir %{_datadir}/zsh/site-functions/ -%{_datadir}/zsh/site-functions/_mercurial -%dir %{_datadir}/emacs/site-lisp/ -%{_datadir}/emacs/site-lisp/mercurial.el -%{_datadir}/emacs/site-lisp/mq.el -%{_bindir}/hg -%{_bindir}/chg -%{_bindir}/hgk -%{_bindir}/hg-ssh -%dir %{_sysconfdir}/bash_completion.d/ -%config(noreplace) %{_sysconfdir}/bash_completion.d/mercurial.sh -%dir %{_sysconfdir}/mercurial -%dir %{_sysconfdir}/mercurial/hgrc.d -%if "%{?withpython}" -%{_bindir}/%{pythonhg} -%{hgpyprefix} -%else -%{_libdir}/python%{pythonver}/site-packages/%{name}-*-py%{pythonver}.egg-info -%{_libdir}/python%{pythonver}/site-packages/%{name} -%{_libdir}/python%{pythonver}/site-packages/hgext -%{_libdir}/python%{pythonver}/site-packages/hgext3rd -%{_libdir}/python%{pythonver}/site-packages/hgdemandimport -%endif
--- a/contrib/packagelib.sh Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -# Extract version number into 4 parts, some of which may be empty: -# -# version: the numeric part of the most recent tag. Will always look like 1.3. -# -# type: if an rc build, "rc", otherwise empty -# -# distance: the distance from the nearest tag, or empty if built from a tag -# -# node: the node|short hg was built from, or empty if built from a tag -gethgversion() { - export HGRCPATH= - export HGPLAIN= - - make cleanbutpackages - make local PURE=--pure - HG="$PWD/hg" - - "$HG" version > /dev/null || { echo 'abort: hg version failed!'; exit 1 ; } - - hgversion=`LANGUAGE=C "$HG" version | sed -ne 's/.*(version \(.*\))$/\1/p'` - - if echo $hgversion | grep + > /dev/null 2>&1 ; then - tmp=`echo $hgversion | cut -d+ -f 2` - hgversion=`echo $hgversion | cut -d+ -f 1` - distance=`echo $tmp | cut -d- -f 1` - node=`echo $tmp | cut -d- -f 2` - else - distance='' - node='' - fi - if echo $hgversion | grep -- '-' > /dev/null 2>&1; then - version=`echo $hgversion | cut -d- -f1` - type=`echo $hgversion | cut -d- -f2` - else - version=$hgversion - type='' - fi -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/Makefile Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,144 @@ +$(eval HGROOT := $(shell cd ../..; pwd)) + +DEBIAN_CODENAMES := \ + jessie \ + stretch \ + buster + +UBUNTU_CODENAMES := \ + trusty \ + xenial \ + artful \ + bionic \ + +FEDORA_RELEASES := \ + 20 \ + 21 \ + 28 + +CENTOS_RELEASES := \ + 5 \ + 6 \ + 7 + +# Build a Python for these CentOS releases. +CENTOS_WITH_PYTHON_RELEASES := 5 6 + +help: + @echo 'Packaging Make Targets' + @echo '' + @echo 'docker-centos{$(strip $(CENTOS_RELEASES))}' + @echo ' Build an RPM for a specific CentOS version using Docker.' + @echo '' + @echo 'docker-debian-{$(strip $(DEBIAN_CODENAMES))}' + @echo ' Build Debian packages specific to a Debian distro using Docker.' + @echo '' + @echo 'docker-fedora{$(strip $(FEDORA_RELEASES))}' + @echo ' Build an RPM for a specific Fedora version using Docker.' + @echo '' + @echo 'docker-ubuntu-{$(strip $(UBUNTU_CODENAMES))}' + @echo ' Build Debian package specific to an Ubuntu distro using Docker.' + @echo '' + @echo 'docker-ubuntu-{$(strip $(UBUNTU_CODENAMES))}-ppa' + @echo ' Build a source-only Debian package specific to an Ubuntu distro' + @echo ' using Docker.' + @echo '' + @echo 'linux-wheels' + @echo ' Build Linux manylinux wheels using Docker.' + @echo '' + @echo 'linux-wheels-{x86_64, i686}' + @echo ' Build Linux manylinux wheels for a specific architecture using Docker' + @echo '' + @echo 'deb' + @echo ' Build a Debian package locally targeting the current system' + @echo '' + @echo 'ppa' + @echo ' Build a Debian source package locally targeting the current system' + @echo '' + @echo 'centos{$(strip $(CENTOS_RELEASES))}' + @echo ' Build an RPM for a specific CentOS version locally' + @echo '' + @echo 'fedora{$(strip $(FEDORA_RELEASES))}' + @echo ' Build an RPM for a specific Fedora version locally' + +.PHONY: help + +.PHONY: deb +deb: + ./builddeb + +.PHONY: ppa +ppa: + ./builddeb --source-only + +# Debian targets. +define debian_targets = +.PHONY: docker-debian-$(1) +docker-debian-$(1): + ./dockerdeb debian $(1) + +endef + +$(foreach codename,$(DEBIAN_CODENAMES),$(eval $(call debian_targets,$(codename)))) + +# Ubuntu targets. +define ubuntu_targets = +.PHONY: docker-ubuntu-$(1) +docker-ubuntu-$(1): + ./dockerdeb ubuntu $(1) + +.PHONY: docker-ubuntu-$(1)-ppa +docker-ubuntu-$(1)-ppa: + ./dockerdeb ubuntu $(1) --source-only + +endef + +$(foreach codename,$(UBUNTU_CODENAMES),$(eval $(call ubuntu_targets,$(codename)))) + +# Fedora targets. +define fedora_targets +.PHONY: fedora$(1) +fedora$(1): + mkdir -p $$(HGROOT)/packages/fedora$(1) + ./buildrpm + cp $$(HGROOT)/contrib/packaging/rpmbuild/RPMS/*/* $$(HGROOT)/packages/fedora$(1) + cp $$(HGROOT)/contrib/packaging/rpmbuild/SRPMS/* $$(HGROOT)/packages/fedora$(1) + rm -rf $(HGROOT)/rpmbuild + +.PHONY: docker-fedora$(1) +docker-fedora$(1): + mkdir -p $$(HGROOT)/packages/fedora$(1) + ./dockerrpm fedora$(1) + +endef + +$(foreach release,$(FEDORA_RELEASES),$(eval $(call fedora_targets,$(release)))) + +# CentOS targets. +define centos_targets +.PHONY: centos$(1) +centos$(1): + mkdir -p $$(HGROOT)/packages/centos$(1) + ./buildrpm $$(if $$(filter $(1),$$(CENTOS_WITH_PYTHON_RELEASES)),--withpython) + cp $$(HGROOT)/rpmbuild/RPMS/*/* $$(HGROOT)/packages/centos$(1) + cp $$(HGROOT)/rpmbuild/SRPMS/* $$(HGROOT)/packages/centos$(1) + +.PHONY: docker-centos$(1) +docker-centos$(1): + mkdir -p $$(HGROOT)/packages/centos$(1) + ./dockerrpm centos$(1) $$(if $$(filter $(1),$$(CENTOS_WITH_PYTHON_RELEASES)),--withpython) + +endef + +$(foreach release,$(CENTOS_RELEASES),$(eval $(call centos_targets,$(release)))) + +.PHONY: linux-wheels +linux-wheels: linux-wheels-x86_64 linux-wheels-i686 + +.PHONY: linux-wheels-x86_64 +linux-wheels-x86_64: + docker run -e "HGTEST_JOBS=$(shell nproc)" --rm -ti -v `pwd`/../..:/src quay.io/pypa/manylinux1_x86_64 /src/contrib/packaging/build-linux-wheels.sh + +.PHONY: linux-wheels-i686 +linux-wheels-i686: + docker run -e "HGTEST_JOBS=$(shell nproc)" --rm -ti -v `pwd`/../..:/src quay.io/pypa/manylinux1_i686 linux32 /src/contrib/packaging/build-linux-wheels.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/build-linux-wheels.sh Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,34 @@ +#!/bin/bash +# This file is directly inspired by +# https://github.com/pypa/python-manylinux-demo/blob/master/travis/build-wheels.sh +set -e -x + +PYTHON_TARGETS=$(ls -d /opt/python/cp27*/bin) + +# Create an user for the tests +useradd hgbuilder + +# Bypass uid/gid problems +cp -R /src /io && chown -R hgbuilder:hgbuilder /io + +# Compile wheels for Python 2.X +for PYBIN in $PYTHON_TARGETS; do + "${PYBIN}/pip" wheel /io/ -w wheelhouse/ +done + +# Bundle external shared libraries into the wheels with +# auditwheel (https://github.com/pypa/auditwheel) repair. +# It also fix the ABI tag on the wheel making it pip installable. +for whl in wheelhouse/*.whl; do + auditwheel repair "$whl" -w /src/wheelhouse/ +done + +# Install packages and run the tests for all Python versions +cd /io/tests/ + +for PYBIN in $PYTHON_TARGETS; do + # Install mercurial wheel as root + "${PYBIN}/pip" install mercurial --no-index -f /src/wheelhouse + # But run tests as hgbuilder user (non-root) + su hgbuilder -c "\"${PYBIN}/python\" /io/tests/run-tests.py --with-hg=\"${PYBIN}/hg\" --blacklist=/io/contrib/packaging/linux-wheel-centos5-blacklist" +done
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/builddeb Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,106 @@ +#!/bin/sh -e +# +# Build a Mercurial debian package from the current repo +# +# Tested on Jessie (stable as of original script authoring.) + +. $(dirname $0)/packagelib.sh + +ROOTDIR=$(cd $(dirname $0)/../.. > /dev/null; pwd) + +BUILD=1 +CLEANUP=1 +DISTID=`(lsb_release -is 2> /dev/null | tr '[:upper:]' '[:lower:]') || echo debian` +CODENAME=`lsb_release -cs 2> /dev/null || echo unknown` +DEBFLAGS=-b +while [ "$1" ]; do + case "$1" in + --distid ) + shift + DISTID="$1" + shift + ;; + --codename ) + shift + CODENAME="$1" + shift + ;; + --cleanup ) + shift + BUILD= + ;; + --build ) + shift + CLEANUP= + ;; + --source-only ) + shift + DEBFLAGS=-S + ;; + * ) + echo "Invalid parameter $1!" 1>&2 + exit 1 + ;; + esac +done + +trap "if [ '$CLEANUP' ] ; then rm -r '$PWD/debian' ; fi" EXIT + +set -u + +if [ ! -d .hg ]; then + echo 'You are not inside a Mercurial repository!' 1>&2 + exit 1 +fi + +gethgversion +debver="$version" +if [ -n "$type" ] ; then + debver="$debver~$type" +fi +if [ -n "$distance" ] ; then + debver="$debver+$distance-$CODENAME-$node" +elif [ "$DEBFLAGS" = "-S" ] ; then + # for building a ppa (--source-only) for a release (distance == 0), we need + # to version the distroseries so that we can upload to launchpad + debver="$debver~${CODENAME}1" +fi + +control=debian/control +changelog=debian/changelog + +if [ "$BUILD" ]; then + if [ -d debian ] ; then + echo "Error! debian control directory already exists!" + exit 1 + fi + + cp -r "$ROOTDIR"/contrib/packaging/debian debian + + sed -i.tmp "s/__VERSION__/$debver/" $changelog + sed -i.tmp "s/__DATE__/$(date --rfc-2822)/" $changelog + sed -i.tmp "s/__CODENAME__/$CODENAME/" $changelog + rm $changelog.tmp + + # remove the node from the version string + SRCFILE="mercurial_$(echo $debver | sed "s,-$node,,").orig.tar.gz" + "$ROOTDIR/hg" archive $SRCFILE + mv $SRCFILE .. + debuild -us -uc -i -I $DEBFLAGS + if [ $? != 0 ]; then + echo 'debuild failed!' + exit 1 + fi + +fi +if [ "$CLEANUP" ] ; then + echo + OUTPUTDIR=${OUTPUTDIR:=packages/$DISTID-$CODENAME} + mkdir -p "$OUTPUTDIR" + find ../mercurial*.deb ../mercurial_*.build ../mercurial_*.changes \ + ../mercurial*.dsc ../mercurial*.gz \ + -type f -newer $control -print0 2>/dev/null | \ + xargs -Inarf -0 mv narf "$OUTPUTDIR" + echo "Built packages for $debver:" + find "$OUTPUTDIR" -type f -newer $control -name '*.deb' +fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/buildrpm Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,162 @@ +#!/bin/bash -e +# +# Build a Mercurial RPM from the current repo +# +# Tested on +# - Fedora 20 +# - CentOS 5 +# - centOS 6 + +. $(dirname $0)/packagelib.sh + +BUILD=1 +RPMBUILDDIR="$PWD/rpmbuild" + +while [ "$1" ]; do + case "$1" in + --prepare ) + shift + BUILD= + ;; + --withpython | --with-python) + shift + PYTHONVER=2.7.14 + PYTHONMD5=cee2e4b33ad3750da77b2e85f2f8b724 + ;; + --rpmbuilddir ) + shift + RPMBUILDDIR="$1" + shift + ;; + * ) + echo "Invalid parameter $1!" 1>&2 + exit 1 + ;; + esac +done + +cd "`dirname $0`/../.." + +specfile=$PWD/contrib/packaging/mercurial.spec +if [ ! -f $specfile ]; then + echo "Cannot find $specfile!" 1>&2 + exit 1 +fi + +if [ ! -d .hg ]; then + echo 'You are not inside a Mercurial repository!' 1>&2 + exit 1 +fi + +gethgversion + +# TODO: handle distance/node set, and type set + +if [ -z "$type" ] ; then + release=1 +else + release=0.9_$type +fi + +if [ -n "$distance" ] ; then + release=$release+$distance_$node +fi + +if [ "$PYTHONVER" ]; then + release=$release+$PYTHONVER + RPMPYTHONVER=$PYTHONVER +else + RPMPYTHONVER=%{nil} +fi + +mkdir -p $RPMBUILDDIR/{SOURCES,BUILD,SRPMS,RPMS} +$HG archive -t tgz $RPMBUILDDIR/SOURCES/mercurial-$version-$release.tar.gz +if [ "$PYTHONVER" ]; then +( + mkdir -p build + cd build + PYTHON_SRCFILE=Python-$PYTHONVER.tgz + [ -f $PYTHON_SRCFILE ] || curl -Lo $PYTHON_SRCFILE http://www.python.org/ftp/python/$PYTHONVER/$PYTHON_SRCFILE + if [ "$PYTHONMD5" ]; then + echo "$PYTHONMD5 $PYTHON_SRCFILE" | md5sum -w -c + fi + ln -f $PYTHON_SRCFILE $RPMBUILDDIR/SOURCES/$PYTHON_SRCFILE + + DOCUTILSVER=`sed -ne "s/^%global docutilsname docutils-//p" $specfile` + DOCUTILS_SRCFILE=docutils-$DOCUTILSVER.tar.gz + [ -f $DOCUTILS_SRCFILE ] || curl -Lo $DOCUTILS_SRCFILE http://downloads.sourceforge.net/project/docutils/docutils/$DOCUTILSVER/$DOCUTILS_SRCFILE + DOCUTILSMD5=`sed -ne "s/^%global docutilsmd5 //p" $specfile` + if [ "$DOCUTILSMD5" ]; then + echo "$DOCUTILSMD5 $DOCUTILS_SRCFILE" | md5sum -w -c + fi + ln -f $DOCUTILS_SRCFILE $RPMBUILDDIR/SOURCES/$DOCUTILS_SRCFILE +) +fi + +mkdir -p $RPMBUILDDIR/SPECS +rpmspec=$RPMBUILDDIR/SPECS/mercurial.spec + +sed -e "s,^Version:.*,Version: $version," \ + -e "s,^Release:.*,Release: $release," \ + $specfile > $rpmspec + +echo >> $rpmspec +echo "%changelog" >> $rpmspec + +if echo $version | grep '+' > /dev/null 2>&1; then + latesttag="`echo $version | sed -e 's/+.*//'`" + $HG log -r .:"$latesttag" -fM \ + --template '{date|hgdate}\t{author}\t{desc|firstline}\n' | python -c ' +import sys, time + +def datestr(date, format): + return time.strftime(format, time.gmtime(float(date[0]) - date[1])) + +changelog = [] +for l in sys.stdin.readlines(): + tok = l.split("\t") + hgdate = tuple(int(v) for v in tok[0].split()) + changelog.append((datestr(hgdate, "%F"), tok[1], hgdate, tok[2])) +prevtitle = "" +for l in sorted(changelog, reverse=True): + title = "* %s %s" % (datestr(l[2], "%a %b %d %Y"), l[1]) + if prevtitle != title: + prevtitle = title + print + print title + print "- %s" % l[3].strip() +' >> $rpmspec + +else + + $HG log \ + --template '{date|hgdate}\t{author}\t{desc|firstline}\n' \ + .hgtags | python -c ' +import sys, time + +def datestr(date, format): + return time.strftime(format, time.gmtime(float(date[0]) - date[1])) + +for l in sys.stdin.readlines(): + tok = l.split("\t") + hgdate = tuple(int(v) for v in tok[0].split()) + print "* %s %s\n- %s" % (datestr(hgdate, "%a %b %d %Y"), tok[1], tok[2]) +' >> $rpmspec + +fi + +sed -i \ + -e "s/^%define withpython.*$/%define withpython $RPMPYTHONVER/" \ + $rpmspec + +if [ "$BUILD" ]; then + rpmbuild --define "_topdir $RPMBUILDDIR" -ba $rpmspec --clean + if [ $? = 0 ]; then + echo + echo "Built packages for $version-$release:" + find $RPMBUILDDIR/*RPMS/ -type f -newer $rpmspec + fi +else + echo "Prepared sources for $version-$release $rpmspec are in $RPMBUILDDIR/SOURCES/ - use like:" + echo "rpmbuild --define '_topdir $RPMBUILDDIR' -ba $rpmspec --clean" +fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/debian/cacerts.rc Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,5 @@ +# This config file points Mercurial at the system-wide certificate +# store from the ca-certificates package. + +[web] +cacerts = /etc/ssl/certs/ca-certificates.crt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/debian/changelog Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,5 @@ +mercurial (__VERSION__) __CODENAME__; urgency=medium + + * Automated build performed by upstream. + + -- Mercurial Devel <mercurial-devel@mercurial-scm.org> __DATE__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/debian/compat Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,1 @@ +9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/debian/control Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,54 @@ +Source: mercurial +Section: vcs +Priority: optional +Maintainer: Mercurial Developers <mercurial-devel@mercurial-scm.org> +Build-Depends: + debhelper (>= 9), + dh-python, + less, + netbase, + python-all, + python-all-dev, + python-docutils, + unzip, + zip +Standards-Version: 3.9.4 +X-Python-Version: >= 2.7 + +Package: mercurial +Depends: + python, + ${shlibs:Depends}, + ${misc:Depends}, + ${python:Depends}, + mercurial-common (= ${source:Version}) +Architecture: any +Description: fast, easy to use, distributed revision control tool. + Mercurial is a fast, lightweight Source Control Management system designed + for efficient handling of very large distributed projects. + . + Its features include: + * O(1) delta-compressed file storage and retrieval scheme + * Complete cross-indexing of files and changesets for efficient exploration + of project history + * Robust SHA1-based integrity checking and append-only storage model + * Decentralized development model with arbitrary merging between trees + * Easy-to-use command-line interface + * Integrated stand-alone web interface + * Small Python codebase + +Package: mercurial-common +Architecture: all +Depends: + ${misc:Depends}, + ${python:Depends}, +Recommends: mercurial (= ${source:Version}), ca-certificates +Suggests: wish +Breaks: mercurial (<< ${source:Version}) +Replaces: mercurial (<< 2.6.3) +Description: easy-to-use, scalable distributed version control system (common files) + Mercurial is a fast, lightweight Source Control Management system designed + for efficient handling of very large distributed projects. + . + This package contains the architecture independent components of Mercurial, + and is generally useless without the mercurial package.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/debian/copyright Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,27 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: mercurial +Source: https://www.mercurial-scm.org/ + +Files: * +Copyright: 2005-2018, Matt Mackall <mpm@selenic.com> and others. +License: GPL-2+ + This program is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + . + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more + details. + . + You should have received a copy of the GNU General Public + License along with this package; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301 USA + . + On Debian systems, the full text of the GNU General Public + License version 2 can be found in the file + `/usr/share/common-licenses/GPL-2'.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/debian/default-tools.rc Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,5 @@ +[ui] +editor = sensible-editor + +[pager] +pager = sensible-pager
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/debian/hgkpath.rc Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,2 @@ +[hgk] +path = /usr/share/mercurial/hgk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/debian/rules Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,44 @@ +#!/usr/bin/make -f +# Uncomment this to turn on verbose mode. +# export DH_VERBOSE=1 + +CPUS=$(shell cat /proc/cpuinfo | grep -E ^processor | wc -l) + +%: + dh $@ --with python2 + +override_dh_auto_test: + http_proxy='' dh_auto_test -- TESTFLAGS="-j$(CPUS)" + +override_dh_python2: + dh_python2 + find debian/mercurial/usr/share -type d -empty -delete + +override_dh_install: + python$(PYVERS) setup.py install --root "$(CURDIR)"/debian/mercurial --install-layout=deb + # chg + make -C contrib/chg \ + DESTDIR="$(CURDIR)"/debian/mercurial \ + PREFIX=/usr \ + clean install + # remove arch-independent python stuff + find "$(CURDIR)"/debian/mercurial/usr/lib \ + ! -name '*.so' ! -type d -delete , \ + -type d -empty -delete + python$(PYVERS) setup.py install --root "$(CURDIR)/debian/mercurial-common" --install-layout=deb + make install-doc PREFIX="$(CURDIR)"/debian/mercurial-common/usr + # remove arch-dependent python stuff + find "$(CURDIR)"/debian/mercurial-common/usr/lib \ + -name '*.so' ! -type d -delete , \ + -type d -empty -delete + cp contrib/hg-ssh "$(CURDIR)"/debian/mercurial-common/usr/bin + mkdir -p "$(CURDIR)"/debian/mercurial-common/usr/share/mercurial + cp contrib/hgk "$(CURDIR)"/debian/mercurial-common/usr/share/mercurial + mkdir -p "$(CURDIR)"/debian/mercurial-common/etc/mercurial/hgrc.d/ + cp contrib/packaging/debian/*.rc "$(CURDIR)"/debian/mercurial-common/etc/mercurial/hgrc.d/ + # completions + mkdir -p "$(CURDIR)"/debian/mercurial-common/usr/share/bash-completion/completions + cp contrib/bash_completion "$(CURDIR)"/debian/mercurial-common/usr/share/bash-completion/completions/hg + mkdir -p "$(CURDIR)"/debian/mercurial-common/usr/share/zsh/vendor-completions + cp contrib/zsh_completion "$(CURDIR)"/debian/mercurial-common/usr/share/zsh/vendor-completions/_hg + rm "$(CURDIR)"/debian/mercurial-common/usr/bin/hg
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/docker/centos5 Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,27 @@ +FROM centos:centos5 + +RUN groupadd -g 1000 build && \ + useradd -u 1000 -g 1000 -s /bin/bash -d /build -m build + +RUN \ + sed -i 's/^mirrorlist/#mirrorlist/' /etc/yum.repos.d/*.repo && \ + sed -i 's/^#\(baseurl=\)http:\/\/mirror.centos.org\/centos/\1http:\/\/vault.centos.org/' /etc/yum.repos.d/*.repo && \ + sed -i 's/\$releasever/5.11/' /etc/yum.repos.d/*.repo + +RUN yum install -y \ + gcc \ + gettext \ + make \ + python-devel \ + python-docutils \ + rpm-build \ + tar + +# For creating repo meta data +RUN yum install -y \ + bzip2-devel \ + createrepo \ + ncurses-devel \ + openssl-devel \ + readline-devel \ + zlib-devel
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/docker/centos6 Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,24 @@ +FROM centos:centos6 + +RUN groupadd -g 1000 build && \ + useradd -u 1000 -g 1000 -s /bin/bash -d /build -m build + +RUN yum install -y \ + gcc \ + gettext \ + make \ + python-devel \ + python-docutils \ + rpm-build \ + tar + +# For creating repo meta data +RUN yum install -y createrepo + +# For python +RUN yum install -y \ + bzip2-devel \ + ncurses-devel \ + openssl-devel \ + readline-devel \ + zlib-devel
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/docker/centos7 Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,16 @@ +FROM centos:centos7 + +RUN groupadd -g 1000 build && \ + useradd -u 1000 -g 1000 -s /bin/bash -d /build -m build + +RUN yum install -y \ + gcc \ + gettext \ + make \ + python-devel \ + python-docutils \ + rpm-build \ + tar + +# For creating repo meta data +RUN yum install -y createrepo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/docker/debian.template Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,16 @@ +FROM debian:%CODENAME% + +RUN groupadd -g 1000 build && \ + useradd -u 1000 -g 1000 -s /bin/bash -d /build -m build + +RUN apt-get update && apt-get install -y \ + build-essential \ + debhelper \ + devscripts \ + dh-python \ + less \ + python \ + python-all-dev \ + python-docutils \ + unzip \ + zip
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/docker/fedora20 Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,15 @@ +FROM fedora:20 + +RUN groupadd -g 1000 build && \ + useradd -u 1000 -g 1000 -s /bin/bash -d /build -m build + +RUN yum install -y \ + gcc \ + gettext \ + make \ + python-devel \ + python-docutils \ + rpm-build + +# For creating repo meta data +RUN yum install -y createrepo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/docker/fedora21 Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,15 @@ +FROM fedora:21 + +RUN groupadd -g 1000 build && \ + useradd -u 1000 -g 1000 -s /bin/bash -d /build -m build + +RUN yum install -y \ + gcc \ + gettext \ + make \ + python-devel \ + python-docutils \ + rpm-build + +# For creating repo meta data +RUN yum install -y createrepo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/docker/fedora28 Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,15 @@ +FROM fedora:28 + +RUN groupadd -g 1000 build && \ + useradd -u 1000 -g 1000 -s /bin/bash -d /build -m build + +RUN dnf install -y \ + gcc \ + gettext \ + make \ + python-devel \ + python-docutils \ + rpm-build + +# For creating repo meta data +RUN dnf install -y createrepo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/docker/ubuntu.template Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,16 @@ +FROM ubuntu:%CODENAME% + +RUN groupadd -g 1000 build && \ + useradd -u 1000 -g 1000 -s /bin/bash -d /build -m build + +RUN apt-get update && apt-get install -y \ + build-essential \ + debhelper \ + devscripts \ + dh-python \ + less \ + python \ + python-all-dev \ + python-docutils \ + unzip \ + zip
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/dockerdeb Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,40 @@ +#!/bin/bash -eu + +. $(dirname $0)/packagelib.sh + +BUILDDIR=$(dirname $0) +export ROOTDIR=$(cd $BUILDDIR/../.. > /dev/null; pwd) + +DISTID="$1" +CODENAME="$2" +PLATFORM="$1-$2" +shift; shift # extra params are passed to build process + +OUTPUTDIR=${OUTPUTDIR:=$ROOTDIR/packages/$PLATFORM} +CONTAINER=hg-docker-$PLATFORM + +DOCKER=$($BUILDDIR/hg-docker docker-path) + +$BUILDDIR/hg-docker build \ + --build-arg CODENAME=$CODENAME \ + $BUILDDIR/docker/$DISTID.template \ + $CONTAINER + +# debuild only appears to be able to save built debs etc to .., so we +# have to share the .. of the current directory with the docker +# container and hope it's writable. Whee. +dn=$(basename $ROOTDIR) + +DBUILDUSER=build + +if [ $(uname) = "Darwin" ] ; then + $DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \ + sh -c "cd /mnt/$dn && make clean && make local" +fi +$DOCKER run -u $DBUILDUSER --rm -v $ROOTDIR/..:/mnt $CONTAINER \ + sh -c "cd /mnt/$dn && DEB_BUILD_OPTIONS='${DEB_BUILD_OPTIONS:=}' contrib/packaging/builddeb --build --distid $DISTID --codename $CODENAME $@" +contrib/packaging/builddeb --cleanup --distid $DISTID --codename $CODENAME +if [ $(uname) = "Darwin" ] ; then + $DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \ + sh -c "cd /mnt/$dn && make clean" +fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/dockerrpm Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,39 @@ +#!/bin/bash -e + +BUILDDIR=$(dirname $0) +export ROOTDIR=$(cd $BUILDDIR/../..; pwd) + +PLATFORM="$1" +shift # extra params are passed to buildrpm + +DOCKER=$($BUILDDIR/hg-docker docker-path) + +CONTAINER=hg-docker-$PLATFORM + +$BUILDDIR/hg-docker build $BUILDDIR/docker/$PLATFORM $CONTAINER + +RPMBUILDDIR=$ROOTDIR/packages/$PLATFORM +$ROOTDIR/contrib/packaging/buildrpm --rpmbuilddir $RPMBUILDDIR --prepare $* + +DSHARED=/mnt/shared +DBUILDUSER=build + +$DOCKER run -e http_proxy -e https_proxy -u $DBUILDUSER --rm -v $RPMBUILDDIR:$DSHARED $CONTAINER \ + rpmbuild --define "_topdir $DSHARED" -ba $DSHARED/SPECS/mercurial.spec --clean + +$DOCKER run -e http_proxy -e https_proxy -u $DBUILDUSER --rm -v $RPMBUILDDIR:$DSHARED $CONTAINER \ + createrepo $DSHARED + +cat << EOF > $RPMBUILDDIR/mercurial.repo +# Place this file in /etc/yum.repos.d/mercurial.repo +[mercurial] +name=Mercurial packages for $PLATFORM +# baseurl=file://$RPMBUILDDIR/ +baseurl=http://hg.example.com/build/$PLATFORM/ +skip_if_unavailable=True +gpgcheck=0 +enabled=1 +EOF + +echo +echo "Build complete - results can be found in $RPMBUILDDIR"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/hg-docker Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +# +# Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +import argparse +import pathlib +import shutil +import subprocess +import sys + +def get_docker() -> str: + docker = shutil.which('docker.io') or shutil.which('docker') + if not docker: + print('could not find docker executable') + return 1 + + try: + out = subprocess.check_output([docker, '-h'], stderr=subprocess.STDOUT) + + if b'Jansens' in out: + print('%s is the Docking System Tray; try installing docker.io' % + docker) + sys.exit(1) + except subprocess.CalledProcessError as e: + print('error calling `%s -h`: %s' % (docker, e.output)) + sys.exit(1) + + out = subprocess.check_output([docker, 'version'], + stderr=subprocess.STDOUT) + + lines = out.splitlines() + if not any(l.startswith((b'Client:', b'Client version:')) for l in lines): + print('`%s version` does not look like Docker' % docker) + sys.exit(1) + + if not any(l.startswith((b'Server:', b'Server version:')) for l in lines): + print('`%s version` does not look like Docker' % docker) + sys.exit(1) + + return docker + +def get_dockerfile(path: pathlib.Path, args: list) -> bytes: + with path.open('rb') as fh: + df = fh.read() + + for k, v in args: + df = df.replace(b'%%%s%%' % k, v) + + return df + +def build_docker_image(dockerfile: pathlib.Path, params: list, tag: str): + """Build a Docker image from a templatized Dockerfile.""" + docker = get_docker() + + dockerfile_path = pathlib.Path(dockerfile) + + dockerfile = get_dockerfile(dockerfile_path, params) + + print('building Dockerfile:') + print(dockerfile.decode('utf-8', 'replace')) + + args = [ + docker, + 'build', + '--build-arg', 'http_proxy', + '--build-arg', 'https_proxy', + '--tag', tag, + '-', + ] + + print('executing: %r' % args) + subprocess.run(args, input=dockerfile, check=True) + +def command_build(args): + build_args = [] + for arg in args.build_arg: + k, v = arg.split('=', 1) + build_args.append((k.encode('utf-8'), v.encode('utf-8'))) + + build_docker_image(pathlib.Path(args.dockerfile), + build_args, + args.tag) + +def command_docker(args): + print(get_docker()) + +def main() -> int: + parser = argparse.ArgumentParser() + + subparsers = parser.add_subparsers(title='subcommands') + + build = subparsers.add_parser('build', help='Build a Docker image') + build.set_defaults(func=command_build) + build.add_argument('--build-arg', action='append', default=[], + help='Substitution to perform in Dockerfile; ' + 'format: key=value') + build.add_argument('dockerfile', help='path to Dockerfile to use') + build.add_argument('tag', help='Tag to apply to created image') + + docker = subparsers.add_parser('docker-path', help='Resolve path to Docker') + docker.set_defaults(func=command_docker) + + args = parser.parse_args() + + return args.func(args) + +if __name__ == '__main__': + sys.exit(main())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/linux-wheel-centos5-blacklist Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,3 @@ +test-convert-git.t +test-subrepo-git.t +test-patchbomb-tls.t
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/macosx/Readme.html Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,37 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<!-- This is the second screen displayed during the install. --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <meta http-equiv="Content-Style-Type" content="text/css"> + <title>Read Me - Important Information</title> + <style type="text/css"> + p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Helvetica} + p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px} + p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica} + p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; color: #000fed} + span.s1 {text-decoration: underline} + span.s2 {font: 12.0px Courier} + </style> +</head> +<body> +<p class="p1"><b>Before you install</b></p> +<p class="p2"><br></p> +<p class="p3">This is an OS X version of Mercurial that depends on the default Python installation.</p> +<p class="p2"><br></p> +<p class="p1"><b>After you install</b></p> +<p class="p2"><br></p> +<p class="p3">This package installs the <span class="s2">hg</span> executable as <span class="s2">/usr/local/bin/hg</span>. See <span class="s2">hg debuginstall</span> for more info on file locations.</p> +<p class="p2"><br></p> +<p class="p1"><b>Documentation</b></p> +<p class="p2"><br></p> +<p class="p3">Visit the <a href="https://mercurial-scm.org/">Mercurial web site and wiki</a></p> +<p class="p2"><br></p> +<p class="p3">There's also a free book, <a href="https://book.mercurial-scm.org/">Distributed revision control with Mercurial</a></p> +<p class="p2"><br></p> +<p class="p1"><b>Reporting problems</b></p> +<p class="p2"><br></p> +<p class="p3">If you run into any problems, please file a bug online:</p> +<p class="p3"><a href="https://bz.mercurial-scm.org/">https://bz.mercurial-scm.org/</a></p> +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/macosx/Welcome.html Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,20 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<!-- This is the first screen displayed during the install. --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <meta http-equiv="Content-Style-Type" content="text/css"> + <title></title> + <style type="text/css"> + p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Helvetica} + p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px} + </style> +</head> +<body> +<p class="p1">This is a prepackaged release of <a href="https://mercurial-scm.org/">Mercurial</a> for Mac OS X.</p> +<p class="p2"><br></p> +<br> +<p> +Please be sure to read the latest <a href="https://mercurial-scm.org/wiki/WhatsNew">release notes</a>.</p> +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/macosx/distribution.xml Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<installer-gui-script minSpecVersion="1"> + <title>Mercurial SCM</title> + <organization>org.mercurial-scm</organization> + <options customize="never" require-scripts="false" rootVolumeOnly="true" /> + <welcome file="Welcome.html" mime-type="text/html" /> + <license file="../../../COPYING" mime-type="text/plain" /> + <readme file="Readme.html" mime-type="text/html" /> + <pkg-ref id="org.mercurial-scm.mercurial" + version="0" + auth="root" + onConclusion="none">mercurial.pkg</pkg-ref> + <choices-outline> + <line choice="org.mercurial-scm.mercurial"/> + </choices-outline> + <choice id="org.mercurial-scm.mercurial" visible="false"> + <pkg-ref id="org.mercurial-scm.mercurial"/> + </choice> +</installer-gui-script>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/mercurial.spec Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,164 @@ +%global emacs_lispdir %{_datadir}/emacs/site-lisp + +%define withpython %{nil} + +%if "%{?withpython}" + +%global pythonver %{withpython} +%global pythonname Python-%{withpython} +%global docutilsname docutils-0.14 +%global docutilsmd5 c53768d63db3873b7d452833553469de +%global pythonhg python-hg +%global hgpyprefix /opt/%{pythonhg} +# byte compilation will fail on some some Python /test/ files +%global _python_bytecompile_errors_terminate_build 0 + +%else + +%global pythonver %(python -c 'import sys;print ".".join(map(str, sys.version_info[:2]))') + +%endif + +Summary: A fast, lightweight Source Control Management system +Name: mercurial +Version: snapshot +Release: 0 +License: GPLv2+ +Group: Development/Tools +URL: https://mercurial-scm.org/ +Source0: %{name}-%{version}-%{release}.tar.gz +%if "%{?withpython}" +Source1: %{pythonname}.tgz +Source2: %{docutilsname}.tar.gz +%endif +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root + +BuildRequires: make, gcc, gettext +%if "%{?withpython}" +BuildRequires: readline-devel, openssl-devel, ncurses-devel, zlib-devel, bzip2-devel +%else +BuildRequires: python >= 2.7, python-devel, python-docutils >= 0.5 +Requires: python >= 2.7 +%endif +# The hgk extension uses the wish tcl interpreter, but we don't enforce it +#Requires: tk + +%description +Mercurial is a fast, lightweight source control management system designed +for efficient handling of very large distributed projects. + +%prep + +%if "%{?withpython}" +%setup -q -n mercurial-%{version}-%{release} -a1 -a2 +# despite the comments in cgi.py, we do this to prevent rpmdeps from picking /usr/local/bin/python up +sed -i '1c#! /usr/bin/env python' %{pythonname}/Lib/cgi.py +%else +%setup -q -n mercurial-%{version}-%{release} +%endif + +%build + +%if "%{?withpython}" + +PYPATH=$PWD/%{pythonname} +cd $PYPATH +./configure --prefix=%{hgpyprefix} +make all %{?_smp_mflags} +cd - + +cd %{docutilsname} +LD_LIBRARY_PATH=$PYPATH $PYPATH/python setup.py build +cd - + +# verify Python environment +LD_LIBRARY_PATH=$PYPATH PYTHONPATH=$PWD/%{docutilsname} $PYPATH/python -c 'import sys, zlib, bz2, ssl, curses, readline' + +# set environment for make +export PATH=$PYPATH:$PATH +export LD_LIBRARY_PATH=$PYPATH +export CFLAGS="-L $PYPATH" +export PYTHONPATH=$PWD/%{docutilsname} + +%endif + +make all +make -C contrib/chg + +%install +rm -rf $RPM_BUILD_ROOT + +%if "%{?withpython}" + +PYPATH=$PWD/%{pythonname} +cd $PYPATH +make install DESTDIR=$RPM_BUILD_ROOT +# these .a are not necessary and they are readonly and strip fails - kill them! +rm -f %{buildroot}%{hgpyprefix}/lib/{,python2.*/config}/libpython2.*.a +cd - + +cd %{docutilsname} +LD_LIBRARY_PATH=$PYPATH $PYPATH/python setup.py install --root="$RPM_BUILD_ROOT" +cd - + +PATH=$PYPATH:$PATH LD_LIBRARY_PATH=$PYPATH make install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{hgpyprefix} MANDIR=%{_mandir} +mkdir -p $RPM_BUILD_ROOT%{_bindir} +( cd $RPM_BUILD_ROOT%{_bindir}/ && ln -s ../..%{hgpyprefix}/bin/hg . ) +( cd $RPM_BUILD_ROOT%{_bindir}/ && ln -s ../..%{hgpyprefix}/bin/python2.? %{pythonhg} ) + +%else + +make install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{_prefix} MANDIR=%{_mandir} + +%endif + +install -m 755 contrib/chg/chg $RPM_BUILD_ROOT%{_bindir}/ +install -m 755 contrib/hgk $RPM_BUILD_ROOT%{_bindir}/ +install -m 755 contrib/hg-ssh $RPM_BUILD_ROOT%{_bindir}/ + +bash_completion_dir=$RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d +mkdir -p $bash_completion_dir +install -m 644 contrib/bash_completion $bash_completion_dir/mercurial.sh + +zsh_completion_dir=$RPM_BUILD_ROOT%{_datadir}/zsh/site-functions +mkdir -p $zsh_completion_dir +install -m 644 contrib/zsh_completion $zsh_completion_dir/_mercurial + +mkdir -p $RPM_BUILD_ROOT%{emacs_lispdir} +install -m 644 contrib/mercurial.el $RPM_BUILD_ROOT%{emacs_lispdir}/ +install -m 644 contrib/mq.el $RPM_BUILD_ROOT%{emacs_lispdir}/ + +mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/mercurial/hgrc.d + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root,-) +%doc CONTRIBUTORS COPYING doc/README doc/hg*.txt doc/hg*.html *.cgi contrib/*.fcgi +%doc %attr(644,root,root) %{_mandir}/man?/hg* +%doc %attr(644,root,root) contrib/*.svg +%dir %{_datadir}/zsh/ +%dir %{_datadir}/zsh/site-functions/ +%{_datadir}/zsh/site-functions/_mercurial +%dir %{_datadir}/emacs/site-lisp/ +%{_datadir}/emacs/site-lisp/mercurial.el +%{_datadir}/emacs/site-lisp/mq.el +%{_bindir}/hg +%{_bindir}/chg +%{_bindir}/hgk +%{_bindir}/hg-ssh +%dir %{_sysconfdir}/bash_completion.d/ +%config(noreplace) %{_sysconfdir}/bash_completion.d/mercurial.sh +%dir %{_sysconfdir}/mercurial +%dir %{_sysconfdir}/mercurial/hgrc.d +%if "%{?withpython}" +%{_bindir}/%{pythonhg} +%{hgpyprefix} +%else +%{_libdir}/python%{pythonver}/site-packages/%{name}-*-py%{pythonver}.egg-info +%{_libdir}/python%{pythonver}/site-packages/%{name} +%{_libdir}/python%{pythonver}/site-packages/hgext +%{_libdir}/python%{pythonver}/site-packages/hgext3rd +%{_libdir}/python%{pythonver}/site-packages/hgdemandimport +%endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/packagelib.sh Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,38 @@ +# Extract version number into 4 parts, some of which may be empty: +# +# version: the numeric part of the most recent tag. Will always look like 1.3. +# +# type: if an rc build, "rc", otherwise empty +# +# distance: the distance from the nearest tag, or empty if built from a tag +# +# node: the node|short hg was built from, or empty if built from a tag +gethgversion() { + export HGRCPATH= + export HGPLAIN= + + make cleanbutpackages + make local PURE=--pure + HG="$PWD/hg" + + "$HG" version > /dev/null || { echo 'abort: hg version failed!'; exit 1 ; } + + hgversion=`LANGUAGE=C "$HG" version | sed -ne 's/.*(version \(.*\))$/\1/p'` + + if echo $hgversion | grep + > /dev/null 2>&1 ; then + tmp=`echo $hgversion | cut -d+ -f 2` + hgversion=`echo $hgversion | cut -d+ -f 1` + distance=`echo $tmp | cut -d- -f 1` + node=`echo $tmp | cut -d- -f 2` + else + distance='' + node='' + fi + if echo $hgversion | grep -- '-' > /dev/null 2>&1; then + version=`echo $hgversion | cut -d- -f1` + type=`echo $hgversion | cut -d- -f2` + else + version=$hgversion + type='' + fi +}
--- a/contrib/perf.py Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/perf.py Thu Jul 19 13:55:54 2018 -0400 @@ -71,6 +71,25 @@ import inspect getargspec = inspect.getargspec +try: + # 4.7+ + queue = pycompat.queue.Queue +except (AttributeError, ImportError): + # <4.7. + try: + queue = pycompat.queue + except (AttributeError, ImportError): + queue = util.queue + +try: + from mercurial import logcmdutil + makelogtemplater = logcmdutil.maketemplater +except (AttributeError, ImportError): + try: + makelogtemplater = cmdutil.makelogtemplater + except (AttributeError, ImportError): + makelogtemplater = None + # for "historical portability": # define util.safehasattr forcibly, because util.safehasattr has been # available since 1.9.3 (or 94b200a11cf7) @@ -159,6 +178,9 @@ configitem('perf', 'parentscount', default=mercurial.configitems.dynamicdefault, ) + configitem('perf', 'all-timing', + default=mercurial.configitems.dynamicdefault, + ) except (ImportError, AttributeError): pass @@ -228,12 +250,15 @@ # experimental config: perf.stub if ui.configbool("perf", "stub", False): return functools.partial(stub_timer, fm), fm - return functools.partial(_timer, fm), fm + + # experimental config: perf.all-timing + displayall = ui.configbool("perf", "all-timing", False) + return functools.partial(_timer, fm, displayall=displayall), fm def stub_timer(fm, func, title=None): func() -def _timer(fm, func, title=None): +def _timer(fm, func, title=None, displayall=False): gc.collect() results = [] begin = util.timer() @@ -258,14 +283,27 @@ fm.write('title', '! %s\n', title) if r: fm.write('result', '! result: %s\n', r) - m = min(results) - fm.plain('!') - fm.write('wall', ' wall %f', m[0]) - fm.write('comb', ' comb %f', m[1] + m[2]) - fm.write('user', ' user %f', m[1]) - fm.write('sys', ' sys %f', m[2]) - fm.write('count', ' (best of %d)', count) - fm.plain('\n') + def display(role, entry): + prefix = '' + if role != 'best': + prefix = '%s.' % role + fm.plain('!') + fm.write(prefix + 'wall', ' wall %f', entry[0]) + fm.write(prefix + 'comb', ' comb %f', entry[1] + entry[2]) + fm.write(prefix + 'user', ' user %f', entry[1]) + fm.write(prefix + 'sys', ' sys %f', entry[2]) + fm.write(prefix + 'count', ' (%s of %d)', role, count) + fm.plain('\n') + results.sort() + min_val = results[0] + display('best', min_val) + if displayall: + max_val = results[-1] + display('max', max_val) + avg = tuple([sum(x) / count for x in zip(*results)]) + display('avg', avg) + median = results[len(results) // 2] + display('median', median) # utilities for historical portability @@ -755,6 +793,10 @@ @command('perfmanifest', [], 'REV') def perfmanifest(ui, repo, rev, **opts): + """benchmark the time to read a manifest from disk and return a usable + dict-like object + + Manifest caches are cleared before retrieval.""" timer, fm = gettimer(ui, opts) ctx = scmutil.revsingle(repo, rev, rev) t = ctx.manifestnode() @@ -887,16 +929,36 @@ timer(moonwalk) fm.end() -@command('perftemplating', formatteropts) -def perftemplating(ui, repo, rev=None, **opts): - if rev is None: - rev=[] +@command('perftemplating', + [('r', 'rev', [], 'revisions to run the template on'), + ] + formatteropts) +def perftemplating(ui, repo, testedtemplate=None, **opts): + """test the rendering time of a given template""" + if makelogtemplater is None: + raise error.Abort(("perftemplating not available with this Mercurial"), + hint="use 4.3 or later") + + nullui = ui.copy() + nullui.fout = open(os.devnull, 'wb') + nullui.disablepager() + revs = opts.get('rev') + if not revs: + revs = ['all()'] + revs = list(scmutil.revrange(repo, revs)) + + defaulttemplate = ('{date|shortdate} [{rev}:{node|short}]' + ' {author|person}: {desc|firstline}\n') + if testedtemplate is None: + testedtemplate = defaulttemplate + displayer = makelogtemplater(nullui, repo, testedtemplate) + def format(): + for r in revs: + ctx = repo[r] + displayer.show(ctx) + displayer.flush(ctx) + timer, fm = gettimer(ui, opts) - ui.pushbuffer() - timer(lambda: commands.log(ui, repo, rev=rev, date='', user='', - template='{date|shortdate} [{rev}:{node|short}]' - ' {author|person}: {desc|firstline}\n')) - ui.popbuffer() + timer(format) fm.end() @command('perfcca', formatteropts) @@ -918,9 +980,10 @@ def perffncachewrite(ui, repo, **opts): timer, fm = gettimer(ui, opts) s = repo.store + lock = repo.lock() s.fncache._load() - lock = repo.lock() tr = repo.transaction('perffncachewrite') + tr.addbackup('fncache') def d(): s.fncache._dirty = True s.fncache.write(tr) @@ -1029,7 +1092,7 @@ else: mdiff.textdiff(*pair) else: - q = util.queue() + q = queue() for i in xrange(threads): q.put(None) ready = threading.Condition()
--- a/contrib/phabricator.py Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/phabricator.py Thu Jul 19 13:55:54 2018 -0400 @@ -21,10 +21,6 @@ # Phabricator URL url = https://phab.example.com/ - # API token. Get it from https://$HOST/conduit/login/ - # Deprecated: see [phabricator.auth] below - #token = cli-xxxxxxxxxxxxxxxxxxxxxxxxxxxx - # Repo callsign. If a repo has a URL https://$HOST/diffusion/FOO, then its # callsign is "FOO". callsign = FOO @@ -35,10 +31,12 @@ # the internal library. curlcmd = curl --connect-timeout 2 --retry 3 --silent - [phabricator.auth] - example.url = https://phab.example.com/ + [auth] + example.schemes = https + example.prefix = phab.example.com + # API token. Get it from https://$HOST/conduit/login/ - example.token = cli-xxxxxxxxxxxxxxxxxxxxxxxxxxxx + example.phabtoken = cli-xxxxxxxxxxxxxxxxxxxxxxxxxxxx """ from __future__ import absolute_import @@ -55,6 +53,7 @@ context, encoding, error, + httpconnection as httpconnectionmod, mdiff, obsutil, parser, @@ -74,13 +73,37 @@ cmdtable = {} command = registrar.command(cmdtable) +configtable = {} +configitem = registrar.configitem(configtable) + +# developer config: phabricator.batchsize +configitem(b'phabricator', b'batchsize', + default=12, +) +configitem(b'phabricator', b'callsign', + default=None, +) +configitem(b'phabricator', b'curlcmd', + default=None, +) +# developer config: phabricator.repophid +configitem(b'phabricator', b'repophid', + default=None, +) +configitem(b'phabricator', b'url', + default=None, +) +configitem(b'phabsend', b'confirm', + default=False, +) + colortable = { - 'phabricator.action.created': 'green', - 'phabricator.action.skipped': 'magenta', - 'phabricator.action.updated': 'magenta', - 'phabricator.desc': '', - 'phabricator.drev': 'bold', - 'phabricator.node': '', + b'phabricator.action.created': b'green', + b'phabricator.action.skipped': b'magenta', + b'phabricator.action.updated': b'magenta', + b'phabricator.desc': b'', + b'phabricator.drev': b'bold', + b'phabricator.node': b'', } def urlencodenested(params): @@ -98,59 +121,69 @@ else: for k, v in items(obj): if prefix: - process('%s[%s]' % (prefix, k), v) + process(b'%s[%s]' % (prefix, k), v) else: process(k, v) - process('', params) + process(b'', params) return util.urlreq.urlencode(flatparams) printed_token_warning = False -def readlegacytoken(repo): +def readlegacytoken(repo, url): """Transitional support for old phabricator tokens. - Remove before the 4.6 release. + Remove before the 4.7 release. """ + groups = {} + for key, val in repo.ui.configitems(b'phabricator.auth'): + if b'.' not in key: + repo.ui.warn(_(b"ignoring invalid [phabricator.auth] key '%s'\n") + % key) + continue + group, setting = key.rsplit(b'.', 1) + groups.setdefault(group, {})[setting] = val + + token = None + for group, auth in groups.iteritems(): + if url != auth.get(b'url'): + continue + token = auth.get(b'token') + if token: + break + global printed_token_warning - token = repo.ui.config('phabricator', 'token') + if token and not printed_token_warning: printed_token_warning = True - repo.ui.warn(_('phabricator.token is deprecated - please ' - 'migrate to the phabricator.auth section.\n')) + repo.ui.warn(_(b'phabricator.auth.token is deprecated - please ' + b'migrate to auth.phabtoken.\n')) return token def readurltoken(repo): """return conduit url, token and make sure they exist - Currently read from [phabricator] config section. In the future, it might + Currently read from [auth] config section. In the future, it might make sense to read from .arcconfig and .arcrc as well. """ - url = repo.ui.config('phabricator', 'url') + url = repo.ui.config(b'phabricator', b'url') if not url: - raise error.Abort(_('config %s.%s is required') - % ('phabricator', 'url')) + raise error.Abort(_(b'config %s.%s is required') + % (b'phabricator', b'url')) - groups = {} - for key, val in repo.ui.configitems('phabricator.auth'): - if '.' not in key: - repo.ui.warn(_("ignoring invalid [phabricator.auth] key '%s'\n") - % key) - continue - group, setting = key.rsplit('.', 1) - groups.setdefault(group, {})[setting] = val + res = httpconnectionmod.readauthforuri(repo.ui, url, util.url(url).user) + token = None - token = None - for group, auth in groups.iteritems(): - if url != auth.get('url'): - continue - token = auth.get('token') - if token: - break + if res: + group, auth = res + + repo.ui.debug(b"using auth.%s.* for authentication\n" % group) + + token = auth.get(b'phabtoken') if not token: - token = readlegacytoken(repo) + token = readlegacytoken(repo, url) if not token: - raise error.Abort(_('Can\'t find conduit token associated to %s') + raise error.Abort(_(b'Can\'t find conduit token associated to %s') % (url,)) return url, token @@ -158,14 +191,14 @@ def callconduit(repo, name, params): """call Conduit API, params is a dict. return json.loads result, or None""" host, token = readurltoken(repo) - url, authinfo = util.url('/'.join([host, 'api', name])).authinfo() - repo.ui.debug('Conduit Call: %s %s\n' % (url, params)) + url, authinfo = util.url(b'/'.join([host, b'api', name])).authinfo() + repo.ui.debug(b'Conduit Call: %s %s\n' % (url, params)) params = params.copy() - params['api.token'] = token + params[b'api.token'] = token data = urlencodenested(params) - curlcmd = repo.ui.config('phabricator', 'curlcmd') + curlcmd = repo.ui.config(b'phabricator', b'curlcmd') if curlcmd: - sin, sout = procutil.popen2('%s -d @- %s' + sin, sout = procutil.popen2(b'%s -d @- %s' % (curlcmd, procutil.shellquote(url))) sin.write(data) sin.close() @@ -174,15 +207,15 @@ urlopener = urlmod.opener(repo.ui, authinfo) request = util.urlreq.request(url, data=data) body = urlopener.open(request).read() - repo.ui.debug('Conduit Response: %s\n' % body) + repo.ui.debug(b'Conduit Response: %s\n' % body) parsed = json.loads(body) if parsed.get(r'error_code'): - msg = (_('Conduit Error (%s): %s') + msg = (_(b'Conduit Error (%s): %s') % (parsed[r'error_code'], parsed[r'error_info'])) raise error.Abort(msg) return parsed[r'result'] -@command('debugcallconduit', [], _('METHOD')) +@command(b'debugcallconduit', [], _(b'METHOD')) def debugcallconduit(ui, repo, name): """call Conduit API @@ -191,29 +224,29 @@ """ params = json.loads(ui.fin.read()) result = callconduit(repo, name, params) - s = json.dumps(result, sort_keys=True, indent=2, separators=(',', ': ')) - ui.write('%s\n' % s) + s = json.dumps(result, sort_keys=True, indent=2, separators=(b',', b': ')) + ui.write(b'%s\n' % s) def getrepophid(repo): """given callsign, return repository PHID or None""" # developer config: phabricator.repophid - repophid = repo.ui.config('phabricator', 'repophid') + repophid = repo.ui.config(b'phabricator', b'repophid') if repophid: return repophid - callsign = repo.ui.config('phabricator', 'callsign') + callsign = repo.ui.config(b'phabricator', b'callsign') if not callsign: return None - query = callconduit(repo, 'diffusion.repository.search', - {'constraints': {'callsigns': [callsign]}}) + query = callconduit(repo, b'diffusion.repository.search', + {b'constraints': {b'callsigns': [callsign]}}) if len(query[r'data']) == 0: return None repophid = encoding.strtolocal(query[r'data'][0][r'phid']) - repo.ui.setconfig('phabricator', 'repophid', repophid) + repo.ui.setconfig(b'phabricator', b'repophid', repophid) return repophid -_differentialrevisiontagre = re.compile('\AD([1-9][0-9]*)\Z') +_differentialrevisiontagre = re.compile(b'\AD([1-9][0-9]*)\Z') _differentialrevisiondescre = re.compile( - '^Differential Revision:\s*(?P<url>(?:.*)D(?P<id>[1-9][0-9]*))$', re.M) + b'^Differential Revision:\s*(?P<url>(?:.*)D(?P<id>[1-9][0-9]*))$', re.M) def getoldnodedrevmap(repo, nodelist): """find previous nodes that has been sent to Phabricator @@ -254,16 +287,16 @@ # Check commit message m = _differentialrevisiondescre.search(ctx.description()) if m: - toconfirm[node] = (1, set(precnodes), int(m.group('id'))) + toconfirm[node] = (1, set(precnodes), int(m.group(b'id'))) # Double check if tags are genuine by collecting all old nodes from # Phabricator, and expect precursors overlap with it. if toconfirm: drevs = [drev for force, precs, drev in toconfirm.values()] - alldiffs = callconduit(unfi, 'differential.querydiffs', - {'revisionIDs': drevs}) + alldiffs = callconduit(unfi, b'differential.querydiffs', + {b'revisionIDs': drevs}) getnode = lambda d: bin(encoding.unitolocal( - getdiffmeta(d).get(r'node', ''))) or None + getdiffmeta(d).get(r'node', b''))) or None for newnode, (force, precset, drev) in toconfirm.items(): diffs = [d for d in alldiffs.values() if int(d[r'revisionID']) == drev] @@ -274,11 +307,11 @@ # Ignore if precursors (Phabricator and local repo) do not overlap, # and force is not set (when commit message says nothing) if not force and not bool(phprecset & precset): - tagname = 'D%d' % drev + tagname = b'D%d' % drev tags.tag(repo, tagname, nullid, message=None, user=None, date=None, local=True) - unfi.ui.warn(_('D%s: local tag removed - does not match ' - 'Differential history\n') % drev) + unfi.ui.warn(_(b'D%s: local tag removed - does not match ' + b'Differential history\n') % drev) continue # Find the last node using Phabricator metadata, and make sure it @@ -307,40 +340,40 @@ repo = ctx.repo() repophid = getrepophid(repo) # Create a "Differential Diff" via "differential.createrawdiff" API - params = {'diff': getdiff(ctx, mdiff.diffopts(git=True, context=32767))} + params = {b'diff': getdiff(ctx, mdiff.diffopts(git=True, context=32767))} if repophid: - params['repositoryPHID'] = repophid - diff = callconduit(repo, 'differential.createrawdiff', params) + params[b'repositoryPHID'] = repophid + diff = callconduit(repo, b'differential.createrawdiff', params) if not diff: - raise error.Abort(_('cannot create diff for %s') % ctx) + raise error.Abort(_(b'cannot create diff for %s') % ctx) return diff def writediffproperties(ctx, diff): """write metadata to diff so patches could be applied losslessly""" params = { - 'diff_id': diff[r'id'], - 'name': 'hg:meta', - 'data': json.dumps({ - 'user': ctx.user(), - 'date': '%d %d' % ctx.date(), - 'node': ctx.hex(), - 'parent': ctx.p1().hex(), + b'diff_id': diff[r'id'], + b'name': b'hg:meta', + b'data': json.dumps({ + b'user': ctx.user(), + b'date': b'%d %d' % ctx.date(), + b'node': ctx.hex(), + b'parent': ctx.p1().hex(), }), } - callconduit(ctx.repo(), 'differential.setdiffproperty', params) + callconduit(ctx.repo(), b'differential.setdiffproperty', params) params = { - 'diff_id': diff[r'id'], - 'name': 'local:commits', - 'data': json.dumps({ + b'diff_id': diff[r'id'], + b'name': b'local:commits', + b'data': json.dumps({ ctx.hex(): { - 'author': stringutil.person(ctx.user()), - 'authorEmail': stringutil.email(ctx.user()), - 'time': ctx.date()[0], + b'author': stringutil.person(ctx.user()), + b'authorEmail': stringutil.email(ctx.user()), + b'time': ctx.date()[0], }, }), } - callconduit(ctx.repo(), 'differential.setdiffproperty', params) + callconduit(ctx.repo(), b'differential.setdiffproperty', params) def createdifferentialrevision(ctx, revid=None, parentrevid=None, oldnode=None, olddiff=None, actions=None): @@ -365,7 +398,7 @@ transactions = [] if neednewdiff: diff = creatediff(ctx) - transactions.append({'type': 'update', 'value': diff[r'phid']}) + transactions.append({b'type': b'update', b'value': diff[r'phid']}) else: # Even if we don't need to upload a new diff because the patch content # does not change. We might still need to update its metadata so @@ -379,52 +412,52 @@ # existing revision (revid is not None) since that introduces visible # churns (someone edited "Summary" twice) on the web page. if parentrevid and revid is None: - summary = 'Depends on D%s' % parentrevid - transactions += [{'type': 'summary', 'value': summary}, - {'type': 'summary', 'value': ' '}] + summary = b'Depends on D%s' % parentrevid + transactions += [{b'type': b'summary', b'value': summary}, + {b'type': b'summary', b'value': b' '}] if actions: transactions += actions # Parse commit message and update related fields. desc = ctx.description() - info = callconduit(repo, 'differential.parsecommitmessage', - {'corpus': desc}) + info = callconduit(repo, b'differential.parsecommitmessage', + {b'corpus': desc}) for k, v in info[r'fields'].items(): - if k in ['title', 'summary', 'testPlan']: - transactions.append({'type': k, 'value': v}) + if k in [b'title', b'summary', b'testPlan']: + transactions.append({b'type': k, b'value': v}) - params = {'transactions': transactions} + params = {b'transactions': transactions} if revid is not None: # Update an existing Differential Revision - params['objectIdentifier'] = revid + params[b'objectIdentifier'] = revid - revision = callconduit(repo, 'differential.revision.edit', params) + revision = callconduit(repo, b'differential.revision.edit', params) if not revision: - raise error.Abort(_('cannot create revision for %s') % ctx) + raise error.Abort(_(b'cannot create revision for %s') % ctx) return revision, diff def userphids(repo, names): """convert user names to PHIDs""" - query = {'constraints': {'usernames': names}} - result = callconduit(repo, 'user.search', query) + query = {b'constraints': {b'usernames': names}} + result = callconduit(repo, b'user.search', query) # username not found is not an error of the API. So check if we have missed # some names here. data = result[r'data'] resolved = set(entry[r'fields'][r'username'] for entry in data) unresolved = set(names) - resolved if unresolved: - raise error.Abort(_('unknown username: %s') - % ' '.join(sorted(unresolved))) + raise error.Abort(_(b'unknown username: %s') + % b' '.join(sorted(unresolved))) return [entry[r'phid'] for entry in data] -@command('phabsend', - [('r', 'rev', [], _('revisions to send'), _('REV')), - ('', 'amend', True, _('update commit messages')), - ('', 'reviewer', [], _('specify reviewers')), - ('', 'confirm', None, _('ask for confirmation before sending'))], - _('REV [OPTIONS]')) +@command(b'phabsend', + [(b'r', b'rev', [], _(b'revisions to send'), _(b'REV')), + (b'', b'amend', True, _(b'update commit messages')), + (b'', b'reviewer', [], _(b'specify reviewers')), + (b'', b'confirm', None, _(b'ask for confirmation before sending'))], + _(b'REV [OPTIONS]')) def phabsend(ui, repo, *revs, **opts): """upload changesets to Phabricator @@ -452,29 +485,29 @@ phabsend will check obsstore and the above association to decide whether to update an existing Differential Revision, or create a new one. """ - revs = list(revs) + opts.get('rev', []) + revs = list(revs) + opts.get(b'rev', []) revs = scmutil.revrange(repo, revs) if not revs: - raise error.Abort(_('phabsend requires at least one changeset')) - if opts.get('amend'): + raise error.Abort(_(b'phabsend requires at least one changeset')) + if opts.get(b'amend'): cmdutil.checkunfinished(repo) # {newnode: (oldnode, olddiff, olddrev} oldmap = getoldnodedrevmap(repo, [repo[r].node() for r in revs]) - confirm = ui.configbool('phabsend', 'confirm') - confirm |= bool(opts.get('confirm')) + confirm = ui.configbool(b'phabsend', b'confirm') + confirm |= bool(opts.get(b'confirm')) if confirm: confirmed = _confirmbeforesend(repo, revs, oldmap) if not confirmed: - raise error.Abort(_('phabsend cancelled')) + raise error.Abort(_(b'phabsend cancelled')) actions = [] - reviewers = opts.get('reviewer', []) + reviewers = opts.get(b'reviewer', []) if reviewers: phids = userphids(repo, reviewers) - actions.append({'type': 'reviewers.add', 'value': phids}) + actions.append({b'type': b'reviewers.add', b'value': phids}) drevids = [] # [int] diffmap = {} # {newnode: diff} @@ -483,54 +516,54 @@ # can provide dependency relationship lastrevid = None for rev in revs: - ui.debug('sending rev %d\n' % rev) + ui.debug(b'sending rev %d\n' % rev) ctx = repo[rev] # Get Differential Revision ID oldnode, olddiff, revid = oldmap.get(ctx.node(), (None, None, None)) - if oldnode != ctx.node() or opts.get('amend'): + if oldnode != ctx.node() or opts.get(b'amend'): # Create or update Differential Revision revision, diff = createdifferentialrevision( ctx, revid, lastrevid, oldnode, olddiff, actions) diffmap[ctx.node()] = diff newrevid = int(revision[r'object'][r'id']) if revid: - action = 'updated' + action = b'updated' else: - action = 'created' + action = b'created' # Create a local tag to note the association, if commit message # does not have it already m = _differentialrevisiondescre.search(ctx.description()) - if not m or int(m.group('id')) != newrevid: - tagname = 'D%d' % newrevid + if not m or int(m.group(b'id')) != newrevid: + tagname = b'D%d' % newrevid tags.tag(repo, tagname, ctx.node(), message=None, user=None, date=None, local=True) else: # Nothing changed. But still set "newrevid" so the next revision # could depend on this one. newrevid = revid - action = 'skipped' + action = b'skipped' actiondesc = ui.label( - {'created': _('created'), - 'skipped': _('skipped'), - 'updated': _('updated')}[action], - 'phabricator.action.%s' % action) - drevdesc = ui.label('D%s' % newrevid, 'phabricator.drev') - nodedesc = ui.label(bytes(ctx), 'phabricator.node') - desc = ui.label(ctx.description().split('\n')[0], 'phabricator.desc') - ui.write(_('%s - %s - %s: %s\n') % (drevdesc, actiondesc, nodedesc, - desc)) + {b'created': _(b'created'), + b'skipped': _(b'skipped'), + b'updated': _(b'updated')}[action], + b'phabricator.action.%s' % action) + drevdesc = ui.label(b'D%s' % newrevid, b'phabricator.drev') + nodedesc = ui.label(bytes(ctx), b'phabricator.node') + desc = ui.label(ctx.description().split(b'\n')[0], b'phabricator.desc') + ui.write(_(b'%s - %s - %s: %s\n') % (drevdesc, actiondesc, nodedesc, + desc)) drevids.append(newrevid) lastrevid = newrevid # Update commit messages and remove tags - if opts.get('amend'): + if opts.get(b'amend'): unfi = repo.unfiltered() - drevs = callconduit(repo, 'differential.query', {'ids': drevids}) - with repo.wlock(), repo.lock(), repo.transaction('phabsend'): - wnode = unfi['.'].node() + drevs = callconduit(repo, b'differential.query', {b'ids': drevids}) + with repo.wlock(), repo.lock(), repo.transaction(b'phabsend'): + wnode = unfi[b'.'].node() mapping = {} # {oldnode: [newnode]} for i, rev in enumerate(revs): old = unfi[rev] @@ -546,23 +579,25 @@ new = context.metadataonlyctx( repo, old, parents=parents, text=newdesc, user=old.user(), date=old.date(), extra=old.extra()) + newnode = new.commit() + mapping[old.node()] = [newnode] # Update diff property writediffproperties(unfi[newnode], diffmap[old.node()]) # Remove local tags since it's no longer necessary - tagname = 'D%d' % drevid + tagname = b'D%d' % drevid if tagname in repo.tags(): tags.tag(repo, tagname, nullid, message=None, user=None, date=None, local=True) - scmutil.cleanupnodes(repo, mapping, 'phabsend') + scmutil.cleanupnodes(repo, mapping, b'phabsend', fixphase=True) if wnode in mapping: unfi.setparents(mapping[wnode][0]) # Map from "hg:meta" keys to header understood by "hg import". The order is # consistent with "hg export" output. -_metanamemap = util.sortdict([(r'user', 'User'), (r'date', 'Date'), - (r'node', 'Node ID'), (r'parent', 'Parent ')]) +_metanamemap = util.sortdict([(r'user', b'User'), (r'date', b'Date'), + (r'node', b'Node ID'), (r'parent', b'Parent ')]) def _confirmbeforesend(repo, revs, oldmap): url, token = readurltoken(repo) @@ -572,68 +607,69 @@ desc = ctx.description().splitlines()[0] oldnode, olddiff, drevid = oldmap.get(ctx.node(), (None, None, None)) if drevid: - drevdesc = ui.label('D%s' % drevid, 'phabricator.drev') + drevdesc = ui.label(b'D%s' % drevid, b'phabricator.drev') else: - drevdesc = ui.label(_('NEW'), 'phabricator.drev') + drevdesc = ui.label(_(b'NEW'), b'phabricator.drev') - ui.write(_('%s - %s: %s\n') % (drevdesc, - ui.label(bytes(ctx), 'phabricator.node'), - ui.label(desc, 'phabricator.desc'))) + ui.write(_(b'%s - %s: %s\n') + % (drevdesc, + ui.label(bytes(ctx), b'phabricator.node'), + ui.label(desc, b'phabricator.desc'))) - if ui.promptchoice(_('Send the above changes to %s (yn)?' - '$$ &Yes $$ &No') % url): + if ui.promptchoice(_(b'Send the above changes to %s (yn)?' + b'$$ &Yes $$ &No') % url): return False return True -_knownstatusnames = {'accepted', 'needsreview', 'needsrevision', 'closed', - 'abandoned'} +_knownstatusnames = {b'accepted', b'needsreview', b'needsrevision', b'closed', + b'abandoned'} def _getstatusname(drev): """get normalized status name from a Differential Revision""" - return drev[r'statusName'].replace(' ', '').lower() + return drev[r'statusName'].replace(b' ', b'').lower() # Small language to specify differential revisions. Support symbols: (), :X, # +, and -. _elements = { # token-type: binding-strength, primary, prefix, infix, suffix - '(': (12, None, ('group', 1, ')'), None, None), - ':': (8, None, ('ancestors', 8), None, None), - '&': (5, None, None, ('and_', 5), None), - '+': (4, None, None, ('add', 4), None), - '-': (4, None, None, ('sub', 4), None), - ')': (0, None, None, None, None), - 'symbol': (0, 'symbol', None, None, None), - 'end': (0, None, None, None, None), + b'(': (12, None, (b'group', 1, b')'), None, None), + b':': (8, None, (b'ancestors', 8), None, None), + b'&': (5, None, None, (b'and_', 5), None), + b'+': (4, None, None, (b'add', 4), None), + b'-': (4, None, None, (b'sub', 4), None), + b')': (0, None, None, None, None), + b'symbol': (0, b'symbol', None, None, None), + b'end': (0, None, None, None, None), } def _tokenize(text): view = memoryview(text) # zero-copy slice - special = '():+-& ' + special = b'():+-& ' pos = 0 length = len(text) while pos < length: - symbol = ''.join(itertools.takewhile(lambda ch: ch not in special, - view[pos:])) + symbol = b''.join(itertools.takewhile(lambda ch: ch not in special, + view[pos:])) if symbol: - yield ('symbol', symbol, pos) + yield (b'symbol', symbol, pos) pos += len(symbol) else: # special char, ignore space - if text[pos] != ' ': + if text[pos] != b' ': yield (text[pos], None, pos) pos += 1 - yield ('end', None, pos) + yield (b'end', None, pos) def _parse(text): tree, pos = parser.parser(_elements).parse(_tokenize(text)) if pos != len(text): - raise error.ParseError('invalid token', pos) + raise error.ParseError(b'invalid token', pos) return tree def _parsedrev(symbol): """str -> int or None, ex. 'D45' -> 45; '12' -> 12; 'x' -> None""" - if symbol.startswith('D') and symbol[1:].isdigit(): + if symbol.startswith(b'D') and symbol[1:].isdigit(): return int(symbol[1:]) if symbol.isdigit(): return int(symbol) @@ -643,11 +679,11 @@ drevs = set() ancestordrevs = set() op = tree[0] - if op == 'symbol': + if op == b'symbol': r = _parsedrev(tree[1]) if r: drevs.add(r) - elif op == 'ancestors': + elif op == b'ancestors': r, a = _prefetchdrevs(tree[1]) drevs.update(r) ancestordrevs.update(r) @@ -706,13 +742,14 @@ key = (params.get(r'ids') or params.get(r'phids') or [None])[0] if key in prefetched: return prefetched[key] - drevs = callconduit(repo, 'differential.query', params) + drevs = callconduit(repo, b'differential.query', params) # Fill prefetched with the result for drev in drevs: prefetched[drev[r'phid']] = drev prefetched[int(drev[r'id'])] = drev if key not in prefetched: - raise error.Abort(_('cannot get Differential Revision %r') % params) + raise error.Abort(_(b'cannot get Differential Revision %r') + % params) return prefetched[key] def getstack(topdrevids): @@ -730,7 +767,7 @@ auxiliary = drev.get(r'auxiliary', {}) depends = auxiliary.get(r'phabricator:depends-on', []) for phid in depends: - queue.append({'phids': [phid]}) + queue.append({b'phids': [phid]}) result.reverse() return smartset.baseset(result) @@ -741,7 +778,7 @@ drevs, ancestordrevs = _prefetchdrevs(tree) # developer config: phabricator.batchsize - batchsize = repo.ui.configint('phabricator', 'batchsize', 12) + batchsize = repo.ui.configint(b'phabricator', b'batchsize') # Prefetch Differential Revisions in batch tofetch = set(drevs) @@ -754,7 +791,7 @@ # Walk through the tree, return smartsets def walk(tree): op = tree[0] - if op == 'symbol': + if op == b'symbol': drev = _parsedrev(tree[1]) if drev: return smartset.baseset([drev]) @@ -763,16 +800,16 @@ if _getstatusname(prefetched[r]) == tree[1]] return smartset.baseset(drevs) else: - raise error.Abort(_('unknown symbol: %s') % tree[1]) - elif op in {'and_', 'add', 'sub'}: + raise error.Abort(_(b'unknown symbol: %s') % tree[1]) + elif op in {b'and_', b'add', b'sub'}: assert len(tree) == 3 return getattr(operator, op)(walk(tree[1]), walk(tree[2])) - elif op == 'group': + elif op == b'group': return walk(tree[1]) - elif op == 'ancestors': + elif op == b'ancestors': return getstack(walk(tree[1])) else: - raise error.ProgrammingError('illegal tree: %r' % tree) + raise error.ProgrammingError(b'illegal tree: %r' % tree) return [prefetched[r] for r in walk(tree)] @@ -786,9 +823,9 @@ summary = drev[r'summary'].rstrip() testplan = drev[r'testPlan'].rstrip() if testplan: - testplan = 'Test Plan:\n%s' % testplan - uri = 'Differential Revision: %s' % drev[r'uri'] - return '\n\n'.join(filter(None, [title, summary, testplan, uri])) + testplan = b'Test Plan:\n%s' % testplan + uri = b'Differential Revision: %s' % drev[r'uri'] + return b'\n\n'.join(filter(None, [title, summary, testplan, uri])) def getdiffmeta(diff): """get commit metadata (date, node, user, p1) from a diff object @@ -848,16 +885,17 @@ """ # Prefetch hg:meta property for all diffs diffids = sorted(set(max(int(v) for v in drev[r'diffs']) for drev in drevs)) - diffs = callconduit(repo, 'differential.querydiffs', {'ids': diffids}) + diffs = callconduit(repo, b'differential.querydiffs', {b'ids': diffids}) # Generate patch for each drev for drev in drevs: - repo.ui.note(_('reading D%s\n') % drev[r'id']) + repo.ui.note(_(b'reading D%s\n') % drev[r'id']) diffid = max(int(v) for v in drev[r'diffs']) - body = callconduit(repo, 'differential.getrawdiff', {'diffID': diffid}) + body = callconduit(repo, b'differential.getrawdiff', + {b'diffID': diffid}) desc = getdescfromdrev(drev) - header = '# HG changeset patch\n' + header = b'# HG changeset patch\n' # Try to preserve metadata from hg:meta property. Write hg patch # headers that can be read by the "import" command. See patchheadermap @@ -865,14 +903,14 @@ meta = getdiffmeta(diffs[str(diffid)]) for k in _metanamemap.keys(): if k in meta: - header += '# %s %s\n' % (_metanamemap[k], meta[k]) + header += b'# %s %s\n' % (_metanamemap[k], meta[k]) - content = '%s%s\n%s' % (header, desc, body) + content = b'%s%s\n%s' % (header, desc, body) write(encoding.unitolocal(content)) -@command('phabread', - [('', 'stack', False, _('read dependencies'))], - _('DREVSPEC [OPTIONS]')) +@command(b'phabread', + [(b'', b'stack', False, _(b'read dependencies'))], + _(b'DREVSPEC [OPTIONS]')) def phabread(ui, repo, spec, **opts): """print patches from Phabricator suitable for importing @@ -892,51 +930,51 @@ If --stack is given, follow dependencies information and read all patches. It is equivalent to the ``:`` operator. """ - if opts.get('stack'): - spec = ':(%s)' % spec + if opts.get(b'stack'): + spec = b':(%s)' % spec drevs = querydrev(repo, spec) readpatch(repo, drevs, ui.write) -@command('phabupdate', - [('', 'accept', False, _('accept revisions')), - ('', 'reject', False, _('reject revisions')), - ('', 'abandon', False, _('abandon revisions')), - ('', 'reclaim', False, _('reclaim revisions')), - ('m', 'comment', '', _('comment on the last revision')), - ], _('DREVSPEC [OPTIONS]')) +@command(b'phabupdate', + [(b'', b'accept', False, _(b'accept revisions')), + (b'', b'reject', False, _(b'reject revisions')), + (b'', b'abandon', False, _(b'abandon revisions')), + (b'', b'reclaim', False, _(b'reclaim revisions')), + (b'm', b'comment', b'', _(b'comment on the last revision')), + ], _(b'DREVSPEC [OPTIONS]')) def phabupdate(ui, repo, spec, **opts): """update Differential Revision in batch DREVSPEC selects revisions. See :hg:`help phabread` for its usage. """ - flags = [n for n in 'accept reject abandon reclaim'.split() if opts.get(n)] + flags = [n for n in b'accept reject abandon reclaim'.split() if opts.get(n)] if len(flags) > 1: - raise error.Abort(_('%s cannot be used together') % ', '.join(flags)) + raise error.Abort(_(b'%s cannot be used together') % b', '.join(flags)) actions = [] for f in flags: - actions.append({'type': f, 'value': 'true'}) + actions.append({b'type': f, b'value': b'true'}) drevs = querydrev(repo, spec) for i, drev in enumerate(drevs): - if i + 1 == len(drevs) and opts.get('comment'): - actions.append({'type': 'comment', 'value': opts['comment']}) + if i + 1 == len(drevs) and opts.get(b'comment'): + actions.append({b'type': b'comment', b'value': opts[b'comment']}) if actions: - params = {'objectIdentifier': drev[r'phid'], - 'transactions': actions} - callconduit(repo, 'differential.revision.edit', params) + params = {b'objectIdentifier': drev[r'phid'], + b'transactions': actions} + callconduit(repo, b'differential.revision.edit', params) templatekeyword = registrar.templatekeyword() -@templatekeyword('phabreview', requires={'ctx'}) +@templatekeyword(b'phabreview', requires={b'ctx'}) def template_review(context, mapping): """:phabreview: Object describing the review for this changeset. Has attributes `url` and `id`. """ - ctx = context.resource(mapping, 'ctx') + ctx = context.resource(mapping, b'ctx') m = _differentialrevisiondescre.search(ctx.description()) if m: return { - 'url': m.group('url'), - 'id': "D{}".format(m.group('id')), + b'url': m.group(b'url'), + b'id': b"D{}".format(m.group(b'id')), }
--- a/contrib/python3-whitelist Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/python3-whitelist Thu Jul 19 13:55:54 2018 -0400 @@ -2,6 +2,7 @@ test-add.t test-addremove-similar.t test-addremove.t +test-alias.t test-amend-subrepo.t test-amend.t test-ancestor.py @@ -14,6 +15,7 @@ test-automv.t test-backout.t test-backwards-remove.t +test-bad-pull.t test-basic.t test-bheads.t test-bisect.t @@ -22,6 +24,7 @@ test-blackbox.t test-bookmarks-current.t test-bookmarks-merge.t +test-bookmarks-pushpull.t test-bookmarks-rebase.t test-bookmarks-strip.t test-bookmarks.t @@ -30,16 +33,24 @@ test-branch-tag-confict.t test-branches.t test-bundle-phases.t +test-bundle-r.t test-bundle-type.t test-bundle-vs-outgoing.t +test-bundle.t +test-bundle2-exchange.t +test-bundle2-format.t test-bundle2-multiple-changegroups.t +test-bundle2-pushback.t +test-bundle2-remote-changegroup.t test-cappedreader.py test-casecollision.t test-cat.t +test-cbor.py test-censor.t test-changelog-exec.t test-check-commit.t test-check-execute.t +test-check-interfaces.py test-check-module-imports.t test-check-pyflakes.t test-check-pylint.t @@ -49,7 +60,7 @@ test-clone-pull-corruption.t test-clone-r.t test-clone-update-order.t -test-command-template.t +test-clonebundles.t test-commit-amend.t test-commit-interactive.t test-commit-multiple.t @@ -61,10 +72,16 @@ test-config.t test-conflict.t test-confused-revert.t +test-context.py test-contrib-check-code.t test-contrib-check-commit.t test-convert-authormap.t test-convert-clonebranches.t +test-convert-cvs-branch.t +test-convert-cvs-detectmerge.t +test-convert-cvs-synthetic.t +test-convert-cvs.t +test-convert-cvsnt-mergepoints.t test-convert-datesort.t test-convert-filemap.t test-convert-hg-sink.t @@ -81,6 +98,7 @@ test-debugindexdot.t test-debugrename.t test-default-push.t +test-diff-antipatience.t test-diff-binary-file.t test-diff-change.t test-diff-copy-depth.t @@ -99,6 +117,7 @@ test-dirstate-backup.t test-dirstate-nonnormalset.t test-dirstate.t +test-dispatch.py test-doctest.py test-double-merge.t test-drawdag.t @@ -114,8 +133,11 @@ test-eol-add.t test-eol-clone.t test-eol-hook.t +test-eol-patch.t test-eol-tag.t test-eol-update.t +test-eol.t +test-eolfilename.t test-excessive-merge.t test-exchange-obsmarkers-case-A1.t test-exchange-obsmarkers-case-A2.t @@ -143,9 +165,15 @@ test-export.t test-extdata.t test-extdiff.t +test-extensions-afterloaded.t +test-extensions-wrapfunction.py test-extra-filelog-entry.t +test-fetch.t test-filebranch.t +test-filecache.py +test-filelog.py test-fileset-generated.t +test-fileset.t test-fix-topology.t test-flags.t test-generaldelta.t @@ -158,10 +186,12 @@ test-hghave.t test-hgignore.t test-hgk.t +test-hgrc.t test-hgweb-bundle.t test-hgweb-descend-empties.t test-hgweb-empty.t test-hgweb-removed.t +test-hgwebdir-paths.py test-hgwebdirsym.t test-histedit-arguments.t test-histedit-base.t @@ -171,6 +201,7 @@ test-histedit-edit.t test-histedit-fold-non-commute.t test-histedit-fold.t +test-histedit-no-backup.t test-histedit-no-change.t test-histedit-non-commute-abort.t test-histedit-non-commute.t @@ -181,12 +212,18 @@ test-http-bundle1.t test-http-clone-r.t test-http.t +test-hybridencode.py test-identify.t +test-impexp-branch.t +test-import-bypass.t +test-import-eol.t +test-import-merge.t test-import-unknown.t test-import.t test-imports-checker.t test-incoming-outgoing.t test-inherit-mode.t +test-init.t test-issue1089.t test-issue1102.t test-issue1175.t @@ -209,12 +246,14 @@ test-journal-exists.t test-journal-share.t test-journal.t +test-known.t test-largefiles-cache.t test-largefiles-misc.t test-largefiles-small-disk.t test-largefiles-update.t test-largefiles.t test-lfs-largefiles.t +test-lfs-pointer.py test-linerange.py test-locate.t test-lock-badness.t @@ -254,6 +293,8 @@ test-merge7.t test-merge8.t test-merge9.t +test-minifileset.py +test-minirst.py test-mq-git.t test-mq-header-date.t test-mq-header-from.t @@ -298,8 +339,11 @@ test-narrow-shallow.t test-narrow-strip.t test-narrow-update.t +test-narrow-widen.t +test-narrow.t test-nested-repo.t test-newbranch.t +test-nointerrupt.t test-obshistory.t test-obsmarker-template.t test-obsmarkers-effectflag.t @@ -307,10 +351,16 @@ test-obsolete-changeset-exchange.t test-obsolete-checkheads.t test-obsolete-distributed.t +test-obsolete-divergent.t test-obsolete-tag-cache.t +test-pager.t test-parents.t +test-parseindex2.py +test-patch-offset.t +test-patch.t test-pathconflicts-merge.t test-pathconflicts-update.t +test-pathencode.py test-pending.t test-permissions.t test-phases.t @@ -320,6 +370,7 @@ test-pull-pull-corruption.t test-pull-r.t test-pull-update.t +test-pull.t test-purge.t test-push-checkheads-partial-C1.t test-push-checkheads-partial-C2.t @@ -350,7 +401,9 @@ test-push-checkheads-unpushed-D7.t test-push-http.t test-push-warn.t +test-push.t test-pushvars.t +test-qrecord.t test-rebase-abort.t test-rebase-base-flag.t test-rebase-bookmarks.t @@ -378,9 +431,11 @@ test-rebase-scenario-global.t test-rebase-templates.t test-rebase-transaction.t +test-rebuildstate.t test-record.t test-relink.t test-remove.t +test-removeemptydirs.t test-rename-after-merge.t test-rename-dir-merge.t test-rename-merge1.t @@ -389,11 +444,14 @@ test-repo-compengines.t test-resolve.t test-revert-flags.t +test-revert-interactive.t test-revert-unknown.t test-revlog-ancestry.py test-revlog-group-emptyiter.t test-revlog-mmapindex.t test-revlog-packentry.t +test-revlog-raw.py +test-revlog-v2.t test-revset-dirstate-parents.t test-revset-legacy-lookup.t test-revset-outgoing.t @@ -409,34 +467,56 @@ test-show-work.t test-show.t test-simple-update.t +test-simplekeyvaluefile.py +test-simplemerge.py test-single-head.t test-sparse-clear.t +test-sparse-clone.t test-sparse-import.t test-sparse-merges.t test-sparse-profiles.t test-sparse-requirement.t test-sparse-verbose-json.t +test-sparse.t +test-split.t +test-ssh-bundle1.t test-ssh-clone-r.t +test-ssh-proto-unbundle.t test-ssh-proto.t +test-ssh.t test-sshserver.py test-stack.t +test-status-inprocess.py test-status-rev.t test-status-terse.t +test-strict.t test-strip-cross.t test-strip.t test-subrepo-deep-nested-change.t test-subrepo-missing.t +test-subrepo-paths.t test-subrepo-recursion.t test-subrepo-relative-path.t test-subrepo.t +test-symlink-os-yes-fs-no.py +test-symlink-placeholder.t test-symlinks.t test-tag.t test-tags.t -test-template-engine.t +test-template-basic.t +test-template-functions.t +test-template-keywords.t +test-template-map.t +test-transplant.t test-treemanifest.t +test-ui-color.py +test-ui-config.py +test-ui-verbosity.py test-unamend.t +test-unbundlehash.t test-uncommit.t test-unified-test.t +test-unionrepo.t test-unrelated-pull.t test-up-local-change.t test-update-branches.t @@ -447,11 +527,16 @@ test-upgrade-repo.t test-url-download.t test-url-rev.t +test-url.py test-username-newline.t test-verify.t +test-walk.t +test-walkrepo.py test-websub.t test-win32text.t test-wireproto-clientreactor.py test-wireproto-framing.py test-wireproto-serverreactor.py +test-wireproto.py +test-wsgirequest.py test-xdg.t
--- a/contrib/synthrepo.py Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/synthrepo.py Thu Jul 19 13:55:54 2018 -0400 @@ -54,13 +54,16 @@ ) from mercurial import ( context, + diffutil, error, hg, patch, registrar, scmutil, ) -from mercurial.utils import dateutil +from mercurial.utils import ( + dateutil, +) # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -172,13 +175,10 @@ revs = scmutil.revrange(repo, revs) revs.sort() - progress = ui.progress - _analyzing = _('analyzing') - _changesets = _('changesets') - _total = len(revs) - + progress = ui.makeprogress(_('analyzing'), unit=_('changesets'), + total=len(revs)) for i, rev in enumerate(revs): - progress(_analyzing, i, unit=_changesets, total=_total) + progress.update(i) ctx = repo[rev] pl = ctx.parents() pctx = pl[0] @@ -196,7 +196,9 @@ if lastctx.rev() != nullrev: timedelta = ctx.date()[0] - lastctx.date()[0] interarrival[roundto(timedelta, 300)] += 1 - diff = sum((d.splitlines() for d in ctx.diff(pctx, git=True)), []) + diffopts = diffutil.diffallopts(ui, {'git': True}) + diff = sum((d.splitlines() + for d in ctx.diff(pctx, opts=diffopts)), []) fileadds, diradds, fileremoves, filechanges = 0, 0, 0, 0 for filename, mar, lineadd, lineremove, isbin in parsegitdiff(diff): if isbin: @@ -222,6 +224,7 @@ filesadded[fileadds] += 1 dirsadded[diradds] += 1 filesremoved[fileremoves] += 1 + progress.complete() invchildren = zerodict() @@ -338,7 +341,6 @@ nevertouch = {'.hgsub', '.hgignore', '.hgtags'} - progress = ui.progress _synthesizing = _('synthesizing') _files = _('initial files') _changesets = _('changesets') @@ -362,8 +364,9 @@ path = os.path.dirname(path) return True + progress = ui.makeprogress(_synthesizing, unit=_files, total=initcount) for i in xrange(0, initcount): - ui.progress(_synthesizing, i, unit=_files, total=initcount) + progress.update(i) path = pickpath() while not validpath(path): @@ -378,7 +381,7 @@ def filectxfn(repo, memctx, path): return context.memfilectx(repo, memctx, path, files[path]) - ui.progress(_synthesizing, None) + progress.complete() message = 'synthesized wide repo with %d files' % (len(files),) mc = context.memctx(repo, [pctx.node(), nullid], message, files, filectxfn, ui.username(), @@ -394,8 +397,9 @@ # Synthesize incremental revisions to the repository, adding repo depth. count = int(opts['count']) heads = set(map(repo.changelog.rev, repo.heads())) + progress = ui.makeprogress(_synthesizing, unit=_changesets, total=count) for i in xrange(count): - progress(_synthesizing, i, unit=_changesets, total=count) + progress.update(i) node = repo.changelog.node revs = len(repo) @@ -485,6 +489,7 @@ heads.add(repo.changelog.rev(newnode)) heads.discard(r1) heads.discard(r2) + progress.complete() lock.release() wlock.release()
--- a/contrib/wix/help.wxs Sun Jul 01 23:36:53 2018 +0900 +++ b/contrib/wix/help.wxs Thu Jul 19 13:55:54 2018 -0400 @@ -19,6 +19,7 @@ <File Name="color.txt" /> <File Name="config.txt" KeyPath="yes" /> <File Name="dates.txt" /> + <File Name="deprecated.txt" /> <File Name="diffs.txt" /> <File Name="environment.txt" /> <File Name="extensions.txt" />
--- a/hgdemandimport/__init__.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgdemandimport/__init__.py Thu Jul 19 13:55:54 2018 -0400 @@ -21,8 +21,9 @@ else: from . import demandimportpy2 as demandimport -# Extensions can add to this list if necessary. -ignore = [ +# Full module names which can't be lazy imported. +# Extensions can add to this set. +IGNORES = { '__future__', '_hashlib', # ImportError during pkg_resources/__init__.py:fixup_namespace_package @@ -55,17 +56,15 @@ '__builtin__', 'builtins', 'urwid.command_map', # for pudb - ] +} _pypy = '__pypy__' in sys.builtin_module_names if _pypy: - ignore.extend([ - # _ctypes.pointer is shadowed by "from ... import pointer" (PyPy 5) - '_ctypes.pointer', - ]) + # _ctypes.pointer is shadowed by "from ... import pointer" (PyPy 5) + IGNORES.add('_ctypes.pointer') -demandimport.init(ignore) +demandimport.init(IGNORES) # Re-export. isenabled = demandimport.isenabled
--- a/hgdemandimport/demandimportpy2.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgdemandimport/demandimportpy2.py Thu Jul 19 13:55:54 2018 -0400 @@ -162,7 +162,7 @@ _pypy = '__pypy__' in sys.builtin_module_names def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1): - if locals is None or name in ignore or fromlist == ('*',): + if locals is None or name in ignores or fromlist == ('*',): # these cases we can't really delay return _hgextimport(_origimport, name, globals, locals, fromlist, level) elif not fromlist: @@ -209,7 +209,7 @@ # while processing the import statement. return mn = '%s.%s' % (mod.__name__, attr) - if mn in ignore: + if mn in ignores: importfunc = _origimport else: importfunc = _demandmod @@ -273,11 +273,11 @@ return mod -ignore = [] +ignores = set() -def init(ignorelist): - global ignore - ignore = ignorelist +def init(ignoreset): + global ignores + ignores = ignoreset def isenabled(): return builtins.__import__ == _demandimport
--- a/hgdemandimport/demandimportpy3.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgdemandimport/demandimportpy3.py Thu Jul 19 13:55:54 2018 -0400 @@ -40,7 +40,7 @@ """ def exec_module(self, module): """Make the module load lazily.""" - if _deactivated or module.__name__ in ignore: + if _deactivated or module.__name__ in ignores: self.loader.exec_module(module) else: super().exec_module(module) @@ -62,11 +62,11 @@ (_bytecode_loader, importlib.machinery.BYTECODE_SUFFIXES), ) -ignore = [] +ignores = set() -def init(ignorelist): - global ignore - ignore = ignorelist +def init(ignoreset): + global ignores + ignores = ignoreset def isenabled(): return _makefinder in sys.path_hooks and not _deactivated
--- a/hgext/acl.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/acl.py Thu Jul 19 13:55:54 2018 -0400 @@ -57,6 +57,28 @@ a glob syntax by default). The corresponding values follow the same syntax as the other sections above. +Bookmark-based Access Control +----------------------------- +Use the ``acl.deny.bookmarks`` and ``acl.allow.bookmarks`` sections to +have bookmark-based access control. Keys in these sections can be +either: + +- a bookmark name, or +- an asterisk, to match any bookmark; + +The corresponding values can be either: + +- a comma-separated list containing users and groups, or +- an asterisk, to match anyone; + +You can add the "!" prefix to a user or group name to invert the sense +of the match. + +Note: for interactions between clients and servers using Mercurial 3.6+ +a rejection will generally reject the entire push, for interactions +involving older clients, the commit transactions will already be accepted, +and only the bookmark movement will be rejected. + Groups ------ @@ -326,9 +348,10 @@ ensureenabled(ui) - if hooktype not in ['pretxnchangegroup', 'pretxncommit']: - raise error.Abort(_('config error - hook type "%s" cannot stop ' - 'incoming changesets nor commits') % hooktype) + if hooktype not in ['pretxnchangegroup', 'pretxncommit', 'prepushkey']: + raise error.Abort( + _('config error - hook type "%s" cannot stop ' + 'incoming changesets, commits, nor bookmarks') % hooktype) if (hooktype == 'pretxnchangegroup' and source not in ui.configlist('acl', 'sources')): ui.debug('acl: changes have source "%s" - skipping\n' % source) @@ -345,6 +368,30 @@ ui.debug('acl: checking access for user "%s"\n' % user) + if hooktype == 'prepushkey': + _pkhook(ui, repo, hooktype, node, source, user, **kwargs) + else: + _txnhook(ui, repo, hooktype, node, source, user, **kwargs) + +def _pkhook(ui, repo, hooktype, node, source, user, **kwargs): + if kwargs['namespace'] == 'bookmarks': + bookmark = kwargs['key'] + ctx = kwargs['new'] + allowbookmarks = buildmatch(ui, None, user, 'acl.allow.bookmarks') + denybookmarks = buildmatch(ui, None, user, 'acl.deny.bookmarks') + + if denybookmarks and denybookmarks(bookmark): + raise error.Abort(_('acl: user "%s" denied on bookmark "%s"' + ' (changeset "%s")') + % (user, bookmark, ctx)) + if allowbookmarks and not allowbookmarks(bookmark): + raise error.Abort(_('acl: user "%s" not allowed on bookmark "%s"' + ' (changeset "%s")') + % (user, bookmark, ctx)) + ui.debug('acl: bookmark access granted: "%s" on bookmark "%s"\n' + % (ctx, bookmark)) + +def _txnhook(ui, repo, hooktype, node, source, user, **kwargs): # deprecated config: acl.config cfg = ui.config('acl', 'config') if cfg:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/beautifygraph.py Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,93 @@ +# -*- coding: UTF-8 -*- +# beautifygraph.py - improve graph output by using Unicode characters +# +# Copyright 2018 John Stiles <johnstiles@gmail.com> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +'''beautify log -G output by using Unicode characters (EXPERIMENTAL) + + A terminal with UTF-8 support and monospace narrow text are required. +''' + +from __future__ import absolute_import + +from mercurial.i18n import _ +from mercurial import ( + encoding, + extensions, + graphmod, + templatekw, +) + +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for +# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should +# be specifying the version(s) of Mercurial they are tested with, or +# leave the attribute unspecified. +testedwith = 'ships-with-hg-core' + +def prettyedge(before, edge, after): + if edge == '~': + return '\xE2\x95\xA7' # U+2567 ╧ + if edge == 'X': + return '\xE2\x95\xB3' # U+2573 ╳ + if edge == '/': + return '\xE2\x95\xB1' # U+2571 ╱ + if edge == '-': + return '\xE2\x94\x80' # U+2500 ─ + if edge == '|': + return '\xE2\x94\x82' # U+2502 │ + if edge == ':': + return '\xE2\x94\x86' # U+2506 ┆ + if edge == '\\': + return '\xE2\x95\xB2' # U+2572 ╲ + if edge == '+': + if before == ' ' and not after == ' ': + return '\xE2\x94\x9C' # U+251C ├ + if after == ' ' and not before == ' ': + return '\xE2\x94\xA4' # U+2524 ┤ + return '\xE2\x94\xBC' # U+253C ┼ + return edge + +def convertedges(line): + line = ' %s ' % line + pretty = [] + for idx in xrange(len(line) - 2): + pretty.append(prettyedge(line[idx], line[idx + 1], line[idx + 2])) + return ''.join(pretty) + +def getprettygraphnode(orig, *args, **kwargs): + node = orig(*args, **kwargs) + if node == 'o': + return '\xE2\x97\x8B' # U+25CB ○ + if node == '@': + return '\xE2\x97\x8D' # U+25CD ◍ + if node == '*': + return '\xE2\x88\x97' # U+2217 ∗ + if node == 'x': + return '\xE2\x97\x8C' # U+25CC ◌ + if node == '_': + return '\xE2\x95\xA4' # U+2564 ╤ + return node + +def outputprettygraph(orig, ui, graph, *args, **kwargs): + (edges, text) = zip(*graph) + graph = zip([convertedges(e) for e in edges], text) + return orig(ui, graph, *args, **kwargs) + +def extsetup(ui): + if encoding.encoding != 'UTF-8': + ui.warn(_('beautifygraph: unsupported encoding, UTF-8 required\n')) + return + + if 'A' in encoding._wide: + ui.warn(_('beautifygraph: unsupported terminal settings, ' + 'monospace narrow text required\n')) + return + + if ui.plain('graph'): + return + + extensions.wrapfunction(graphmod, 'outputgraph', outputprettygraph) + extensions.wrapfunction(templatekw, 'getgraphnode', getprettygraphnode)
--- a/hgext/censor.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/censor.py Thu Jul 19 13:55:54 2018 -0400 @@ -32,7 +32,6 @@ from mercurial import ( error, - lock as lockmod, registrar, revlog, scmutil, @@ -52,13 +51,8 @@ ('t', 'tombstone', '', _('replacement tombstone data'), _('TEXT'))], _('-r REV [-t TEXT] [FILE]')) def censor(ui, repo, path, rev='', tombstone='', **opts): - wlock = lock = None - try: - wlock = repo.wlock() - lock = repo.lock() + with repo.wlock(), repo.lock(): return _docensor(ui, repo, path, rev, tombstone, **opts) - finally: - lockmod.release(lock, wlock) def _docensor(ui, repo, path, rev='', tombstone='', **opts): if not path:
--- a/hgext/churn.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/churn.py Thu Jul 19 13:55:54 2018 -0400 @@ -52,7 +52,7 @@ def getkey(ctx): t, tz = ctx.date() date = datetime.datetime(*time.gmtime(float(t) - tz)[:6]) - return date.strftime(opts['dateformat']) + return date.strftime(encoding.strfromlocal(opts['dateformat'])) else: tmpl = opts.get('oldtemplate') or opts.get('template') tmpl = logcmdutil.maketemplater(ui, repo, tmpl) @@ -61,7 +61,8 @@ tmpl.show(ctx) return ui.popbuffer() - state = {'count': 0} + progress = ui.makeprogress(_('analyzing'), unit=_('revisions'), + total=len(repo)) rate = {} df = False if opts.get('date'): @@ -87,14 +88,12 @@ lines = changedlines(ui, repo, ctx1, ctx, fns) rate[key] = [r + l for r, l in zip(rate.get(key, (0, 0)), lines)] - state['count'] += 1 - ui.progress(_('analyzing'), state['count'], total=len(repo), - unit=_('revisions')) + progress.increment() for ctx in cmdutil.walkchangerevs(repo, m, opts, prep): continue - ui.progress(_('analyzing'), None) + progress.complete() return rate @@ -161,7 +160,7 @@ if not aliases and os.path.exists(repo.wjoin('.hgchurn')): aliases = repo.wjoin('.hgchurn') if aliases: - for l in open(aliases, "r"): + for l in open(aliases, "rb"): try: alias, actual = l.rsplit('=' in l and '=' or None, 1) amap[alias.strip()] = actual.strip()
--- a/hgext/convert/__init__.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/convert/__init__.py Thu Jul 19 13:55:54 2018 -0400 @@ -204,6 +204,14 @@ :convert.hg.revs: revset specifying the source revisions to convert. + Bazaar Source + ############# + + The following options can be used with ``--config``: + + :convert.bzr.saverev: whether to store the original Bazaar commit ID in + the metadata of the destination commit. The default is True. + CVS Source ##########
--- a/hgext/convert/bzr.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/convert/bzr.py Thu Jul 19 13:55:54 2018 -0400 @@ -19,7 +19,7 @@ from . import common # these do not work with demandimport, blacklist -demandimport.ignore.extend([ +demandimport.IGNORES.update([ 'bzrlib.transactions', 'bzrlib.urlutils', 'ElementPath', @@ -65,6 +65,7 @@ raise common.NoRepo(_('%s does not look like a Bazaar repository') % path) self._parentids = {} + self._saverev = ui.configbool('convert', 'bzr.saverev') def _checkrepotype(self, path): # Lightweight checkouts detection is informational but probably @@ -175,7 +176,8 @@ author=self.recode(rev.committer), desc=self.recode(rev.message), branch=branch, - rev=version) + rev=version, + saverev=self._saverev) def gettags(self): bytetags = {}
--- a/hgext/convert/common.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/convert/common.py Thu Jul 19 13:55:54 2018 -0400 @@ -214,7 +214,7 @@ if not encoding: encoding = self.encoding or 'utf-8' - if isinstance(s, unicode): + if isinstance(s, pycompat.unicode): return s.encode("utf-8") try: return s.decode(pycompat.sysstr(encoding)).encode("utf-8")
--- a/hgext/convert/convcmd.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/convert/convcmd.py Thu Jul 19 13:55:54 2018 -0400 @@ -55,7 +55,7 @@ orig_encoding = 'ascii' def recode(s): - if isinstance(s, unicode): + if isinstance(s, pycompat.unicode): return s.encode(pycompat.sysstr(orig_encoding), 'replace') else: return s.decode('utf-8').encode( @@ -123,7 +123,7 @@ exceptions.append(inst) if not ui.quiet: for inst in exceptions: - ui.write("%s\n" % inst) + ui.write("%s\n" % pycompat.bytestr(inst)) raise error.Abort(_('%s: missing or unsupported repository') % path) def convertsink(ui, path, type): @@ -143,13 +143,11 @@ def __init__(self, ui, source, filecount): self.ui = ui self.source = source - self.filecount = filecount - self.retrieved = 0 + self.progress = ui.makeprogress(_('getting files'), unit=_('files'), + total=filecount) def getfile(self, file, rev): - self.retrieved += 1 - self.ui.progress(_('getting files'), self.retrieved, - item=file, total=self.filecount, unit=_('files')) + self.progress.increment(item=file) return self.source.getfile(file, rev) def targetfilebelongstosource(self, targetfilename): @@ -159,7 +157,7 @@ return self.source.lookuprev(rev) def close(self): - self.ui.progress(_('getting files'), None) + self.progress.complete() class converter(object): def __init__(self, ui, source, dest, revmapfile, opts): @@ -234,10 +232,12 @@ def walktree(self, heads): '''Return a mapping that identifies the uncommitted parents of every uncommitted changeset.''' - visit = heads + visit = list(heads) known = set() parents = {} numcommits = self.source.numcommits() + progress = self.ui.makeprogress(_('scanning'), unit=_('revisions'), + total=numcommits) while visit: n = visit.pop(0) if n in known: @@ -247,14 +247,13 @@ if m == SKIPREV or self.dest.hascommitfrommap(m): continue known.add(n) - self.ui.progress(_('scanning'), len(known), unit=_('revisions'), - total=numcommits) + progress.update(len(known)) commit = self.cachecommit(n) parents[n] = [] for p in commit.parents: parents[n].append(p) visit.append(p) - self.ui.progress(_('scanning'), None) + progress.complete() return parents @@ -510,6 +509,8 @@ c = None self.ui.status(_("converting...\n")) + progress = self.ui.makeprogress(_('converting'), + unit=_('revisions'), total=len(t)) for i, c in enumerate(t): num -= 1 desc = self.commitcache[c].desc @@ -520,10 +521,9 @@ # uses is 'utf-8' self.ui.status("%d %s\n" % (num, recode(desc))) self.ui.note(_("source: %s\n") % recode(c)) - self.ui.progress(_('converting'), i, unit=_('revisions'), - total=len(t)) + progress.update(i) self.copy(c) - self.ui.progress(_('converting'), None) + progress.complete() if not self.ui.configbool('convert', 'skiptags'): tags = self.source.gettags()
--- a/hgext/convert/cvsps.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/convert/cvsps.py Thu Jul 19 13:55:54 2018 -0400 @@ -6,6 +6,7 @@ # GNU General Public License version 2 or any later version. from __future__ import absolute_import +import functools import os import re @@ -50,8 +51,8 @@ self.__dict__.update(entries) def __repr__(self): - items = ("%s=%r"%(k, self.__dict__[k]) for k in sorted(self.__dict__)) - return "%s(%s)"%(type(self).__name__, ", ".join(items)) + items = (r"%s=%r"%(k, self.__dict__[k]) for k in sorted(self.__dict__)) + return r"%s(%s)"%(type(self).__name__, r", ".join(items)) class logerror(Exception): pass @@ -110,25 +111,25 @@ log = [] # list of logentry objects containing the CVS state # patterns to match in CVS (r)log output, by state of use - re_00 = re.compile('RCS file: (.+)$') - re_01 = re.compile('cvs \\[r?log aborted\\]: (.+)$') - re_02 = re.compile('cvs (r?log|server): (.+)\n$') - re_03 = re.compile("(Cannot access.+CVSROOT)|" - "(can't create temporary directory.+)$") - re_10 = re.compile('Working file: (.+)$') - re_20 = re.compile('symbolic names:') - re_30 = re.compile('\t(.+): ([\\d.]+)$') - re_31 = re.compile('----------------------------$') - re_32 = re.compile('=======================================' - '======================================$') - re_50 = re.compile('revision ([\\d.]+)(\s+locked by:\s+.+;)?$') - re_60 = re.compile(r'date:\s+(.+);\s+author:\s+(.+);\s+state:\s+(.+?);' - r'(\s+lines:\s+(\+\d+)?\s+(-\d+)?;)?' - r'(\s+commitid:\s+([^;]+);)?' - r'(.*mergepoint:\s+([^;]+);)?') - re_70 = re.compile('branches: (.+);$') + re_00 = re.compile(b'RCS file: (.+)$') + re_01 = re.compile(b'cvs \\[r?log aborted\\]: (.+)$') + re_02 = re.compile(b'cvs (r?log|server): (.+)\n$') + re_03 = re.compile(b"(Cannot access.+CVSROOT)|" + b"(can't create temporary directory.+)$") + re_10 = re.compile(b'Working file: (.+)$') + re_20 = re.compile(b'symbolic names:') + re_30 = re.compile(b'\t(.+): ([\\d.]+)$') + re_31 = re.compile(b'----------------------------$') + re_32 = re.compile(b'=======================================' + b'======================================$') + re_50 = re.compile(b'revision ([\\d.]+)(\s+locked by:\s+.+;)?$') + re_60 = re.compile(br'date:\s+(.+);\s+author:\s+(.+);\s+state:\s+(.+?);' + br'(\s+lines:\s+(\+\d+)?\s+(-\d+)?;)?' + br'(\s+commitid:\s+([^;]+);)?' + br'(.*mergepoint:\s+([^;]+);)?') + re_70 = re.compile(b'branches: (.+);$') - file_added_re = re.compile(r'file [^/]+ was (initially )?added on branch') + file_added_re = re.compile(br'file [^/]+ was (initially )?added on branch') prefix = '' # leading path to strip of what we get from CVS @@ -509,7 +510,8 @@ comment = entry.comment for e in encodings: try: - entry.comment = comment.decode(e).encode('utf-8') + entry.comment = comment.decode( + pycompat.sysstr(e)).encode('utf-8') if ui.debugflag: ui.debug("transcoding by %s: %s of %s\n" % (e, revstr(entry.revision), entry.file)) @@ -565,11 +567,15 @@ mindate = {} for e in log: if e.commitid: - mindate[e.commitid] = min(e.date, mindate.get(e.commitid)) + if e.commitid not in mindate: + mindate[e.commitid] = e.date + else: + mindate[e.commitid] = min(e.date, mindate[e.commitid]) # Merge changesets - log.sort(key=lambda x: (mindate.get(x.commitid), x.commitid, x.comment, - x.author, x.branch, x.date, x.branchpoints)) + log.sort(key=lambda x: (mindate.get(x.commitid, (-1, 0)), + x.commitid or '', x.comment, + x.author, x.branch or '', x.date, x.branchpoints)) changesets = [] files = set() @@ -653,7 +659,7 @@ return 0 for c in changesets: - c.entries.sort(entitycompare) + c.entries.sort(key=functools.cmp_to_key(entitycompare)) # Sort changesets by date @@ -706,7 +712,7 @@ d = c(len(l.branchpoints), len(r.branchpoints)) return d - changesets.sort(cscmp) + changesets.sort(key=functools.cmp_to_key(cscmp)) # Collect tags @@ -729,12 +735,12 @@ # {{mergefrombranch BRANCHNAME}} by setting two parents. if mergeto is None: - mergeto = r'{{mergetobranch ([-\w]+)}}' + mergeto = br'{{mergetobranch ([-\w]+)}}' if mergeto: mergeto = re.compile(mergeto) if mergefrom is None: - mergefrom = r'{{mergefrombranch ([-\w]+)}}' + mergefrom = br'{{mergefrombranch ([-\w]+)}}' if mergefrom: mergefrom = re.compile(mergefrom) @@ -797,7 +803,7 @@ except KeyError: ui.warn(_("warning: CVS commit message references " "non-existent branch %r:\n%s\n") - % (m, c.comment)) + % (pycompat.bytestr(m), c.comment)) if m in branches and c.branch != m and not candidate.synthetic: c.parents.append(candidate) @@ -940,7 +946,8 @@ if fn.startswith(opts["prefix"]): fn = fn[len(opts["prefix"]):] ui.write('\t%s:%s->%s%s \n' % ( - fn, '.'.join([str(x) for x in f.parent]) or 'INITIAL', + fn, + '.'.join([b"%d" % x for x in f.parent]) or 'INITIAL', '.'.join([(b"%d" % x) for x in f.revision]), ['', '(DEAD)'][f.dead])) ui.write('\n')
--- a/hgext/convert/darcs.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/convert/darcs.py Thu Jul 19 13:55:54 2018 -0400 @@ -10,10 +10,11 @@ import os import re import shutil -import tempfile + from mercurial.i18n import _ from mercurial import ( error, + pycompat, util, ) from mercurial.utils import dateutil @@ -76,7 +77,7 @@ self.ui.warn(_('failed to detect repository format!')) def before(self): - self.tmppath = tempfile.mkdtemp( + self.tmppath = pycompat.mkdtemp( prefix='convert-' + os.path.basename(self.path) + '-') output, status = self.run('init', repodir=self.tmppath) self.checkexit(status) @@ -103,7 +104,7 @@ shutil.rmtree(self.tmppath, ignore_errors=True) def recode(self, s, encoding=None): - if isinstance(s, unicode): + if isinstance(s, pycompat.unicode): # XMLParser returns unicode objects for anything it can't # encode into ASCII. We convert them back to str to get # recode's normal conversion behavior. @@ -125,8 +126,7 @@ return etree.getroot() def format(self): - output, status = self.run('show', 'repo', no_files=True, - repodir=self.path) + output, status = self.run('show', 'repo', repodir=self.path) self.checkexit(status) m = re.search(r'^\s*Format:\s*(.*)$', output, re.MULTILINE) if not m:
--- a/hgext/convert/subversion.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/convert/subversion.py Thu Jul 19 13:55:54 2018 -0400 @@ -5,7 +5,6 @@ import os import re -import tempfile import xml.dom.minidom from mercurial.i18n import _ @@ -751,9 +750,10 @@ self.module = new_module self.reparent(self.module) + progress = self.ui.makeprogress(_('scanning paths'), unit=_('paths'), + total=len(paths)) for i, (path, ent) in enumerate(paths): - self.ui.progress(_('scanning paths'), i, item=path, - total=len(paths), unit=_('paths')) + progress.update(i, item=path) entrypath = self.getrelpath(path) kind = self._checkpath(entrypath, revnum) @@ -839,7 +839,7 @@ copytopath = self.getrelpath(copytopath) copies[self.recode(copytopath)] = self.recode(childpath) - self.ui.progress(_('scanning paths'), None) + progress.complete() changed.update(removed) return (list(changed), removed, copies) @@ -1081,7 +1081,7 @@ ' hg executable is in PATH')) return logstream(stdout) -pre_revprop_change = '''#!/bin/sh +pre_revprop_change = b'''#!/bin/sh REPOS="$1" REV="$2" @@ -1098,8 +1098,8 @@ ''' class svn_sink(converter_sink, commandline): - commit_re = re.compile(r'Committed revision (\d+).', re.M) - uuid_re = re.compile(r'Repository UUID:\s*(\S+)', re.M) + commit_re = re.compile(br'Committed revision (\d+).', re.M) + uuid_re = re.compile(br'Repository UUID:\s*(\S+)', re.M) def prerun(self): if self.wc: @@ -1225,7 +1225,7 @@ wdest = self.wjoin(dest) exists = os.path.lexists(wdest) if exists: - fd, tempname = tempfile.mkstemp( + fd, tempname = pycompat.mkstemp( prefix='hg-copy-', dir=os.path.dirname(wdest)) os.close(fd) os.unlink(tempname) @@ -1313,7 +1313,7 @@ self.xargs(self.setexec, 'propset', 'svn:executable', '*') self.setexec = [] - fd, messagefile = tempfile.mkstemp(prefix='hg-convert-') + fd, messagefile = pycompat.mkstemp(prefix='hg-convert-') fp = os.fdopen(fd, r'wb') fp.write(util.tonativeeol(commit.desc)) fp.close()
--- a/hgext/eol.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/eol.py Thu Jul 19 13:55:54 2018 -0400 @@ -142,7 +142,7 @@ if ui.configbool('eol', 'only-consistent') and inconsistenteol(s): return s if (ui.configbool('eol', 'fix-trailing-newline') - and s and s[-1] != '\n'): + and s and not s.endswith('\n')): s = s + '\n' return util.tolf(s) @@ -153,7 +153,7 @@ if ui.configbool('eol', 'only-consistent') and inconsistenteol(s): return s if (ui.configbool('eol', 'fix-trailing-newline') - and s and s[-1] != '\n'): + and s and not s.endswith('\n')): s = s + '\n' return util.tocrlf(s)
--- a/hgext/extdiff.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/extdiff.py Thu Jul 19 13:55:54 2018 -0400 @@ -71,7 +71,7 @@ import re import shutil import stat -import tempfile + from mercurial.i18n import _ from mercurial.node import ( nullid, @@ -210,7 +210,7 @@ if not common: return 0 - tmproot = tempfile.mkdtemp(prefix='extdiff.') + tmproot = pycompat.mkdtemp(prefix='extdiff.') try: if not opts.get('patch'): # Always make a copy of node1a (and node1b, if applicable)
--- a/hgext/fix.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/fix.py Thu Jul 19 13:55:54 2018 -0400 @@ -70,6 +70,7 @@ registrar, scmutil, util, + worker, ) # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for @@ -133,32 +134,56 @@ raise error.Abort(_('cannot specify both "--rev" and "--all"')) opts['rev'] = ['not public() and not obsolete()'] opts['working_dir'] = True - with repo.wlock(), repo.lock(): + with repo.wlock(), repo.lock(), repo.transaction('fix'): revstofix = getrevstofix(ui, repo, opts) basectxs = getbasectxs(repo, opts, revstofix) workqueue, numitems = getworkqueue(ui, repo, pats, opts, revstofix, basectxs) + fixers = getfixers(ui) + + # There are no data dependencies between the workers fixing each file + # revision, so we can use all available parallelism. + def getfixes(items): + for rev, path in items: + ctx = repo[rev] + olddata = ctx[path].data() + newdata = fixfile(ui, opts, fixers, ctx, path, basectxs[rev]) + # Don't waste memory/time passing unchanged content back, but + # produce one result per item either way. + yield (rev, path, newdata if newdata != olddata else None) + results = worker.worker(ui, 1.0, getfixes, tuple(), workqueue) + + # We have to hold on to the data for each successor revision in memory + # until all its parents are committed. We ensure this by committing and + # freeing memory for the revisions in some topological order. This + # leaves a little bit of memory efficiency on the table, but also makes + # the tests deterministic. It might also be considered a feature since + # it makes the results more easily reproducible. filedata = collections.defaultdict(dict) replacements = {} - fixers = getfixers(ui) - # Some day this loop can become a worker pool, but for now it's easier - # to fix everything serially in topological order. - for rev, path in sorted(workqueue): - ctx = repo[rev] - olddata = ctx[path].data() - newdata = fixfile(ui, opts, fixers, ctx, path, basectxs[rev]) - if newdata != olddata: - filedata[rev][path] = newdata - numitems[rev] -= 1 - if not numitems[rev]: - if rev == wdirrev: - writeworkingdir(repo, ctx, filedata[rev], replacements) - else: - replacerev(ui, repo, ctx, filedata[rev], replacements) - del filedata[rev] + commitorder = sorted(revstofix, reverse=True) + with ui.makeprogress(topic=_('fixing'), unit=_('files'), + total=sum(numitems.values())) as progress: + for rev, path, newdata in results: + progress.increment(item=path) + if newdata is not None: + filedata[rev][path] = newdata + numitems[rev] -= 1 + # Apply the fixes for this and any other revisions that are + # ready and sitting at the front of the queue. Using a loop here + # prevents the queue from being blocked by the first revision to + # be ready out of order. + while commitorder and not numitems[commitorder[-1]]: + rev = commitorder.pop() + ctx = repo[rev] + if rev == wdirrev: + writeworkingdir(repo, ctx, filedata[rev], replacements) + else: + replacerev(ui, repo, ctx, filedata[rev], replacements) + del filedata[rev] replacements = {prec: [succ] for prec, succ in replacements.iteritems()} - scmutil.cleanupnodes(repo, replacements, 'fix') + scmutil.cleanupnodes(repo, replacements, 'fix', fixphase=True) def getworkqueue(ui, repo, pats, opts, revstofix, basectxs): """"Constructs the list of files to be fixed at specific revisions @@ -168,11 +193,19 @@ topological order. Each work item represents a file in the working copy or in some revision that should be fixed and written back to the working copy or into a replacement revision. + + Work items for the same revision are grouped together, so that a worker + pool starting with the first N items in parallel is likely to finish the + first revision's work before other revisions. This can allow us to write + the result to disk and reduce memory footprint. At time of writing, the + partition strategy in worker.py seems favorable to this. We also sort the + items by ascending revision number to match the order in which we commit + the fixes later. """ workqueue = [] numitems = collections.defaultdict(int) maxfilesize = ui.configbytes('fix', 'maxfilesize') - for rev in revstofix: + for rev in sorted(revstofix): fixctx = repo[rev] match = scmutil.match(fixctx, pats, opts) for path in pathstofix(ui, repo, pats, opts, match, basectxs[rev], @@ -352,7 +385,9 @@ """Returns a map of the base contexts for each revision The base contexts determine which lines are considered modified when we - attempt to fix just the modified lines in a file. + attempt to fix just the modified lines in a file. It also determines which + files we attempt to fix, so it is important to compute this even when + --whole is used. """ # The --base flag overrides the usual logic, and we give every revision # exactly the set of baserevs that the user specified. @@ -484,25 +519,23 @@ isexec=fctx.isexec(), copied=copied) - overrides = {('phases', 'new-commit'): ctx.phase()} - with ui.configoverride(overrides, source='fix'): - memctx = context.memctx( - repo, - parents=(newp1node, newp2node), - text=ctx.description(), - files=set(ctx.files()) | set(filedata.keys()), - filectxfn=filectxfn, - user=ctx.user(), - date=ctx.date(), - extra=ctx.extra(), - branch=ctx.branch(), - editor=None) - sucnode = memctx.commit() - prenode = ctx.node() - if prenode == sucnode: - ui.debug('node %s already existed\n' % (ctx.hex())) - else: - replacements[ctx.node()] = sucnode + memctx = context.memctx( + repo, + parents=(newp1node, newp2node), + text=ctx.description(), + files=set(ctx.files()) | set(filedata.keys()), + filectxfn=filectxfn, + user=ctx.user(), + date=ctx.date(), + extra=ctx.extra(), + branch=ctx.branch(), + editor=None) + sucnode = memctx.commit() + prenode = ctx.node() + if prenode == sucnode: + ui.debug('node %s already existed\n' % (ctx.hex())) + else: + replacements[ctx.node()] = sucnode def getfixers(ui): """Returns a map of configured fixer tools indexed by their names
--- a/hgext/githelp.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/githelp.py Thu Jul 19 13:55:54 2018 -0400 @@ -67,7 +67,7 @@ cmd = args[0] if not cmd in gitcommands: - raise error.Abort("error: unknown git command %s" % (cmd)) + raise error.Abort(_("error: unknown git command %s") % (cmd)) ui.pager('githelp') args = args[1:] @@ -90,14 +90,13 @@ elif ('-' + ex.opt) in ex.msg: flag = '-' + ex.opt else: - raise error.Abort("unknown option %s" % ex.opt) + raise error.Abort(_("unknown option %s") % ex.opt) try: args.remove(flag) except Exception: - raise error.Abort( - "unknown option {0} packed with other options\n" - "Please try passing the option as it's own flag: -{0}" \ - .format(ex.opt)) + msg = _("unknown option '%s' packed with other options") + hint = _("please try passing the option as its own flag: -%s") + raise error.Abort(msg % ex.opt, hint=hint % ex.opt) ui.warn(_("ignoring unknown option %s\n") % flag) @@ -171,7 +170,7 @@ cmd.extend(args) else: ui.status(_("note: use hg addremove to remove files that have " - "been deleted.\n\n")) + "been deleted\n\n")) ui.status((bytes(cmd)), "\n") @@ -196,7 +195,7 @@ ui.status((bytes(cmd)), "\n") def bisect(ui, repo, *args, **kwargs): - ui.status(_("See 'hg help bisect' for how to use bisect.\n\n")) + ui.status(_("see 'hg help bisect' for how to use bisect\n\n")) def blame(ui, repo, *args, **kwargs): cmdoptions = [ @@ -236,6 +235,8 @@ # shell command to output the active bookmark for the active # revision old = '`hg log -T"{activebookmark}" -r .`' + else: + raise error.Abort(_('missing newbranch argument')) new = args[0] cmd['-m'] = old cmd.append(new) @@ -334,7 +335,7 @@ cmd = Command('revert') cmd['--all'] = None else: - raise error.Abort("a commit must be specified") + raise error.Abort(_("a commit must be specified")) ui.status((bytes(cmd)), "\n") @@ -353,7 +354,7 @@ if opts.get('continue'): cmd['--continue'] = None elif opts.get('abort'): - ui.status(_("note: hg graft does not have --abort.\n\n")) + ui.status(_("note: hg graft does not have --abort\n\n")) return else: cmd.extend(args) @@ -384,7 +385,7 @@ args, opts = parseoptions(ui, cmdoptions, args) if len(args) == 0: - raise error.Abort("a repository to clone must be specified") + raise error.Abort(_("a repository to clone must be specified")) cmd = Command('clone') cmd.append(args[0]) @@ -393,8 +394,8 @@ if opts.get('bare'): cmd['-U'] = None - ui.status(_("note: Mercurial does not have bare clones. " + - "-U will clone the repo without checking out a commit\n\n")) + ui.status(_("note: Mercurial does not have bare clones. " + "-U will clone the repo without checking out a commit\n\n")) elif opts.get('no_checkout'): cmd['-U'] = None @@ -436,9 +437,9 @@ cmd['-m'] = "'%s'" % (opts.get('message'),) if opts.get('all'): - ui.status(_("note: Mercurial doesn't have a staging area, " + - "so there is no --all. -A will add and remove files " + - "for you though.\n\n")) + ui.status(_("note: Mercurial doesn't have a staging area, " + "so there is no --all. -A will add and remove files " + "for you though.\n\n")) if opts.get('file'): cmd['-l'] = opts.get('file') @@ -454,8 +455,8 @@ ui.status((bytes(cmd)), "\n") def deprecated(ui, repo, *args, **kwargs): - ui.warn(_('This command has been deprecated in the git project, ' + - 'thus isn\'t supported by this tool.\n\n')) + ui.warn(_('this command has been deprecated in the git project, ' + 'thus isn\'t supported by this tool\n\n')) def diff(ui, repo, *args, **kwargs): cmdoptions = [ @@ -468,8 +469,8 @@ cmd = Command('diff') if opts.get('cached'): - ui.status(_('note: Mercurial has no concept of a staging area, ' + - 'so --cached does nothing.\n\n')) + ui.status(_('note: Mercurial has no concept of a staging area, ' + 'so --cached does nothing\n\n')) if opts.get('reverse'): cmd['--reverse'] = None @@ -505,10 +506,10 @@ if len(args) > 0: cmd.append(args[0]) if len(args) > 1: - ui.status(_("note: Mercurial doesn't have refspecs. " + - "-r can be used to specify which commits you want to pull. " + - "-B can be used to specify which bookmark you want to pull." + - "\n\n")) + ui.status(_("note: Mercurial doesn't have refspecs. " + "-r can be used to specify which commits you want to " + "pull. -B can be used to specify which bookmark you " + "want to pull.\n\n")) for v in args[1:]: if v in repo._bookmarks: cmd['-B'] = v @@ -556,10 +557,10 @@ ('p', 'patch', None, ''), ] args, opts = parseoptions(ui, cmdoptions, args) - ui.status(_('note: -v prints the entire commit message like Git does. To ' + - 'print just the first line, drop the -v.\n\n')) - ui.status(_("note: see hg help revset for information on how to filter " + - "log output.\n\n")) + ui.status(_('note: -v prints the entire commit message like Git does. To ' + 'print just the first line, drop the -v.\n\n')) + ui.status(_("note: see hg help revset for information on how to filter " + "log output\n\n")) cmd = Command('log') cmd['-v'] = None @@ -578,13 +579,13 @@ if opts.get('pretty') or opts.get('format') or opts.get('oneline'): format = opts.get('format', '') if 'format:' in format: - ui.status(_("note: --format format:??? equates to Mercurial's " + - "--template. See hg help templates for more info.\n\n")) + ui.status(_("note: --format format:??? equates to Mercurial's " + "--template. See hg help templates for more info.\n\n")) cmd['--template'] = '???' else: - ui.status(_("note: --pretty/format/oneline equate to Mercurial's " + - "--style or --template. See hg help templates for more info." + - "\n\n")) + ui.status(_("note: --pretty/format/oneline equate to Mercurial's " + "--style or --template. See hg help templates for " + "more info.\n\n")) cmd['--style'] = '???' if len(args) > 0: @@ -654,8 +655,8 @@ cmd = Command("log -T '{node}\\n' -r 'ancestor(%s,%s)'" % (args[0], args[1])) - ui.status(_('NOTE: ancestors() is part of the revset language.\n'), - _("Learn more about revsets with 'hg help revsets'\n\n")) + ui.status(_('note: ancestors() is part of the revset language\n'), + _("(learn more about revsets with 'hg help revsets')\n\n")) ui.status((bytes(cmd)), "\n") def mergetool(ui, repo, *args, **kwargs): @@ -697,10 +698,10 @@ if len(args) > 0: cmd.append(args[0]) if len(args) > 1: - ui.status(_("note: Mercurial doesn't have refspecs. " + - "-r can be used to specify which commits you want to pull. " + - "-B can be used to specify which bookmark you want to pull." + - "\n\n")) + ui.status(_("note: Mercurial doesn't have refspecs. " + "-r can be used to specify which commits you want to " + "pull. -B can be used to specify which bookmark you " + "want to pull.\n\n")) for v in args[1:]: if v in repo._bookmarks: cmd['-B'] = v @@ -721,10 +722,10 @@ if len(args) > 0: cmd.append(args[0]) if len(args) > 1: - ui.status(_("note: Mercurial doesn't have refspecs. " + - "-r can be used to specify which commits you want to push. " + - "-B can be used to specify which bookmark you want to push." + - "\n\n")) + ui.status(_("note: Mercurial doesn't have refspecs. " + "-r can be used to specify which commits you want " + "to push. -B can be used to specify which bookmark " + "you want to push.\n\n")) for v in args[1:]: if v in repo._bookmarks: cmd['-B'] = v @@ -748,12 +749,12 @@ args, opts = parseoptions(ui, cmdoptions, args) if opts.get('interactive'): - ui.status(_("note: hg histedit does not perform a rebase. " + - "It just edits history.\n\n")) + ui.status(_("note: hg histedit does not perform a rebase. " + "It just edits history.\n\n")) cmd = Command('histedit') if len(args) > 0: ui.status(_("also note: 'hg histedit' will automatically detect" - " your stack, so no second argument is necessary.\n\n")) + " your stack, so no second argument is necessary\n\n")) ui.status((bytes(cmd)), "\n") return @@ -769,12 +770,12 @@ cmd['--abort'] = None if opts.get('onto'): - ui.status(_("note: if you're trying to lift a commit off one branch, " + - "try hg rebase -d <destination commit> -s <commit to be lifted>" + - "\n\n")) + ui.status(_("note: if you're trying to lift a commit off one branch, " + "try hg rebase -d <destination commit> -s <commit to be " + "lifted>\n\n")) cmd['-d'] = convert(opts.get('onto')) if len(args) < 2: - raise error.Abort("Expected format: git rebase --onto X Y Z") + raise error.Abort(_("expected format: git rebase --onto X Y Z")) cmd['-s'] = "'::%s - ::%s'" % (convert(args[1]), convert(args[0])) else: if len(args) == 1: @@ -799,7 +800,7 @@ ui.status(bytes(cmd), "\n\n") ui.status(_("note: in hg commits can be deleted from repo but we always" - " have backups.\n")) + " have backups\n")) def reset(ui, repo, *args, **kwargs): cmdoptions = [ @@ -813,10 +814,10 @@ hard = opts.get('hard') if opts.get('mixed'): - ui.status(_('NOTE: --mixed has no meaning since Mercurial has no ' + ui.status(_('note: --mixed has no meaning since Mercurial has no ' 'staging area\n\n')) if opts.get('soft'): - ui.status(_('NOTE: --soft has no meaning since Mercurial has no ' + ui.status(_('note: --soft has no meaning since Mercurial has no ' 'staging area\n\n')) cmd = Command('update') @@ -833,7 +834,7 @@ args, opts = parseoptions(ui, cmdoptions, args) if len(args) > 1: - ui.status(_("note: hg backout doesn't support multiple commits at " + + ui.status(_("note: hg backout doesn't support multiple commits at " "once\n\n")) cmd = Command('backout') @@ -930,8 +931,8 @@ cmd['--keep'] = None elif (action == 'branch' or action == 'show' or action == 'clear' or action == 'create'): - ui.status(_("note: Mercurial doesn't have equivalents to the " + - "git stash branch, show, clear, or create actions.\n\n")) + ui.status(_("note: Mercurial doesn't have equivalents to the " + "git stash branch, show, clear, or create actions\n\n")) return else: if len(args) > 0: @@ -957,9 +958,11 @@ ui.status((bytes(cmd)), "\n") def svn(ui, repo, *args, **kwargs): + if not args: + raise error.Abort(_('missing svn command')) svncmd = args[0] - if not svncmd in gitsvncommands: - ui.warn(_("error: unknown git svn command %s\n") % (svncmd)) + if svncmd not in gitsvncommands: + raise error.Abort(_('unknown git svn command "%s"') % (svncmd)) args = args[1:] return gitsvncommands[svncmd](ui, repo, *args, **kwargs) @@ -988,6 +991,9 @@ ] args, opts = parseoptions(ui, cmdoptions, args) + if not args: + raise error.Abort(_('missing find-rev argument')) + cmd = Command('log') cmd['-r'] = args[0] @@ -1020,6 +1026,10 @@ cmd = Command('tags') else: cmd = Command('tag') + + if not args: + raise error.Abort(_('missing tag argument')) + cmd.append(args[0]) if len(args) > 1: cmd['-r'] = args[1]
--- a/hgext/gpg.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/gpg.py Thu Jul 19 13:55:54 2018 -0400 @@ -9,7 +9,6 @@ import binascii import os -import tempfile from mercurial.i18n import _ from mercurial import ( @@ -61,11 +60,11 @@ sigfile = datafile = None try: # create temporary files - fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig") + fd, sigfile = pycompat.mkstemp(prefix="hg-gpg-", suffix=".sig") fp = os.fdopen(fd, r'wb') fp.write(sig) fp.close() - fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt") + fd, datafile = pycompat.mkstemp(prefix="hg-gpg-", suffix=".txt") fp = os.fdopen(fd, r'wb') fp.write(data) fp.close()
--- a/hgext/highlight/__init__.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/highlight/__init__.py Thu Jul 19 13:55:54 2018 -0400 @@ -36,7 +36,6 @@ from mercurial import ( extensions, - fileset, ) # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for @@ -51,9 +50,8 @@ filenameonly = web.configbool('web', 'highlightonlymatchfilename', False) ctx = fctx.changectx() - tree = fileset.parse(expr) - mctx = fileset.matchctx(ctx, subset=[fctx.path()], status=None) - if fctx.path() in fileset.getset(mctx, tree): + m = ctx.matchfileset(expr) + if m(fctx.path()): highlight.pygmentize(field, fctx, style, tmpl, guessfilenameonly=filenameonly)
--- a/hgext/highlight/highlight.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/highlight/highlight.py Thu Jul 19 13:55:54 2018 -0400 @@ -11,7 +11,7 @@ from __future__ import absolute_import from mercurial import demandimport -demandimport.ignore.extend(['pkgutil', 'pkg_resources', '__main__']) +demandimport.IGNORES.update(['pkgutil', 'pkg_resources', '__main__']) from mercurial import ( encoding, @@ -44,7 +44,8 @@ def pygmentize(field, fctx, style, tmpl, guessfilenameonly=False): # append a <link ...> to the syntax highlighting css - old_header = tmpl.load('header') + tmpl.load('header') + old_header = tmpl.cache['header'] if SYNTAX_CSS not in old_header: new_header = old_header + SYNTAX_CSS tmpl.cache['header'] = new_header @@ -89,7 +90,7 @@ coloriter = (s.encode(encoding.encoding, 'replace') for s in colorized.splitlines()) - tmpl.filters['colorize'] = lambda x: next(coloriter) + tmpl._filters['colorize'] = lambda x: next(coloriter) oldl = tmpl.cache[field] newl = oldl.replace('line|escape', 'line|colorize')
--- a/hgext/histedit.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/histedit.py Thu Jul 19 13:55:54 2018 -0400 @@ -183,7 +183,6 @@ from __future__ import absolute_import -import errno import os from mercurial.i18n import _ @@ -207,6 +206,7 @@ registrar, repair, scmutil, + state as statemod, util, ) from mercurial.utils import ( @@ -304,6 +304,7 @@ self.lock = lock self.wlock = wlock self.backupfile = None + self.stateobj = statemod.cmdstate(repo, 'histedit-state') if replacements is None: self.replacements = [] else: @@ -311,29 +312,33 @@ def read(self): """Load histedit state from disk and set fields appropriately.""" - try: - state = self.repo.vfs.read('histedit-state') - except IOError as err: - if err.errno != errno.ENOENT: - raise + if not self.stateobj.exists(): cmdutil.wrongtooltocontinue(self.repo, _('histedit')) - if state.startswith('v1\n'): + data = self._read() + + self.parentctxnode = data['parentctxnode'] + actions = parserules(data['rules'], self) + self.actions = actions + self.keep = data['keep'] + self.topmost = data['topmost'] + self.replacements = data['replacements'] + self.backupfile = data['backupfile'] + + def _read(self): + fp = self.repo.vfs.read('histedit-state') + if fp.startswith('v1\n'): data = self._load() parentctxnode, rules, keep, topmost, replacements, backupfile = data else: - data = pickle.loads(state) + data = pickle.loads(fp) parentctxnode, rules, keep, topmost, replacements = data backupfile = None + rules = "\n".join(["%s %s" % (verb, rest) for [verb, rest] in rules]) - self.parentctxnode = parentctxnode - rules = "\n".join(["%s %s" % (verb, rest) for [verb, rest] in rules]) - actions = parserules(rules, self) - self.actions = actions - self.keep = keep - self.topmost = topmost - self.replacements = replacements - self.backupfile = backupfile + return {'parentctxnode': parentctxnode, "rules": rules, "keep": keep, + "topmost": topmost, "replacements": replacements, + "backupfile": backupfile} def write(self, tr=None): if tr: @@ -779,9 +784,7 @@ def finishfold(self, ui, repo, ctx, oldctx, newnode, internalchanges): parent = ctx.parents()[0].node() - repo.ui.pushbuffer() - hg.update(repo, parent) - repo.ui.popbuffer() + hg.updaterepo(repo, parent, overwrite=False) ### prepare new commit data commitopts = {} commitopts['user'] = ctx.user() @@ -812,9 +815,7 @@ skipprompt=self.skipprompt()) if n is None: return ctx, [] - repo.ui.pushbuffer() - hg.update(repo, n) - repo.ui.popbuffer() + hg.updaterepo(repo, n, overwrite=False) replacements = [(oldctx.node(), (newnode,)), (ctx.node(), (n,)), (newnode, (n,)), @@ -1109,6 +1110,8 @@ fm.startitem() goal = _getgoal(opts) revs = opts.get('rev', []) + # experimental config: ui.history-editing-backup + nobackup = not ui.configbool('ui', 'history-editing-backup') rules = opts.get('commands', '') state.keep = opts.get('keep', False) @@ -1122,7 +1125,7 @@ _edithisteditplan(ui, repo, state, rules) return elif goal == goalabort: - _aborthistedit(ui, repo, state) + _aborthistedit(ui, repo, state, nobackup=nobackup) return else: # goal == goalnew @@ -1149,8 +1152,6 @@ # even if there's an exception before the first transaction serialize. state.write() - total = len(state.actions) - pos = 0 tr = None # Don't use singletransaction by default since it rolls the entire # transaction back if an unexpected exception happens (like a @@ -1160,13 +1161,13 @@ # and reopen a transaction. For example, if the action executes an # external process it may choose to commit the transaction first. tr = repo.transaction('histedit') - with util.acceptintervention(tr): + progress = ui.makeprogress(_("editing"), unit=_('changes'), + total=len(state.actions)) + with progress, util.acceptintervention(tr): while state.actions: state.write(tr=tr) actobj = state.actions[0] - pos += 1 - ui.progress(_("editing"), pos, actobj.torule(), - _('changes'), total) + progress.increment(item=actobj.torule()) ui.debug('histedit: processing %s %s\n' % (actobj.verb,\ actobj.torule())) parentctx, replacement_ = actobj.run() @@ -1175,13 +1176,10 @@ state.actions.pop(0) state.write() - ui.progress(_("editing"), None) def _finishhistedit(ui, repo, state, fm): """This action runs when histedit is finishing its session""" - repo.ui.pushbuffer() - hg.update(repo, state.parentctxnode, quietempty=True) - repo.ui.popbuffer() + hg.updaterepo(repo, state.parentctxnode, overwrite=False) mapping, tmpnodes, created, ntm = processreplacement(state) if mapping: @@ -1225,7 +1223,7 @@ if repo.vfs.exists('histedit-last-edit.txt'): repo.vfs.unlink('histedit-last-edit.txt') -def _aborthistedit(ui, repo, state): +def _aborthistedit(ui, repo, state, nobackup=False): try: state.read() __, leafs, tmpnodes, __ = processreplacement(state) @@ -1247,8 +1245,8 @@ if repo.unfiltered().revs('parents() and (%n or %ln::)', state.parentctxnode, leafs | tmpnodes): hg.clean(repo, state.topmost, show_stats=True, quietempty=True) - cleanupnode(ui, repo, tmpnodes) - cleanupnode(ui, repo, leafs) + cleanupnode(ui, repo, tmpnodes, nobackup=nobackup) + cleanupnode(ui, repo, leafs, nobackup=nobackup) except Exception: if state.inprogress(): ui.warn(_('warning: encountered an exception during histedit ' @@ -1605,7 +1603,7 @@ changes.append((name, newtopmost)) marks.applychanges(repo, tr, changes) -def cleanupnode(ui, repo, nodes): +def cleanupnode(ui, repo, nodes, nobackup=False): """strip a group of nodes from the repository The set of node to strip may contains unknown nodes.""" @@ -1620,7 +1618,8 @@ nodes = sorted(n for n in nodes if n in nm) roots = [c.node() for c in repo.set("roots(%ln)", nodes)] if roots: - repair.strip(ui, repo, roots) + backup = not nobackup + repair.strip(ui, repo, roots, backup=backup) def stripwrapper(orig, ui, repo, nodelist, *args, **kwargs): if isinstance(nodelist, str):
--- a/hgext/infinitepush/__init__.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/infinitepush/__init__.py Thu Jul 19 13:55:54 2018 -0400 @@ -94,7 +94,6 @@ import re import socket import subprocess -import tempfile import time from mercurial.node import ( @@ -565,19 +564,19 @@ if isinstance(localkey, str) and _scratchbranchmatcher(localkey): scratchnode = repo.bundlestore.index.getnode(localkey) if scratchnode: - return "%s %s\n" % (1, scratchnode) + return "%d %s\n" % (1, scratchnode) else: - return "%s %s\n" % (0, 'scratch branch %s not found' % localkey) + return "%d %s\n" % (0, 'scratch branch %s not found' % localkey) else: try: r = hex(repo.lookup(localkey)) - return "%s %s\n" % (1, r) + return "%d %s\n" % (1, r) except Exception as inst: if repo.bundlestore.index.getbundle(localkey): - return "%s %s\n" % (1, localkey) + return "%d %s\n" % (1, localkey) else: - r = str(inst) - return "%s %s\n" % (0, r) + r = stringutil.forcebytestr(inst) + return "%d %s\n" % (0, r) return _lookup def _pull(orig, ui, repo, source="default", **opts): @@ -912,7 +911,7 @@ # storing the bundle in the bundlestore buf = util.chunkbuffer(bundler.getchunks()) - fd, bundlefile = tempfile.mkstemp() + fd, bundlefile = pycompat.mkstemp() try: try: fp = os.fdopen(fd, r'wb') @@ -998,7 +997,7 @@ # If commits were sent, store them if cgparams: buf = util.chunkbuffer(bundler.getchunks()) - fd, bundlefile = tempfile.mkstemp() + fd, bundlefile = pycompat.mkstemp() try: try: fp = os.fdopen(fd, r'wb') @@ -1110,7 +1109,7 @@ bundler.addpart(cgpart) buf = util.chunkbuffer(bundler.getchunks()) - fd, bundlefile = tempfile.mkstemp() + fd, bundlefile = pycompat.mkstemp() try: try: fp = os.fdopen(fd, r'wb')
--- a/hgext/infinitepush/common.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/infinitepush/common.py Thu Jul 19 13:55:54 2018 -0400 @@ -6,13 +6,13 @@ from __future__ import absolute_import import os -import tempfile from mercurial.node import hex from mercurial import ( error, extensions, + pycompat, ) def isremotebooksenabled(ui): @@ -30,7 +30,7 @@ def _makebundlefromraw(data): fp = None - fd, bundlefile = tempfile.mkstemp() + fd, bundlefile = pycompat.mkstemp() try: # guards bundlefile try: # guards fp fp = os.fdopen(fd, 'wb')
--- a/hgext/infinitepush/store.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/infinitepush/store.py Thu Jul 19 13:55:54 2018 -0400 @@ -120,6 +120,8 @@ def write(self, data): # Won't work on windows because you can't open file second time without # closing it + # TODO: rewrite without str.format() and replace NamedTemporaryFile() + # with pycompat.namedtempfile() with NamedTemporaryFile() as temp: temp.write(data) temp.flush() @@ -142,6 +144,8 @@ def read(self, handle): # Won't work on windows because you can't open file second time without # closing it + # TODO: rewrite without str.format() and replace NamedTemporaryFile() + # with pycompat.namedtempfile() with NamedTemporaryFile() as temp: formatted_args = [arg.format(filename=temp.name, handle=handle) for arg in self.get_args]
--- a/hgext/keyword.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/keyword.py Thu Jul 19 13:55:54 2018 -0400 @@ -87,7 +87,6 @@ import os import re -import tempfile import weakref from mercurial.i18n import _ @@ -246,7 +245,7 @@ @util.propertycache def escape(self): '''Returns bar-separated and escaped keywords.''' - return '|'.join(map(re.escape, self.templates.keys())) + return '|'.join(map(stringutil.reescape, self.templates.keys())) @util.propertycache def rekw(self): @@ -434,7 +433,7 @@ ui.write('%s = %s\n' % (k, v)) fn = 'demo.txt' - tmpdir = tempfile.mkdtemp('', 'kwdemo.') + tmpdir = pycompat.mkdtemp('', 'kwdemo.') ui.note(_('creating temporary repository at %s\n') % tmpdir) if repo is None: baseui = ui
--- a/hgext/largefiles/basestore.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/largefiles/basestore.py Thu Jul 19 13:55:54 2018 -0400 @@ -62,9 +62,10 @@ at = 0 available = self.exists(set(hash for (_filename, hash) in files)) + progress = ui.makeprogress(_('getting largefiles'), unit=_('files'), + total=len(files)) for filename, hash in files: - ui.progress(_('getting largefiles'), at, unit=_('files'), - total=len(files)) + progress.update(at) at += 1 ui.note(_('getting %s:%s\n') % (filename, hash)) @@ -79,7 +80,7 @@ else: missing.append(filename) - ui.progress(_('getting largefiles'), None) + progress.complete() return (success, missing) def _gethash(self, filename, hash):
--- a/hgext/largefiles/lfcommands.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/largefiles/lfcommands.py Thu Jul 19 13:55:54 2018 -0400 @@ -118,12 +118,14 @@ matcher = None lfiletohash = {} + progress = ui.makeprogress(_('converting revisions'), + unit=_('revisions'), + total=rsrc['tip'].rev()) for ctx in ctxs: - ui.progress(_('converting revisions'), ctx.rev(), - unit=_('revisions'), total=rsrc['tip'].rev()) + progress.update(ctx.rev()) _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles, matcher, size, lfiletohash) - ui.progress(_('converting revisions'), None) + progress.complete() if rdst.wvfs.exists(lfutil.shortname): rdst.wvfs.rmtree(lfutil.shortname) @@ -368,9 +370,10 @@ files = [h for h in files if not retval[h]] ui.debug("%d largefiles need to be uploaded\n" % len(files)) + progress = ui.makeprogress(_('uploading largefiles'), unit=_('files'), + total=len(files)) for hash in files: - ui.progress(_('uploading largefiles'), at, unit=_('files'), - total=len(files)) + progress.update(at) source = lfutil.findfile(rsrc, hash) if not source: raise error.Abort(_('largefile %s missing from store' @@ -378,7 +381,7 @@ # XXX check for errors here store.put(source, hash) at += 1 - ui.progress(_('uploading largefiles'), None) + progress.complete() def verifylfiles(ui, repo, all=False, contents=False): '''Verify that every largefile revision in the current changeset
--- a/hgext/largefiles/lfutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/largefiles/lfutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -501,9 +501,10 @@ return filelist def getlfilestoupload(repo, missing, addfunc): + progress = repo.ui.makeprogress(_('finding outgoing largefiles'), + unit=_('revisions'), total=len(missing)) for i, n in enumerate(missing): - repo.ui.progress(_('finding outgoing largefiles'), i, - unit=_('revisions'), total=len(missing)) + progress.update(i) parents = [p for p in repo[n].parents() if p != node.nullid] oldlfstatus = repo.lfstatus @@ -530,7 +531,7 @@ for fn in files: if isstandin(fn) and fn in ctx: addfunc(fn, readasstandin(ctx[fn])) - repo.ui.progress(_('finding outgoing largefiles'), None) + progress.complete() def updatestandinsbymatch(repo, match): '''Update standins in the working directory according to specified match
--- a/hgext/lfs/__init__.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/lfs/__init__.py Thu Jul 19 13:55:54 2018 -0400 @@ -362,8 +362,10 @@ """File that uses LFS storage.""" # i18n: "lfs" is a keyword fileset.getargs(x, 0, 0, _("lfs takes no arguments")) - return [f for f in mctx.subset - if wrapper.pointerfromctx(mctx.ctx, f, removed=True) is not None] + ctx = mctx.ctx + def lfsfilep(f): + return wrapper.pointerfromctx(ctx, f, removed=True) is not None + return mctx.predicate(lfsfilep, predrepr='<lfs>') @templatekeyword('lfs_files', requires={'ctx'}) def lfsfiles(context, mapping):
--- a/hgext/lfs/blobstore.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/lfs/blobstore.py Thu Jul 19 13:55:54 2018 -0400 @@ -405,7 +405,8 @@ if len(objects) > 1: self.ui.note(_('lfs: need to transfer %d objects (%s)\n') % (len(objects), util.bytecount(total))) - self.ui.progress(topic, 0, total=total) + progress = self.ui.makeprogress(topic, total=total) + progress.update(0) def transfer(chunk): for obj in chunk: objsize = obj.get('size', 0) @@ -443,9 +444,9 @@ for _one, oid in oids: processed += sizes[oid] blobs += 1 - self.ui.progress(topic, processed, total=total) + progress.update(processed) self.ui.note(_('lfs: processed: %s\n') % oid) - self.ui.progress(topic, pos=None, total=total) + progress.complete() if blobs > 0: if action == 'upload':
--- a/hgext/lfs/pointer.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/lfs/pointer.py Thu Jul 19 13:55:54 2018 -0400 @@ -15,6 +15,9 @@ error, pycompat, ) +from mercurial.utils import ( + stringutil, +) class InvalidPointer(error.RevlogError): pass @@ -32,7 +35,8 @@ try: return cls(l.split(' ', 1) for l in text.splitlines()).validate() except ValueError: # l.split returns 1 item instead of 2 - raise InvalidPointer(_('cannot parse git-lfs text: %r') % text) + raise InvalidPointer(_('cannot parse git-lfs text: %s') + % stringutil.pprint(text)) def serialize(self): sortkeyfunc = lambda x: (x[0] != 'version', x) @@ -52,7 +56,7 @@ _requiredre = { 'size': re.compile(br'\A[0-9]+\Z'), 'oid': re.compile(br'\Asha256:[0-9a-f]{64}\Z'), - 'version': re.compile(br'\A%s\Z' % re.escape(VERSION)), + 'version': re.compile(br'\A%s\Z' % stringutil.reescape(VERSION)), } def validate(self): @@ -61,15 +65,19 @@ for k, v in self.iteritems(): if k in self._requiredre: if not self._requiredre[k].match(v): - raise InvalidPointer(_('unexpected value: %s=%r') % (k, v)) + raise InvalidPointer( + _('unexpected lfs pointer value: %s=%s') + % (k, stringutil.pprint(v))) requiredcount += 1 elif not self._keyre.match(k): - raise InvalidPointer(_('unexpected key: %s') % k) + raise InvalidPointer(_('unexpected lfs pointer key: %s') % k) if not self._valuere.match(v): - raise InvalidPointer(_('unexpected value: %s=%r') % (k, v)) + raise InvalidPointer(_('unexpected lfs pointer value: %s=%s') + % (k, stringutil.pprint(v))) if len(self._requiredre) != requiredcount: miss = sorted(set(self._requiredre.keys()).difference(self.keys())) - raise InvalidPointer(_('missed keys: %s') % ', '.join(miss)) + raise InvalidPointer(_('missing lfs pointer keys: %s') + % ', '.join(miss)) return self deserialize = gitlfspointer.deserialize
--- a/hgext/mq.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/mq.py Thu Jul 19 13:55:54 2018 -0400 @@ -492,7 +492,8 @@ n, name = entry yield statusentry(bin(n), name) elif l.strip(): - self.ui.warn(_('malformated mq status line: %s\n') % entry) + self.ui.warn(_('malformated mq status line: %s\n') % + stringutil.pprint(entry)) # else we ignore empty lines try: lines = self.opener.read(self.statuspath).splitlines() @@ -2872,7 +2873,7 @@ patch = None args = list(args) if opts.get(r'list'): - if args or opts.get('none'): + if args or opts.get(r'none'): raise error.Abort(_('cannot mix -l/--list with options or ' 'arguments')) for i in xrange(len(q.series)): @@ -2886,7 +2887,7 @@ patch = args.pop(0) if patch is None: raise error.Abort(_('no patch to work with')) - if args or opts.get('none'): + if args or opts.get(r'none'): idx = q.findseries(patch) if idx is None: raise error.Abort(_('no patch named %s') % patch)
--- a/hgext/narrow/__init__.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/narrow/__init__.py Thu Jul 19 13:55:54 2018 -0400 @@ -28,8 +28,6 @@ narrowchangegroup, narrowcommands, narrowcopies, - narrowdirstate, - narrowmerge, narrowpatch, narrowrepo, narrowrevlog, @@ -64,7 +62,6 @@ localrepo.featuresetupfuncs.add(featuresetup) narrowrevlog.setup() narrowbundle2.setup() - narrowmerge.setup() narrowcommands.setup() narrowchangegroup.setup() narrowwirepeer.uisetup() @@ -74,10 +71,9 @@ if not repo.local(): return - narrowrepo.wraprepo(repo) if changegroup.NARROW_REQUIREMENT in repo.requirements: + narrowrepo.wraprepo(repo) narrowcopies.setup(repo) - narrowdirstate.setup(repo) narrowpatch.setup(repo) narrowwirepeer.reposetup(repo)
--- a/hgext/narrow/narrowbundle2.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/narrow/narrowbundle2.py Thu Jul 19 13:55:54 2018 -0400 @@ -408,6 +408,8 @@ topic='widen') repo._bookmarks = bmstore if chgrpfile: + op._widen_uninterr = repo.ui.uninterruptable() + op._widen_uninterr.__enter__() # presence of _widen_bundle attribute activates widen handler later op._widen_bundle = chgrpfile # Set the new narrowspec if we're widening. The setnewnarrowpats() method @@ -455,6 +457,7 @@ (undovfs.join(undofile), stringutil.forcebytestr(e))) # Remove partial backup only if there were no exceptions + op._widen_uninterr.__exit__(None, None, None) vfs.unlink(chgrpfile) def setup():
--- a/hgext/narrow/narrowcommands.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/narrow/narrowcommands.py Thu Jul 19 13:55:54 2018 -0400 @@ -203,50 +203,51 @@ hint=_('use --force-delete-local-changes to ' 'ignore')) - if revstostrip: - tostrip = [unfi.changelog.node(r) for r in revstostrip] - if repo['.'].node() in tostrip: - # stripping working copy, so move to a different commit first - urev = max(repo.revs('(::%n) - %ln + null', - repo['.'].node(), visibletostrip)) - hg.clean(repo, urev) - repair.strip(ui, unfi, tostrip, topic='narrow') + with ui.uninterruptable(): + if revstostrip: + tostrip = [unfi.changelog.node(r) for r in revstostrip] + if repo['.'].node() in tostrip: + # stripping working copy, so move to a different commit first + urev = max(repo.revs('(::%n) - %ln + null', + repo['.'].node(), visibletostrip)) + hg.clean(repo, urev) + repair.strip(ui, unfi, tostrip, topic='narrow') - todelete = [] - for f, f2, size in repo.store.datafiles(): - if f.startswith('data/'): - file = f[5:-2] - if not newmatch(file): - todelete.append(f) - elif f.startswith('meta/'): - dir = f[5:-13] - dirs = ['.'] + sorted(util.dirs({dir})) + [dir] - include = True - for d in dirs: - visit = newmatch.visitdir(d) - if not visit: - include = False - break - if visit == 'all': - break - if not include: - todelete.append(f) + todelete = [] + for f, f2, size in repo.store.datafiles(): + if f.startswith('data/'): + file = f[5:-2] + if not newmatch(file): + todelete.append(f) + elif f.startswith('meta/'): + dir = f[5:-13] + dirs = ['.'] + sorted(util.dirs({dir})) + [dir] + include = True + for d in dirs: + visit = newmatch.visitdir(d) + if not visit: + include = False + break + if visit == 'all': + break + if not include: + todelete.append(f) - repo.destroying() + repo.destroying() - with repo.transaction("narrowing"): - for f in todelete: - ui.status(_('deleting %s\n') % f) - util.unlinkpath(repo.svfs.join(f)) - repo.store.markremoved(f) + with repo.transaction("narrowing"): + for f in todelete: + ui.status(_('deleting %s\n') % f) + util.unlinkpath(repo.svfs.join(f)) + repo.store.markremoved(f) - for f in repo.dirstate: - if not newmatch(f): - repo.dirstate.drop(f) - repo.wvfs.unlinkpath(f) - repo.setnarrowpats(newincludes, newexcludes) + for f in repo.dirstate: + if not newmatch(f): + repo.dirstate.drop(f) + repo.wvfs.unlinkpath(f) + repo.setnarrowpats(newincludes, newexcludes) - repo.destroyed() + repo.destroyed() def _widen(ui, repo, remote, commoninc, newincludes, newexcludes): newmatch = narrowspec.match(repo.root, newincludes, newexcludes) @@ -269,28 +270,29 @@ repo.setnarrowpats(newincludes, newexcludes) repo.setnewnarrowpats = setnewnarrowpats - ds = repo.dirstate - p1, p2 = ds.p1(), ds.p2() - with ds.parentchange(): - ds.setparents(node.nullid, node.nullid) - common = commoninc[0] - with wrappedextraprepare: - exchange.pull(repo, remote, heads=common) - with ds.parentchange(): - ds.setparents(p1, p2) + with ui.uninterruptable(): + ds = repo.dirstate + p1, p2 = ds.p1(), ds.p2() + with ds.parentchange(): + ds.setparents(node.nullid, node.nullid) + common = commoninc[0] + with wrappedextraprepare: + exchange.pull(repo, remote, heads=common) + with ds.parentchange(): + ds.setparents(p1, p2) - actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()} - addgaction = actions['g'].append + actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()} + addgaction = actions['g'].append - mf = repo['.'].manifest().matches(newmatch) - for f, fn in mf.iteritems(): - if f not in repo.dirstate: - addgaction((f, (mf.flags(f), False), - "add from widened narrow clone")) + mf = repo['.'].manifest().matches(newmatch) + for f, fn in mf.iteritems(): + if f not in repo.dirstate: + addgaction((f, (mf.flags(f), False), + "add from widened narrow clone")) - merge.applyupdates(repo, actions, wctx=repo[None], - mctx=repo['.'], overwrite=False) - merge.recordupdates(repo, actions, branchmerge=False) + merge.applyupdates(repo, actions, wctx=repo[None], + mctx=repo['.'], overwrite=False) + merge.recordupdates(repo, actions, branchmerge=False) # TODO(rdamazio): Make new matcher format and update description @command('tracked',
--- a/hgext/narrow/narrowdirstate.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/narrow/narrowdirstate.py Thu Jul 19 13:55:54 2018 -0400 @@ -9,74 +9,91 @@ from mercurial.i18n import _ from mercurial import ( - dirstate, error, - extensions, match as matchmod, narrowspec, util as hgutil, ) -def setup(repo): +def wrapdirstate(repo, dirstate): """Add narrow spec dirstate ignore, block changes outside narrow spec.""" - def walk(orig, self, match, subrepos, unknown, ignored, full=True, - narrowonly=True): - if narrowonly: - # hack to not exclude explicitly-specified paths so that they can - # be warned later on e.g. dirstate.add() - em = matchmod.exact(match._root, match._cwd, match.files()) - nm = matchmod.unionmatcher([repo.narrowmatch(), em]) - match = matchmod.intersectmatchers(match, nm) - return orig(self, match, subrepos, unknown, ignored, full) - - extensions.wrapfunction(dirstate.dirstate, 'walk', walk) - - # Prevent adding files that are outside the sparse checkout - editfuncs = ['normal', 'add', 'normallookup', 'copy', 'remove', 'merge'] - for func in editfuncs: - def _wrapper(orig, self, *args): + def _editfunc(fn): + def _wrapper(self, *args): dirstate = repo.dirstate narrowmatch = repo.narrowmatch() for f in args: if f is not None and not narrowmatch(f) and f not in dirstate: raise error.Abort(_("cannot track '%s' - it is outside " + "the narrow clone") % f) - return orig(self, *args) - extensions.wrapfunction(dirstate.dirstate, func, _wrapper) - - def filterrebuild(orig, self, parent, allfiles, changedfiles=None): - if changedfiles is None: - # Rebuilding entire dirstate, let's filter allfiles to match the - # narrowspec. - allfiles = [f for f in allfiles if repo.narrowmatch()(f)] - orig(self, parent, allfiles, changedfiles) - - extensions.wrapfunction(dirstate.dirstate, 'rebuild', filterrebuild) + return fn(self, *args) + return _wrapper def _narrowbackupname(backupname): assert 'dirstate' in backupname return backupname.replace('dirstate', narrowspec.FILENAME) - def restorebackup(orig, self, tr, backupname): - self._opener.rename(_narrowbackupname(backupname), narrowspec.FILENAME, - checkambig=True) - orig(self, tr, backupname) + class narrowdirstate(dirstate.__class__): + def walk(self, match, subrepos, unknown, ignored, full=True, + narrowonly=True): + if narrowonly: + # hack to not exclude explicitly-specified paths so that they + # can be warned later on e.g. dirstate.add() + em = matchmod.exact(match._root, match._cwd, match.files()) + nm = matchmod.unionmatcher([repo.narrowmatch(), em]) + match = matchmod.intersectmatchers(match, nm) + return super(narrowdirstate, self).walk(match, subrepos, unknown, + ignored, full) - extensions.wrapfunction(dirstate.dirstate, 'restorebackup', restorebackup) + # Prevent adding/editing/copying/deleting files that are outside the + # sparse checkout + @_editfunc + def normal(self, *args): + return super(narrowdirstate, self).normal(*args) - def savebackup(orig, self, tr, backupname): - orig(self, tr, backupname) + @_editfunc + def add(self, *args): + return super(narrowdirstate, self).add(*args) + + @_editfunc + def normallookup(self, *args): + return super(narrowdirstate, self).normallookup(*args) + + @_editfunc + def copy(self, *args): + return super(narrowdirstate, self).copy(*args) - narrowbackupname = _narrowbackupname(backupname) - self._opener.tryunlink(narrowbackupname) - hgutil.copyfile(self._opener.join(narrowspec.FILENAME), - self._opener.join(narrowbackupname), hardlink=True) + @_editfunc + def remove(self, *args): + return super(narrowdirstate, self).remove(*args) + + @_editfunc + def merge(self, *args): + return super(narrowdirstate, self).merge(*args) + + def rebuild(self, parent, allfiles, changedfiles=None): + if changedfiles is None: + # Rebuilding entire dirstate, let's filter allfiles to match the + # narrowspec. + allfiles = [f for f in allfiles if repo.narrowmatch()(f)] + super(narrowdirstate, self).rebuild(parent, allfiles, changedfiles) - extensions.wrapfunction(dirstate.dirstate, 'savebackup', savebackup) + def restorebackup(self, tr, backupname): + self._opener.rename(_narrowbackupname(backupname), + narrowspec.FILENAME, checkambig=True) + super(narrowdirstate, self).restorebackup(tr, backupname) + + def savebackup(self, tr, backupname): + super(narrowdirstate, self).savebackup(tr, backupname) - def clearbackup(orig, self, tr, backupname): - orig(self, tr, backupname) - self._opener.unlink(_narrowbackupname(backupname)) + narrowbackupname = _narrowbackupname(backupname) + self._opener.tryunlink(narrowbackupname) + hgutil.copyfile(self._opener.join(narrowspec.FILENAME), + self._opener.join(narrowbackupname), hardlink=True) - extensions.wrapfunction(dirstate.dirstate, 'clearbackup', clearbackup) + def clearbackup(self, tr, backupname): + super(narrowdirstate, self).clearbackup(tr, backupname) + self._opener.unlink(_narrowbackupname(backupname)) + + dirstate.__class__ = narrowdirstate + return dirstate
--- a/hgext/narrow/narrowmerge.py Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -# narrowmerge.py - extensions to mercurial merge module to support narrow clones -# -# Copyright 2017 Google, Inc. -# -# 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 mercurial.i18n import _ -from mercurial import ( - copies, - error, - extensions, - merge, -) - -def setup(): - def _manifestmerge(orig, repo, wctx, p2, pa, branchmerge, *args, **kwargs): - """Filter updates to only lay out files that match the narrow spec.""" - actions, diverge, renamedelete = orig( - repo, wctx, p2, pa, branchmerge, *args, **kwargs) - - narrowmatch = repo.narrowmatch() - if narrowmatch.always(): - return actions, diverge, renamedelete - - nooptypes = set(['k']) # TODO: handle with nonconflicttypes - nonconflicttypes = set('a am c cm f g r e'.split()) - # We mutate the items in the dict during iteration, so iterate - # over a copy. - for f, action in list(actions.items()): - if narrowmatch(f): - pass - elif not branchmerge: - del actions[f] # just updating, ignore changes outside clone - elif action[0] in nooptypes: - del actions[f] # merge does not affect file - elif action[0] in nonconflicttypes: - raise error.Abort(_('merge affects file \'%s\' outside narrow, ' - 'which is not yet supported') % f, - hint=_('merging in the other direction ' - 'may work')) - else: - raise error.Abort(_('conflict in file \'%s\' is outside ' - 'narrow clone') % f) - - return actions, diverge, renamedelete - - extensions.wrapfunction(merge, 'manifestmerge', _manifestmerge) - - def _checkcollision(orig, repo, wmf, actions): - narrowmatch = repo.narrowmatch() - if not narrowmatch.always(): - wmf = wmf.matches(narrowmatch) - if actions: - narrowactions = {} - for m, actionsfortype in actions.iteritems(): - narrowactions[m] = [] - for (f, args, msg) in actionsfortype: - if narrowmatch(f): - narrowactions[m].append((f, args, msg)) - actions = narrowactions - return orig(repo, wmf, actions) - - extensions.wrapfunction(merge, '_checkcollision', _checkcollision) - - def _computenonoverlap(orig, repo, *args, **kwargs): - u1, u2 = orig(repo, *args, **kwargs) - narrowmatch = repo.narrowmatch() - if narrowmatch.always(): - return u1, u2 - - u1 = [f for f in u1 if narrowmatch(f)] - u2 = [f for f in u2 if narrowmatch(f)] - return u1, u2 - extensions.wrapfunction(copies, '_computenonoverlap', _computenonoverlap)
--- a/hgext/narrow/narrowrepo.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/narrow/narrowrepo.py Thu Jul 19 13:55:54 2018 -0400 @@ -15,6 +15,7 @@ ) from . import ( + narrowdirstate, narrowrevlog, ) @@ -62,4 +63,8 @@ return scmutil.status(modified, added, removed, deleted, unknown, ignored, clean) + def _makedirstate(self): + dirstate = super(narrowrepository, self)._makedirstate() + return narrowdirstate.wrapdirstate(self, dirstate) + repo.__class__ = narrowrepository
--- a/hgext/notify.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/notify.py Thu Jul 19 13:55:54 2018 -0400 @@ -113,6 +113,9 @@ notify.diffstat Set to True to include a diffstat before diff content. Default: True. +notify.showfunc + If set, override ``diff.showfunc`` for the diff content. Default: None. + notify.merge If True, send notifications for merge changesets. Default: True. @@ -206,6 +209,9 @@ configitem('notify', 'sources', default='serve', ) +configitem('notify', 'showfunc', + default=None, +) configitem('notify', 'strip', default=0, ) @@ -260,6 +266,9 @@ self.charsets = mail._charsets(self.ui) self.subs = self.subscribers() self.merge = self.ui.configbool('notify', 'merge') + self.showfunc = self.ui.configbool('notify', 'showfunc') + if self.showfunc is None: + self.showfunc = self.ui.configbool('diff', 'showfunc') mapfile = None template = (self.ui.config('notify', hooktype) or @@ -420,8 +429,9 @@ ref = ref.node() else: ref = ctx.node() - chunks = patch.diff(self.repo, prev, ref, - opts=patch.diffallopts(self.ui)) + diffopts = patch.diffallopts(self.ui) + diffopts.showfunc = self.showfunc + chunks = patch.diff(self.repo, prev, ref, opts=diffopts) difflines = ''.join(chunks).splitlines() if self.ui.configbool('notify', 'diffstat'):
--- a/hgext/patchbomb.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/patchbomb.py Thu Jul 19 13:55:54 2018 -0400 @@ -75,11 +75,12 @@ import email as emailmod import email.generator as emailgen +import email.mime.base as emimebase +import email.mime.multipart as emimemultipart import email.utils as eutil import errno import os import socket -import tempfile from mercurial.i18n import _ from mercurial import ( @@ -94,7 +95,6 @@ patch, pycompat, registrar, - repair, scmutil, templater, util, @@ -256,7 +256,7 @@ body += '\n'.join(patchlines) if addattachment: - msg = emailmod.MIMEMultipart.MIMEMultipart() + msg = emimemultipart.MIMEMultipart() if body: msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test'))) p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', @@ -318,7 +318,7 @@ The bundle is a returned as a single in-memory binary blob. """ ui = repo.ui - tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-') + tmpdir = pycompat.mkdtemp(prefix='hg-email-bundle-') tmpfn = os.path.join(tmpdir, 'bundle') btype = ui.config('patchbomb', 'bundletype') if btype: @@ -367,10 +367,10 @@ or prompt(ui, 'Subject:', 'A bundle for your repository')) body = _getdescription(repo, '', sender, **opts) - msg = emailmod.MIMEMultipart.MIMEMultipart() + msg = emimemultipart.MIMEMultipart() if body: msg.attach(mail.mimeencode(ui, body, _charsets, opts.get(r'test'))) - datapart = emailmod.MIMEBase.MIMEBase('application', 'x-mercurial-bundle') + datapart = emimebase.MIMEBase('application', 'x-mercurial-bundle') datapart.set_payload(bundle) bundlename = '%s.hg' % opts.get(r'bundlename', 'bundle') datapart.add_header('Content-Disposition', 'attachment', @@ -624,7 +624,7 @@ elif bookmark: if bookmark not in repo._bookmarks: raise error.Abort(_("bookmark '%s' not found") % bookmark) - revs = repair.stripbmrevset(repo, bookmark) + revs = scmutil.bookmarkrevs(repo, bookmark) revs = scmutil.revrange(repo, revs) if outgoing: @@ -753,6 +753,7 @@ sender = mail.addressencode(ui, sender, _charsets, opts.get('test')) sendmail = None firstpatch = None + progress = ui.makeprogress(_('sending'), unit=_('emails'), total=len(msgs)) for i, (m, subj, ds) in enumerate(msgs): try: m['Message-Id'] = genmsgid(m['X-Mercurial-Node']) @@ -793,8 +794,7 @@ if not sendmail: sendmail = mail.connect(ui, mbox=mbox) ui.status(_('sending '), subj, ' ...\n') - ui.progress(_('sending'), i, item=subj, total=len(msgs), - unit=_('emails')) + progress.update(i, item=subj) if not mbox: # Exim does not remove the Bcc field del m['Bcc'] @@ -803,5 +803,4 @@ generator.flatten(m, 0) sendmail(sender_addr, to + bcc + cc, fp.getvalue()) - ui.progress(_('writing'), None) - ui.progress(_('sending'), None) + progress.complete()
--- a/hgext/rebase.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/rebase.py Thu Jul 19 13:55:54 2018 -0400 @@ -34,7 +34,6 @@ error, extensions, hg, - lock, merge as mergemod, mergeutil, obsolete, @@ -48,11 +47,10 @@ revsetlang, scmutil, smartset, + state as statemod, util, ) -release = lock.release - # The following constants are used throughout the rebase module. The ordering of # their values must be maintained. @@ -184,6 +182,7 @@ self.obsoletenotrebased = {} self.obsoletewithoutsuccessorindestination = set() self.inmemory = inmemory + self.stateobj = statemod.cmdstate(repo, 'rebasestate') @property def repo(self): @@ -225,40 +224,55 @@ def restorestatus(self): """Restore a previously stored status""" + if not self.stateobj.exists(): + cmdutil.wrongtooltocontinue(self.repo, _('rebase')) + + data = self._read() + self.repo.ui.debug('rebase status resumed\n') + + self.originalwd = data['originalwd'] + self.destmap = data['destmap'] + self.state = data['state'] + self.skipped = data['skipped'] + self.collapsef = data['collapse'] + self.keepf = data['keep'] + self.keepbranchesf = data['keepbranches'] + self.external = data['external'] + self.activebookmark = data['activebookmark'] + + def _read(self): self.prepared = True repo = self.repo assert repo.filtername is None - keepbranches = None + data = {'keepbranches': None, 'collapse': None, 'activebookmark': None, + 'external': nullrev, 'keep': None, 'originalwd': None} legacydest = None - collapse = False - external = nullrev - activebookmark = None state = {} destmap = {} - try: + if True: f = repo.vfs("rebasestate") for i, l in enumerate(f.read().splitlines()): if i == 0: - originalwd = repo[l].rev() + data['originalwd'] = repo[l].rev() elif i == 1: # this line should be empty in newer version. but legacy # clients may still use it if l: legacydest = repo[l].rev() elif i == 2: - external = repo[l].rev() + data['external'] = repo[l].rev() elif i == 3: - collapse = bool(int(l)) + data['collapse'] = bool(int(l)) elif i == 4: - keep = bool(int(l)) + data['keep'] = bool(int(l)) elif i == 5: - keepbranches = bool(int(l)) + data['keepbranches'] = bool(int(l)) elif i == 6 and not (len(l) == 81 and ':' in l): # line 6 is a recent addition, so for backwards # compatibility check that the line doesn't look like the # oldrev:newrev lines - activebookmark = l + data['activebookmark'] = l else: args = l.split(':') oldrev = repo[args[0]].rev() @@ -276,35 +290,24 @@ else: state[oldrev] = repo[newrev].rev() - except IOError as err: - if err.errno != errno.ENOENT: - raise - cmdutil.wrongtooltocontinue(repo, _('rebase')) - - if keepbranches is None: + if data['keepbranches'] is None: raise error.Abort(_('.hg/rebasestate is incomplete')) + data['destmap'] = destmap + data['state'] = state skipped = set() # recompute the set of skipped revs - if not collapse: + if not data['collapse']: seen = set(destmap.values()) for old, new in sorted(state.items()): if new != revtodo and new in seen: skipped.add(old) seen.add(new) + data['skipped'] = skipped repo.ui.debug('computed skipped revs: %s\n' % (' '.join('%d' % r for r in sorted(skipped)) or '')) - repo.ui.debug('rebase status resumed\n') - self.originalwd = originalwd - self.destmap = destmap - self.state = state - self.skipped = skipped - self.collapsef = collapse - self.keepf = keep - self.keepbranchesf = keepbranches - self.external = external - self.activebookmark = activebookmark + return data def _handleskippingobsolete(self, obsoleterevs, destmap): """Compute structures necessary for skipping obsolete revisions @@ -325,7 +328,7 @@ skippedset.update(obsoleteextinctsuccessors) _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset) - def _prepareabortorcontinue(self, isabort): + def _prepareabortorcontinue(self, isabort, backup=True, suppwarns=False): try: self.restorestatus() self.collapsemsg = restorecollapsemsg(self.repo, isabort) @@ -341,8 +344,9 @@ hint = _('use "hg rebase --abort" to clear broken state') raise error.Abort(msg, hint=hint) if isabort: - return abort(self.repo, self.originalwd, self.destmap, - self.state, activebookmark=self.activebookmark) + return abort(self.repo, self.originalwd, self.destmap, self.state, + activebookmark=self.activebookmark, backup=backup, + suppwarns=suppwarns) def _preparenewrebase(self, destmap): if not destmap: @@ -433,13 +437,10 @@ self.storestatus(tr) cands = [k for k, v in self.state.iteritems() if v == revtodo] - total = len(cands) - posholder = [0] + p = repo.ui.makeprogress(_("rebasing"), unit=_('changesets'), + total=len(cands)) def progress(ctx): - posholder[0] += 1 - self.repo.ui.progress(_("rebasing"), posholder[0], - ("%d:%s" % (ctx.rev(), ctx)), - _('changesets'), total) + p.increment(item=("%d:%s" % (ctx.rev(), ctx))) allowdivergence = self.ui.configbool( 'experimental', 'evolution.allowdivergence') for subset in sortsource(self.destmap): @@ -452,7 +453,7 @@ ) for rev in sortedrevs: self._rebasenode(tr, rev, allowdivergence, progress) - ui.progress(_('rebasing'), None) + p.complete() ui.note(_('rebase merging completed\n')) def _concludenode(self, rev, p1, p2, editor, commitmsg=None): @@ -625,7 +626,7 @@ newwd = self.originalwd if newwd not in [c.rev() for c in repo[None].parents()]: ui.note(_("update back to initial working directory parent\n")) - hg.updaterepo(repo, newwd, False) + hg.updaterepo(repo, newwd, overwrite=False) collapsedas = None if self.collapsef and not self.keepf: @@ -673,8 +674,7 @@ ('a', 'abort', False, _('abort an interrupted rebase')), ('', 'auto-orphans', '', _('automatically rebase orphan revisions ' 'in the specified revset (EXPERIMENTAL)')), - ] + - cmdutil.formatteropts, + ] + cmdutil.dryrunopts + cmdutil.formatteropts + cmdutil.confirmopts, _('[-s REV | -b REV] [-d REV] [OPTION]')) def rebase(ui, repo, **opts): """move changeset (and descendants) to a different branch @@ -797,7 +797,23 @@ unresolved conflicts. """ + opts = pycompat.byteskwargs(opts) inmemory = ui.configbool('rebase', 'experimental.inmemory') + dryrun = opts.get('dry_run') + if dryrun: + if opts.get('abort'): + raise error.Abort(_('cannot specify both --dry-run and --abort')) + if opts.get('continue'): + raise error.Abort(_('cannot specify both --dry-run and --continue')) + if opts.get('confirm'): + dryrun = True + if opts.get('dry_run'): + raise error.Abort(_('cannot specify both --confirm and --dry-run')) + if opts.get('abort'): + raise error.Abort(_('cannot specify both --confirm and --abort')) + if opts.get('continue'): + raise error.Abort(_('cannot specify both --confirm and --continue')) + if (opts.get('continue') or opts.get('abort') or repo.currenttransaction() is not None): # in-memory rebase is not compatible with resuming rebases. @@ -814,25 +830,67 @@ opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)] opts['dest'] = '_destautoorphanrebase(SRC)' - if inmemory: + if dryrun: + return _dryrunrebase(ui, repo, opts) + elif inmemory: try: # in-memory merge doesn't support conflicts, so if we hit any, abort # and re-run as an on-disk merge. overrides = {('rebase', 'singletransaction'): True} with ui.configoverride(overrides, 'rebase'): - return _origrebase(ui, repo, inmemory=inmemory, **opts) + return _dorebase(ui, repo, opts, inmemory=inmemory) except error.InMemoryMergeConflictsError: ui.warn(_('hit merge conflicts; re-running rebase without in-memory' ' merge\n')) - _origrebase(ui, repo, **{'abort': True}) - return _origrebase(ui, repo, inmemory=False, **opts) + _dorebase(ui, repo, {'abort': True}) + return _dorebase(ui, repo, opts, inmemory=False) else: - return _origrebase(ui, repo, **opts) + return _dorebase(ui, repo, opts) -def _origrebase(ui, repo, inmemory=False, **opts): - opts = pycompat.byteskwargs(opts) +def _dryrunrebase(ui, repo, opts): + rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts) + confirm = opts.get('confirm') + if confirm: + ui.status(_('starting in-memory rebase\n')) + else: + ui.status(_('starting dry-run rebase; repository will not be ' + 'changed\n')) + with repo.wlock(), repo.lock(): + needsabort = True + try: + overrides = {('rebase', 'singletransaction'): True} + with ui.configoverride(overrides, 'rebase'): + _origrebase(ui, repo, opts, rbsrt, inmemory=True, + leaveunfinished=True) + except error.InMemoryMergeConflictsError: + ui.status(_('hit a merge conflict\n')) + return 1 + else: + if confirm: + ui.status(_('rebase completed successfully\n')) + if not ui.promptchoice(_(b'apply changes (yn)?' + b'$$ &Yes $$ &No')): + # finish unfinished rebase + rbsrt._finishrebase() + else: + rbsrt._prepareabortorcontinue(isabort=True, backup=False, + suppwarns=True) + needsabort = False + else: + ui.status(_('dry-run rebase completed successfully; run without' + ' -n/--dry-run to perform this rebase\n')) + return 0 + finally: + if needsabort: + # no need to store backup in case of dryrun + rbsrt._prepareabortorcontinue(isabort=True, backup=False, + suppwarns=True) + +def _dorebase(ui, repo, opts, inmemory=False): rbsrt = rebaseruntime(repo, ui, inmemory, opts) + return _origrebase(ui, repo, opts, rbsrt, inmemory=inmemory) +def _origrebase(ui, repo, opts, rbsrt, inmemory=False, leaveunfinished=False): with repo.wlock(), repo.lock(): # Validate input and define rebasing points destf = opts.get('dest', None) @@ -902,7 +960,8 @@ dsguard = dirstateguard.dirstateguard(repo, 'rebase') with util.acceptintervention(dsguard): rbsrt._performrebase(tr) - rbsrt._finishrebase() + if not leaveunfinished: + rbsrt._finishrebase() def _definedestmap(ui, repo, inmemory, destf=None, srcf=None, basef=None, revf=None, destspace=None): @@ -1255,13 +1314,7 @@ # use unfiltered changelog since successorrevs may return filtered nodes assert repo.filtername is None cl = repo.changelog - def isancestor(a, b): - # take revision numbers instead of nodes - if a == b: - return True - elif a > b: - return False - return cl.isancestor(cl.node(a), cl.node(b)) + isancestor = cl.isancestorrev dest = destmap[rev] oldps = repo.changelog.parentrevs(rev) # old parents @@ -1527,7 +1580,8 @@ return False -def abort(repo, originalwd, destmap, state, activebookmark=None): +def abort(repo, originalwd, destmap, state, activebookmark=None, backup=True, + suppwarns=False): '''Restore the repository to its original state. Additional args: activebookmark: the name of the bookmark that should be active after the @@ -1572,8 +1626,7 @@ # Strip from the first rebased revision if rebased: - # no backup of rebased cset versions needed - repair.strip(repo.ui, repo, strippoints) + repair.strip(repo.ui, repo, strippoints, backup=backup) if activebookmark and activebookmark in repo._bookmarks: bookmarks.activate(repo, activebookmark) @@ -1581,7 +1634,8 @@ finally: clearstatus(repo) clearcollapsemsg(repo) - repo.ui.warn(_('rebase aborted\n')) + if not suppwarns: + repo.ui.warn(_('rebase aborted\n')) return 0 def sortsource(destmap): @@ -1790,33 +1844,31 @@ assert repo.filtername is None cl = repo.changelog nodemap = cl.nodemap - extinctnodes = set(cl.node(r) for r in repo.revs('extinct()')) + extinctrevs = set(repo.revs('extinct()')) for srcrev in rebaseobsrevs: srcnode = cl.node(srcrev) - destnode = cl.node(destmap[srcrev]) # XXX: more advanced APIs are required to handle split correctly successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode])) # obsutil.allsuccessors includes node itself successors.remove(srcnode) - if successors.issubset(extinctnodes): + succrevs = {nodemap[s] for s in successors if s in nodemap} + if succrevs.issubset(extinctrevs): # all successors are extinct obsoleteextinctsuccessors.add(srcrev) if not successors: # no successor obsoletenotrebased[srcrev] = None else: - for succnode in successors: - if succnode not in nodemap: - continue - if cl.isancestor(succnode, destnode): - obsoletenotrebased[srcrev] = nodemap[succnode] + dstrev = destmap[srcrev] + for succrev in succrevs: + if cl.isancestorrev(succrev, dstrev): + obsoletenotrebased[srcrev] = succrev break else: # If 'srcrev' has a successor in rebase set but none in # destination (which would be catched above), we shall skip it # and its descendants to avoid divergence. - if any(nodemap[s] in destmap for s in successors - if s in nodemap): + if any(s in destmap for s in succrevs): obsoletewithoutsuccessorindestination.add(srcrev) return (
--- a/hgext/relink.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/relink.py Thu Jul 19 13:55:54 2018 -0400 @@ -70,17 +70,10 @@ # No point in continuing raise error.Abort(_('source and destination are on different devices')) - locallock = repo.lock() - try: - remotelock = src.lock() - try: - candidates = sorted(collect(src, ui)) - targets = prune(candidates, src.store.path, repo.store.path, ui) - do_relink(src.store.path, repo.store.path, targets, ui) - finally: - remotelock.release() - finally: - locallock.release() + with repo.lock(), src.lock(): + candidates = sorted(collect(src, ui)) + targets = prune(candidates, src.store.path, repo.store.path, ui) + do_relink(src.store.path, repo.store.path, targets, ui) def collect(src, ui): seplen = len(os.path.sep) @@ -94,6 +87,7 @@ # mozilla-central as of 2010-06-10 had a ratio of just over 7:5. total = live * 3 // 2 src = src.store.path + progress = ui.makeprogress(_('collecting'), unit=_('files'), total=total) pos = 0 ui.status(_("tip has %d files, estimated total number of files: %d\n") % (live, total)) @@ -108,9 +102,9 @@ continue pos += 1 candidates.append((os.path.join(relpath, filename), st)) - ui.progress(_('collecting'), pos, filename, _('files'), total) + progress.update(pos, item=filename) - ui.progress(_('collecting'), None) + progress.complete() ui.status(_('collected %d candidate storage files\n') % len(candidates)) return candidates @@ -132,7 +126,8 @@ return st targets = [] - total = len(candidates) + progress = ui.makeprogress(_('pruning'), unit=_('files'), + total=len(candidates)) pos = 0 for fn, st in candidates: pos += 1 @@ -143,9 +138,9 @@ ui.debug('not linkable: %s\n' % fn) continue targets.append((fn, ts.st_size)) - ui.progress(_('pruning'), pos, fn, _('files'), total) + progress.update(pos, item=fn) - ui.progress(_('pruning'), None) + progress.complete() ui.status(_('pruned down to %d probably relinkable files\n') % len(targets)) return targets @@ -164,8 +159,9 @@ relinked = 0 savedbytes = 0 + progress = ui.makeprogress(_('relinking'), unit=_('files'), + total=len(files)) pos = 0 - total = len(files) for f, sz in files: pos += 1 source = os.path.join(src, f) @@ -186,13 +182,13 @@ continue try: relinkfile(source, tgt) - ui.progress(_('relinking'), pos, f, _('files'), total) + progress.update(pos, item=f) relinked += 1 savedbytes += sz except OSError as inst: ui.warn('%s: %s\n' % (tgt, stringutil.forcebytestr(inst))) - ui.progress(_('relinking'), None) + progress.complete() ui.status(_('relinked %d files (%s reclaimed)\n') % (relinked, util.bytecount(savedbytes)))
--- a/hgext/remotenames.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/remotenames.py Thu Jul 19 13:55:54 2018 -0400 @@ -249,6 +249,10 @@ extensions.wrapfunction(bookmarks, '_printbookmarks', wrapprintbookmarks) def reposetup(ui, repo): + + # set the config option to store remotenames + repo.ui.setconfig('experimental', 'remotenames', True, 'remotenames-ext') + if not repo.local(): return
--- a/hgext/schemes.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/schemes.py Thu Jul 19 13:55:54 2018 -0400 @@ -114,7 +114,7 @@ def extsetup(ui): schemes.update(dict(ui.configitems('schemes'))) - t = templater.engine(lambda x: x) + t = templater.engine(templater.parse) for scheme, url in schemes.items(): if (pycompat.iswindows and len(scheme) == 1 and scheme.isalpha() and os.path.exists('%s:\\' % scheme)):
--- a/hgext/shelve.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/shelve.py Thu Jul 19 13:55:54 2018 -0400 @@ -594,10 +594,15 @@ for chunk, label in patch.diffstatui(difflines, width=width): ui.write(chunk, label=label) -def patchcmds(ui, repo, pats, opts, subcommand): +def patchcmds(ui, repo, pats, opts): """subcommand that displays shelves""" if len(pats) == 0: - raise error.Abort(_("--%s expects at least one shelf") % subcommand) + shelves = listshelves(repo) + if not shelves: + raise error.Abort(_("there are no shelves to show")) + mtime, name = shelves[0] + sname = util.split(name)[1] + pats = [sname] for shelfname in pats: if not shelvedfile(repo, shelfname, patchextension).exists(): @@ -621,14 +626,14 @@ try: checkparents(repo, state) - repo.vfs.rename('unshelverebasestate', 'rebasestate') - try: - rebase.rebase(ui, repo, **{ - r'abort' : True - }) - except Exception: - repo.vfs.rename('rebasestate', 'unshelverebasestate') - raise + merge.update(repo, state.pendingctx, False, True) + if (state.activebookmark + and state.activebookmark in repo._bookmarks): + bookmarks.activate(repo, state.activebookmark) + + if repo.vfs.exists('unshelverebasestate'): + repo.vfs.rename('unshelverebasestate', 'rebasestate') + rebase.clearstatus(repo) mergefiles(ui, repo, state.wctx, state.pendingctx) repair.strip(ui, repo, state.nodestoremove, backup=False, @@ -683,22 +688,41 @@ _("unresolved conflicts, can't continue"), hint=_("see 'hg resolve', then 'hg unshelve --continue'")) - repo.vfs.rename('unshelverebasestate', 'rebasestate') - try: - rebase.rebase(ui, repo, **{ - r'continue' : True - }) - except Exception: - repo.vfs.rename('rebasestate', 'unshelverebasestate') - raise + shelvectx = repo[state.parents[1]] + pendingctx = state.pendingctx + + with repo.dirstate.parentchange(): + repo.setparents(state.pendingctx.node(), nodemod.nullid) + repo.dirstate.write(repo.currenttransaction()) + + overrides = {('phases', 'new-commit'): phases.secret} + with repo.ui.configoverride(overrides, 'unshelve'): + with repo.dirstate.parentchange(): + repo.setparents(state.parents[0], nodemod.nullid) + newnode = repo.commit(text=shelvectx.description(), + extra=shelvectx.extra(), + user=shelvectx.user(), + date=shelvectx.date()) - shelvectx = repo['tip'] - if state.pendingctx not in shelvectx.parents(): - # rebase was a no-op, so it produced no child commit + if newnode is None: + # If it ended up being a no-op commit, then the normal + # merge state clean-up path doesn't happen, so do it + # here. Fix issue5494 + merge.mergestate.clean(repo) shelvectx = state.pendingctx + msg = _('note: unshelved changes already existed ' + 'in the working copy\n') + ui.status(msg) else: - # only strip the shelvectx if the rebase produced it - state.nodestoremove.append(shelvectx.node()) + # only strip the shelvectx if we produced one + state.nodestoremove.append(newnode) + shelvectx = repo[newnode] + + hg.updaterepo(repo, pendingctx.node(), overwrite=False) + + if repo.vfs.exists('unshelverebasestate'): + repo.vfs.rename('unshelverebasestate', 'rebasestate') + rebase.clearstatus(repo) mergefiles(ui, repo, state.wctx, shelvectx) restorebranch(ui, repo, state.branchtorestore) @@ -746,33 +770,46 @@ if tmpwctx.node() == shelvectx.parents()[0].node(): return shelvectx - ui.status(_('rebasing shelved changes\n')) - try: - rebase.rebase(ui, repo, **{ - r'rev': [shelvectx.rev()], - r'dest': "%d" % tmpwctx.rev(), - r'keep': True, - r'tool': opts.get('tool', ''), - }) - except error.InterventionRequired: - tr.close() + overrides = { + ('ui', 'forcemerge'): opts.get('tool', ''), + ('phases', 'new-commit'): phases.secret, + } + with repo.ui.configoverride(overrides, 'unshelve'): + ui.status(_('rebasing shelved changes\n')) + stats = merge.graft(repo, shelvectx, shelvectx.p1(), + labels=['shelve', 'working-copy'], + keepconflictparent=True) + if stats.unresolvedcount: + tr.close() + + nodestoremove = [repo.changelog.node(rev) + for rev in xrange(oldtiprev, len(repo))] + shelvedstate.save(repo, basename, pctx, tmpwctx, nodestoremove, + branchtorestore, opts.get('keep'), activebookmark) + raise error.InterventionRequired( + _("unresolved conflicts (see 'hg resolve', then " + "'hg unshelve --continue')")) - nodestoremove = [repo.changelog.node(rev) - for rev in xrange(oldtiprev, len(repo))] - shelvedstate.save(repo, basename, pctx, tmpwctx, nodestoremove, - branchtorestore, opts.get('keep'), activebookmark) + with repo.dirstate.parentchange(): + repo.setparents(tmpwctx.node(), nodemod.nullid) + newnode = repo.commit(text=shelvectx.description(), + extra=shelvectx.extra(), + user=shelvectx.user(), + date=shelvectx.date()) - repo.vfs.rename('rebasestate', 'unshelverebasestate') - raise error.InterventionRequired( - _("unresolved conflicts (see 'hg resolve', then " - "'hg unshelve --continue')")) + if newnode is None: + # If it ended up being a no-op commit, then the normal + # merge state clean-up path doesn't happen, so do it + # here. Fix issue5494 + merge.mergestate.clean(repo) + shelvectx = tmpwctx + msg = _('note: unshelved changes already existed ' + 'in the working copy\n') + ui.status(msg) + else: + shelvectx = repo[newnode] + hg.updaterepo(repo, tmpwctx.node(), False) - # refresh ctx after rebase completes - shelvectx = repo['tip'] - - if tmpwctx not in shelvectx.parents(): - # rebase was a no-op, so it produced no child commit - shelvectx = tmpwctx return shelvectx def _forgetunknownfiles(repo, shelvectx, addedbefore): @@ -933,27 +970,27 @@ # to the original pctx. activebookmark = _backupactivebookmark(repo) + tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts, + tmpwctx) + repo, shelvectx = _unshelverestorecommit(ui, repo, basename) + _checkunshelveuntrackedproblems(ui, repo, shelvectx) + branchtorestore = '' + if shelvectx.branch() != shelvectx.p1().branch(): + branchtorestore = shelvectx.branch() + + shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, + basename, pctx, tmpwctx, + shelvectx, branchtorestore, + activebookmark) overrides = {('ui', 'forcemerge'): opts.get('tool', '')} with ui.configoverride(overrides, 'unshelve'): - tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts, - tmpwctx) - repo, shelvectx = _unshelverestorecommit(ui, repo, basename) - _checkunshelveuntrackedproblems(ui, repo, shelvectx) - branchtorestore = '' - if shelvectx.branch() != shelvectx.p1().branch(): - branchtorestore = shelvectx.branch() + mergefiles(ui, repo, pctx, shelvectx) + restorebranch(ui, repo, branchtorestore) + _forgetunknownfiles(repo, shelvectx, addedbefore) - shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, - basename, pctx, tmpwctx, - shelvectx, branchtorestore, - activebookmark) - mergefiles(ui, repo, pctx, shelvectx) - restorebranch(ui, repo, branchtorestore) - _forgetunknownfiles(repo, shelvectx, addedbefore) - - shelvedstate.clear(repo) - _finishunshelve(repo, oldtiprev, tr, activebookmark) - unshelvecleanup(ui, repo, basename, opts) + shelvedstate.clear(repo) + _finishunshelve(repo, oldtiprev, tr, activebookmark) + unshelvecleanup(ui, repo, basename, opts) finally: if tr: tr.release() @@ -979,11 +1016,14 @@ ('n', 'name', '', _('use the given name for the shelved commit'), _('NAME')), ('p', 'patch', None, - _('show patch')), + _('output patches for changes (provide the names of the shelved ' + 'changes as positional arguments)')), ('i', 'interactive', None, _('interactive mode, only works while creating a shelve')), ('', 'stat', None, - _('output diffstat-style summary of changes'))] + cmdutil.walkopts, + _('output diffstat-style summary of changes (provide the names of ' + 'the shelved changes as positional arguments)') + )] + cmdutil.walkopts, _('hg shelve [OPTION]... [FILE]...')) def shelvecmd(ui, repo, *pats, **opts): '''save and set aside changes from the working directory @@ -1047,10 +1087,8 @@ return deletecmd(ui, repo, pats) elif checkopt('list'): return listcmd(ui, repo, pats, opts) - elif checkopt('patch'): - return patchcmds(ui, repo, pats, opts, subcommand='patch') - elif checkopt('stat'): - return patchcmds(ui, repo, pats, opts, subcommand='stat') + elif checkopt('patch') or checkopt('stat'): + return patchcmds(ui, repo, pats, opts) else: return createcmd(ui, repo, pats, opts)
--- a/hgext/sparse.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/sparse.py Thu Jul 19 13:55:54 2018 -0400 @@ -138,9 +138,9 @@ extensions.wrapfunction(logcmdutil, '_initialrevs', _initialrevs) def _clonesparsecmd(orig, ui, repo, *args, **opts): - include_pat = opts.get('include') - exclude_pat = opts.get('exclude') - enableprofile_pat = opts.get('enable_profile') + include_pat = opts.get(r'include') + exclude_pat = opts.get(r'exclude') + enableprofile_pat = opts.get(r'enable_profile') include = exclude = enableprofile = False if include_pat: pat = include_pat @@ -178,7 +178,7 @@ 'also include directories of added files in sparse config')) def _add(orig, ui, repo, *pats, **opts): - if opts.get('sparse'): + if opts.get(r'sparse'): dirs = set() for pat in pats: dirname, basename = util.split(pat)
--- a/hgext/split.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/split.py Thu Jul 19 13:55:54 2018 -0400 @@ -60,6 +60,7 @@ By default, rebase connected non-obsoleted descendants onto the new changeset. Use --no-rebase to avoid the rebase. """ + opts = pycompat.byteskwargs(opts) revlist = [] if opts.get('rev'): revlist.append(opts.get('rev')) @@ -169,7 +170,7 @@ raise error.Abort(_('cannot split an empty revision')) scmutil.cleanupnodes(repo, {ctx.node(): [c.node() for c in committed]}, - operation='split') + operation='split', fixphase=True) return committed[-1]
--- a/hgext/strip.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/strip.py Thu Jul 19 13:55:54 2018 -0400 @@ -103,8 +103,9 @@ 'option)'), _('REV')), ('f', 'force', None, _('force removal of changesets, discard ' 'uncommitted changes (no backup)')), - ('', 'no-backup', None, _('no backups')), - ('', 'nobackup', None, _('no backups (DEPRECATED)')), + ('', 'no-backup', None, _('do not save backup bundle')), + ('', 'nobackup', None, _('do not save backup bundle ' + '(DEPRECATED)')), ('n', '', None, _('ignored (DEPRECATED)')), ('k', 'keep', None, _("do not modify working directory during " "strip")), @@ -165,7 +166,7 @@ nodetobookmarks.setdefault(node, []).append(mark) for marks in nodetobookmarks.values(): if bookmarks.issuperset(marks): - rsrevs = repair.stripbmrevset(repo, marks[0]) + rsrevs = scmutil.bookmarkrevs(repo, marks[0]) revs.update(set(rsrevs)) if not revs: with repo.lock(), repo.transaction('bookmark') as tr:
--- a/hgext/transplant.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/transplant.py Thu Jul 19 13:55:54 2018 -0400 @@ -16,7 +16,7 @@ from __future__ import absolute_import import os -import tempfile + from mercurial.i18n import _ from mercurial import ( bundlerepo, @@ -215,7 +215,7 @@ if skipmerge: patchfile = None else: - fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-') + fd, patchfile = pycompat.mkstemp(prefix='hg-transplant-') fp = os.fdopen(fd, r'wb') gen = patch.diff(source, parent, node, opts=diffopts) for chunk in gen: @@ -263,7 +263,7 @@ self.ui.status(_('filtering %s\n') % patchfile) user, date, msg = (changelog[1], changelog[2], changelog[4]) - fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-') + fd, headerfile = pycompat.mkstemp(prefix='hg-transplant-') fp = os.fdopen(fd, r'wb') fp.write("# HG changeset patch\n") fp.write("# User %s\n" % user) @@ -523,7 +523,8 @@ displayer.show(repo[node]) action = None while not action: - action = 'ynmpcq?'[ui.promptchoice(prompt)] + choice = ui.promptchoice(prompt) + action = 'ynmpcq?'[choice:choice + 1] if action == '?': for c, t in ui.extractchoices(prompt)[1]: ui.write('%s: %s\n' % (c, t)) @@ -682,7 +683,7 @@ sourcerepo = opts.get('source') if sourcerepo: peer = hg.peer(repo, opts, ui.expandpath(sourcerepo)) - heads = map(peer.lookup, opts.get('branch', ())) + heads = pycompat.maplist(peer.lookup, opts.get('branch', ())) target = set(heads) for r in revs: try: @@ -693,7 +694,7 @@ onlyheads=sorted(target), force=True) else: source = repo - heads = map(source.lookup, opts.get('branch', ())) + heads = pycompat.maplist(source.lookup, opts.get('branch', ())) cleanupfn = None try: @@ -708,7 +709,7 @@ matchfn = lambda x: tf(x) and x not in prune else: matchfn = tf - merges = map(source.lookup, opts.get('merge', ())) + merges = pycompat.maplist(source.lookup, opts.get('merge', ())) revmap = {} if revs: for r in scmutil.revrange(source, revs):
--- a/hgext/uncommit.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/uncommit.py Thu Jul 19 13:55:54 2018 -0400 @@ -91,12 +91,7 @@ user=ctx.user(), date=ctx.date(), extra=ctx.extra()) - # phase handling - commitphase = ctx.phase() - overrides = {('phases', 'new-commit'): commitphase} - with repo.ui.configoverride(overrides, 'uncommit'): - newid = repo.commitctx(new) - return newid + return repo.commitctx(new) def _fixdirstate(repo, oldctx, newctx, status): """ fix the dirstate after switching the working directory from oldctx to @@ -183,7 +178,7 @@ # Fully removed the old commit mapping[old.node()] = () - scmutil.cleanupnodes(repo, mapping, 'uncommit') + scmutil.cleanupnodes(repo, mapping, 'uncommit', fixphase=True) with repo.dirstate.parentchange(): repo.dirstate.setparents(newid, node.nullid) @@ -242,12 +237,7 @@ user=predctx.user(), date=predctx.date(), extra=extras) - # phase handling - commitphase = curctx.phase() - overrides = {('phases', 'new-commit'): commitphase} - with repo.ui.configoverride(overrides, 'uncommit'): - newprednode = repo.commitctx(newctx) - + newprednode = repo.commitctx(newctx) newpredctx = repo[newprednode] dirstate = repo.dirstate @@ -257,4 +247,4 @@ _fixdirstate(repo, curctx, newpredctx, s) mapping = {curctx.node(): (newprednode,)} - scmutil.cleanupnodes(repo, mapping, 'unamend') + scmutil.cleanupnodes(repo, mapping, 'unamend', fixphase=True)
--- a/hgext/win32mbcs.py Sun Jul 01 23:36:53 2018 +0900 +++ b/hgext/win32mbcs.py Thu Jul 19 13:55:54 2018 -0400 @@ -90,7 +90,7 @@ return arg def encode(arg): - if isinstance(arg, unicode): + if isinstance(arg, pycompat.unicode): return arg.encode(_encoding) elif isinstance(arg, tuple): return tuple(map(encode, arg)) @@ -127,7 +127,7 @@ " %s encoding\n") % (_encoding)) def wrapper(func, args, kwds): - return basewrapper(func, unicode, encode, decode, args, kwds) + return basewrapper(func, pycompat.unicode, encode, decode, args, kwds) def reversewrapper(func, args, kwds):
--- a/i18n/da.po Sun Jul 01 23:36:53 2018 +0900 +++ b/i18n/da.po Thu Jul 19 13:55:54 2018 -0400 @@ -13696,7 +13696,7 @@ msgstr "" msgid "" -"``allow_archive``\n" +"``allow-archive``\n" " List of archive format (bz2, gz, zip) allowed for downloading.\n" " Default is empty." msgstr ""
--- a/i18n/de.po Sun Jul 01 23:36:53 2018 +0900 +++ b/i18n/de.po Thu Jul 19 13:55:54 2018 -0400 @@ -17347,7 +17347,7 @@ msgstr "" msgid "" -"``allow_archive``\n" +"``allow-archive``\n" " List of archive format (bz2, gz, zip) allowed for downloading.\n" " Default is empty." msgstr ""
--- a/i18n/ja.po Sun Jul 01 23:36:53 2018 +0900 +++ b/i18n/ja.po Thu Jul 19 13:55:54 2018 -0400 @@ -27712,11 +27712,11 @@ " サーバの待ちうけアドレス。 (デフォルト値: ホストの持つ全アドレス)" msgid "" -"``allow_archive``\n" +"``allow-archive``\n" " List of archive format (bz2, gz, zip) allowed for downloading.\n" " (default: empty)" msgstr "" -"``allow_archive``\n" +"``allow-archive``\n" " 利用可能なダウンロード向けのアーカイブ形式 (bz2, gz, zip) 一覧。\n" " (デフォルト値: 空 = ダウンロード不可)"
--- a/i18n/pt_BR.po Sun Jul 01 23:36:53 2018 +0900 +++ b/i18n/pt_BR.po Thu Jul 19 13:55:54 2018 -0400 @@ -28663,11 +28663,11 @@ " (padrão: usa todos os endereços)" msgid "" -"``allow_archive``\n" +"``allow-archive``\n" " List of archive format (bz2, gz, zip) allowed for downloading.\n" " (default: empty)" msgstr "" -"``allow_archive``\n" +"``allow-archive``\n" " Lista de formatos de pacote (bz2, gz, zip) permitidos para download.\n" " (padrão: lista vazia)"
--- a/i18n/ro.po Sun Jul 01 23:36:53 2018 +0900 +++ b/i18n/ro.po Thu Jul 19 13:55:54 2018 -0400 @@ -12099,7 +12099,7 @@ msgstr "" msgid "" -"``allow_archive``\n" +"``allow-archive``\n" " List of archive format (bz2, gz, zip) allowed for downloading.\n" " Default is empty." msgstr ""
--- a/i18n/ru.po Sun Jul 01 23:36:53 2018 +0900 +++ b/i18n/ru.po Thu Jul 19 13:55:54 2018 -0400 @@ -19776,11 +19776,11 @@ " Адрес прослушиваемого интерфейса. По умолчанию все интерфейсы." msgid "" -"``allow_archive``\n" +"``allow-archive``\n" " List of archive format (bz2, gz, zip) allowed for downloading.\n" " Default is empty." msgstr "" -"``allow_archive``\n" +"``allow-archive``\n" " Список форматов архивов (bz2, gz, zip), которые можно скачивать.\n" " По умолчанию пуст."
--- a/i18n/sv.po Sun Jul 01 23:36:53 2018 +0900 +++ b/i18n/sv.po Thu Jul 19 13:55:54 2018 -0400 @@ -15034,7 +15034,7 @@ msgstr "" msgid "" -"``allow_archive``\n" +"``allow-archive``\n" " List of archive format (bz2, gz, zip) allowed for downloading.\n" " Default is empty." msgstr ""
--- a/mercurial/ancestor.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/ancestor.py Thu Jul 19 13:55:54 2018 -0400 @@ -339,6 +339,10 @@ seen = self._containsseen if target in seen: return True + # Only integer target is valid, but some callers expect 'None in self' + # to be False. So we explicitly allow it. + if target is None: + return False parentrevs = self._parentrevs visit = self._containsvisit
--- a/mercurial/archival.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/archival.py Thu Jul 19 13:55:54 2018 -0400 @@ -322,13 +322,14 @@ files.sort() scmutil.prefetchfiles(repo, [ctx.rev()], scmutil.matchfiles(repo, files)) - repo.ui.progress(_('archiving'), 0, unit=_('files'), total=total) - for i, f in enumerate(files): + progress = scmutil.progress(repo.ui, _('archiving'), unit=_('files'), + total=total) + progress.update(0) + for f in files: ff = ctx.flags(f) write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, ctx[f].data) - repo.ui.progress(_('archiving'), i + 1, item=f, - unit=_('files'), total=total) - repo.ui.progress(_('archiving'), None) + progress.increment(item=f) + progress.complete() if subrepos: for subpath in sorted(ctx.substate):
--- a/mercurial/bdiff.c Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/bdiff.c Thu Jul 19 13:55:54 2018 -0400 @@ -310,6 +310,7 @@ return count; } +/* deallocate list of hunks; l may be NULL */ void bdiff_freehunks(struct bdiff_hunk *l) { struct bdiff_hunk *n;
--- a/mercurial/bitmanipulation.h Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/bitmanipulation.h Thu Jul 19 13:55:54 2018 -0400 @@ -9,7 +9,8 @@ { const unsigned char *d = (const unsigned char *)c; - return ((d[0] << 24) | (d[1] << 16) | (d[2] << 8) | (d[3])); + return ((((uint32_t)d[0]) << 24) | (((uint32_t)d[1]) << 16) | + (((uint32_t)d[2]) << 8) | (d[3])); } static inline int16_t getbeint16(const char *c)
--- a/mercurial/bookmarks.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/bookmarks.py Thu Jul 19 13:55:54 2018 -0400 @@ -43,7 +43,7 @@ fp, pending = txnutil.trypending(repo.root, repo.vfs, 'bookmarks') return fp -class bmstore(dict): +class bmstore(object): """Storage for bookmarks. This object should do all bookmark-related reads and writes, so @@ -58,13 +58,13 @@ """ def __init__(self, repo): - dict.__init__(self) self._repo = repo + self._refmap = refmap = {} # refspec: node + self._nodemap = nodemap = {} # node: sorted([refspec, ...]) self._clean = True self._aclean = True nm = repo.changelog.nodemap tonode = bin # force local lookup - setitem = dict.__setitem__ try: with _getbkfile(repo) as bkfile: for line in bkfile: @@ -76,7 +76,15 @@ node = tonode(sha) if node in nm: refspec = encoding.tolocal(refspec) - setitem(self, refspec, node) + refmap[refspec] = node + nrefs = nodemap.get(node) + if nrefs is None: + nodemap[node] = [refspec] + else: + nrefs.append(refspec) + if nrefs[-2] > refspec: + # bookmarks weren't sorted before 4.5 + nrefs.sort() except (TypeError, ValueError): # TypeError: # - bin(...) @@ -96,38 +104,78 @@ @active.setter def active(self, mark): - if mark is not None and mark not in self: + if mark is not None and mark not in self._refmap: raise AssertionError('bookmark %s does not exist!' % mark) self._active = mark self._aclean = False - def __setitem__(self, *args, **kwargs): - raise error.ProgrammingError("use 'bookmarks.applychanges' instead") + def __len__(self): + return len(self._refmap) + + def __iter__(self): + return iter(self._refmap) + + def iteritems(self): + return self._refmap.iteritems() + + def items(self): + return self._refmap.items() - def _set(self, key, value): - self._clean = False - return dict.__setitem__(self, key, value) + # TODO: maybe rename to allnames()? + def keys(self): + return self._refmap.keys() + + # TODO: maybe rename to allnodes()? but nodes would have to be deduplicated + # could be self._nodemap.keys() + def values(self): + return self._refmap.values() + + def __contains__(self, mark): + return mark in self._refmap + + def __getitem__(self, mark): + return self._refmap[mark] - def __delitem__(self, key): - raise error.ProgrammingError("use 'bookmarks.applychanges' instead") + def get(self, mark, default=None): + return self._refmap.get(mark, default) - def _del(self, key): + def _set(self, mark, node): self._clean = False - return dict.__delitem__(self, key) + if mark in self._refmap: + self._del(mark) + self._refmap[mark] = node + nrefs = self._nodemap.get(node) + if nrefs is None: + self._nodemap[node] = [mark] + else: + nrefs.append(mark) + nrefs.sort() - def update(self, *others): - raise error.ProgrammingError("use 'bookmarks.applychanges' instead") + def _del(self, mark): + self._clean = False + node = self._refmap.pop(mark) + nrefs = self._nodemap[node] + if len(nrefs) == 1: + assert nrefs[0] == mark + del self._nodemap[node] + else: + nrefs.remove(mark) + + def names(self, node): + """Return a sorted list of bookmarks pointing to the specified node""" + return self._nodemap.get(node, []) def changectx(self, mark): - return self._repo[self[mark]] + node = self._refmap[mark] + return self._repo[node] def applychanges(self, repo, tr, changes): """Apply a list of changes to bookmarks """ bmchanges = tr.changes.get('bookmarks') for name, node in changes: - old = self.get(name) + old = self._refmap.get(name) if node is None: self._del(name) else: @@ -151,7 +199,7 @@ def _writerepo(self, repo): """Factored out for extensibility""" rbm = repo._bookmarks - if rbm.active not in self: + if rbm.active not in self._refmap: rbm.active = None rbm._writeactive() @@ -182,7 +230,7 @@ self._aclean = True def _write(self, fp): - for name, node in sorted(self.iteritems()): + for name, node in sorted(self._refmap.iteritems()): fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name))) self._clean = True self._repo.invalidatevolatilesets() @@ -208,15 +256,15 @@ If divergent bookmark are to be deleted, they will be returned as list. """ cur = self._repo['.'].node() - if mark in self and not force: + if mark in self._refmap and not force: if target: - if self[mark] == target and target == cur: + if self._refmap[mark] == target and target == cur: # re-activating a bookmark return [] rev = self._repo[target].rev() anc = self._repo.changelog.ancestors([rev]) bmctx = self.changectx(mark) - divs = [self[b] for b in self + divs = [self._refmap[b] for b in self._refmap if b.split('@', 1)[0] == mark.split('@', 1)[0]] # allow resolving a single divergent bookmark even if moving @@ -765,7 +813,7 @@ return new.node() in obsutil.foreground(repo, [old.node()]) else: # still an independent clause as it is lazier (and therefore faster) - return old.descendant(new) + return old.isancestorof(new) def checkformat(repo, mark): """return a valid version of a potential bookmark name @@ -875,11 +923,14 @@ """ opts = pycompat.byteskwargs(opts) fm = ui.formatter('bookmarks', opts) + contexthint = fm.contexthint('bookmark rev node active') hexfn = fm.hexfunc if len(bmarks) == 0 and fm.isplain(): ui.status(_("no bookmarks set\n")) for bmark, (n, prefix, label) in sorted(bmarks.iteritems()): fm.startitem() + if 'ctx' in contexthint: + fm.context(ctx=repo[n]) if not ui.quiet: fm.plain(' %s ' % prefix, label=label) fm.write('bookmark', '%s', bmark, label=label)
--- a/mercurial/bundle2.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/bundle2.py Thu Jul 19 13:55:54 2018 -0400 @@ -628,9 +628,10 @@ def addparam(self, name, value=None): """add a stream level parameter""" if not name: - raise ValueError(r'empty parameter name') + raise error.ProgrammingError(b'empty parameter name') if name[0:1] not in pycompat.bytestr(string.ascii_letters): - raise ValueError(r'non letter first character: %s' % name) + raise error.ProgrammingError(b'non letter first character: %s' + % name) self._params.append((name, value)) def addpart(self, part): @@ -1877,7 +1878,7 @@ real_part.validate() except error.Abort as e: raise error.Abort(_('bundle at %s is corrupted:\n%s') % - (util.hidepassword(raw_url), str(e))) + (util.hidepassword(raw_url), bytes(e))) assert not inpart.read() @parthandler('reply:changegroup', ('return', 'in-reply-to'))
--- a/mercurial/bundlerepo.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/bundlerepo.py Thu Jul 19 13:55:54 2018 -0400 @@ -15,7 +15,6 @@ import os import shutil -import tempfile from .i18n import _ from .node import nullid @@ -270,7 +269,7 @@ try: localrepo.localrepository.__init__(self, ui, repopath) except error.RepoError: - self._tempparent = tempfile.mkdtemp() + self._tempparent = pycompat.mkdtemp() localrepo.instance(ui, self._tempparent, 1) localrepo.localrepository.__init__(self, ui, self._tempparent) self.ui.setconfig('phases', 'publish', False, 'bundlerepo')
--- a/mercurial/cext/bdiff.c Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/cext/bdiff.c Thu Jul 19 13:55:54 2018 -0400 @@ -157,9 +157,7 @@ PyBuffer_Release(&bb); free(al); free(bl); - if (l.next) { - bdiff_freehunks(l.next); - } + bdiff_freehunks(l.next); return result; }
--- a/mercurial/cext/parsers.c Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/cext/parsers.c Thu Jul 19 13:55:54 2018 -0400 @@ -713,7 +713,7 @@ void manifest_module_init(PyObject *mod); void revlog_module_init(PyObject *mod); -static const int version = 4; +static const int version = 5; static void module_init(PyObject *mod) {
--- a/mercurial/cext/pathencode.c Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/cext/pathencode.c Thu Jul 19 13:55:54 2018 -0400 @@ -474,7 +474,10 @@ static const uint32_t twobytes[8] = {0, 0, 0x87fffffe}; static const uint32_t onebyte[8] = { - 1, 0x2bff3bfa, 0x68000001, 0x2fffffff, + 1, + 0x2bff3bfa, + 0x68000001, + 0x2fffffff, }; Py_ssize_t destlen = 0; @@ -655,16 +658,10 @@ PyObject *shaobj, *hashobj; if (shafunc == NULL) { - PyObject *hashlib, *name = PyBytes_FromString("hashlib"); - - if (name == NULL) - return -1; - - hashlib = PyImport_Import(name); - Py_DECREF(name); - + PyObject *hashlib = PyImport_ImportModule("hashlib"); if (hashlib == NULL) { - PyErr_SetString(PyExc_ImportError, "hashlib"); + PyErr_SetString(PyExc_ImportError, + "pathencode failed to find hashlib"); return -1; } shafunc = PyObject_GetAttrString(hashlib, "sha1"); @@ -673,12 +670,12 @@ if (shafunc == NULL) { PyErr_SetString(PyExc_AttributeError, "module 'hashlib' has no " - "attribute 'sha1'"); + "attribute 'sha1' in pathencode"); return -1; } } - shaobj = PyObject_CallFunction(shafunc, "s#", str, len); + shaobj = PyObject_CallFunction(shafunc, PY23("s#", "y#"), str, len); if (shaobj == NULL) return -1;
--- a/mercurial/cext/revlog.c Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/cext/revlog.c Thu Jul 19 13:55:54 2018 -0400 @@ -248,6 +248,20 @@ return data ? data + 32 : NULL; } +/* + * Return the 20-byte SHA of the node corresponding to the given rev. The + * rev is assumed to be existing. If not, an exception is set. + */ +static const char *index_node_existing(indexObject *self, Py_ssize_t pos) +{ + const char *node = index_node(self, pos); + if (node == NULL) { + PyErr_Format(PyExc_IndexError, "could not access rev %d", + (int)pos); + } + return node; +} + static int nt_insert(indexObject *self, const char *node, int rev); static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen) @@ -1052,10 +1066,12 @@ return 0; } if (v < 0) { - const char *oldnode = index_node(self, -(v + 1)); + const char *oldnode = index_node_existing(self, -(v + 1)); int noff; - if (!oldnode || !memcmp(oldnode, node, 20)) { + if (oldnode == NULL) + return -1; + if (!memcmp(oldnode, node, 20)) { n->children[k] = -rev - 1; return 0; } @@ -1135,9 +1151,9 @@ */ if (self->ntmisses++ < 4) { for (rev = self->ntrev - 1; rev >= 0; rev--) { - const char *n = index_node(self, rev); + const char *n = index_node_existing(self, rev); if (n == NULL) - return -2; + return -3; if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) { if (nt_insert(self, n, rev) == -1) return -3; @@ -1146,11 +1162,9 @@ } } else { for (rev = self->ntrev - 1; rev >= 0; rev--) { - const char *n = index_node(self, rev); - if (n == NULL) { - self->ntrev = rev + 1; - return -2; - } + const char *n = index_node_existing(self, rev); + if (n == NULL) + return -3; if (nt_insert(self, n, rev) == -1) { self->ntrev = rev + 1; return -3; @@ -1216,27 +1230,84 @@ return NULL; } +/* + * Fully populate the radix tree. + */ +static int nt_populate(indexObject *self) { + int rev; + if (self->ntrev > 0) { + for (rev = self->ntrev - 1; rev >= 0; rev--) { + const char *n = index_node_existing(self, rev); + if (n == NULL) + return -1; + if (nt_insert(self, n, rev) == -1) + return -1; + } + self->ntrev = -1; + } + return 0; +} + static int nt_partialmatch(indexObject *self, const char *node, Py_ssize_t nodelen) { - int rev; + if (nt_init(self) == -1) + return -3; + if (nt_populate(self) == -1) + return -3; + + return nt_find(self, node, nodelen, 1); +} + +/* + * Find the length of the shortest unique prefix of node. + * + * Return values: + * + * -3: error (exception set) + * -2: not found (no exception set) + * rest: length of shortest prefix + */ +static int nt_shortest(indexObject *self, const char *node) +{ + int level, off; if (nt_init(self) == -1) return -3; + if (nt_populate(self) == -1) + return -3; - if (self->ntrev > 0) { - /* ensure that the radix tree is fully populated */ - for (rev = self->ntrev - 1; rev >= 0; rev--) { - const char *n = index_node(self, rev); + for (level = off = 0; level < 40; level++) { + int k, v; + nodetree *n = &self->nt[off]; + k = nt_level(node, level); + v = n->children[k]; + if (v < 0) { + const char *n; + v = -(v + 1); + n = index_node_existing(self, v); if (n == NULL) + return -3; + if (memcmp(node, n, 20) != 0) + /* + * Found a unique prefix, but it wasn't for the + * requested node (i.e the requested node does + * not exist). + */ return -2; - if (nt_insert(self, n, rev) == -1) - return -3; + return level + 1; } - self->ntrev = rev; + if (v == 0) + return -2; + off = v; } - - return nt_find(self, node, nodelen, 1); + /* + * The node was still not unique after 40 hex digits, so this won't + * happen. Also, if we get here, then there's a programming error in + * this file that made us insert a node longer than 40 hex digits. + */ + PyErr_SetString(PyExc_Exception, "broken node tree"); + return -3; } static PyObject *index_partialmatch(indexObject *self, PyObject *args) @@ -1249,7 +1320,7 @@ if (!PyArg_ParseTuple(args, PY23("s#", "y#"), &node, &nodelen)) return NULL; - if (nodelen < 4) { + if (nodelen < 1) { PyErr_SetString(PyExc_ValueError, "key too short"); return NULL; } @@ -1280,15 +1351,36 @@ return PyBytes_FromStringAndSize(nullid, 20); } - fullnode = index_node(self, rev); + fullnode = index_node_existing(self, rev); if (fullnode == NULL) { - PyErr_Format(PyExc_IndexError, - "could not access rev %d", rev); return NULL; } return PyBytes_FromStringAndSize(fullnode, 20); } +static PyObject *index_shortest(indexObject *self, PyObject *args) +{ + Py_ssize_t nodelen; + PyObject *val; + char *node; + int length; + + if (!PyArg_ParseTuple(args, "O", &val)) + return NULL; + if (node_check(val, &node, &nodelen) == -1) + return NULL; + + self->ntlookups++; + length = nt_shortest(self, node); + if (length == -3) + return NULL; + if (length == -2) { + raise_revlog_error(); + return NULL; + } + return PyInt_FromLong(length); +} + static PyObject *index_m_get(indexObject *self, PyObject *args) { Py_ssize_t nodelen; @@ -1758,10 +1850,11 @@ Py_ssize_t i; for (i = start + 1; i < self->length - 1; i++) { - const char *node = index_node(self, i); + const char *node = index_node_existing(self, i); + if (node == NULL) + return -1; - if (node) - nt_insert(self, node, -1); + nt_insert(self, node, -1); } if (self->added) nt_invalidate_added(self, 0); @@ -1977,6 +2070,8 @@ "insert an index entry"}, {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS, "match a potentially ambiguous node ID"}, + {"shortest", (PyCFunction)index_shortest, METH_VARARGS, + "find length of shortest hex nodeid of a binary ID"}, {"stats", (PyCFunction)index_stats, METH_NOARGS, "stats for the index"}, {NULL} /* Sentinel */
--- a/mercurial/changegroup.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/changegroup.py Thu Jul 19 13:55:54 2018 -0400 @@ -9,7 +9,6 @@ import os import struct -import tempfile import weakref from .i18n import _ @@ -80,7 +79,7 @@ # small (4k is common on Linux). fh = open(filename, "wb", 131072) else: - fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg") + fd, filename = pycompat.mkstemp(prefix="hg-bundle-", suffix=".hg") fh = os.fdopen(fd, r"wb") cleanup = filename for c in chunks: @@ -238,18 +237,16 @@ pos = next yield closechunk() - def _unpackmanifests(self, repo, revmap, trp, prog, numchanges): - # We know that we'll never have more manifests than we had - # changesets. - self.callback = prog(_('manifests'), numchanges) + def _unpackmanifests(self, repo, revmap, trp, prog): + self.callback = prog.increment # no need to check for empty manifest group here: # if the result of the merge of 1 and 2 is the same in 3 and 4, # no new manifest will be created and the manifest group will # be empty during the pull self.manifestheader() deltas = self.deltaiter() - repo.manifestlog._revlog.addgroup(deltas, revmap, trp) - repo.ui.progress(_('manifests'), None) + repo.manifestlog.addgroup(deltas, revmap, trp) + prog.complete() self.callback = None def apply(self, repo, tr, srctype, url, targetphase=phases.draft, @@ -294,16 +291,9 @@ # pull off the changeset group repo.ui.status(_("adding changesets\n")) clstart = len(cl) - class prog(object): - def __init__(self, step, total): - self._step = step - self._total = total - self._count = 1 - def __call__(self): - repo.ui.progress(self._step, self._count, unit=_('chunks'), - total=self._total) - self._count += 1 - self.callback = prog(_('changesets'), expectedtotal) + progress = repo.ui.makeprogress(_('changesets'), unit=_('chunks'), + total=expectedtotal) + self.callback = progress.increment efiles = set() def onchangelog(cl, node): @@ -319,12 +309,16 @@ config='warn-empty-changegroup') clend = len(cl) changesets = clend - clstart - repo.ui.progress(_('changesets'), None) + progress.complete() self.callback = None # pull off the manifest group repo.ui.status(_("adding manifests\n")) - self._unpackmanifests(repo, revmap, trp, prog, changesets) + # We know that we'll never have more manifests than we had + # changesets. + progress = repo.ui.makeprogress(_('manifests'), unit=_('chunks'), + total=changesets) + self._unpackmanifests(repo, revmap, trp, progress) needfiles = {} if repo.ui.configbool('server', 'validate'): @@ -476,9 +470,8 @@ node, p1, p2, deltabase, cs, flags = headertuple return node, p1, p2, deltabase, cs, flags - def _unpackmanifests(self, repo, revmap, trp, prog, numchanges): - super(cg3unpacker, self)._unpackmanifests(repo, revmap, trp, prog, - numchanges) + def _unpackmanifests(self, repo, revmap, trp, prog): + super(cg3unpacker, self)._unpackmanifests(repo, revmap, trp, prog) for chunkdata in iter(self.filelogheader, {}): # If we get here, there are directory manifests in the changegroup d = chunkdata["filename"] @@ -523,7 +516,6 @@ reorder = stringutil.parsebool(reorder) self._repo = repo self._reorder = reorder - self._progress = repo.ui.progress if self._repo.ui.verbose and not self._repo.ui.debugflag: self._verbosenote = self._repo.ui.note else: @@ -572,18 +564,20 @@ revs.insert(0, p) # build deltas - total = len(revs) - 1 - msgbundling = _('bundling') + progress = None + if units is not None: + progress = self._repo.ui.makeprogress(_('bundling'), unit=units, + total=(len(revs) - 1)) for r in xrange(len(revs) - 1): - if units is not None: - self._progress(msgbundling, r + 1, unit=units, total=total) + if progress: + progress.update(r + 1) prev, curr = revs[r], revs[r + 1] linknode = lookup(revlog.node(curr)) for c in self.revchunk(revlog, curr, prev, linknode): yield c - if units is not None: - self._progress(msgbundling, None) + if progress: + progress.complete() yield self.close() # filter any nodes that claim to be part of the known set @@ -749,12 +743,8 @@ # The 'source' parameter is useful for extensions def generatefiles(self, changedfiles, linknodes, commonrevs, source): repo = self._repo - progress = self._progress - msgbundling = _('bundling') - - total = len(changedfiles) - # for progress output - msgfiles = _('files') + progress = repo.ui.makeprogress(_('bundling'), unit=_('files'), + total=len(changedfiles)) for i, fname in enumerate(sorted(changedfiles)): filerevlog = repo.file(fname) if not filerevlog: @@ -769,8 +759,7 @@ filenodes = self.prune(filerevlog, linkrevnodes, commonrevs) if filenodes: - progress(msgbundling, i + 1, item=fname, unit=msgfiles, - total=total) + progress.update(i + 1, item=fname) h = self.fileheader(fname) size = len(h) yield h @@ -778,7 +767,7 @@ size += len(chunk) yield chunk self._verbosenote(_('%8.i %s\n') % (size, fname)) - progress(msgbundling, None) + progress.complete() def deltaparent(self, revlog, rev, p1, p2, prev): if not revlog.candelta(prev, rev): @@ -982,12 +971,13 @@ def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles): revisions = 0 files = 0 + progress = repo.ui.makeprogress(_('files'), unit=_('files'), + total=expectedfiles) for chunkdata in iter(source.filelogheader, {}): files += 1 f = chunkdata["filename"] repo.ui.debug("adding %s revisions\n" % f) - repo.ui.progress(_('files'), files, unit=_('files'), - total=expectedfiles) + progress.increment() fl = repo.file(f) o = len(fl) try: @@ -1008,7 +998,7 @@ _("received spurious file revlog entry")) if not needs: del needfiles[f] - repo.ui.progress(_('files'), None) + progress.complete() for f, needs in needfiles.iteritems(): fl = repo.file(f)
--- a/mercurial/cmdutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/cmdutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -10,7 +10,6 @@ import errno import os import re -import tempfile from .i18n import _ from .node import ( @@ -36,8 +35,8 @@ obsolete, patch, pathutil, + phases, pycompat, - registrar, revlog, rewriteutil, scmutil, @@ -203,17 +202,21 @@ return oldwrite def filterchunks(ui, originalhunks, usecurses, testfile, operation=None): - if usecurses: - if testfile: - recordfn = crecordmod.testdecorator(testfile, - crecordmod.testchunkselector) - else: - recordfn = crecordmod.chunkselector - - return crecordmod.filterpatch(ui, originalhunks, recordfn, operation) - - else: - return patch.filterpatch(ui, originalhunks, operation) + try: + if usecurses: + if testfile: + recordfn = crecordmod.testdecorator( + testfile, crecordmod.testchunkselector) + else: + recordfn = crecordmod.chunkselector + + return crecordmod.filterpatch(ui, originalhunks, recordfn, + operation) + except crecordmod.fallbackerror as e: + ui.warn('%s\n' % e.message) + ui.warn(_('falling back to text mode\n')) + + return patch.filterpatch(ui, originalhunks, operation) def recordfilter(ui, originalhunks, operation=None): """ Prompts the user to filter the originalhunks and return a list of @@ -331,7 +334,7 @@ try: # backup continues for f in tobackup: - fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.', + fd, tmpname = pycompat.mkstemp(prefix=f.replace('/', '_') + '.', dir=backupdir) os.close(fd) ui.debug('backup %r as %r\n' % (f, tmpname)) @@ -419,7 +422,7 @@ Represent a directory in user working copy with information required for the purpose of tersing its status. - path is the path to the directory + path is the path to the directory, without a trailing '/' statuses is a set of statuses of all files in this directory (this includes all the files in all the subdirectories too) @@ -456,7 +459,7 @@ # does the dirnode object for subdir exists if subdir not in self.subdirs: - subdirpath = os.path.join(self.path, subdir) + subdirpath = pathutil.join(self.path, subdir) self.subdirs[subdir] = dirnode(subdirpath) # try adding the file in subdir @@ -471,7 +474,7 @@ def iterfilepaths(self): """Yield (status, path) for files directly under this directory.""" for f, st in self.files: - yield st, os.path.join(self.path, f) + yield st, pathutil.join(self.path, f) def tersewalk(self, terseargs): """ @@ -485,7 +488,7 @@ 1) All the files in the directory (including all the files in its subdirectories) share the same status and the user has asked us to terse - that status. -> yield (status, dirpath) + that status. -> yield (status, dirpath). dirpath will end in '/'. 2) Otherwise, we do following: @@ -502,7 +505,7 @@ # Making sure we terse only when the status abbreviation is # passed as terse argument if onlyst in terseargs: - yield onlyst, self.path + pycompat.ossep + yield onlyst, self.path + '/' return # add the files to status list @@ -591,8 +594,8 @@ return _commentlines(msg) def _helpmessage(continuecmd, abortcmd): - msg = _('To continue: %s\n' - 'To abort: %s') % (continuecmd, abortcmd) + msg = _('To continue: %s\n' + 'To abort: %s') % (continuecmd, abortcmd) return _commentlines(msg) def _rebasemsg(): @@ -606,7 +609,7 @@ def _updatecleanmsg(dest=None): warning = _('warning: this will discard uncommitted changes') - return 'hg update --clean %s (%s)' % (dest or '.', warning) + return 'hg update --clean %s (%s)' % (dest or '.', warning) def _graftmsg(): # tweakdefaults requires `update` to have a rev hence the `.` @@ -633,7 +636,7 @@ ('histedit', fileexistspredicate('histedit-state'), _histeditmsg), ('bisect', fileexistspredicate('bisect.state'), _bisectmsg), ('graft', fileexistspredicate('graftstate'), _graftmsg), - ('unshelve', fileexistspredicate('unshelverebasestate'), _unshelvemsg), + ('unshelve', fileexistspredicate('shelvedstate'), _unshelvemsg), ('rebase', fileexistspredicate('rebasestate'), _rebasemsg), # The merge state is part of a list that will be iterated over. # They need to be last because some of the other unfinished states may also @@ -787,16 +790,12 @@ extra=extra, branch=label) - commitphase = ctx.phase() - overrides = {('phases', 'new-commit'): commitphase} - with repo.ui.configoverride(overrides, 'branch-change'): - newnode = repo.commitctx(mc) - + newnode = repo.commitctx(mc) replacements[ctx.node()] = (newnode,) ui.debug('new node id is %s\n' % hex(newnode)) # create obsmarkers and move bookmarks - scmutil.cleanupnodes(repo, replacements, 'branch-change') + scmutil.cleanupnodes(repo, replacements, 'branch-change', fixphase=True) # move the working copy too wctx = repo[None] @@ -1248,7 +1247,8 @@ dryrun=dryrun, cwd=cwd) if rename and not dryrun: if not after and srcexists and not samefile: - repo.wvfs.unlinkpath(abssrc) + rmdir = repo.ui.configbool('experimental', 'removeemptydirs') + repo.wvfs.unlinkpath(abssrc, rmdir=rmdir) wctx.forget([abssrc]) # pat: ossep @@ -1685,7 +1685,7 @@ fm.write('date', '(%s) ', fm.formatdate(marker.date())) meta = marker.metadata().copy() meta.pop('date', None) - smeta = util.rapply(pycompat.maybebytestr, meta) + smeta = pycompat.rapply(pycompat.maybebytestr, meta) fm.write('metadata', '{%s}', fm.formatdict(smeta, fmt='%r: %r', sep=', ')) fm.plain('\n') @@ -1884,10 +1884,14 @@ yielding each context, the iterator will first call the prepare function on each context in the window in forward order.''' + allfiles = opts.get('all_files') follow = opts.get('follow') or opts.get('follow_first') revs = _walkrevs(repo, opts) if not revs: return [] + if allfiles and len(revs) > 1: + raise error.Abort(_("multiple revisions not supported with " + "--all-files")) wanted = set() slowpath = match.anypats() or (not match.always() and opts.get('removed')) fncache = {} @@ -1993,7 +1997,11 @@ ctx = change(rev) if not fns: def fns_generator(): - for f in ctx.files(): + if allfiles: + fiter = iter(ctx) + else: + fiter = ctx.files() + for f in fiter: if match(f): yield f fns = fns_generator() @@ -2137,15 +2145,13 @@ return bad, forgot def files(ui, ctx, m, fm, fmt, subrepos): - rev = ctx.rev() ret = 1 - ds = ctx.repo().dirstate - + + needsfctx = ui.verbose or {'size', 'flags'} & fm.datahint() for f in ctx.matches(m): - if rev is None and ds[f] == 'r': - continue fm.startitem() - if ui.verbose: + fm.context(ctx=ctx) + if needsfctx: fc = ctx[f] fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags()) fm.data(abspath=f) @@ -2181,13 +2187,12 @@ warn = False subs = sorted(wctx.substate) - total = len(subs) - count = 0 + progress = ui.makeprogress(_('searching'), total=len(subs), + unit=_('subrepos')) for subpath in subs: - count += 1 submatch = matchmod.subdirmatcher(subpath, m) if subrepos or m.exact(subpath) or any(submatch.files()): - ui.progress(_('searching'), count, total=total, unit=_('subrepos')) + progress.increment() sub = wctx.sub(subpath) try: if sub.removefiles(submatch, prefix, after, force, subrepos, @@ -2196,13 +2201,13 @@ except error.LookupError: warnings.append(_("skipping missing subrepository: %s\n") % join(subpath)) - ui.progress(_('searching'), None) + progress.complete() # warn about failure to delete explicit files/dirs deleteddirs = util.dirs(deleted) files = m.files() - total = len(files) - count = 0 + progress = ui.makeprogress(_('deleting'), total=len(files), + unit=_('files')) for f in files: def insubrepo(): for subpath in wctx.substate: @@ -2210,8 +2215,7 @@ return True return False - count += 1 - ui.progress(_('deleting'), count, total=total, unit=_('files')) + progress.increment() isdir = f in deleteddirs or wctx.hasdir(f) if (f in repo.dirstate or isdir or f == '.' or insubrepo() or f in subs): @@ -2226,50 +2230,47 @@ % m.rel(f)) # missing files will generate a warning elsewhere ret = 1 - ui.progress(_('deleting'), None) + progress.complete() if force: list = modified + deleted + clean + added elif after: list = deleted remaining = modified + added + clean - total = len(remaining) - count = 0 + progress = ui.makeprogress(_('skipping'), total=len(remaining), + unit=_('files')) for f in remaining: - count += 1 - ui.progress(_('skipping'), count, total=total, unit=_('files')) + progress.increment() if ui.verbose or (f in files): warnings.append(_('not removing %s: file still exists\n') % m.rel(f)) ret = 1 - ui.progress(_('skipping'), None) + progress.complete() else: list = deleted + clean - total = len(modified) + len(added) - count = 0 + progress = ui.makeprogress(_('skipping'), + total=(len(modified) + len(added)), + unit=_('files')) for f in modified: - count += 1 - ui.progress(_('skipping'), count, total=total, unit=_('files')) + progress.increment() warnings.append(_('not removing %s: file is modified (use -f' ' to force removal)\n') % m.rel(f)) ret = 1 for f in added: - count += 1 - ui.progress(_('skipping'), count, total=total, unit=_('files')) + progress.increment() warnings.append(_("not removing %s: file has been marked for add" " (use 'hg forget' to undo add)\n") % m.rel(f)) ret = 1 - ui.progress(_('skipping'), None) + progress.complete() list = sorted(list) - total = len(list) - count = 0 + progress = ui.makeprogress(_('deleting'), total=len(list), + unit=_('files')) for f in list: - count += 1 if ui.verbose or not m.exact(f): - ui.progress(_('deleting'), count, total=total, unit=_('files')) + progress.increment() ui.status(_('removing %s\n') % m.rel(f)) - ui.progress(_('deleting'), None) + progress.complete() if not dryrun: with repo.wlock(): @@ -2277,7 +2278,9 @@ for f in list: if f in added: continue # we never unlink added files on remove - repo.wvfs.unlinkpath(f, ignoremissing=True) + rmdir = repo.ui.configbool('experimental', + 'removeemptydirs') + repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir) repo[None].forget(list) if warn: @@ -2295,6 +2298,7 @@ if decode: data = ctx.repo().wwritedata(path, data) fm.startitem() + fm.context(ctx=ctx) fm.write('data', '%s', data) fm.data(abspath=path, path=matcher.rel(path)) @@ -2541,21 +2545,19 @@ # This not what we expect from amend. return old.node() + commitphase = None if opts.get('secret'): - commitphase = 'secret' - else: - commitphase = old.phase() - overrides = {('phases', 'new-commit'): commitphase} - with ui.configoverride(overrides, 'amend'): - newid = repo.commitctx(new) + commitphase = phases.secret + newid = repo.commitctx(new) # Reroute the working copy parent to the new changeset repo.setparents(newid, nullid) mapping = {old.node(): (newid,)} obsmetadata = None if opts.get('note'): - obsmetadata = {'note': opts['note']} - scmutil.cleanupnodes(repo, mapping, 'amend', metadata=obsmetadata) + obsmetadata = {'note': encoding.fromlocal(opts['note'])} + scmutil.cleanupnodes(repo, mapping, 'amend', metadata=obsmetadata, + fixphase=True, targetphase=commitphase) # Fixing the dirstate because localrepo.commitctx does not update # it. This is rather convenient because we did not need to update @@ -3002,12 +3004,6 @@ if not opts.get('dry_run'): needdata = ('revert', 'add', 'undelete') - if _revertprefetch is not _revertprefetchstub: - ui.deprecwarn("'cmdutil._revertprefetch' is deprecated, " - "add a callback to 'scmutil.fileprefetchhooks'", - '4.6', stacklevel=1) - _revertprefetch(repo, ctx, - *[actions[name][0] for name in needdata]) oplist = [actions[name][0] for name in needdata] prefetch = scmutil.prefetchfiles matchfiles = scmutil.matchfiles @@ -3026,12 +3022,6 @@ raise error.Abort("subrepository '%s' does not exist in %s!" % (sub, short(ctx.node()))) -def _revertprefetchstub(repo, ctx, *files): - """Stub method for detecting extension wrapping of _revertprefetch(), to - issue a deprecation warning.""" - -_revertprefetch = _revertprefetchstub - def _performrevert(repo, parents, ctx, actions, interactive=False, tobackup=None): """function that actually perform all the actions computed for revert @@ -3051,7 +3041,8 @@ def doremove(f): try: - repo.wvfs.unlinkpath(f) + rmdir = repo.ui.configbool('experimental', 'removeemptydirs') + repo.wvfs.unlinkpath(f, rmdir=rmdir) except OSError: pass repo.dirstate.remove(f) @@ -3168,12 +3159,6 @@ if f in copied: repo.dirstate.copy(copied[f], f) -class command(registrar.command): - """deprecated: used registrar.command instead""" - def _doregister(self, func, name, *args, **kwargs): - func._deprecatedregistrar = True # flag for deprecwarn in extensions.py - return super(command, self)._doregister(func, name, *args, **kwargs) - # a list of (ui, repo, otherpeer, opts, missing) functions called by # commands.outgoing. "missing" is "missing" of the result of # "findcommonoutgoing()" @@ -3198,7 +3183,7 @@ # (state file, clearable, allowcommit, error, hint) unfinishedstates = [ ('graftstate', True, False, _('graft in progress'), - _("use 'hg graft --continue' or 'hg update' to abort")), + _("use 'hg graft --continue' or 'hg graft --stop' to stop")), ('updatestate', True, False, _('last update was interrupted'), _("use 'hg update' to get a consistent checkout")) ] @@ -3285,23 +3270,3 @@ if after[1]: hint = after[0] raise error.Abort(_('no %s in progress') % task, hint=hint) - -class changeset_printer(logcmdutil.changesetprinter): - - def __init__(self, ui, *args, **kwargs): - msg = ("'cmdutil.changeset_printer' is deprecated, " - "use 'logcmdutil.logcmdutil'") - ui.deprecwarn(msg, "4.6") - super(changeset_printer, self).__init__(ui, *args, **kwargs) - -def displaygraph(ui, *args, **kwargs): - msg = ("'cmdutil.displaygraph' is deprecated, " - "use 'logcmdutil.displaygraph'") - ui.deprecwarn(msg, "4.6") - return logcmdutil.displaygraph(ui, *args, **kwargs) - -def show_changeset(ui, *args, **kwargs): - msg = ("'cmdutil.show_changeset' is deprecated, " - "use 'logcmdutil.changesetdisplayer'") - ui.deprecwarn(msg, "4.6") - return logcmdutil.changesetdisplayer(ui, *args, **kwargs)
--- a/mercurial/commands.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/commands.py Thu Jul 19 13:55:54 2018 -0400 @@ -40,7 +40,6 @@ hbisect, help, hg, - lock as lockmod, logcmdutil, merge as mergemod, obsolete, @@ -50,10 +49,12 @@ pycompat, rcutil, registrar, + repair, revsetlang, rewriteutil, scmutil, server, + state as statemod, streamclone, tags as tagsmod, templatekw, @@ -63,12 +64,9 @@ ) from .utils import ( dateutil, - procutil, stringutil, ) -release = lockmod.release - table = {} table.update(debugcommandsmod.command._table) @@ -335,13 +333,13 @@ formatrev = formathex = pycompat.bytestr opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser), - ('number', ' ', lambda x: x.fctx.rev(), formatrev), - ('changeset', ' ', lambda x: hexfn(x.fctx.node()), formathex), + ('rev', ' ', lambda x: x.fctx.rev(), formatrev), + ('node', ' ', lambda x: hexfn(x.fctx.node()), formathex), ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)), ('file', ' ', lambda x: x.fctx.path(), pycompat.bytestr), ('line_number', ':', lambda x: x.lineno, pycompat.bytestr), ] - fieldnamemap = {'number': 'rev', 'changeset': 'node'} + opnamemap = {'rev': 'number', 'node': 'changeset'} if (not opts.get('user') and not opts.get('changeset') and not opts.get('date') and not opts.get('file')): @@ -359,11 +357,12 @@ else: def makefunc(get, fmt): return get - funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap - if opts.get(op)] + datahint = rootfm.datahint() + funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap + if opts.get(opnamemap.get(fn, fn)) or fn in datahint] funcmap[0] = (funcmap[0][0], '') # no separator in front of first column - fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap - if opts.get(op)) + fields = ' '.join(fn for fn, sep, get, fmt in opmap + if opts.get(opnamemap.get(fn, fn)) or fn in datahint) def bad(x, y): raise error.Abort("%s: %s" % (x, y)) @@ -560,13 +559,8 @@ Returns 0 on success, 1 if nothing to backout or there are unresolved files. ''' - wlock = lock = None - try: - wlock = repo.wlock() - lock = repo.lock() + with repo.wlock(), repo.lock(): return _dobackout(ui, repo, node, rev, **opts) - finally: - release(lock, wlock) def _dobackout(ui, repo, node=None, rev=None, **opts): opts = pycompat.byteskwargs(opts) @@ -617,21 +611,16 @@ bheads = repo.branchheads(branch) rctx = scmutil.revsingle(repo, hex(parent)) if not opts.get('merge') and op1 != node: - dsguard = dirstateguard.dirstateguard(repo, 'backout') - try: - ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), - 'backout') - stats = mergemod.update(repo, parent, True, True, node, False) + with dirstateguard.dirstateguard(repo, 'backout'): + overrides = {('ui', 'forcemerge'): opts.get('tool', '')} + with ui.configoverride(overrides, 'backout'): + stats = mergemod.update(repo, parent, True, True, node, False) repo.setparents(op1, op2) - dsguard.close() - hg._showstats(repo, stats) - if stats.unresolvedcount: - repo.ui.status(_("use 'hg resolve' to retry unresolved " - "file merges\n")) - return 1 - finally: - ui.setconfig('ui', 'forcemerge', '', '') - lockmod.release(dsguard) + hg._showstats(repo, stats) + if stats.unresolvedcount: + repo.ui.status(_("use 'hg resolve' to retry unresolved " + "file merges\n")) + return 1 else: hg.clean(repo, node, show_stats=False) repo.dirstate.setbranch(branch) @@ -667,12 +656,9 @@ hg.clean(repo, op1, show_stats=False) ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip())) - try: - ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), - 'backout') + overrides = {('ui', 'forcemerge'): opts.get('tool', '')} + with ui.configoverride(overrides, 'backout'): return hg.merge(repo, hex(repo.changelog.tip())) - finally: - ui.setconfig('ui', 'forcemerge', '', '') return 0 @command('bisect', @@ -1234,7 +1220,7 @@ other = hg.peer(repo, opts, dest) revs = [repo[r].hex() for r in revs] revs, checkout = hg.addbranchrevs(repo, repo, branches, revs) - heads = revs and map(repo.lookup, revs) or revs + heads = revs and pycompat.maplist(repo.lookup, revs) or revs outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=heads, force=opts.get('force'), @@ -1536,13 +1522,8 @@ hg commit --amend --date now """ - wlock = lock = None - try: - wlock = repo.wlock() - lock = repo.lock() + with repo.wlock(), repo.lock(): return _docommit(ui, repo, *pats, **opts) - finally: - release(lock, wlock) def _docommit(ui, repo, *pats, **opts): if opts.get(r'interactive'): @@ -1895,7 +1876,9 @@ root=opts.get('root')) @command('^export', - [('o', 'output', '', + [('B', 'bookmark', '', + _('export changes only reachable by given bookmark')), + ('o', 'output', '', _('print output to file with formatted name'), _('FORMAT')), ('', 'switch-parent', None, _('diff against the second parent')), ('r', 'rev', [], _('revisions to export'), _('REV')), @@ -1938,6 +1921,9 @@ of files it detects as binary. With -a, export will generate a diff anyway, probably with undesirable results. + With -B/--bookmark changesets reachable by the given bookmark are + selected. + Use the -g/--git option to generate diffs in the git extended diff format. See :hg:`help diffs` for more information. @@ -1966,11 +1952,24 @@ Returns 0 on success. """ opts = pycompat.byteskwargs(opts) + bookmark = opts.get('bookmark') changesets += tuple(opts.get('rev', [])) - if not changesets: - changesets = ['.'] - repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn') - revs = scmutil.revrange(repo, changesets) + + if bookmark and changesets: + raise error.Abort(_("-r and -B are mutually exclusive")) + + if bookmark: + if bookmark not in repo._bookmarks: + raise error.Abort(_("bookmark '%s' not found") % bookmark) + + revs = scmutil.bookmarkrevs(repo, bookmark) + else: + if not changesets: + changesets = ['.'] + + repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn') + revs = scmutil.revrange(repo, changesets) + if not revs: raise error.Abort(_("export requires at least one changeset")) if len(revs) > 1: @@ -2108,8 +2107,12 @@ 'graft', [('r', 'rev', [], _('revisions to graft'), _('REV')), ('c', 'continue', False, _('resume interrupted graft')), + ('', 'stop', False, _('stop interrupted graft')), + ('', 'abort', False, _('abort interrupted graft')), ('e', 'edit', False, _('invoke editor on commit messages')), ('', 'log', None, _('append graft info to log message')), + ('', 'no-commit', None, + _("don't commit, just apply the changes in working directory")), ('f', 'force', False, _('force graft')), ('D', 'currentdate', False, _('record the current date as commit date')), @@ -2143,10 +2146,7 @@ Once all conflicts are addressed, the graft process can be continued with the -c/--continue option. - .. note:: - - The -c/--continue option does not reapply earlier options, except - for --force. + The -c/--continue option reapplies all the earlier options. .. container:: verbose @@ -2188,6 +2188,10 @@ revs = list(revs) revs.extend(opts.get('rev')) + # a dict of data to be stored in state file + statedata = {} + # list of new nodes created by ongoing graft + statedata['newnodes'] = [] if not opts.get('user') and opts.get('currentuser'): opts['user'] = ui.username() @@ -2198,17 +2202,62 @@ **pycompat.strkwargs(opts)) cont = False - if opts.get('continue'): + if opts.get('no_commit'): + if opts.get('edit'): + raise error.Abort(_("cannot specify --no-commit and " + "--edit together")) + if opts.get('currentuser'): + raise error.Abort(_("cannot specify --no-commit and " + "--currentuser together")) + if opts.get('currentdate'): + raise error.Abort(_("cannot specify --no-commit and " + "--currentdate together")) + if opts.get('log'): + raise error.Abort(_("cannot specify --no-commit and " + "--log together")) + + graftstate = statemod.cmdstate(repo, 'graftstate') + + if opts.get('stop'): + if opts.get('continue'): + raise error.Abort(_("cannot use '--continue' and " + "'--stop' together")) + if opts.get('abort'): + raise error.Abort(_("cannot use '--abort' and '--stop' together")) + + if any((opts.get('edit'), opts.get('log'), opts.get('user'), + opts.get('date'), opts.get('currentdate'), + opts.get('currentuser'), opts.get('rev'))): + raise error.Abort(_("cannot specify any other flag with '--stop'")) + return _stopgraft(ui, repo, graftstate) + elif opts.get('abort'): + if opts.get('continue'): + raise error.Abort(_("cannot use '--continue' and " + "'--abort' together")) + if any((opts.get('edit'), opts.get('log'), opts.get('user'), + opts.get('date'), opts.get('currentdate'), + opts.get('currentuser'), opts.get('rev'))): + raise error.Abort(_("cannot specify any other flag with '--abort'")) + + return _abortgraft(ui, repo, graftstate) + elif opts.get('continue'): cont = True if revs: raise error.Abort(_("can't specify --continue and revisions")) # read in unfinished revisions - try: - nodes = repo.vfs.read('graftstate').splitlines() + if graftstate.exists(): + statedata = _readgraftstate(repo, graftstate) + if statedata.get('date'): + opts['date'] = statedata['date'] + if statedata.get('user'): + opts['user'] = statedata['user'] + if statedata.get('log'): + opts['log'] = True + if statedata.get('no_commit'): + opts['no_commit'] = statedata.get('no_commit') + nodes = statedata['nodes'] revs = [repo[node].rev() for node in nodes] - except IOError as inst: - if inst.errno != errno.ENOENT: - raise + else: cmdutil.wrongtooltocontinue(repo, _('graft')) else: if not revs: @@ -2292,6 +2341,8 @@ if not revs: return -1 + if opts.get('no_commit'): + statedata['no_commit'] = True for pos, ctx in enumerate(repo.set("%ld", revs)): desc = '%d:%s "%s"' % (ctx.rev(), ctx, ctx.description().split('\n', 1)[0]) @@ -2312,60 +2363,134 @@ user = ctx.user() if opts.get('user'): user = opts['user'] + statedata['user'] = user date = ctx.date() if opts.get('date'): date = opts['date'] + statedata['date'] = date message = ctx.description() if opts.get('log'): message += '\n(grafted from %s)' % ctx.hex() + statedata['log'] = True # we don't merge the first commit when continuing if not cont: # perform the graft merge with p1(rev) as 'ancestor' - try: - # ui.forcemerge is an internal variable, do not document - repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), - 'graft') - stats = mergemod.graft(repo, ctx, ctx.p1(), - ['local', 'graft']) - finally: - repo.ui.setconfig('ui', 'forcemerge', '', 'graft') + overrides = {('ui', 'forcemerge'): opts.get('tool', '')} + with ui.configoverride(overrides, 'graft'): + stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'graft']) # report any conflicts if stats.unresolvedcount > 0: # write out state for --continue - nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]] - repo.vfs.write('graftstate', ''.join(nodelines)) - extra = '' - if opts.get('user'): - extra += ' --user %s' % procutil.shellquote(opts['user']) - if opts.get('date'): - extra += ' --date %s' % procutil.shellquote(opts['date']) - if opts.get('log'): - extra += ' --log' - hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra + nodes = [repo[rev].hex() for rev in revs[pos:]] + statedata['nodes'] = nodes + stateversion = 1 + graftstate.save(stateversion, statedata) + hint = _("use 'hg resolve' and 'hg graft --continue'") raise error.Abort( _("unresolved conflicts, can't continue"), hint=hint) else: cont = False - # commit - node = repo.commit(text=message, user=user, - date=date, extra=extra, editor=editor) - if node is None: - ui.warn( - _('note: graft of %d:%s created no changes to commit\n') % - (ctx.rev(), ctx)) + # commit if --no-commit is false + if not opts.get('no_commit'): + node = repo.commit(text=message, user=user, date=date, extra=extra, + editor=editor) + if node is None: + ui.warn( + _('note: graft of %d:%s created no changes to commit\n') % + (ctx.rev(), ctx)) + # checking that newnodes exist because old state files won't have it + elif statedata.get('newnodes') is not None: + statedata['newnodes'].append(node) # remove state when we complete successfully if not opts.get('dry_run'): - repo.vfs.unlinkpath('graftstate', ignoremissing=True) - + graftstate.delete() + + return 0 + +def _abortgraft(ui, repo, graftstate): + """abort the interrupted graft and rollbacks to the state before interrupted + graft""" + if not graftstate.exists(): + raise error.Abort(_("no interrupted graft to abort")) + statedata = _readgraftstate(repo, graftstate) + newnodes = statedata.get('newnodes') + if newnodes is None: + # and old graft state which does not have all the data required to abort + # the graft + raise error.Abort(_("cannot abort using an old graftstate")) + + # changeset from which graft operation was started + startctx = None + if len(newnodes) > 0: + startctx = repo[newnodes[0]].p1() + else: + startctx = repo['.'] + # whether to strip or not + cleanup = False + if newnodes: + newnodes = [repo[r].rev() for r in newnodes] + cleanup = True + # checking that none of the newnodes turned public or is public + immutable = [c for c in newnodes if not repo[c].mutable()] + if immutable: + repo.ui.warn(_("cannot clean up public changesets %s\n") + % ', '.join(bytes(repo[r]) for r in immutable), + hint=_("see 'hg help phases' for details")) + cleanup = False + + # checking that no new nodes are created on top of grafted revs + desc = set(repo.changelog.descendants(newnodes)) + if desc - set(newnodes): + repo.ui.warn(_("new changesets detected on destination " + "branch, can't strip\n")) + cleanup = False + + if cleanup: + with repo.wlock(), repo.lock(): + hg.updaterepo(repo, startctx.node(), overwrite=True) + # stripping the new nodes created + strippoints = [c.node() for c in repo.set("roots(%ld)", + newnodes)] + repair.strip(repo.ui, repo, strippoints, backup=False) + + if not cleanup: + # we don't update to the startnode if we can't strip + startctx = repo['.'] + hg.updaterepo(repo, startctx.node(), overwrite=True) + + ui.status(_("graft aborted\n")) + ui.status(_("working directory is now at %s\n") % startctx.hex()[:12]) + graftstate.delete() + return 0 + +def _readgraftstate(repo, graftstate): + """read the graft state file and return a dict of the data stored in it""" + try: + return graftstate.read() + except error.CorruptedState: + nodes = repo.vfs.read('graftstate').splitlines() + return {'nodes': nodes} + +def _stopgraft(ui, repo, graftstate): + """stop the interrupted graft""" + if not graftstate.exists(): + raise error.Abort(_("no interrupted graft found")) + pctx = repo['.'] + hg.updaterepo(repo, pctx.node(), overwrite=True) + graftstate.delete() + ui.status(_("stopped the interrupted graft\n")) + ui.status(_("working directory is now at %s\n") % pctx.hex()[:12]) return 0 @command('grep', [('0', 'print0', None, _('end fields with NUL')), - ('', 'all', None, _('print all revisions that match')), + ('', 'all', None, _('print all revisions that match (DEPRECATED) ')), + ('', 'diff', None, _('print all revisions when the term was introduced ' + 'or removed')), ('a', 'text', None, _('treat all files as text')), ('f', 'follow', None, _('follow changeset history,' @@ -2376,6 +2501,8 @@ ('n', 'line-number', None, _('print matching line numbers')), ('r', 'rev', [], _('only search files changed within revision range'), _('REV')), + ('', 'all-files', None, + _('include all files in the changeset while grepping (EXPERIMENTAL)')), ('u', 'user', None, _('list the author (long with -v)')), ('d', 'date', None, _('list the date (short with -q)')), ] + formatteropts + walkopts, @@ -2392,7 +2519,7 @@ file in which it finds a match. To get it to print every revision that contains a change in match status ("-" for a match that becomes a non-match, or "+" for a non-match that becomes a match), use the - --all flag. + --diff flag. PATTERN can be any Python (roughly Perl-compatible) regular expression. @@ -2404,6 +2531,17 @@ Returns 0 if a match is found, 1 otherwise. """ opts = pycompat.byteskwargs(opts) + diff = opts.get('all') or opts.get('diff') + if diff and opts.get('all_files'): + raise error.Abort(_('--diff and --all-files are mutually exclusive')) + # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working + if opts.get('all_files') is None and not opts.get('rev') and not diff: + # experimental config: commands.grep.all-files + opts['all_files'] = ui.configbool('commands', 'grep.all-files') + plaingrep = opts.get('all_files') and not opts.get('rev') + if plaingrep: + opts['rev'] = ['wdir()'] + reflags = re.M if opts.get('ignore_case'): reflags |= re.I @@ -2481,7 +2619,7 @@ yield ('+', b[i]) def display(fm, fn, ctx, pstates, states): - rev = ctx.rev() + rev = scmutil.intrev(ctx) if fm.isplain(): formatuser = ui.shortuser else: @@ -2494,22 +2632,27 @@ @util.cachefunc def binary(): flog = getfile(fn) - return stringutil.binary(flog.read(ctx.filenode(fn))) + try: + return stringutil.binary(flog.read(ctx.filenode(fn))) + except error.WdirUnsupported: + return ctx[fn].isbinary() fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'} - if opts.get('all'): + if diff: iter = difflinestates(pstates, states) else: iter = [('', l) for l in states] for change, l in iter: fm.startitem() - fm.data(node=fm.hexfunc(ctx.node())) + fm.context(ctx=ctx) + fm.data(node=fm.hexfunc(scmutil.binnode(ctx))) + cols = [ ('filename', fn, True), - ('rev', rev, True), + ('rev', rev, not plaingrep), ('linenumber', l.linenum, opts.get('line_number')), ] - if opts.get('all'): + if diff: cols.append(('change', change, True)) cols.extend([ ('user', formatuser(ctx.user()), opts.get('user')), @@ -2569,8 +2712,10 @@ fnode = ctx.filenode(fn) except error.LookupError: continue - - copied = flog.renamed(fnode) + try: + copied = flog.renamed(fnode) + except error.WdirUnsupported: + copied = ctx[fn].renamed() copy = follow and copied and copied[0] if copy: copies.setdefault(rev, {})[fn] = copy @@ -2581,7 +2726,11 @@ files.append(fn) if fn not in matches[rev]: - grepbody(fn, rev, flog.read(fnode)) + try: + content = flog.read(fnode) + except error.WdirUnsupported: + content = ctx[fn].data() + grepbody(fn, rev, content) pfn = copy or fn if pfn not in matches[parent]: @@ -2607,7 +2756,7 @@ if pstates or states: r = display(fm, fn, ctx, pstates, states) found = found or r - if r and not opts.get('all'): + if r and not diff: skip[fn] = True if copy: skip[copy] = True @@ -3071,69 +3220,62 @@ raise error.Abort(_('cannot use --exact with --prefix')) base = opts["base"] - wlock = dsguard = lock = tr = None msgs = [] ret = 0 - - try: - wlock = repo.wlock() - + with repo.wlock(): if update: cmdutil.checkunfinished(repo) if (exact or not opts.get('force')): cmdutil.bailifchanged(repo) if not opts.get('no_commit'): - lock = repo.lock() - tr = repo.transaction('import') + lock = repo.lock + tr = lambda: repo.transaction('import') + dsguard = util.nullcontextmanager else: - dsguard = dirstateguard.dirstateguard(repo, 'import') - parents = repo[None].parents() - for patchurl in patches: - if patchurl == '-': - ui.status(_('applying patch from stdin\n')) - patchfile = ui.fin - patchurl = 'stdin' # for error message - else: - patchurl = os.path.join(base, patchurl) - ui.status(_('applying %s\n') % patchurl) - patchfile = hg.openpath(ui, patchurl) - - haspatch = False - for hunk in patch.split(patchfile): - with patch.extract(ui, hunk) as patchdata: - msg, node, rej = cmdutil.tryimportone(ui, repo, patchdata, - parents, opts, - msgs, hg.clean) - if msg: - haspatch = True - ui.note(msg + '\n') - if update or exact: - parents = repo[None].parents() + lock = util.nullcontextmanager + tr = util.nullcontextmanager + dsguard = lambda: dirstateguard.dirstateguard(repo, 'import') + with lock(), tr(), dsguard(): + parents = repo[None].parents() + for patchurl in patches: + if patchurl == '-': + ui.status(_('applying patch from stdin\n')) + patchfile = ui.fin + patchurl = 'stdin' # for error message else: - parents = [repo[node]] - if rej: - ui.write_err(_("patch applied partially\n")) - ui.write_err(_("(fix the .rej files and run " - "`hg commit --amend`)\n")) - ret = 1 - break - - if not haspatch: - raise error.Abort(_('%s: no diffs found') % patchurl) - - if tr: - tr.close() - if msgs: - repo.savecommitmessage('\n* * *\n'.join(msgs)) - if dsguard: - dsguard.close() + patchurl = os.path.join(base, patchurl) + ui.status(_('applying %s\n') % patchurl) + patchfile = hg.openpath(ui, patchurl) + + haspatch = False + for hunk in patch.split(patchfile): + with patch.extract(ui, hunk) as patchdata: + msg, node, rej = cmdutil.tryimportone(ui, repo, + patchdata, + parents, opts, + msgs, hg.clean) + if msg: + haspatch = True + ui.note(msg + '\n') + if update or exact: + parents = repo[None].parents() + else: + parents = [repo[node]] + if rej: + ui.write_err(_("patch applied partially\n")) + ui.write_err(_("(fix the .rej files and run " + "`hg commit --amend`)\n")) + ret = 1 + break + + if not haspatch: + raise error.Abort(_('%s: no diffs found') % patchurl) + + if msgs: + repo.savecommitmessage('\n* * *\n'.join(msgs)) return ret - finally: - if tr: - tr.release() - release(lock, dsguard, wlock) @command('incoming|in', [('f', 'force', None, @@ -3291,7 +3433,13 @@ badfn=lambda x, y: False) ui.pager('locate') - for abs in ctx.matches(m): + if ctx.rev() is None: + # When run on the working copy, "locate" includes removed files, so + # we get the list of files from the dirstate. + filesgen = sorted(repo.dirstate.matches(m)) + else: + filesgen = ctx.matches(m) + for abs in filesgen: if opts.get('fullpath'): ui.write(repo.wjoin(abs), end) else: @@ -3545,6 +3693,7 @@ ui.pager('manifest') for f in ctx: fm.startitem() + fm.context(ctx=ctx) fl = ctx[f].flags() fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f])) fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl]) @@ -3623,15 +3772,13 @@ displayer.close() return 0 - try: - # ui.forcemerge is an internal variable, do not document - repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge') + # ui.forcemerge is an internal variable, do not document + overrides = {('ui', 'forcemerge'): opts.get('tool', '')} + with ui.configoverride(overrides, 'merge'): force = opts.get('force') labels = ['working copy', 'merge rev'] return hg.merge(repo, node, force=force, mergeforce=force, labels=labels, abort=abort) - finally: - ui.setconfig('ui', 'forcemerge', '', 'merge') @command('outgoing|out', [('f', 'force', None, _('run even when the destination is unrelated')), @@ -3679,6 +3826,13 @@ Returns 0 if there are outgoing changes, 1 otherwise. """ + # hg._outgoing() needs to re-resolve the path in order to handle #branch + # style URLs, so don't overwrite dest. + path = ui.paths.getpath(dest, default=('default-push', 'default')) + if not path: + raise error.Abort(_('default repository not configured!'), + hint=_("see 'hg help config.paths'")) + opts = pycompat.byteskwargs(opts) if opts.get('graph'): logcmdutil.checkunsupportedgraphflags([], opts) @@ -3696,8 +3850,7 @@ return 0 if opts.get('bookmarks'): - dest = ui.expandpath(dest or 'default-push', dest or 'default') - dest, branches = hg.parseurl(dest, opts.get('branch')) + dest = path.pushloc or path.loc other = hg.peer(repo, opts, dest) if 'bookmarks' not in other.listkeys('namespaces'): ui.warn(_("remote doesn't support bookmarks\n")) @@ -3706,7 +3859,7 @@ ui.pager('outgoing') return bookmarks.outgoing(ui, repo, other) - repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default') + repo._subtoppath = path.pushloc or path.loc try: return hg.outgoing(ui, repo, dest, opts) finally: @@ -4391,7 +4544,8 @@ ui.pager('resolve') fm = ui.formatter('resolve', opts) ms = mergemod.mergestate.read(repo) - m = scmutil.match(repo[None], pats, opts) + wctx = repo[None] + m = scmutil.match(wctx, pats, opts) # Labels and keys based on merge state. Unresolved path conflicts show # as 'P'. Resolved path conflicts show as 'R', the same as normal @@ -4411,6 +4565,7 @@ label, key = mergestateinfo[ms[f]] fm.startitem() + fm.context(ctx=wctx) fm.condwrite(not nostatus, 'status', '%s ', key, label=label) fm.write('path', '%s\n', f, label=label) fm.end() @@ -4488,15 +4643,14 @@ try: # preresolve file - ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), - 'resolve') - complete, r = ms.preresolve(f, wctx) + overrides = {('ui', 'forcemerge'): opts.get('tool', '')} + with ui.configoverride(overrides, 'resolve'): + complete, r = ms.preresolve(f, wctx) if not complete: tocomplete.append(f) elif r: ret = 1 finally: - ui.setconfig('ui', 'forcemerge', '', 'resolve') ms.commit() # replace filemerge's .orig file with our resolve file, but only @@ -4512,13 +4666,12 @@ for f in tocomplete: try: # resolve file - ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), - 'resolve') - r = ms.resolve(f, wctx) + overrides = {('ui', 'forcemerge'): opts.get('tool', '')} + with ui.configoverride(overrides, 'resolve'): + r = ms.resolve(f, wctx) if r: ret = 1 finally: - ui.setconfig('ui', 'forcemerge', '', 'resolve') ms.commit() # replace filemerge's .orig file with our resolve file @@ -4747,7 +4900,8 @@ ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')), ('', 'style', '', _('template style to use'), _('STYLE')), ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')), - ('', 'certificate', '', _('SSL certificate file'), _('FILE'))] + ('', 'certificate', '', _('SSL certificate file'), _('FILE')), + ('', 'print-url', None, _('start and print only the URL'))] + subrepoopts, _('[OPTION]...'), optionalrepo=True) @@ -4779,6 +4933,8 @@ opts = pycompat.byteskwargs(opts) if opts["stdio"] and opts["cmdserver"]: raise error.Abort(_("cannot use --stdio with --cmdserver")) + if opts["print_url"] and ui.verbose: + raise error.Abort(_("cannot use --print-url with --verbose")) if opts["stdio"]: if repo is None: @@ -4790,6 +4946,8 @@ service = server.createservice(ui, repo, opts) return server.runservice(opts, initfn=service.init, runfn=service.run) +_NOTTERSE = 'nothing' + @command('^status|st', [('A', 'all', None, _('show status of all files')), ('m', 'modified', None, _('show only modified files')), @@ -4800,7 +4958,7 @@ ('u', 'unknown', None, _('show only unknown (not tracked) files')), ('i', 'ignored', None, _('show only ignored files')), ('n', 'no-status', None, _('hide status prefix')), - ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')), + ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')), ('C', 'copies', None, _('show source of copied files')), ('0', 'print0', None, _('end filenames with NUL, for use with xargs')), ('', 'rev', [], _('show difference from revision'), _('REV')), @@ -4898,6 +5056,11 @@ revs = opts.get('rev') change = opts.get('change') terse = opts.get('terse') + if terse is _NOTTERSE: + if revs: + terse = '' + else: + terse = ui.config('commands', 'status.terse') if revs and change: msg = _('cannot specify --rev and --change at the same time') @@ -4939,7 +5102,8 @@ # we need to compute clean and unknown to terse stat = repo.status(ctx1.node(), ctx2.node(), m, 'ignored' in show or 'i' in terse, - True, True, opts.get('subrepos')) + clean=True, unknown=True, + listsubrepos=opts.get('subrepos')) stat = cmdutil.tersedir(stat, terse) else: @@ -4963,6 +5127,7 @@ label = 'status.' + state for f in files: fm.startitem() + fm.context(ctx=ctx2) fm.condwrite(showchar, 'status', '%s ', char, label=label) fm.write('path', fmt, repo.pathto(f, cwd), label=label) if f in copy: @@ -5301,10 +5466,7 @@ Returns 0 on success. """ opts = pycompat.byteskwargs(opts) - wlock = lock = None - try: - wlock = repo.wlock() - lock = repo.lock() + with repo.wlock(), repo.lock(): rev_ = "." names = [t.strip() for t in (name1,) + names] if len(names) != len(set(names)): @@ -5375,8 +5537,6 @@ tagsmod.tag(repo, names, node, message, opts.get('local'), opts.get('user'), date, editor=editor) - finally: - release(lock, wlock) @command('tags', formatteropts, '', intents={INTENT_READONLY}) def tags(ui, repo, **opts): @@ -5392,6 +5552,7 @@ opts = pycompat.byteskwargs(opts) ui.pager('tags') fm = ui.formatter('tags', opts) + contexthint = fm.contexthint('tag rev node type') hexfunc = fm.hexfunc tagtype = "" @@ -5404,6 +5565,8 @@ tagtype = 'local' fm.startitem() + if 'ctx' in contexthint: + fm.context(ctx=repo[n]) fm.write('tag', '%s', t, label=label) fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s' fm.condwrite(not ui.quiet, 'rev node', fmt, @@ -5583,18 +5746,19 @@ repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn') ctx = scmutil.revsingle(repo, rev, rev) rev = ctx.rev() - if ctx.hidden(): + hidden = ctx.hidden() + overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')} + with ui.configoverride(overrides, 'update'): + ret = hg.updatetotally(ui, repo, rev, brev, clean=clean, + updatecheck=updatecheck) + if hidden: ctxstr = ctx.hex()[:12] - ui.warn(_("updating to a hidden changeset %s\n") % ctxstr) + ui.warn(_("updated to hidden changeset %s\n") % ctxstr) if ctx.obsolete(): obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx) ui.warn("(%s)\n" % obsfatemsg) - - repo.ui.setconfig('ui', 'forcemerge', opts.get(r'tool'), 'update') - - return hg.updatetotally(ui, repo, rev, brev, clean=clean, - updatecheck=updatecheck) + return ret @command('verify', []) def verify(ui, repo):
--- a/mercurial/commandserver.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/commandserver.py Thu Jul 19 13:55:54 2018 -0400 @@ -256,7 +256,7 @@ self.cout, self.cerr) try: - ret = (dispatch.dispatch(req) or 0) & 255 # might return None + ret = dispatch.dispatch(req) & 255 self.cresult.write(struct.pack('>i', int(ret))) finally: # restore old cwd @@ -494,6 +494,8 @@ conn.close() # release handle in parent process else: try: + selector.close() + self._sock.close() self._runworker(conn) conn.close() os._exit(0)
--- a/mercurial/config.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/config.py Thu Jul 19 13:55:54 2018 -0400 @@ -215,7 +215,7 @@ parts.append('') if s[offset:offset + 1] == '"' and not parts[-1]: return _parse_quote, parts, offset + 1 - elif s[offset:offset + 1] == '"' and parts[-1][-1] == '\\': + elif s[offset:offset + 1] == '"' and parts[-1][-1:] == '\\': parts[-1] = parts[-1][:-1] + s[offset:offset + 1] return _parse_plain, parts, offset + 1 parts[-1] += s[offset:offset + 1]
--- a/mercurial/configitems.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/configitems.py Thu Jul 19 13:55:54 2018 -0400 @@ -147,6 +147,9 @@ coreconfigitem('annotate', 'noprefix', default=False, ) +coreconfigitem('annotate', 'word-diff', + default=False, +) coreconfigitem('auth', 'cookiefile', default=None, ) @@ -184,6 +187,9 @@ coreconfigitem('color', 'pagermode', default=dynamicdefault, ) +coreconfigitem('commands', 'grep.all-files', + default=False, +) coreconfigitem('commands', 'show.aliasprefix', default=list, ) @@ -193,13 +199,14 @@ coreconfigitem('commands', 'status.skipstates', default=[], ) +coreconfigitem('commands', 'status.terse', + default='', +) coreconfigitem('commands', 'status.verbose', default=False, ) coreconfigitem('commands', 'update.check', default=None, - # Deprecated, remove after 4.4 release - alias=[('experimental', 'updatecheck')] ) coreconfigitem('commands', 'update.requiredest', default=False, @@ -208,6 +215,9 @@ default=None, generic=True, ) +coreconfigitem('convert', 'bzr.saverev', + default=True, +) coreconfigitem('convert', 'cvsps.cache', default=True, ) @@ -362,6 +372,9 @@ coreconfigitem('devel', 'warn-config-unknown', default=None, ) +coreconfigitem('devel', 'debug.extensions', + default=False, +) coreconfigitem('devel', 'debug.peer-request', default=False, ) @@ -395,6 +408,9 @@ coreconfigitem('diff', 'noprefix', default=False, ) +coreconfigitem('diff', 'word-diff', + default=False, +) coreconfigitem('email', 'bcc', default=None, ) @@ -508,9 +524,6 @@ coreconfigitem('experimental', 'evolution.track-operation', default=True, ) -coreconfigitem('experimental', 'worddiff', - default=False, -) coreconfigitem('experimental', 'maxdeltachainspan', default=-1, ) @@ -559,12 +572,18 @@ coreconfigitem('experimental', 'mergedriver', default=None, ) +coreconfigitem('experimental', 'nointerrupt', default=False) +coreconfigitem('experimental', 'nointerrupt-interactiveonly', default=True) + coreconfigitem('experimental', 'obsmarkers-exchange-debug', default=False, ) coreconfigitem('experimental', 'remotenames', default=False, ) +coreconfigitem('experimental', 'removeemptydirs', + default=True, +) coreconfigitem('experimental', 'revlogv2', default=None, ) @@ -581,10 +600,10 @@ default=False, ) coreconfigitem('experimental', 'sparse-read.density-threshold', - default=0.25, + default=0.50, ) coreconfigitem('experimental', 'sparse-read.min-gap-size', - default='256K', + default='65K', ) coreconfigitem('experimental', 'treemanifest', default=False, @@ -604,6 +623,9 @@ coreconfigitem('experimental', 'web.api.debugreflect', default=False, ) +coreconfigitem('experimental', 'worker.wdir-get-thread-safe', + default=False, +) coreconfigitem('experimental', 'xdiff', default=False, ) @@ -615,9 +637,6 @@ default=None, generic=True, ) -coreconfigitem('format', 'aggressivemergedeltas', - default=False, -) coreconfigitem('format', 'chunkcachesize', default=None, ) @@ -636,6 +655,9 @@ coreconfigitem('format', 'obsstore-version', default=None, ) +coreconfigitem('format', 'sparse-revlog', + default=False, +) coreconfigitem('format', 'usefncache', default=True, ) @@ -866,6 +888,9 @@ coreconfigitem('profiling', 'statformat', default='hotpath', ) +coreconfigitem('profiling', 'time-track', + default='cpu', +) coreconfigitem('profiling', 'type', default='stat', ) @@ -902,6 +927,10 @@ coreconfigitem('push', 'pushvars.server', default=False, ) +coreconfigitem('revlog', 'optimize-delta-parent-choice', + default=True, + # formely an experimental option: format.aggressivemergedeltas +) coreconfigitem('server', 'bookmarks-pushkey-compat', default=True, ) @@ -932,16 +961,16 @@ coreconfigitem('server', 'disablefullbundle', default=False, ) -coreconfigitem('server', 'streamunbundle', - default=False, +coreconfigitem('server', 'maxhttpheaderlen', + default=1024, ) coreconfigitem('server', 'pullbundle', default=False, ) -coreconfigitem('server', 'maxhttpheaderlen', - default=1024, +coreconfigitem('server', 'preferuncompressed', + default=False, ) -coreconfigitem('server', 'preferuncompressed', +coreconfigitem('server', 'streamunbundle', default=False, ) coreconfigitem('server', 'uncompressed', @@ -1065,6 +1094,9 @@ coreconfigitem('ui', 'graphnodetemplate', default=None, ) +coreconfigitem('ui', 'history-editing-backup', + default=True, +) coreconfigitem('ui', 'interactive', default=None, ) @@ -1074,6 +1106,9 @@ coreconfigitem('ui', 'interface.chunkselector', default=None, ) +coreconfigitem('ui', 'large-file-limit', + default=10000000, +) coreconfigitem('ui', 'logblockedtimes', default=False, ) @@ -1225,7 +1260,8 @@ coreconfigitem('web', 'address', default='', ) -coreconfigitem('web', 'allow_archive', +coreconfigitem('web', 'allow-archive', + alias=[('web', 'allow_archive')], default=list, ) coreconfigitem('web', 'allow_read',
--- a/mercurial/context.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/context.py Thu Jul 19 13:55:54 2018 -0400 @@ -10,7 +10,6 @@ import errno import filecmp import os -import re import stat from .i18n import _ @@ -24,7 +23,6 @@ short, wdirfilenodeids, wdirid, - wdirrev, ) from . import ( dagop, @@ -52,8 +50,6 @@ propertycache = util.propertycache -nonascii = re.compile(br'[^\x21-\x7f]').search - class basectx(object): """A basectx object represents the common logic for its children: changectx: read-only context that is already present in the repo, @@ -185,8 +181,8 @@ def mutable(self): return self.phase() > phases.public - def getfileset(self, expr): - return fileset.getfileset(self, expr) + def matchfileset(self, expr, badfn=None): + return fileset.match(self, expr, badfn=badfn) def obsolete(self): """True if the changeset is obsolete""" @@ -298,14 +294,18 @@ auditor=r.nofsauditor, ctx=self, listsubrepos=listsubrepos, badfn=badfn) - def diff(self, ctx2=None, match=None, **opts): + def diff(self, ctx2=None, match=None, changes=None, opts=None, + losedatafn=None, prefix='', relroot='', copy=None, + hunksfilterfn=None): """Returns a diff generator for the given contexts and matcher""" if ctx2 is None: ctx2 = self.p1() if ctx2 is not None: ctx2 = self._repo[ctx2] - diffopts = patch.diffopts(self._repo.ui, pycompat.byteskwargs(opts)) - return patch.diff(self._repo, ctx2, self, match=match, opts=diffopts) + return patch.diff(self._repo, ctx2, self, match=match, changes=changes, + opts=opts, losedatafn=losedatafn, prefix=prefix, + relroot=relroot, copy=copy, + hunksfilterfn=hunksfilterfn) def dirs(self): return self._manifest.dirs() @@ -377,31 +377,6 @@ return r -def changectxdeprecwarn(repo): - # changectx's constructor will soon lose support for these forms of - # changeids: - # * stringinfied ints - # * bookmarks, tags, branches, and other namespace identifiers - # * hex nodeid prefixes - # - # Depending on your use case, replace repo[x] by one of these: - # * If you want to support general revsets, use scmutil.revsingle(x) - # * If you know that "x" is a stringified int, use repo[int(x)] - # * If you know that "x" is a bookmark, use repo._bookmarks.changectx(x) - # * If you know that "x" is a tag, use repo[repo.tags()[x]] - # * If you know that "x" is a branch or in some other namespace, - # use the appropriate mechanism for that namespace - # * If you know that "x" is a hex nodeid prefix, use - # repo[scmutil.resolvehexnodeidprefix(repo, x)] - # * If "x" is a string that can be any of the above, but you don't want - # to allow general revsets (perhaps because "x" may come from a remote - # user and the revset may be too costly), use scmutil.revsymbol(repo, x) - # * If "x" can be a mix of the above, you'll have to figure it out - # yourself - repo.ui.deprecwarn("changectx.__init__ is getting more limited, see " - "context.changectxdeprecwarn() for details", "4.6", - stacklevel=4) - class changectx(basectx): """A changecontext object makes access to data related to a particular changeset convenient. It represents a read-only context already present in @@ -415,22 +390,22 @@ self._node = repo.changelog.node(changeid) self._rev = changeid return - if changeid == 'null': + elif changeid == 'null': self._node = nullid self._rev = nullrev return - if changeid == 'tip': + elif changeid == 'tip': self._node = repo.changelog.tip() self._rev = repo.changelog.rev(self._node) return - if (changeid == '.' - or repo.local() and changeid == repo.dirstate.p1()): + elif (changeid == '.' + or repo.local() and changeid == repo.dirstate.p1()): # this is a hack to delay/avoid loading obsmarkers # when we know that '.' won't be hidden self._node = repo.dirstate.p1() self._rev = repo.unfiltered().changelog.rev(self._node) return - if len(changeid) == 20: + elif len(changeid) == 20: try: self._node = changeid self._rev = repo.changelog.rev(changeid) @@ -438,27 +413,17 @@ except error.FilteredLookupError: raise except LookupError: - pass + # check if it might have come from damaged dirstate + # + # XXX we could avoid the unfiltered if we had a recognizable + # exception for filtered changeset access + if (repo.local() + and changeid in repo.unfiltered().dirstate.parents()): + msg = _("working directory has unknown parent '%s'!") + raise error.Abort(msg % short(changeid)) + changeid = hex(changeid) # for the error message - try: - r = int(changeid) - if '%d' % r != changeid: - raise ValueError - l = len(repo.changelog) - if r < 0: - r += l - if r < 0 or r >= l and r != wdirrev: - raise ValueError - self._rev = r - self._node = repo.changelog.node(r) - changectxdeprecwarn(repo) - return - except error.FilteredIndexError: - raise - except (ValueError, OverflowError, IndexError): - pass - - if len(changeid) == 40: + elif len(changeid) == 40: try: self._node = bin(changeid) self._rev = repo.changelog.rev(self._node) @@ -467,39 +432,15 @@ raise except (TypeError, LookupError): pass - - # lookup bookmarks through the name interface - try: - self._node = repo.names.singlenode(repo, changeid) - self._rev = repo.changelog.rev(self._node) - changectxdeprecwarn(repo) - return - except KeyError: - pass - - self._node = scmutil.resolvehexnodeidprefix(repo, changeid) - if self._node is not None: - self._rev = repo.changelog.rev(self._node) - changectxdeprecwarn(repo) - return + else: + raise error.ProgrammingError( + "unsupported changeid '%s' of type %s" % + (changeid, type(changeid))) # lookup failed - # check if it might have come from damaged dirstate - # - # XXX we could avoid the unfiltered if we had a recognizable - # exception for filtered changeset access - if (repo.local() - and changeid in repo.unfiltered().dirstate.parents()): - msg = _("working directory has unknown parent '%s'!") - raise error.Abort(msg % short(changeid)) - try: - if len(changeid) == 20 and nonascii(changeid): - changeid = hex(changeid) - except TypeError: - pass except (error.FilteredIndexError, error.FilteredLookupError): raise error.FilteredRepoLookupError(_("filtered revision '%s'") - % changeid) + % pycompat.bytestr(changeid)) except error.FilteredRepoLookupError: raise except IndexError: @@ -649,8 +590,14 @@ return changectx(self._repo, anc) def descendant(self, other): - """True if other is descendant of this changeset""" - return self._repo.changelog.descendant(self._rev, other._rev) + msg = (b'ctx.descendant(other) is deprecated, ' + 'use ctx.isancestorof(other)') + self._repo.ui.deprecwarn(msg, b'4.7') + return self.isancestorof(other) + + def isancestorof(self, other): + """True if this changeset is an ancestor of other""" + return self._repo.changelog.isancestorrev(self._rev, other._rev) def walk(self, match): '''Generates matching file names.''' @@ -1294,7 +1241,8 @@ unknown=True, ignored=False)) def matches(self, match): - return sorted(self._repo.dirstate.matches(match)) + ds = self._repo.dirstate + return sorted(f for f in ds.matches(match) if ds[f] != 'r') def ancestors(self): for p in self._parents: @@ -1399,7 +1347,8 @@ ui.warn(_("%s does not exist!\n") % uipath(f)) rejected.append(f) continue - if st.st_size > 10000000: + limit = ui.configbytes('ui', 'large-file-limit') + if limit != 0 and st.st_size > limit: ui.warn(_("%s: up to %d MB of RAM may be required " "to manage this file\n" "(use 'hg revert %s' to cancel the " @@ -1773,7 +1722,9 @@ def remove(self, ignoremissing=False): """wraps unlink for a repo's working directory""" - self._repo.wvfs.unlinkpath(self._path, ignoremissing=ignoremissing) + rmdir = self._repo.ui.configbool('experimental', 'removeemptydirs') + self._repo.wvfs.unlinkpath(self._path, ignoremissing=ignoremissing, + rmdir=rmdir) def write(self, data, flags, backgroundclose=False, **kwargs): """wraps repo.wwrite"""
--- a/mercurial/copies.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/copies.py Thu Jul 19 13:55:54 2018 -0400 @@ -254,6 +254,11 @@ repo.ui.debug("%s:\n %s\n" % (header % 'local', "\n ".join(u1))) if u2: repo.ui.debug("%s:\n %s\n" % (header % 'other', "\n ".join(u2))) + + narrowmatch = repo.narrowmatch() + if not narrowmatch.always(): + u1 = [f for f in u1 if narrowmatch(f)] + u2 = [f for f in u2 if narrowmatch(f)] return u1, u2 def _makegetfctx(ctx): @@ -411,14 +416,14 @@ # common ancestor or not without explicitly checking it, it's better to # determine that here. # - # base.descendant(wc) and base.descendant(base) are False, work around that + # base.isancestorof(wc) is False, work around that _c1 = c1.p1() if c1.rev() is None else c1 _c2 = c2.p1() if c2.rev() is None else c2 # an endpoint is "dirty" if it isn't a descendant of the merge base # if we have a dirty endpoint, we need to trigger graft logic, and also # keep track of which endpoint is dirty - dirtyc1 = not (base == _c1 or base.descendant(_c1)) - dirtyc2 = not (base == _c2 or base.descendant(_c2)) + dirtyc1 = not base.isancestorof(_c1) + dirtyc2 = not base.isancestorof(_c2) graft = dirtyc1 or dirtyc2 tca = base if graft:
--- a/mercurial/crecord.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/crecord.py Thu Jul 19 13:55:54 2018 -0400 @@ -65,6 +65,11 @@ # compiled with curses curses = False +class fallbackerror(error.Abort): + """Error that indicates the client should try to fallback to text mode.""" + # Inherits from error.Abort so that existing behavior is preserved if the + # calling code does not know how to fallback. + def checkcurses(ui): """Return True if the user wants to use curses @@ -529,8 +534,8 @@ origsigtstp = signal.getsignal(signal.SIGTSTP) try: curses.wrapper(chunkselector.main) - if chunkselector.initerr is not None: - raise error.Abort(chunkselector.initerr) + if chunkselector.initexc is not None: + raise chunkselector.initexc # ncurses does not restore signal handler for SIGTSTP finally: if origsigtstp is not sentinel: @@ -549,7 +554,7 @@ """ chunkselector = curseschunkselector(headerlist, ui, operation) if testfn and os.path.exists(testfn): - testf = open(testfn) + testf = open(testfn, 'rb') testcommands = [x.rstrip('\n') for x in testf.readlines()] testf.close() while True: @@ -666,6 +671,7 @@ nextitem = currentitem self.currentselecteditem = nextitem + self.recenterdisplayedarea() def downarrowevent(self): """ @@ -705,6 +711,7 @@ nextitem = currentitem self.currentselecteditem = nextitem + self.recenterdisplayedarea() def rightarrowevent(self): """ @@ -1718,7 +1725,7 @@ self.stdscr = stdscr # error during initialization, cannot be printed in the curses # interface, it should be printed by the calling code - self.initerr = None + self.initexc = None self.yscreensize, self.xscreensize = self.stdscr.getmaxyx() curses.start_color() @@ -1751,7 +1758,8 @@ try: self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize) except curses.error: - self.initerr = _('this diff is too large to be displayed') + self.initexc = fallbackerror( + _('this diff is too large to be displayed')) return # initialize selecteditemendline (initial start-line is 0) self.selecteditemendline = self.getnumlinesdisplayed(
--- a/mercurial/debugcommands.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/debugcommands.py Thu Jul 19 13:55:54 2018 -0400 @@ -21,7 +21,6 @@ import string import subprocess import sys -import tempfile import time from .i18n import _ @@ -71,7 +70,6 @@ scmutil, setdiscovery, simplemerge, - smartset, sshpeer, sslutil, streamclone, @@ -183,18 +181,14 @@ initialmergedlines.append("") tags = [] - - wlock = lock = tr = None - try: - wlock = repo.wlock() - lock = repo.lock() - tr = repo.transaction("builddag") - + progress = ui.makeprogress(_('building'), unit=_('revisions'), + total=total) + with progress, repo.wlock(), repo.lock(), repo.transaction("builddag"): at = -1 atbranch = 'default' nodeids = [] id = 0 - ui.progress(_('building'), id, unit=_('revisions'), total=total) + progress.update(id) for type, data in dagparser.parsedag(text): if type == 'n': ui.note(('node %s\n' % pycompat.bytestr(data))) @@ -267,14 +261,10 @@ elif type == 'a': ui.note(('branch %s\n' % data)) atbranch = data - ui.progress(_('building'), id, unit=_('revisions'), total=total) - tr.close() + progress.update(id) if tags: repo.vfs.write("localtags", "".join(tags)) - finally: - ui.progress(_('building'), None) - release(tr, lock, wlock) def _debugchangegroup(ui, gen, all=None, indent=0, **opts): indent_string = ' ' * indent @@ -437,7 +427,7 @@ 'hg debugcolor') def debugcolor(ui, repo, **opts): """show available color, effects or style""" - ui.write(('color mode: %s\n') % ui._colormode) + ui.write(('color mode: %s\n') % stringutil.pprint(ui._colormode)) if opts.get(r'style'): return _debugdisplaystyle(ui) else: @@ -630,6 +620,8 @@ opts = pycompat.byteskwargs(opts) r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts) index = r.index + start = r.start + length = r.length generaldelta = r.version & revlog.FLAG_GENERALDELTA withsparseread = getattr(r, '_withsparseread', False) @@ -677,8 +669,6 @@ comp, uncomp, deltatype, chain, chainsize = revinfo(rev) chainbase = chain[0] chainid = chainbases.setdefault(chainbase, len(chainbases) + 1) - start = r.start - length = r.length basestart = start(chainbase) revstart = start(rev) lineardist = revstart + comp - basestart @@ -688,8 +678,15 @@ except IndexError: prevrev = -1 - chainratio = float(chainsize) / float(uncomp) - extraratio = float(extradist) / float(chainsize) + if uncomp != 0: + chainratio = float(chainsize) / float(uncomp) + else: + chainratio = chainsize + + if chainsize != 0: + extraratio = float(extradist) / float(chainsize) + else: + extraratio = extradist fm.startitem() fm.write('rev chainid chainlen prevrev deltatype compsize ' @@ -718,7 +715,10 @@ if largestblock < blksize: largestblock = blksize - readdensity = float(chainsize) / float(readsize) + if readsize: + readdensity = float(chainsize) / float(readsize) + else: + readdensity = 1 fm.write('readsize largestblock readdensity srchunks', ' %10d %10d %9.5f %8d', @@ -838,8 +838,8 @@ if output: dest.close() -@command('debugextensions', cmdutil.formatteropts, [], norepo=True) -def debugextensions(ui, **opts): +@command('debugextensions', cmdutil.formatteropts, [], optionalrepo=True) +def debugextensions(ui, repo, **opts): '''show information about active extensions''' opts = pycompat.byteskwargs(opts) exts = extensions.extensions(ui) @@ -885,16 +885,38 @@ fm.end() @command('debugfileset', - [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))], - _('[-r REV] FILESPEC')) + [('r', 'rev', '', _('apply the filespec on this revision'), _('REV')), + ('', 'all-files', False, + _('test files from all revisions and working directory'))], + _('[-r REV] [--all-files] FILESPEC')) def debugfileset(ui, repo, expr, **opts): '''parse and apply a fileset specification''' - ctx = scmutil.revsingle(repo, opts.get(r'rev'), None) + opts = pycompat.byteskwargs(opts) + ctx = scmutil.revsingle(repo, opts.get('rev'), None) if ui.verbose: tree = fileset.parse(expr) ui.note(fileset.prettyformat(tree), "\n") - for f in ctx.getfileset(expr): + files = set() + if opts['all_files']: + for r in repo: + c = repo[r] + files.update(c.files()) + files.update(c.substate) + if opts['all_files'] or ctx.rev() is None: + wctx = repo[None] + files.update(repo.dirstate.walk(scmutil.matchall(repo), + subrepos=list(wctx.substate), + unknown=True, ignored=True)) + files.update(wctx.substate) + else: + files.update(ctx.files()) + files.update(ctx.substate) + + m = ctx.matchfileset(expr) + for f in sorted(files): + if not m(f): + continue ui.write("%s\n" % f) @command('debugformat', @@ -971,7 +993,7 @@ ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no')) casesensitive = '(unknown)' try: - with tempfile.NamedTemporaryFile(prefix='.debugfsinfo', dir=path) as f: + with pycompat.namedtempfile(prefix='.debugfsinfo', dir=path) as f: casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no' except OSError: pass @@ -1143,7 +1165,7 @@ opts = pycompat.byteskwargs(opts) def writetemp(contents): - (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-") + (fd, name) = pycompat.mkstemp(prefix="hg-debuginstall-") f = os.fdopen(fd, r"wb") f.write(contents) f.close() @@ -1597,7 +1619,7 @@ if opts['rev']: raise error.Abort('cannot select revision when creating marker') metadata = {} - metadata['user'] = opts['user'] or ui.username() + metadata['user'] = encoding.fromlocal(opts['user'] or ui.username()) succs = tuple(parsenodeid(succ) for succ in successors) l = repo.lock() try: @@ -2237,8 +2259,8 @@ arevs = revset.makematcher(treebystage['analyzed'])(repo) brevs = revset.makematcher(treebystage['optimized'])(repo) if opts['show_set'] or (opts['show_set'] is None and ui.verbose): - ui.write(("* analyzed set:\n"), smartset.prettyformat(arevs), "\n") - ui.write(("* optimized set:\n"), smartset.prettyformat(brevs), "\n") + ui.write(("* analyzed set:\n"), stringutil.prettyrepr(arevs), "\n") + ui.write(("* optimized set:\n"), stringutil.prettyrepr(brevs), "\n") arevs = list(arevs) brevs = list(brevs) if arevs == brevs: @@ -2261,7 +2283,7 @@ func = revset.makematcher(tree) revs = func(repo) if opts['show_set'] or (opts['show_set'] is None and ui.verbose): - ui.write(("* set:\n"), smartset.prettyformat(revs), "\n") + ui.write(("* set:\n"), stringutil.prettyrepr(revs), "\n") if not opts['show_revs']: return for c in revs: @@ -2291,7 +2313,13 @@ if opts['logiofd']: # Line buffered because output is line based. - logfh = os.fdopen(int(opts['logiofd']), r'ab', 1) + try: + logfh = os.fdopen(int(opts['logiofd']), r'ab', 1) + except OSError as e: + if e.errno != errno.ESPIPE: + raise + # can't seek a pipe, so `ab` mode fails on py3 + logfh = os.fdopen(int(opts['logiofd']), r'wb', 1) elif opts['logiofile']: logfh = open(opts['logiofile'], 'ab', 1) @@ -2484,9 +2512,17 @@ if revs is None: tres = formatter.templateresources(ui, repo) t = formatter.maketemplater(ui, tmpl, resources=tres) + if ui.verbose: + kwds, funcs = t.symbolsuseddefault() + ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds))) + ui.write(("* functions: %s\n") % ', '.join(sorted(funcs))) ui.write(t.renderdefault(props)) else: displayer = logcmdutil.maketemplater(ui, repo, tmpl) + if ui.verbose: + kwds, funcs = displayer.t.symbolsuseddefault() + ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds))) + ui.write(("* functions: %s\n") % ', '.join(sorted(funcs))) for r in revs: displayer.show(repo[r], **pycompat.strkwargs(props)) displayer.close() @@ -2544,7 +2580,8 @@ """show how files match on given patterns""" opts = pycompat.byteskwargs(opts) m = scmutil.match(repo[None], pats, opts) - ui.write(('matcher: %r\n' % m)) + if ui.verbose: + ui.write(('* matcher:\n'), stringutil.prettyrepr(m), '\n') items = list(repo[None].walk(m)) if not items: return @@ -3018,10 +3055,12 @@ if isinstance(res, wireprotov2peer.commandresponse): val = list(res.cborobjects()) - ui.status(_('response: %s\n') % stringutil.pprint(val)) + ui.status(_('response: %s\n') % + stringutil.pprint(val, bprefix=True)) else: - ui.status(_('response: %s\n') % stringutil.pprint(res)) + ui.status(_('response: %s\n') % + stringutil.pprint(res, bprefix=True)) elif action == 'batchbegin': if batchedcommands is not None: @@ -3093,7 +3132,8 @@ continue if res.headers.get('Content-Type') == 'application/mercurial-cbor': - ui.write(_('cbor> %s\n') % stringutil.pprint(cbor.loads(body))) + ui.write(_('cbor> %s\n') % + stringutil.pprint(cbor.loads(body), bprefix=True)) elif action == 'close': peer.close()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/diffutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,105 @@ +# diffutil.py - utility functions related to diff and patch +# +# Copyright 2006 Brendan Cully <brendan@kublai.com> +# Copyright 2007 Chris Mason <chris.mason@oracle.com> +# Copyright 2018 Octobus <octobus@octobus.net> +# +# 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 ( + mdiff, + pycompat, +) + +def diffallopts(ui, opts=None, untrusted=False, section='diff'): + '''return diffopts with all features supported and parsed''' + return difffeatureopts(ui, opts=opts, untrusted=untrusted, section=section, + git=True, whitespace=True, formatchanging=True) + +def difffeatureopts(ui, opts=None, untrusted=False, section='diff', git=False, + whitespace=False, formatchanging=False): + '''return diffopts with only opted-in features parsed + + Features: + - git: git-style diffs + - whitespace: whitespace options like ignoreblanklines and ignorews + - formatchanging: options that will likely break or cause correctness issues + with most diff parsers + ''' + def get(key, name=None, getter=ui.configbool, forceplain=None): + if opts: + v = opts.get(key) + # diffopts flags are either None-default (which is passed + # through unchanged, so we can identify unset values), or + # some other falsey default (eg --unified, which defaults + # to an empty string). We only want to override the config + # entries from hgrc with command line values if they + # appear to have been set, which is any truthy value, + # True, or False. + if v or isinstance(v, bool): + return v + if forceplain is not None and ui.plain(): + return forceplain + return getter(section, name or key, untrusted=untrusted) + + # core options, expected to be understood by every diff parser + buildopts = { + 'nodates': get('nodates'), + 'showfunc': get('show_function', 'showfunc'), + 'context': get('unified', getter=ui.config), + } + buildopts['xdiff'] = ui.configbool('experimental', 'xdiff') + + if git: + buildopts['git'] = get('git') + + # since this is in the experimental section, we need to call + # ui.configbool directory + buildopts['showsimilarity'] = ui.configbool('experimental', + 'extendedheader.similarity') + + # need to inspect the ui object instead of using get() since we want to + # test for an int + hconf = ui.config('experimental', 'extendedheader.index') + if hconf is not None: + hlen = None + try: + # the hash config could be an integer (for length of hash) or a + # word (e.g. short, full, none) + hlen = int(hconf) + if hlen < 0 or hlen > 40: + msg = _("invalid length for extendedheader.index: '%d'\n") + ui.warn(msg % hlen) + except ValueError: + # default value + if hconf == 'short' or hconf == '': + hlen = 12 + elif hconf == 'full': + hlen = 40 + elif hconf != 'none': + msg = _("invalid value for extendedheader.index: '%s'\n") + ui.warn(msg % hconf) + finally: + buildopts['index'] = hlen + + if whitespace: + buildopts['ignorews'] = get('ignore_all_space', 'ignorews') + buildopts['ignorewsamount'] = get('ignore_space_change', + 'ignorewsamount') + buildopts['ignoreblanklines'] = get('ignore_blank_lines', + 'ignoreblanklines') + buildopts['ignorewseol'] = get('ignore_space_at_eol', 'ignorewseol') + if formatchanging: + buildopts['text'] = opts and opts.get('text') + binary = None if opts is None else opts.get('binary') + buildopts['nobinary'] = (not binary if binary is not None + else get('nobinary', forceplain=False)) + buildopts['noprefix'] = get('noprefix', forceplain=False) + buildopts['worddiff'] = get('word_diff', 'word-diff', forceplain=False) + + return mdiff.diffopts(**pycompat.strkwargs(buildopts))
--- a/mercurial/dispatch.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/dispatch.py Thu Jul 19 13:55:54 2018 -0400 @@ -83,20 +83,23 @@ def run(): "run the command in sys.argv" - _initstdio() + initstdio() req = request(pycompat.sysargv[1:]) err = None try: - status = (dispatch(req) or 0) + status = dispatch(req) except error.StdioError as e: err = e status = -1 + + # In all cases we try to flush stdio streams. if util.safehasattr(req.ui, 'fout'): try: req.ui.fout.flush() except IOError as e: err = e status = -1 + if util.safehasattr(req.ui, 'ferr'): try: if err is not None and err.errno != errno.EPIPE: @@ -112,7 +115,7 @@ sys.exit(status & 255) if pycompat.ispy3: - def _initstdio(): + def initstdio(): pass def _silencestdio(): @@ -132,7 +135,7 @@ except IOError: pass else: - def _initstdio(): + def initstdio(): for fp in (sys.stdin, sys.stdout, sys.stderr): procutil.setbinary(fp) @@ -172,7 +175,7 @@ return ' '.join(procutil.shellquote(a) for a in args) def dispatch(req): - "run the command specified in req.args" + """run the command specified in req.args; returns an integer status code""" if req.ferr: ferr = req.ferr elif req.ui: @@ -205,9 +208,9 @@ msg = _formatargs(req.args) starttime = util.timer() - ret = None + ret = 1 # default of Python exit code on unhandled exception try: - ret = _runcatch(req) + ret = _runcatch(req) or 0 except error.ProgrammingError as inst: req.ui.warn(_('** ProgrammingError: %s\n') % inst) if inst.hint: @@ -236,7 +239,7 @@ req.ui.log('uiblocked', 'ui blocked ms', **pycompat.strkwargs(req.ui._blockedtimes)) req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n", - msg, ret or 0, duration) + msg, ret & 255, duration) try: req._runexithandlers() except: # exiting, so no re-raises @@ -285,8 +288,8 @@ req.args[2] != 'serve' or req.args[3] != '--stdio'): raise error.Abort( - _('potentially unsafe serve --stdio invocation: %r') % - (req.args,)) + _('potentially unsafe serve --stdio invocation: %s') % + (stringutil.pprint(req.args),)) try: debugger = 'pdb' @@ -808,6 +811,13 @@ if req.repo: uis.add(req.repo.ui) + if (req.earlyoptions['verbose'] or req.earlyoptions['debug'] + or req.earlyoptions['quiet']): + for opt in ('verbose', 'debug', 'quiet'): + val = pycompat.bytestr(bool(req.earlyoptions[opt])) + for ui_ in uis: + ui_.setconfig('ui', opt, val, '--' + opt) + if req.earlyoptions['profile']: for ui_ in uis: ui_.setconfig('profiling', 'enabled', 'true', '--profile') @@ -873,8 +883,11 @@ if options["profile"]: profiler.start() + # if abbreviated version of this were used, take them in account, now if options['verbose'] or options['debug'] or options['quiet']: for opt in ('verbose', 'debug', 'quiet'): + if options[opt] == req.earlyoptions[opt]: + continue val = pycompat.bytestr(bool(options[opt])) for ui_ in uis: ui_.setconfig('ui', opt, val, '--' + opt) @@ -1025,7 +1038,7 @@ '** which supports versions %s of Mercurial.\n' '** Please disable %s and try your action again.\n' '** If that fixes the bug please report it to %s\n') - % (name, testedwith, name, report)) + % (name, testedwith, name, stringutil.forcebytestr(report))) else: bugtracker = ui.config('ui', 'supportcontact') if bugtracker is None:
--- a/mercurial/encoding.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/encoding.py Thu Jul 19 13:55:54 2018 -0400 @@ -98,6 +98,16 @@ def __hash__(self): return hash(self._utf8) # avoid collisions in local string space +class safelocalstr(bytes): + """Tagged string denoting it was previously an internal UTF-8 string, + and can be converted back to UTF-8 losslessly + + >>> assert safelocalstr(b'\\xc3') == b'\\xc3' + >>> assert b'\\xc3' == safelocalstr(b'\\xc3') + >>> assert b'\\xc3' in {safelocalstr(b'\\xc3'): 0} + >>> assert safelocalstr(b'\\xc3') in {b'\\xc3': 0} + """ + def tolocal(s): """ Convert a string from internal UTF-8 to local encoding @@ -145,7 +155,7 @@ r = u.encode(_sysstr(encoding), u"replace") if u == r.decode(_sysstr(encoding)): # r is a safe, non-lossy encoding of s - return r + return safelocalstr(r) return localstr(s, r) except UnicodeDecodeError: # we should only get here if we're looking at an ancient changeset @@ -154,7 +164,7 @@ r = u.encode(_sysstr(encoding), u"replace") if u == r.decode(_sysstr(encoding)): # r is a safe, non-lossy encoding of s - return r + return safelocalstr(r) return localstr(u.encode('UTF-8'), r) except UnicodeDecodeError: u = s.decode("utf-8", "replace") # last ditch @@ -407,7 +417,7 @@ JSON is problematic for us because it doesn't support non-Unicode bytes. To deal with this, we take the following approach: - - localstr objects are converted back to UTF-8 + - localstr/safelocalstr objects are converted back to UTF-8 - valid UTF-8/ASCII strings are passed as-is - other strings are converted to UTF-8b surrogate encoding - apply JSON-specified string escaping @@ -500,6 +510,7 @@ - local strings that have a cached known UTF-8 encoding (aka localstr) get sent as UTF-8 so Unicode-oriented clients get the Unicode data they want + - non-lossy local strings (aka safelocalstr) get sent as UTF-8 as well - because we must preserve UTF-8 bytestring in places such as filenames, metadata can't be roundtripped without help @@ -509,11 +520,17 @@ internal surrogate encoding as a UTF-8 string.) ''' - if not isinstance(s, localstr) and isasciistr(s): + if isinstance(s, localstr): + # assume that the original UTF-8 sequence would never contain + # invalid characters in U+DCxx range + return s._utf8 + elif isinstance(s, safelocalstr): + # already verified that s is non-lossy in legacy encoding, which + # shouldn't contain characters in U+DCxx range + return fromlocal(s) + elif isasciistr(s): return s if "\xed" not in s: - if isinstance(s, localstr): - return s._utf8 try: s.decode('utf-8', _utf8strict) return s
--- a/mercurial/error.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/error.py Thu Jul 19 13:55:54 2018 -0400 @@ -241,7 +241,7 @@ if val is None: entries.append(val) else: - entries.append("%s=%r" % (par, val)) + entries.append("%s=%r" % (par, pycompat.maybebytestr(val))) if entries: msg = '%s - %s' % (msg, ', '.join(entries)) ValueError.__init__(self, msg)
--- a/mercurial/exchange.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/exchange.py Thu Jul 19 13:55:54 2018 -0400 @@ -531,6 +531,9 @@ _pushobsolete(pushop) _pushbookmark(pushop) + if repo.ui.configbool('experimental', 'remotenames'): + logexchange.pullremotenames(repo, remote) + return pushop # list of steps to perform discovery before push @@ -658,7 +661,7 @@ ui.debug("checking for updated bookmarks\n") ancestors = () if pushop.revs: - revnums = map(repo.changelog.rev, pushop.revs) + revnums = pycompat.maplist(repo.changelog.rev, pushop.revs) ancestors = repo.changelog.ancestors(revnums, inclusive=True) remotebookmark = listkeys(remote, 'bookmarks')
--- a/mercurial/extensions.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/extensions.py Thu Jul 19 13:55:54 2018 -0400 @@ -7,6 +7,8 @@ from __future__ import absolute_import +import ast +import collections import functools import imp import inspect @@ -121,10 +123,11 @@ def _reportimporterror(ui, err, failed, next): # note: this ui.debug happens before --debug is processed, # Use --config ui.debug=1 to see them. - ui.debug('could not import %s (%s): trying %s\n' - % (failed, stringutil.forcebytestr(err), next)) - if ui.debugflag: - ui.traceback() + if ui.configbool('devel', 'debug.extensions'): + ui.debug('could not import %s (%s): trying %s\n' + % (failed, stringutil.forcebytestr(err), next)) + if ui.debugflag: + ui.traceback() def _rejectunicode(name, xs): if isinstance(xs, (list, set, tuple)): @@ -145,9 +148,6 @@ """Check if extension commands have required attributes""" for c, e in cmdtable.iteritems(): f = e[0] - if getattr(f, '_deprecatedregistrar', False): - ui.deprecwarn("cmdutil.command is deprecated, use " - "registrar.command to register '%s'" % c, '4.6') missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)] if not missing: continue @@ -541,9 +541,8 @@ fn = getattr(fn, '_origfunc', None) return result -def _disabledpaths(strip_init=False): - '''find paths of disabled extensions. returns a dict of {name: path} - removes /__init__.py from packages if strip_init is True''' +def _disabledpaths(): + '''find paths of disabled extensions. returns a dict of {name: path}''' import hgext extpath = os.path.dirname( os.path.abspath(pycompat.fsencode(hgext.__file__))) @@ -562,8 +561,6 @@ path = os.path.join(extpath, e, '__init__.py') if not os.path.exists(path): continue - if strip_init: - path = os.path.dirname(path) if name in exts or name in _order or name == '__init__': continue exts[name] = path @@ -609,12 +606,10 @@ def _disabledhelp(path): '''retrieve help synopsis of a disabled extension (without importing)''' try: - file = open(path) + with open(path, 'rb') as src: + doc = _moduledoc(src) except IOError: return - else: - doc = _moduledoc(file) - file.close() if doc: # extracting localized synopsis return gettext(doc) @@ -658,48 +653,82 @@ if name in paths: return _disabledhelp(paths[name]) +def _walkcommand(node): + """Scan @command() decorators in the tree starting at node""" + todo = collections.deque([node]) + while todo: + node = todo.popleft() + if not isinstance(node, ast.FunctionDef): + todo.extend(ast.iter_child_nodes(node)) + continue + for d in node.decorator_list: + if not isinstance(d, ast.Call): + continue + if not isinstance(d.func, ast.Name): + continue + if d.func.id != r'command': + continue + yield d + +def _disabledcmdtable(path): + """Construct a dummy command table without loading the extension module + + This may raise IOError or SyntaxError. + """ + with open(path, 'rb') as src: + root = ast.parse(src.read(), path) + cmdtable = {} + for node in _walkcommand(root): + if not node.args: + continue + a = node.args[0] + if isinstance(a, ast.Str): + name = pycompat.sysbytes(a.s) + elif pycompat.ispy3 and isinstance(a, ast.Bytes): + name = a.s + else: + continue + cmdtable[name] = (None, [], b'') + return cmdtable + +def _finddisabledcmd(ui, cmd, name, path, strict): + try: + cmdtable = _disabledcmdtable(path) + except (IOError, SyntaxError): + return + try: + aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict) + except (error.AmbiguousCommand, error.UnknownCommand): + return + for c in aliases: + if c.startswith(cmd): + cmd = c + break + else: + cmd = aliases[0] + doc = _disabledhelp(path) + return (cmd, name, doc) + def disabledcmd(ui, cmd, strict=False): - '''import disabled extensions until cmd is found. - returns (cmdname, extname, module)''' + '''find cmd from disabled extensions without importing. + returns (cmdname, extname, doc)''' - paths = _disabledpaths(strip_init=True) + paths = _disabledpaths() if not paths: raise error.UnknownCommand(cmd) - def findcmd(cmd, name, path): - try: - mod = loadpath(path, 'hgext.%s' % name) - except Exception: - return - try: - aliases, entry = cmdutil.findcmd(cmd, - getattr(mod, 'cmdtable', {}), strict) - except (error.AmbiguousCommand, error.UnknownCommand): - return - except Exception: - ui.warn(_('warning: error finding commands in %s\n') % path) - ui.traceback() - return - for c in aliases: - if c.startswith(cmd): - cmd = c - break - else: - cmd = aliases[0] - return (cmd, name, mod) - ext = None # first, search for an extension with the same name as the command path = paths.pop(cmd, None) if path: - ext = findcmd(cmd, cmd, path) + ext = _finddisabledcmd(ui, cmd, cmd, path, strict=strict) if not ext: # otherwise, interrogate each extension until there's a match for name, path in paths.iteritems(): - ext = findcmd(cmd, name, path) + ext = _finddisabledcmd(ui, cmd, name, path, strict=strict) if ext: break - if ext and 'DEPRECATED' not in ext.__doc__: + if ext: return ext raise error.UnknownCommand(cmd) @@ -729,7 +758,7 @@ else: version = '' if isinstance(version, (list, tuple)): - version = '.'.join(str(o) for o in version) + version = '.'.join(pycompat.bytestr(o) for o in version) return version def ismoduleinternal(module):
--- a/mercurial/filelog.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/filelog.py Thu Jul 19 13:55:54 2018 -0400 @@ -215,12 +215,12 @@ self._revlog._lazydeltabase = value @property - def _aggressivemergedeltas(self): - return self._revlog._aggressivemergedeltas + def _deltabothparents(self): + return self._revlog._deltabothparents - @_aggressivemergedeltas.setter - def _aggressivemergedeltas(self, value): - self._revlog._aggressivemergedeltas = value + @_deltabothparents.setter + def _deltabothparents(self, value): + self._revlog._deltabothparents = value @property def _inline(self):
--- a/mercurial/filemerge.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/filemerge.py Thu Jul 19 13:55:54 2018 -0400 @@ -11,7 +11,6 @@ import os import re import shutil -import tempfile from .i18n import _ from .node import nullid, short @@ -114,8 +113,16 @@ def _findtool(ui, tool): if tool in internals: return tool + cmd = _toolstr(ui, tool, "executable", tool) + if cmd.startswith('python:'): + return cmd return findexternaltool(ui, tool) +def _quotetoolpath(cmd): + if cmd.startswith('python:'): + return cmd + return procutil.shellquote(cmd) + def findexternaltool(ui, tool): for kn in ("regkey", "regkeyalt"): k = _toolstr(ui, tool, kn) @@ -165,7 +172,7 @@ return ":prompt", None else: if toolpath: - return (force, procutil.shellquote(toolpath)) + return (force, _quotetoolpath(toolpath)) else: # mimic HGMERGE if given tool not found return (force, force) @@ -183,7 +190,7 @@ mf = match.match(repo.root, '', [pat]) if mf(path) and check(tool, pat, symlink, False, changedelete): toolpath = _findtool(ui, tool) - return (tool, procutil.shellquote(toolpath)) + return (tool, _quotetoolpath(toolpath)) # then merge tools tools = {} @@ -208,7 +215,7 @@ for p, t in tools: if check(t, None, symlink, binary, changedelete): toolpath = _findtool(ui, t) - return (t, procutil.shellquote(toolpath)) + return (t, _quotetoolpath(toolpath)) # internal merge or prompt as last resort if symlink or binary or changedelete: @@ -325,7 +332,7 @@ return filectx def _premerge(repo, fcd, fco, fca, toolconf, files, labels=None): - tool, toolpath, binary, symlink = toolconf + tool, toolpath, binary, symlink, scriptfn = toolconf if symlink or fcd.isabsent() or fco.isabsent(): return 1 unused, unused, unused, back = files @@ -361,7 +368,7 @@ return 1 # continue merging def _mergecheck(repo, mynode, orig, fcd, fco, fca, toolconf): - tool, toolpath, binary, symlink = toolconf + tool, toolpath, binary, symlink, scriptfn = toolconf if symlink: repo.ui.warn(_('warning: internal %s cannot merge symlinks ' 'for %s\n') % (tool, fcd.path())) @@ -430,7 +437,7 @@ Generic driver for _imergelocal and _imergeother """ assert localorother is not None - tool, toolpath, binary, symlink = toolconf + tool, toolpath, binary, symlink, scriptfn = toolconf r = simplemerge.simplemerge(repo.ui, fcd, fca, fco, label=labels, localorother=localorother) return True, r @@ -510,7 +517,7 @@ 'external merge tools') def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): - tool, toolpath, binary, symlink = toolconf + tool, toolpath, binary, symlink, scriptfn = toolconf if fcd.isabsent() or fco.isabsent(): repo.ui.warn(_('warning: %s cannot merge change/delete conflict ' 'for %s\n') % (tool, fcd.path())) @@ -551,12 +558,36 @@ args = util.interpolate( br'\$', replace, args, lambda s: procutil.shellquote(util.localpath(s))) - cmd = toolpath + ' ' + args if _toolbool(ui, tool, "gui"): repo.ui.status(_('running merge tool %s for file %s\n') % (tool, fcd.path())) - repo.ui.debug('launching merge tool: %s\n' % cmd) - r = ui.system(cmd, cwd=repo.root, environ=env, blockedtag='mergetool') + if scriptfn is None: + cmd = toolpath + ' ' + args + repo.ui.debug('launching merge tool: %s\n' % cmd) + r = ui.system(cmd, cwd=repo.root, environ=env, + blockedtag='mergetool') + else: + repo.ui.debug('launching python merge script: %s:%s\n' % + (toolpath, scriptfn)) + r = 0 + try: + # avoid cycle cmdutil->merge->filemerge->extensions->cmdutil + from . import extensions + mod = extensions.loadpath(toolpath, 'hgmerge.%s' % tool) + except Exception: + raise error.Abort(_("loading python merge script failed: %s") % + toolpath) + mergefn = getattr(mod, scriptfn, None) + if mergefn is None: + raise error.Abort(_("%s does not have function: %s") % + (toolpath, scriptfn)) + argslist = procutil.shellsplit(args) + # avoid cycle cmdutil->merge->filemerge->hook->extensions->cmdutil + from . import hook + ret, raised = hook.pythonhook(ui, repo, "merge", toolpath, + mergefn, {'args': argslist}, True) + if raised: + r = 1 repo.ui.debug('merge tool returned: %d\n' % r) return True, r, False @@ -681,7 +712,7 @@ tmproot = None tmprootprefix = repo.ui.config('experimental', 'mergetempdirprefix') if tmprootprefix: - tmproot = tempfile.mkdtemp(prefix=tmprootprefix) + tmproot = pycompat.mkdtemp(prefix=tmprootprefix) def maketempfrompath(prefix, path): fullbase, ext = os.path.splitext(path) @@ -692,7 +723,7 @@ name += ext f = open(name, r"wb") else: - fd, name = tempfile.mkstemp(prefix=pre + '.', suffix=ext) + fd, name = pycompat.mkstemp(prefix=pre + '.', suffix=ext) f = os.fdopen(fd, r"wb") return f, name @@ -751,9 +782,24 @@ symlink = 'l' in fcd.flags() + fco.flags() changedelete = fcd.isabsent() or fco.isabsent() tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete) + scriptfn = None if tool in internals and tool.startswith('internal:'): # normalize to new-style names (':merge' etc) tool = tool[len('internal'):] + if toolpath and toolpath.startswith('python:'): + invalidsyntax = False + if toolpath.count(':') >= 2: + script, scriptfn = toolpath[7:].rsplit(':', 1) + if not scriptfn: + invalidsyntax = True + # missing :callable can lead to spliting on windows drive letter + if '\\' in scriptfn or '/' in scriptfn: + invalidsyntax = True + else: + invalidsyntax = True + if invalidsyntax: + raise error.Abort(_("invalid 'python:' syntax: %s") % toolpath) + toolpath = script ui.debug("picked tool '%s' for %s (binary %s symlink %s changedelete %s)\n" % (tool, fd, pycompat.bytestr(binary), pycompat.bytestr(symlink), pycompat.bytestr(changedelete))) @@ -774,7 +820,7 @@ precheck = None isexternal = True - toolconf = tool, toolpath, binary, symlink + toolconf = tool, toolpath, binary, symlink, scriptfn if mergetype == nomerge: r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf, labels)
--- a/mercurial/fileset.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/fileset.py Thu Jul 19 13:55:54 2018 -0400 @@ -7,6 +7,7 @@ from __future__ import absolute_import +import errno import re from .i18n import _ @@ -126,148 +127,6 @@ return _getkindpat(x[1], x[2], allkinds, err) return getstring(x, err) -def getset(mctx, x): - if not x: - raise error.ParseError(_("missing argument")) - return methods[x[0]](mctx, *x[1:]) - -def stringset(mctx, x): - m = mctx.matcher([x]) - return [f for f in mctx.subset if m(f)] - -def kindpatset(mctx, x, y): - return stringset(mctx, _getkindpat(x, y, matchmod.allpatternkinds, - _("pattern must be a string"))) - -def andset(mctx, x, y): - return getset(mctx.narrow(getset(mctx, x)), y) - -def orset(mctx, x, y): - # needs optimizing - xl = getset(mctx, x) - yl = getset(mctx, y) - return xl + [f for f in yl if f not in xl] - -def notset(mctx, x): - s = set(getset(mctx, x)) - return [r for r in mctx.subset if r not in s] - -def minusset(mctx, x, y): - xl = getset(mctx, x) - yl = set(getset(mctx, y)) - return [f for f in xl if f not in yl] - -def negateset(mctx, x): - raise error.ParseError(_("can't use negate operator in this context")) - -def listset(mctx, a, b): - raise error.ParseError(_("can't use a list in this context"), - hint=_('see hg help "filesets.x or y"')) - -# symbols are callable like: -# fun(mctx, x) -# with: -# mctx - current matchctx instance -# x - argument in tree form -symbols = {} - -# filesets using matchctx.status() -_statuscallers = set() - -# filesets using matchctx.existing() -_existingcallers = set() - -predicate = registrar.filesetpredicate() - -@predicate('modified()', callstatus=True) -def modified(mctx, x): - """File that is modified according to :hg:`status`. - """ - # i18n: "modified" is a keyword - getargs(x, 0, 0, _("modified takes no arguments")) - s = set(mctx.status().modified) - return [f for f in mctx.subset if f in s] - -@predicate('added()', callstatus=True) -def added(mctx, x): - """File that is added according to :hg:`status`. - """ - # i18n: "added" is a keyword - getargs(x, 0, 0, _("added takes no arguments")) - s = set(mctx.status().added) - return [f for f in mctx.subset if f in s] - -@predicate('removed()', callstatus=True) -def removed(mctx, x): - """File that is removed according to :hg:`status`. - """ - # i18n: "removed" is a keyword - getargs(x, 0, 0, _("removed takes no arguments")) - s = set(mctx.status().removed) - return [f for f in mctx.subset if f in s] - -@predicate('deleted()', callstatus=True) -def deleted(mctx, x): - """Alias for ``missing()``. - """ - # i18n: "deleted" is a keyword - getargs(x, 0, 0, _("deleted takes no arguments")) - s = set(mctx.status().deleted) - return [f for f in mctx.subset if f in s] - -@predicate('missing()', callstatus=True) -def missing(mctx, x): - """File that is missing according to :hg:`status`. - """ - # i18n: "missing" is a keyword - getargs(x, 0, 0, _("missing takes no arguments")) - s = set(mctx.status().deleted) - return [f for f in mctx.subset if f in s] - -@predicate('unknown()', callstatus=True) -def unknown(mctx, x): - """File that is unknown according to :hg:`status`. These files will only be - considered if this predicate is used. - """ - # i18n: "unknown" is a keyword - getargs(x, 0, 0, _("unknown takes no arguments")) - s = set(mctx.status().unknown) - return [f for f in mctx.subset if f in s] - -@predicate('ignored()', callstatus=True) -def ignored(mctx, x): - """File that is ignored according to :hg:`status`. These files will only be - considered if this predicate is used. - """ - # i18n: "ignored" is a keyword - getargs(x, 0, 0, _("ignored takes no arguments")) - s = set(mctx.status().ignored) - return [f for f in mctx.subset if f in s] - -@predicate('clean()', callstatus=True) -def clean(mctx, x): - """File that is clean according to :hg:`status`. - """ - # i18n: "clean" is a keyword - getargs(x, 0, 0, _("clean takes no arguments")) - s = set(mctx.status().clean) - return [f for f in mctx.subset if f in s] - -def func(mctx, a, b): - funcname = getsymbol(a) - if funcname in symbols: - enabled = mctx._existingenabled - mctx._existingenabled = funcname in _existingcallers - try: - return symbols[funcname](mctx, b) - finally: - mctx._existingenabled = enabled - - keep = lambda fn: getattr(fn, '__doc__', None) is not None - - syms = [s for (s, fn) in symbols.items() if keep(fn)] - raise error.UnknownIdentifier(funcname, syms) - def getlist(x): if not x: return [] @@ -281,29 +140,169 @@ raise error.ParseError(err) return l -@predicate('binary()', callexisting=True) +def getmatch(mctx, x): + if not x: + raise error.ParseError(_("missing argument")) + return methods[x[0]](mctx, *x[1:]) + +def stringmatch(mctx, x): + return mctx.matcher([x]) + +def kindpatmatch(mctx, x, y): + return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds, + _("pattern must be a string"))) + +def andmatch(mctx, x, y): + xm = getmatch(mctx, x) + ym = getmatch(mctx, y) + return matchmod.intersectmatchers(xm, ym) + +def ormatch(mctx, x, y): + xm = getmatch(mctx, x) + ym = getmatch(mctx, y) + return matchmod.unionmatcher([xm, ym]) + +def notmatch(mctx, x): + m = getmatch(mctx, x) + return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m)) + +def minusmatch(mctx, x, y): + xm = getmatch(mctx, x) + ym = getmatch(mctx, y) + return matchmod.differencematcher(xm, ym) + +def negatematch(mctx, x): + raise error.ParseError(_("can't use negate operator in this context")) + +def listmatch(mctx, x, y): + raise error.ParseError(_("can't use a list in this context"), + hint=_('see hg help "filesets.x or y"')) + +def func(mctx, a, b): + funcname = getsymbol(a) + if funcname in symbols: + return symbols[funcname](mctx, b) + + keep = lambda fn: getattr(fn, '__doc__', None) is not None + + syms = [s for (s, fn) in symbols.items() if keep(fn)] + raise error.UnknownIdentifier(funcname, syms) + +# symbols are callable like: +# fun(mctx, x) +# with: +# mctx - current matchctx instance +# x - argument in tree form +symbols = {} + +# filesets using matchctx.status() +_statuscallers = set() + +predicate = registrar.filesetpredicate() + +@predicate('modified()', callstatus=True) +def modified(mctx, x): + """File that is modified according to :hg:`status`. + """ + # i18n: "modified" is a keyword + getargs(x, 0, 0, _("modified takes no arguments")) + s = set(mctx.status().modified) + return mctx.predicate(s.__contains__, predrepr='modified') + +@predicate('added()', callstatus=True) +def added(mctx, x): + """File that is added according to :hg:`status`. + """ + # i18n: "added" is a keyword + getargs(x, 0, 0, _("added takes no arguments")) + s = set(mctx.status().added) + return mctx.predicate(s.__contains__, predrepr='added') + +@predicate('removed()', callstatus=True) +def removed(mctx, x): + """File that is removed according to :hg:`status`. + """ + # i18n: "removed" is a keyword + getargs(x, 0, 0, _("removed takes no arguments")) + s = set(mctx.status().removed) + return mctx.predicate(s.__contains__, predrepr='removed') + +@predicate('deleted()', callstatus=True) +def deleted(mctx, x): + """Alias for ``missing()``. + """ + # i18n: "deleted" is a keyword + getargs(x, 0, 0, _("deleted takes no arguments")) + s = set(mctx.status().deleted) + return mctx.predicate(s.__contains__, predrepr='deleted') + +@predicate('missing()', callstatus=True) +def missing(mctx, x): + """File that is missing according to :hg:`status`. + """ + # i18n: "missing" is a keyword + getargs(x, 0, 0, _("missing takes no arguments")) + s = set(mctx.status().deleted) + return mctx.predicate(s.__contains__, predrepr='deleted') + +@predicate('unknown()', callstatus=True) +def unknown(mctx, x): + """File that is unknown according to :hg:`status`.""" + # i18n: "unknown" is a keyword + getargs(x, 0, 0, _("unknown takes no arguments")) + s = set(mctx.status().unknown) + return mctx.predicate(s.__contains__, predrepr='unknown') + +@predicate('ignored()', callstatus=True) +def ignored(mctx, x): + """File that is ignored according to :hg:`status`.""" + # i18n: "ignored" is a keyword + getargs(x, 0, 0, _("ignored takes no arguments")) + s = set(mctx.status().ignored) + return mctx.predicate(s.__contains__, predrepr='ignored') + +@predicate('clean()', callstatus=True) +def clean(mctx, x): + """File that is clean according to :hg:`status`. + """ + # i18n: "clean" is a keyword + getargs(x, 0, 0, _("clean takes no arguments")) + s = set(mctx.status().clean) + return mctx.predicate(s.__contains__, predrepr='clean') + +@predicate('tracked()') +def tracked(mctx, x): + """File that is under Mercurial control.""" + # i18n: "tracked" is a keyword + getargs(x, 0, 0, _("tracked takes no arguments")) + return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked') + +@predicate('binary()') def binary(mctx, x): """File that appears to be binary (contains NUL bytes). """ # i18n: "binary" is a keyword getargs(x, 0, 0, _("binary takes no arguments")) - return [f for f in mctx.existing() if mctx.ctx[f].isbinary()] + return mctx.fpredicate(lambda fctx: fctx.isbinary(), + predrepr='binary', cache=True) -@predicate('exec()', callexisting=True) +@predicate('exec()') def exec_(mctx, x): """File that is marked as executable. """ # i18n: "exec" is a keyword getargs(x, 0, 0, _("exec takes no arguments")) - return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x'] + ctx = mctx.ctx + return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec') -@predicate('symlink()', callexisting=True) +@predicate('symlink()') def symlink(mctx, x): """File that is marked as a symlink. """ # i18n: "symlink" is a keyword getargs(x, 0, 0, _("symlink takes no arguments")) - return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l'] + ctx = mctx.ctx + return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink') @predicate('resolved()') def resolved(mctx, x): @@ -312,9 +311,10 @@ # i18n: "resolved" is a keyword getargs(x, 0, 0, _("resolved takes no arguments")) if mctx.ctx.rev() is not None: - return [] + return mctx.never() ms = merge.mergestate.read(mctx.ctx.repo()) - return [f for f in mctx.subset if f in ms and ms[f] == 'r'] + return mctx.predicate(lambda f: f in ms and ms[f] == 'r', + predrepr='resolved') @predicate('unresolved()') def unresolved(mctx, x): @@ -323,9 +323,10 @@ # i18n: "unresolved" is a keyword getargs(x, 0, 0, _("unresolved takes no arguments")) if mctx.ctx.rev() is not None: - return [] + return mctx.never() ms = merge.mergestate.read(mctx.ctx.repo()) - return [f for f in mctx.subset if f in ms and ms[f] == 'u'] + return mctx.predicate(lambda f: f in ms and ms[f] == 'u', + predrepr='unresolved') @predicate('hgignore()') def hgignore(mctx, x): @@ -333,8 +334,7 @@ """ # i18n: "hgignore" is a keyword getargs(x, 0, 0, _("hgignore takes no arguments")) - ignore = mctx.ctx.repo().dirstate._ignore - return [f for f in mctx.subset if ignore(f)] + return mctx.ctx.repo().dirstate._ignore @predicate('portable()') def portable(mctx, x): @@ -343,10 +343,10 @@ """ # i18n: "portable" is a keyword getargs(x, 0, 0, _("portable takes no arguments")) - checkwinfilename = util.checkwinfilename - return [f for f in mctx.subset if checkwinfilename(f) is None] + return mctx.predicate(lambda f: util.checkwinfilename(f) is None, + predrepr='portable') -@predicate('grep(regex)', callexisting=True) +@predicate('grep(regex)') def grep(mctx, x): """File contains the given regular expression. """ @@ -354,8 +354,10 @@ # i18n: "grep" is a keyword r = re.compile(getstring(x, _("grep requires a pattern"))) except re.error as e: - raise error.ParseError(_('invalid match pattern: %s') % e) - return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())] + raise error.ParseError(_('invalid match pattern: %s') % + stringutil.forcebytestr(e)) + return mctx.fpredicate(lambda fctx: r.search(fctx.data()), + predrepr=('grep(%r)', r.pattern), cache=True) def _sizetomax(s): try: @@ -373,11 +375,9 @@ except ValueError: raise error.ParseError(_("couldn't parse size: %s") % s) -def sizematcher(x): +def sizematcher(expr): """Return a function(size) -> bool from the ``size()`` expression""" - - # i18n: "size" is a keyword - expr = getstring(x, _("size requires an expression")).strip() + expr = expr.strip() if '-' in expr: # do we have a range? a, b = expr.split('-', 1) a = util.sizetoint(a) @@ -400,7 +400,7 @@ b = _sizetomax(expr) return lambda x: x >= a and x <= b -@predicate('size(expression)', callexisting=True) +@predicate('size(expression)') def size(mctx, x): """File size matches the given expression. Examples: @@ -409,10 +409,13 @@ - size('>= .5MB') - files at least 524288 bytes - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes """ - m = sizematcher(x) - return [f for f in mctx.existing() if m(mctx.ctx[f].size())] + # i18n: "size" is a keyword + expr = getstring(x, _("size requires an expression")) + m = sizematcher(expr) + return mctx.fpredicate(lambda fctx: m(fctx.size()), + predrepr=('size(%r)', expr), cache=True) -@predicate('encoding(name)', callexisting=True) +@predicate('encoding(name)') def encoding(mctx, x): """File can be successfully decoded with the given character encoding. May not be useful for encodings other than ASCII and @@ -422,20 +425,19 @@ # i18n: "encoding" is a keyword enc = getstring(x, _("encoding requires an encoding name")) - s = [] - for f in mctx.existing(): - d = mctx.ctx[f].data() + def encp(fctx): + d = fctx.data() try: - d.decode(enc) + d.decode(pycompat.sysstr(enc)) + return True except LookupError: raise error.Abort(_("unknown encoding '%s'") % enc) except UnicodeDecodeError: - continue - s.append(f) + return False - return s + return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True) -@predicate('eol(style)', callexisting=True) +@predicate('eol(style)') def eol(mctx, x): """File contains newlines of the given style (dos, unix, mac). Binary files are excluded, files with mixed line endings match multiple @@ -445,18 +447,18 @@ # i18n: "eol" is a keyword enc = getstring(x, _("eol requires a style name")) - s = [] - for f in mctx.existing(): - d = mctx.ctx[f].data() - if stringutil.binary(d): - continue + def eolp(fctx): + if fctx.isbinary(): + return False + d = fctx.data() if (enc == 'dos' or enc == 'win') and '\r\n' in d: - s.append(f) + return True elif enc == 'unix' and re.search('(?<!\r)\n', d): - s.append(f) + return True elif enc == 'mac' and re.search('\r(?!\n)', d): - s.append(f) - return s + return True + return False + return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True) @predicate('copied()') def copied(mctx, x): @@ -464,13 +466,10 @@ """ # i18n: "copied" is a keyword getargs(x, 0, 0, _("copied takes no arguments")) - s = [] - for f in mctx.subset: - if f in mctx.ctx: - p = mctx.ctx[f].parents() - if p and p[0].path() != f: - s.append(f) - return s + def copiedp(fctx): + p = fctx.parents() + return p and p[0].path() != fctx.path() + return mctx.fpredicate(copiedp, predrepr='copied', cache=True) @predicate('revs(revs, pattern)') def revs(mctx, x): @@ -484,15 +483,15 @@ repo = mctx.ctx.repo() revs = scmutil.revrange(repo, [revspec]) - found = set() - result = [] + matchers = [] for r in revs: ctx = repo[r] - for f in getset(mctx.switch(ctx, _buildstatus(ctx, x)), x): - if f not in found: - found.add(f) - result.append(f) - return result + matchers.append(getmatch(mctx.switch(ctx, _buildstatus(ctx, x)), x)) + if not matchers: + return mctx.never() + if len(matchers) == 1: + return matchers[0] + return matchmod.unionmatcher(matchers) @predicate('status(base, rev, pattern)') def status(mctx, x): @@ -514,7 +513,7 @@ if not revspec: raise error.ParseError(reverr) basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec]) - return getset(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x) + return getmatch(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x) @predicate('subrepo([pattern])') def subrepo(mctx, x): @@ -523,7 +522,7 @@ # i18n: "subrepo" is a keyword getargs(x, 0, 1, _("subrepo takes at most one argument")) ctx = mctx.ctx - sstate = sorted(ctx.substate) + sstate = ctx.substate if x: pat = getpattern(x, matchmod.allpatternkinds, # i18n: "subrepo" is a keyword @@ -534,60 +533,85 @@ return (s == pat) else: m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx) - return [sub for sub in sstate if m(sub)] + return mctx.predicate(lambda f: f in sstate and m(f), + predrepr=('subrepo(%r)', pat)) else: - return [sub for sub in sstate] + return mctx.predicate(sstate.__contains__, predrepr='subrepo') methods = { - 'string': stringset, - 'symbol': stringset, - 'kindpat': kindpatset, - 'and': andset, - 'or': orset, - 'minus': minusset, - 'negate': negateset, - 'list': listset, - 'group': getset, - 'not': notset, + 'string': stringmatch, + 'symbol': stringmatch, + 'kindpat': kindpatmatch, + 'and': andmatch, + 'or': ormatch, + 'minus': minusmatch, + 'negate': negatematch, + 'list': listmatch, + 'group': getmatch, + 'not': notmatch, 'func': func, } class matchctx(object): - def __init__(self, ctx, subset, status=None): + def __init__(self, ctx, status=None, badfn=None): self.ctx = ctx - self.subset = subset self._status = status - self._existingenabled = False + self._badfn = badfn + def status(self): return self._status + def matcher(self, patterns): - return self.ctx.match(patterns) - def filter(self, files): - return [f for f in files if f in self.subset] - def existing(self): - assert self._existingenabled, 'unexpected existing() invocation' - if self._status is not None: - removed = set(self._status[3]) - unknown = set(self._status[4] + self._status[5]) + return self.ctx.match(patterns, badfn=self._badfn) + + def predicate(self, predfn, predrepr=None, cache=False): + """Create a matcher to select files by predfn(filename)""" + if cache: + predfn = util.cachefunc(predfn) + repo = self.ctx.repo() + return matchmod.predicatematcher(repo.root, repo.getcwd(), predfn, + predrepr=predrepr, badfn=self._badfn) + + def fpredicate(self, predfn, predrepr=None, cache=False): + """Create a matcher to select files by predfn(fctx) at the current + revision + + Missing files are ignored. + """ + ctx = self.ctx + if ctx.rev() is None: + def fctxpredfn(f): + try: + fctx = ctx[f] + except error.LookupError: + return False + try: + fctx.audit() + except error.Abort: + return False + try: + return predfn(fctx) + except (IOError, OSError) as e: + if e.errno in (errno.ENOENT, errno.ENOTDIR, errno.EISDIR): + return False + raise else: - removed = set() - unknown = set() - return (f for f in self.subset - if (f in self.ctx and f not in removed) or f in unknown) - def narrow(self, files): - return matchctx(self.ctx, self.filter(files), self._status) + def fctxpredfn(f): + try: + fctx = ctx[f] + except error.LookupError: + return False + return predfn(fctx) + return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache) + + def never(self): + """Create a matcher to select nothing""" + repo = self.ctx.repo() + return matchmod.nevermatcher(repo.root, repo.getcwd(), + badfn=self._badfn) + def switch(self, ctx, status=None): - subset = self.filter(_buildsubset(ctx, status)) - return matchctx(ctx, subset, status) - -class fullmatchctx(matchctx): - """A match context where any files in any revisions should be valid""" - - def __init__(self, ctx, status=None): - subset = _buildsubset(ctx, status) - super(fullmatchctx, self).__init__(ctx, subset, status) - def switch(self, ctx, status=None): - return fullmatchctx(ctx, status) + return matchctx(ctx, status, self._badfn) # filesets using matchctx.switch() _switchcallers = [ @@ -608,29 +632,16 @@ return True return False -def _buildsubset(ctx, status): - if status: - subset = [] - for c in status: - subset.extend(c) - return subset - else: - return list(ctx.walk(ctx.match([]))) - -def getfileset(ctx, expr): +def match(ctx, expr, badfn=None): + """Create a matcher for a single fileset expression""" tree = parse(expr) - return getset(fullmatchctx(ctx, _buildstatus(ctx, tree)), tree) + mctx = matchctx(ctx, _buildstatus(ctx, tree), badfn=badfn) + return getmatch(mctx, tree) def _buildstatus(ctx, tree, basectx=None): # do we need status info? - # temporaty boolean to simplify the next conditional - purewdir = ctx.rev() is None and basectx is None - - if (_intree(_statuscallers, tree) or - # Using matchctx.existing() on a workingctx requires us to check - # for deleted files. - (purewdir and _intree(_existingcallers, tree))): + if _intree(_statuscallers, tree): unknown = _intree(['unknown'], tree) ignored = _intree(['ignored'], tree) @@ -652,10 +663,8 @@ symbols[name] = func if func._callstatus: _statuscallers.add(name) - if func._callexisting: - _existingcallers.add(name) -# load built-in predicates explicitly to setup _statuscallers/_existingcallers +# load built-in predicates explicitly to setup _statuscallers loadpredicate(None, None, predicate) # tell hggettext to extract docstrings from these functions:
--- a/mercurial/formatter.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/formatter.py Thu Jul 19 13:55:54 2018 -0400 @@ -107,7 +107,6 @@ from __future__ import absolute_import, print_function -import collections import contextlib import itertools import os @@ -117,11 +116,15 @@ hex, short, ) +from .thirdparty import ( + attr, +) from . import ( error, pycompat, templatefilters, + templatefuncs, templatekw, templater, templateutil, @@ -190,12 +193,18 @@ # name is mandatory argument for now, but it could be optional if # we have default template keyword, e.g. {item} return self._converter.formatlist(data, name, fmt, sep) + def contexthint(self, datafields): + '''set of context object keys to be required given datafields set''' + return set() def context(self, **ctxs): '''insert context objects to be used to render template keywords''' ctxs = pycompat.byteskwargs(ctxs) assert all(k in {'ctx', 'fctx'} for k in ctxs) if self._converter.storecontext: self._item.update(ctxs) + def datahint(self): + '''set of field names to be referenced''' + return set() def data(self, **data): '''insert data into item that's not shown in default output''' data = pycompat.byteskwargs(data) @@ -365,7 +374,7 @@ @staticmethod def formatdate(date, fmt): '''return date tuple''' - return date + return templateutil.date(date) @staticmethod def formatdict(data, key, value, fmt, sep): '''build object that can be evaluated as either plain string or dict''' @@ -409,12 +418,41 @@ ref = self._parts[part] self._out.write(self._t.render(ref, item)) + @util.propertycache + def _symbolsused(self): + return self._t.symbolsused(self._tref) + + def contexthint(self, datafields): + '''set of context object keys to be required by the template, given + datafields overridden by immediate values''' + requires = set() + ksyms, fsyms = self._symbolsused + ksyms = ksyms - set(datafields.split()) # exclude immediate fields + symtables = [(ksyms, templatekw.keywords), + (fsyms, templatefuncs.funcs)] + for syms, table in symtables: + for k in syms: + f = table.get(k) + if not f: + continue + requires.update(getattr(f, '_requires', ())) + if 'repo' in requires: + requires.add('ctx') # there's no API to pass repo to formatter + return requires & {'ctx', 'fctx'} + + def datahint(self): + '''set of field names to be referenced from the template''' + return self._symbolsused[0] + def end(self): baseformatter.end(self) self._renderitem('docfooter', {}) -templatespec = collections.namedtuple(r'templatespec', - r'ref tmpl mapfile') +@attr.s(frozen=True) +class templatespec(object): + ref = attr.ib() + tmpl = attr.ib() + mapfile = attr.ib() def lookuptemplate(ui, topic, tmpl): """Find the template matching the given -T/--template spec 'tmpl'
--- a/mercurial/graphmod.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/graphmod.py Thu Jul 19 13:55:54 2018 -0400 @@ -341,6 +341,22 @@ 'graphshorten': False, } +def outputgraph(ui, graph): + """outputs an ASCII graph of a DAG + + this is a helper function for 'ascii' below. + + takes the following arguments: + + - ui to write to + - graph data: list of { graph nodes/edges, text } + + this function can be monkey-patched by extensions to alter graph display + without needing to mimic all of the edge-fixup logic in ascii() + """ + for (ln, logstr) in graph: + ui.write((ln + logstr).rstrip() + "\n") + def ascii(ui, state, type, char, text, coldata): """prints an ASCII graph of the DAG @@ -469,9 +485,8 @@ # print lines indentation_level = max(ncols, ncols + coldiff) - for (line, logstr) in zip(lines, text): - ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr) - ui.write(ln.rstrip() + '\n') + lines = ["%-*s " % (2 * indentation_level, "".join(line)) for line in lines] + outputgraph(ui, zip(lines, text)) # ... and start over state['lastcoldiff'] = coldiff
--- a/mercurial/help.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/help.py Thu Jul 19 13:55:54 2018 -0400 @@ -232,6 +232,7 @@ (['bundlespec'], _("Bundle File Formats"), loaddoc('bundlespec')), (['color'], _("Colorizing Outputs"), loaddoc('color')), (["config", "hgrc"], _("Configuration Files"), loaddoc('config')), + (['deprecated'], _("Deprecated Features"), loaddoc('deprecated')), (["dates"], _("Date Formats"), loaddoc('dates')), (["flags"], _("Command-line flags"), loaddoc('flags')), (["patterns"], _("File Name Patterns"), loaddoc('patterns')), @@ -574,9 +575,9 @@ return rst def helpextcmd(name, subtopic=None): - cmd, ext, mod = extensions.disabledcmd(ui, name, + cmd, ext, doc = extensions.disabledcmd(ui, name, ui.configbool('ui', 'strict')) - doc = gettext(pycompat.getdoc(mod)).splitlines()[0] + doc = doc.splitlines()[0] rst = listexts(_("'%s' is provided by the following " "extension:") % cmd, {ext: doc}, indent=4,
--- a/mercurial/help/config.txt Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/help/config.txt Thu Jul 19 13:55:54 2018 -0400 @@ -442,6 +442,10 @@ Make paths in :hg:`status` output relative to the current directory. (default: False) +``status.terse`` + Default value for the --terse flag, which condenes status output. + (default: empty) + ``update.check`` Determines what level of checking :hg:`update` will perform before moving to a destination revision. Valid values are ``abort``, ``none``, @@ -698,6 +702,9 @@ ``unified`` Number of lines of context to show. +``word-diff`` + Highlight changed words. + ``email`` --------- @@ -783,6 +790,14 @@ ``format`` ---------- +Configuration that controls the repository format. Newer format options are more +powerful but incompatible with some older versions of Mercurial. Format options +are considered at repository initialization only. You need to make a new clone +for config change to be taken into account. + +For more details about repository format and version compatibility, see +https://www.mercurial-scm.org/wiki/MissingRequirement + ``usegeneraldelta`` Enable or disable the "generaldelta" repository format which improves repository compression by allowing "revlog" to store delta against arbitrary @@ -882,6 +897,23 @@ of the hook in the config, respectively. In the example above, this will be ``$HG_HOOKTYPE=incoming`` and ``$HG_HOOKNAME=incoming.email``. +.. container:: windows + + Some basic Unix syntax can be enabled for portability, including ``$VAR`` + and ``${VAR}`` style variables. A ``~`` followed by ``\`` or ``/`` will + be expanded to ``%USERPROFILE%`` to simulate a subset of tilde expansion + on Unix. To use a literal ``$`` or ``~``, it must be escaped with a back + slash or inside of a strong quote. Strong quotes will be replaced by + double quotes after processing. + + This feature is enabled by adding a prefix of ``tonative.`` to the hook + name on a new line, and setting it to ``True``. For example:: + + [hooks] + incoming.autobuild = /my/build/hook + # enable translation to cmd.exe syntax for autobuild hook + tonative.incoming.autobuild = True + ``changegroup`` Run after a changegroup has been added via push, pull or unbundle. The ID of the first new changeset is in ``$HG_NODE`` and last is in ``$HG_NODE_LAST``. @@ -1648,6 +1680,10 @@ ``inlinetime``. (default: inlinetime) +``time-track`` + Control if the stat profiler track ``cpu`` or ``real`` time. + (default: ``cpu``) + ``limit`` Number of lines to show. Specific to the ``ls`` instrumenting profiler. (default: 30) @@ -1737,6 +1773,20 @@ Alias definitions for revsets. See :hg:`help revsets` for details. +``revlog`` +---------- + +Control the strategy Mercurial uses internally to store history. Options in this +category impact performance and repository size. + +``optimize-delta-parent-choice`` + When storing a merge revision, both parents will be equally considered as + a possible delta base. This results in better delta selection and improved + revlog compression. This option is enabled by default. + + Turning this option off can result in large increase of repository size for + repository with many merges. + ``server`` ---------- @@ -2121,6 +2171,11 @@ Possible values are 'text' and 'curses'. This config overrides the interface specified by ui.interface. +``large-file-limit`` + Largest file size that gives no memory use warning. + Possible values are integers or 0 to disable the check. + (default: 10000000) + ``logtemplate`` Template string for commands that print changesets. @@ -2325,7 +2380,7 @@ ``address`` Interface address to bind to. (default: all) -``allow_archive`` +``allow-archive`` List of archive format (bz2, gz, zip) allowed for downloading. (default: empty)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/help/deprecated.txt Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,30 @@ +Mercurial evolves over time, some features, options, commands may be replaced by +better and more secure alternatives. This topic will help you migrating your +existing usage and/or configuration to newer features. + +Commands +======== + +The following commands are still available but their use are not recommended: + +``locate`` + +This command has been replaced by `hg files`. + +``parents`` + +This command can be replaced by `hg summary` or `hg log` with appropriate +revsets. See `hg help revsets` for more information. + +``tip`` + +The recommended alternative is `hg heads`. + +Options +======= + +``web.allowpull`` + Renamed to `allow-pull`. + +``web.allow_push`` + Renamed to `allow-push`.
--- a/mercurial/hg.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/hg.py Thu Jul 19 13:55:54 2018 -0400 @@ -372,13 +372,9 @@ destlock = None try: hardlink = None + topic = _('linking') if hardlink else _('copying') + progress = ui.makeprogress(topic) num = 0 - closetopic = [None] - def prog(topic, pos): - if pos is None: - closetopic[0] = topic - else: - ui.progress(topic, pos + num) srcpublishing = srcrepo.publishing() srcvfs = vfsmod.vfs(srcrepo.sharedpath) dstvfs = vfsmod.vfs(destpath) @@ -395,16 +391,13 @@ # lock to avoid premature writing to the target destlock = lock.lock(dstvfs, lockfile) hardlink, n = util.copyfiles(srcvfs.join(f), dstvfs.join(f), - hardlink, progress=prog) + hardlink, progress) num += n if hardlink: ui.debug("linked %d files\n" % num) - if closetopic[0]: - ui.progress(closetopic[0], None) else: ui.debug("copied %d files\n" % num) - if closetopic[0]: - ui.progress(closetopic[0], None) + progress.complete() return destlock except: # re-raises release(destlock)
--- a/mercurial/hgweb/__init__.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/hgweb/__init__.py Thu Jul 19 13:55:54 2018 -0400 @@ -57,7 +57,9 @@ procutil.setsignalhandler() self.httpd = server.create_server(self.ui, self.app) - if self.opts['port'] and not self.ui.verbose: + if (self.opts['port'] and + not self.ui.verbose and + not self.opts['print_url']): return if self.httpd.prefix: @@ -78,13 +80,18 @@ fqaddr = self.httpd.fqaddr if r':' in fqaddr: fqaddr = r'[%s]' % fqaddr - if self.opts['port']: - write = self.ui.status + + url = 'http://%s%s/%s' % ( + pycompat.sysbytes(fqaddr), pycompat.sysbytes(port), prefix) + if self.opts['print_url']: + self.ui.write('%s\n' % url) else: - write = self.ui.write - write(_('listening at http://%s%s/%s (bound to %s:%d)\n') % - (pycompat.sysbytes(fqaddr), pycompat.sysbytes(port), - prefix, pycompat.sysbytes(bindaddr), self.httpd.port)) + if self.opts['port']: + write = self.ui.status + else: + write = self.ui.write + write(_('listening at %s (bound to %s:%d)\n') % + (url, pycompat.sysbytes(bindaddr), self.httpd.port)) self.ui.flush() # avoid buffering of status message def run(self):
--- a/mercurial/hgweb/hgweb_mod.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/hgweb/hgweb_mod.py Thu Jul 19 13:55:54 2018 -0400 @@ -441,6 +441,8 @@ res.headers['Content-Type'] = ctype return rctx.sendtemplate('error', error=pycompat.bytestr(e)) except ErrorResponse as e: + for k, v in e.headers: + res.headers[k] = v res.status = statusmessage(e.code, pycompat.bytestr(e)) res.headers['Content-Type'] = ctype return rctx.sendtemplate('error', error=pycompat.bytestr(e))
--- a/mercurial/hgweb/server.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/hgweb/server.py Thu Jul 19 13:55:54 2018 -0400 @@ -125,8 +125,9 @@ # 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._start_response(pycompat.strurl(common.statusmessage(404)), + []) + self._write(b"Not Found") self._done() return @@ -214,6 +215,7 @@ self.sent_headers = True def _start_response(self, http_status, headers, exc_info=None): + assert isinstance(http_status, str) code, msg = http_status.split(None, 1) code = int(code) self.saved_status = http_status @@ -244,7 +246,7 @@ def version_string(self): if self.server.serverheader: - return self.server.serverheader + return encoding.strfromlocal(self.server.serverheader) return httpservermod.basehttprequesthandler.version_string(self) class _httprequesthandlerssl(_httprequesthandler):
--- a/mercurial/hgweb/webcommands.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/hgweb/webcommands.py Thu Jul 19 13:55:54 2018 -0400 @@ -13,7 +13,7 @@ import re from ..i18n import _ -from ..node import hex, nullid, short +from ..node import hex, short from .common import ( ErrorResponse, @@ -149,7 +149,7 @@ mt = mimetypes.guess_type(f)[0] or 'application/octet-stream' text = '(binary:%s)' % mt - def lines(): + def lines(context): for lineno, t in enumerate(text.splitlines(True)): yield {"line": t, "lineid": "l%d" % (lineno + 1), @@ -160,7 +160,7 @@ 'filerevision', file=f, path=webutil.up(f), - text=lines(), + text=templateutil.mappinggenerator(lines), symrev=webutil.symrevorshortnode(web.req, fctx), rename=webutil.renamelink(fctx), permissions=fctx.manifest().flags(f), @@ -295,9 +295,8 @@ for ctx in searchfunc[0](funcarg): count += 1 n = ctx.node() - showtags = webutil.showtag(web.repo, web.tmpl, 'changelogtag', n) - files = webutil.listfilediffs(web.tmpl, ctx.files(), n, - web.maxfiles) + showtags = webutil.showtag(web.repo, 'changelogtag', n) + files = webutil.listfilediffs(ctx.files(), n, web.maxfiles) lm = webutil.commonentry(web.repo, ctx) lm.update({ @@ -399,14 +398,8 @@ revs = [] if pos != -1: revs = web.repo.changelog.revs(pos, 0) - curcount = 0 - for rev in revs: - curcount += 1 - if curcount > revcount + 1: - break - entry = webutil.changelistentry(web, web.repo[rev]) - entry['parity'] = next(parity) + for entry in webutil.changelistentries(web, revs, revcount, parity): yield entry if shortlog: @@ -448,9 +441,9 @@ rev=pos, symrev=symrev, changesets=count, - entries=entries, - latestentry=latestentry, - nextentry=nextentry, + entries=templateutil.mappinglist(entries), + latestentry=templateutil.mappinglist(latestentry), + nextentry=templateutil.mappinglist(nextentry), archives=web.archivelist('tip'), revcount=revcount, morevars=morevars, @@ -563,7 +556,7 @@ if mf and not files and not dirs: raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path) - def filelist(**map): + def filelist(context): for f in sorted(files): full = files[f] @@ -575,7 +568,7 @@ "size": fctx.size(), "permissions": mf.flags(full)} - def dirlist(**map): + def dirlist(context): for d in sorted(dirs): emptydirs = [] @@ -598,8 +591,8 @@ path=abspath, up=webutil.up(abspath), upparity=next(parity), - fentries=filelist, - dentries=dirlist, + fentries=templateutil.mappinggenerator(filelist), + dentries=templateutil.mappinggenerator(dirlist), archives=web.archivelist(hex(node)), **pycompat.strkwargs(webutil.commonentry(web.repo, ctx))) @@ -618,7 +611,7 @@ i = list(reversed(web.repo.tagslist())) parity = paritygen(web.stripecount) - def entries(notip, latestonly, **map): + def entries(context, notip, latestonly): t = i if notip: t = [(k, n) for k, n in i if k != "tip"] @@ -633,9 +626,10 @@ return web.sendtemplate( 'tags', node=hex(web.repo.changelog.tip()), - entries=lambda **x: entries(False, False, **x), - entriesnotip=lambda **x: entries(True, False, **x), - latestentry=lambda **x: entries(True, True, **x)) + entries=templateutil.mappinggenerator(entries, args=(False, False)), + entriesnotip=templateutil.mappinggenerator(entries, + args=(True, False)), + latestentry=templateutil.mappinggenerator(entries, args=(True, True))) @webcommand('bookmarks') def bookmarks(web): @@ -654,7 +648,7 @@ i = sorted(i, key=sortkey, reverse=True) parity = paritygen(web.stripecount) - def entries(latestonly, **map): + def entries(context, latestonly): t = i if latestonly: t = i[:1] @@ -668,13 +662,14 @@ latestrev = i[0][1] else: latestrev = -1 + lastdate = web.repo[latestrev].date() return web.sendtemplate( 'bookmarks', node=hex(web.repo.changelog.tip()), - lastchange=[{'date': web.repo[latestrev].date()}], - entries=lambda **x: entries(latestonly=False, **x), - latestentry=lambda **x: entries(latestonly=True, **x)) + lastchange=templateutil.mappinglist([{'date': lastdate}]), + entries=templateutil.mappinggenerator(entries, args=(False,)), + latestentry=templateutil.mappinggenerator(entries, args=(True,))) @webcommand('branches') def branches(web): @@ -732,7 +727,7 @@ 'date': web.repo[n].date(), } - def bookmarks(**map): + def bookmarks(context): parity = paritygen(web.stripecount) marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo] sortkey = lambda b: (web.repo[b[1]].rev(), b[0]) @@ -774,7 +769,7 @@ owner=get_contact(web.config) or 'unknown', lastchange=tip.date(), tags=templateutil.mappinggenerator(tagentries, name='tagentry'), - bookmarks=bookmarks, + bookmarks=templateutil.mappinggenerator(bookmarks), branches=webutil.branchentries(web.repo, web.stripecount, 10), shortlog=templateutil.mappinggenerator(changelist, name='shortlogentry'), @@ -819,7 +814,7 @@ rename = webutil.renamelink(fctx) ctx = fctx else: - rename = [] + rename = templateutil.mappinglist([]) ctx = ctx return web.sendtemplate( @@ -887,12 +882,12 @@ pfctx = ctx.parents()[0][path] leftlines = filelines(pfctx) - comparison = webutil.compare(web.tmpl, context, leftlines, rightlines) + comparison = webutil.compare(context, leftlines, rightlines) if fctx is not None: rename = webutil.renamelink(fctx) ctx = fctx else: - rename = [] + rename = templateutil.mappinglist([]) ctx = ctx return web.sendtemplate( @@ -934,7 +929,7 @@ # TODO there are still redundant operations within basefilectx.parents() # and from the fctx.annotate() call itself that could be cached. parentscache = {} - def parents(f): + def parents(context, f): rev = f.rev() if rev not in parentscache: parentscache[rev] = [] @@ -948,7 +943,7 @@ for p in parentscache[rev]: yield p - def annotate(**map): + def annotate(context): if fctx.isbinary(): mt = (mimetypes.guess_type(fctx.path())[0] or 'application/octet-stream') @@ -972,7 +967,7 @@ "node": f.hex(), "rev": rev, "author": f.user(), - "parents": parents(f), + "parents": templateutil.mappinggenerator(parents, args=(f,)), "desc": f.description(), "extra": f.extra(), "file": f.path(), @@ -991,13 +986,13 @@ return web.sendtemplate( 'fileannotate', file=f, - annotate=annotate, + annotate=templateutil.mappinggenerator(annotate), path=webutil.up(f), symrev=webutil.symrevorshortnode(web.req, fctx), rename=webutil.renamelink(fctx), permissions=fctx.manifest().flags(f), ishead=int(ishead), - diffopts=diffopts, + diffopts=templateutil.hybriddict(diffopts), **pycompat.strkwargs(webutil.commonentry(web.repo, fctx))) @webcommand('filelog') @@ -1095,13 +1090,16 @@ diffs = diff(c, linerange=lr) # follow renames accross filtered (not in range) revisions path = c.path() - entries.append(dict( - parity=next(parity), - filerev=c.rev(), - file=path, - diff=diffs, - linerange=webutil.formatlinerange(*lr), - **pycompat.strkwargs(webutil.commonentry(repo, c)))) + lm = webutil.commonentry(repo, c) + lm.update({ + 'parity': next(parity), + 'filerev': c.rev(), + 'file': path, + 'diff': diffs, + 'linerange': webutil.formatlinerange(*lr), + 'rename': templateutil.mappinglist([]), + }) + entries.append(lm) if i == revcount: break lessvars['linerange'] = webutil.formatlinerange(*lrange) @@ -1112,13 +1110,15 @@ diffs = None if patch: diffs = diff(iterfctx) - entries.append(dict( - parity=next(parity), - filerev=i, - file=f, - diff=diffs, - rename=webutil.renamelink(iterfctx), - **pycompat.strkwargs(webutil.commonentry(repo, iterfctx)))) + lm = webutil.commonentry(repo, iterfctx) + lm.update({ + 'parity': next(parity), + 'filerev': i, + 'file': f, + 'diff': diffs, + 'rename': webutil.renamelink(iterfctx), + }) + entries.append(lm) entries.reverse() revnav = webutil.filerevnav(web.repo, fctx.path()) nav = revnav.gen(end - 1, revcount, count) @@ -1130,10 +1130,10 @@ file=f, nav=nav, symrev=webutil.symrevorshortnode(web.req, fctx), - entries=entries, + entries=templateutil.mappinglist(entries), descend=descend, patch=patch, - latestentry=latestentry, + latestentry=templateutil.mappinglist(latestentry), linerange=linerange, revcount=revcount, morevars=morevars, @@ -1162,7 +1162,7 @@ """ type_ = web.req.qsparams.get('type') - allowed = web.configlist("web", "allow_archive") + allowed = web.configlist("web", "allow-archive") key = web.req.qsparams['node'] if type_ not in webutil.archivespecs: @@ -1314,24 +1314,6 @@ tree = list(item for item in graphmod.colored(dag, web.repo) if item[1] == graphmod.CHANGESET) - def nodecurrent(ctx): - wpnodes = web.repo.dirstate.parents() - if wpnodes[1] == nullid: - wpnodes = wpnodes[:1] - if ctx.node() in wpnodes: - return '@' - return '' - - def nodesymbol(ctx): - if ctx.obsolete(): - return 'x' - elif ctx.isunstable(): - return '*' - elif ctx.closesbranch(): - return '_' - else: - return 'o' - def fulltree(): pos = web.repo[graphtop].rev() tree = [] @@ -1342,14 +1324,14 @@ if item[1] == graphmod.CHANGESET) return tree - def jsdata(): - return [{'node': pycompat.bytestr(ctx), - 'graphnode': nodecurrent(ctx) + nodesymbol(ctx), - 'vertex': vtx, - 'edges': edges} - for (id, type, ctx, vtx, edges) in fulltree()] + def jsdata(context): + for (id, type, ctx, vtx, edges) in fulltree(): + yield {'node': pycompat.bytestr(ctx), + 'graphnode': webutil.getgraphnode(web.repo, ctx), + 'vertex': vtx, + 'edges': edges} - def nodes(): + def nodes(context): parity = paritygen(web.stripecount) for row, (id, type, ctx, vtx, edges) in enumerate(tree): entry = webutil.commonentry(web.repo, ctx) @@ -1363,7 +1345,7 @@ entry.update({'col': vtx[0], 'color': (vtx[1] - 1) % 6 + 1, 'parity': next(parity), - 'edges': edgedata, + 'edges': templateutil.mappinglist(edgedata), 'row': row, 'nextrow': row + 1}) @@ -1384,10 +1366,11 @@ rows=rows, bg_height=bg_height, changesets=count, - nextentry=nextentry, - jsdata=lambda **x: jsdata(), - nodes=lambda **x: nodes(), + nextentry=templateutil.mappinglist(nextentry), + jsdata=templateutil.mappinggenerator(jsdata), + nodes=templateutil.mappinggenerator(nodes), node=ctx.hex(), + archives=web.archivelist('tip'), changenav=changenav) def _getdoc(e): @@ -1417,7 +1400,7 @@ topicname = web.req.qsparams.get('node') if not topicname: - def topics(**map): + def topics(context): for entries, summary, _doc in helpmod.helptable: yield {'topic': entries[0], 'summary': summary} @@ -1436,19 +1419,19 @@ early.sort() other.sort() - def earlycommands(**map): + def earlycommands(context): for c, doc in early: yield {'topic': c, 'summary': doc} - def othercommands(**map): + def othercommands(context): for c, doc in other: yield {'topic': c, 'summary': doc} return web.sendtemplate( 'helptopics', - topics=topics, - earlycommands=earlycommands, - othercommands=othercommands, + topics=templateutil.mappinggenerator(topics), + earlycommands=templateutil.mappinggenerator(earlycommands), + othercommands=templateutil.mappinggenerator(othercommands), title='Index') # Render an index of sub-topics. @@ -1463,7 +1446,7 @@ return web.sendtemplate( 'helptopics', - topics=topics, + topics=templateutil.mappinglist(topics), title=topicname, subindex=True)
--- a/mercurial/hgweb/webutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/hgweb/webutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -25,6 +25,7 @@ from .. import ( context, + diffutil, error, match, mdiff, @@ -51,7 +52,7 @@ )) def archivelist(ui, nodeid, url=None): - allowed = ui.configlist('web', 'allow_archive', untrusted=True) + allowed = ui.configlist('web', 'allow-archive', untrusted=True) archives = [] for typ, spec in archivespecs.iteritems(): @@ -206,8 +207,8 @@ return templateutil.mappinggenerator(_ctxsgen, args=(siblings,)) def difffeatureopts(req, ui, section): - diffopts = patch.difffeatureopts(ui, untrusted=True, - section=section, whitespace=True) + diffopts = diffutil.difffeatureopts(ui, untrusted=True, + section=section, whitespace=True) for k in ('ignorews', 'ignorewsamount', 'ignorewseol', 'ignoreblanklines'): v = req.qsparams.get(k) @@ -234,14 +235,14 @@ def renamelink(fctx): r = fctx.renamed() if r: - return [{'file': r[0], 'node': hex(r[1])}] - return [] + return templateutil.mappinglist([{'file': r[0], 'node': hex(r[1])}]) + return templateutil.mappinglist([]) def nodetagsdict(repo, node): - return [{"name": i} for i in repo.nodetags(node)] + return templateutil.hybridlist(repo.nodetags(node), name='name') def nodebookmarksdict(repo, node): - return [{"name": i} for i in repo.nodebookmarks(node)] + return templateutil.hybridlist(repo.nodebookmarks(node), name='name') def nodebranchdict(repo, ctx): branches = [] @@ -253,8 +254,8 @@ except error.RepoLookupError: branchnode = None if branchnode == ctx.node(): - branches.append({"name": branch}) - return branches + branches.append(branch) + return templateutil.hybridlist(branches, name='name') def nodeinbranch(repo, ctx): branches = [] @@ -264,29 +265,27 @@ except error.RepoLookupError: branchnode = None if branch != 'default' and branchnode != ctx.node(): - branches.append({"name": branch}) - return branches + branches.append(branch) + return templateutil.hybridlist(branches, name='name') def nodebranchnodefault(ctx): branches = [] branch = ctx.branch() if branch != 'default': - branches.append({"name": branch}) - return branches + branches.append(branch) + return templateutil.hybridlist(branches, name='name') + +def _nodenamesgen(context, f, node, name): + for t in f(node): + yield {name: t} -def showtag(repo, tmpl, t1, node=nullid, **args): - args = pycompat.byteskwargs(args) - for t in repo.nodetags(node): - lm = args.copy() - lm['tag'] = t - yield tmpl.generate(t1, lm) +def showtag(repo, t1, node=nullid): + args = (repo.nodetags, node, 'tag') + return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1) -def showbookmark(repo, tmpl, t1, node=nullid, **args): - args = pycompat.byteskwargs(args) - for t in repo.nodebookmarks(node): - lm = args.copy() - lm['bookmark'] = t - yield tmpl.generate(t1, lm) +def showbookmark(repo, t1, node=nullid): + args = (repo.nodebookmarks, node, 'bookmark') + return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1) def branchentries(repo, stripecount, limit=0): tips = [] @@ -294,7 +293,7 @@ parity = paritygen(stripecount) sortkey = lambda item: (not item[1], item[0].rev()) - def entries(**map): + def entries(context): count = 0 if not tips: for tag, hs, tip, closed in repo.branchmap().iterbranches(): @@ -317,7 +316,7 @@ 'date': ctx.date() } - return entries + return templateutil.mappinggenerator(entries) def cleanpath(repo, path): path = path.lstrip('/') @@ -380,7 +379,7 @@ def formatlinerange(fromline, toline): return '%d:%d' % (fromline + 1, toline) -def succsandmarkers(context, mapping): +def _succsandmarkersgen(context, mapping): repo = context.resource(mapping, 'repo') itemmappings = templatekw.showsuccsandmarkers(context, mapping) for item in itemmappings.tovalue(context, mapping): @@ -388,10 +387,13 @@ for successor in item['successors']) yield item +def succsandmarkers(context, mapping): + return templateutil.mappinggenerator(_succsandmarkersgen, args=(mapping,)) + # teach templater succsandmarkers is switched to (context, mapping) API succsandmarkers._requires = {'repo', 'ctx'} -def whyunstable(context, mapping): +def _whyunstablegen(context, mapping): repo = context.resource(mapping, 'repo') ctx = context.resource(mapping, 'ctx') @@ -401,6 +403,9 @@ entry['divergentnodes'] = _siblings(entry['divergentnodes']) yield entry +def whyunstable(context, mapping): + return templateutil.mappinggenerator(_whyunstablegen, args=(mapping,)) + whyunstable._requires = {'repo', 'ctx'} def commonentry(repo, ctx): @@ -419,7 +424,8 @@ 'phase': ctx.phasestr(), 'obsolete': ctx.obsolete(), 'succsandmarkers': succsandmarkers, - 'instabilities': [{"instability": i} for i in ctx.instabilities()], + 'instabilities': templateutil.hybridlist(ctx.instabilities(), + name='instability'), 'whyunstable': whyunstable, 'branch': nodebranchnodefault(ctx), 'inbranch': nodeinbranch(repo, ctx), @@ -439,8 +445,8 @@ repo = web.repo rev = ctx.rev() n = ctx.node() - showtags = showtag(repo, web.tmpl, 'changelogtag', n) - files = listfilediffs(web.tmpl, ctx.files(), n, web.maxfiles) + showtags = showtag(repo, 'changelogtag', n) + files = listfilediffs(ctx.files(), n, web.maxfiles) entry = commonentry(repo, ctx) entry.update( @@ -452,30 +458,45 @@ ) return entry +def changelistentries(web, revs, maxcount, parityfn): + """Emit up to N records for an iterable of revisions.""" + repo = web.repo + + count = 0 + for rev in revs: + if count >= maxcount: + break + + count += 1 + + entry = changelistentry(web, repo[rev]) + entry['parity'] = next(parityfn) + + yield entry + def symrevorshortnode(req, ctx): if 'node' in req.qsparams: return templatefilters.revescape(req.qsparams['node']) else: return short(ctx.node()) -def changesetentry(web, ctx): - '''Obtain a dictionary to be used to render the "changeset" template.''' - - showtags = showtag(web.repo, web.tmpl, 'changesettag', ctx.node()) - showbookmarks = showbookmark(web.repo, web.tmpl, 'changesetbookmark', - ctx.node()) - showbranch = nodebranchnodefault(ctx) - - files = [] - parity = paritygen(web.stripecount) +def _listfilesgen(context, ctx, stripecount): + parity = paritygen(stripecount) for blockno, f in enumerate(ctx.files()): template = 'filenodelink' if f in ctx else 'filenolink' - files.append(web.tmpl.generate(template, { + yield context.process(template, { 'node': ctx.hex(), 'file': f, 'blockno': blockno + 1, 'parity': next(parity), - })) + }) + +def changesetentry(web, ctx): + '''Obtain a dictionary to be used to render the "changeset" template.''' + + showtags = showtag(web.repo, 'changesettag', ctx.node()) + showbookmarks = showbookmark(web.repo, 'changesetbookmark', ctx.node()) + showbranch = nodebranchnodefault(ctx) basectx = basechangectx(web.repo, web.req) if basectx is None: @@ -488,8 +509,8 @@ diff = diffs(web, ctx, basectx, None, style) parity = paritygen(web.stripecount) - diffstatsgen = diffstatgen(ctx, basectx) - diffstats = diffstat(web.tmpl, ctx, diffstatsgen, parity) + diffstatsgen = diffstatgen(web.repo.ui, ctx, basectx) + diffstats = diffstat(ctx, diffstatsgen, parity) return dict( diff=diff, @@ -498,40 +519,43 @@ changesettag=showtags, changesetbookmark=showbookmarks, changesetbranch=showbranch, - files=files, + files=templateutil.mappedgenerator(_listfilesgen, + args=(ctx, web.stripecount)), diffsummary=lambda **x: diffsummary(diffstatsgen), diffstat=diffstats, archives=web.archivelist(ctx.hex()), **pycompat.strkwargs(commonentry(web.repo, ctx))) -def listfilediffs(tmpl, files, node, max): +def _listfilediffsgen(context, files, node, max): for f in files[:max]: - yield tmpl.generate('filedifflink', {'node': hex(node), 'file': f}) + yield context.process('filedifflink', {'node': hex(node), 'file': f}) if len(files) > max: - yield tmpl.generate('fileellipses', {}) + yield context.process('fileellipses', {}) -def diffs(web, ctx, basectx, files, style, linerange=None, - lineidprefix=''): +def listfilediffs(files, node, max): + return templateutil.mappedgenerator(_listfilediffsgen, + args=(files, node, max)) - def prettyprintlines(lines, blockno): - for lineno, l in enumerate(lines, 1): - difflineno = "%d.%d" % (blockno, lineno) - if l.startswith('+'): - ltype = "difflineplus" - elif l.startswith('-'): - ltype = "difflineminus" - elif l.startswith('@'): - ltype = "difflineat" - else: - ltype = "diffline" - yield web.tmpl.generate(ltype, { - 'line': l, - 'lineno': lineno, - 'lineid': lineidprefix + "l%s" % difflineno, - 'linenumber': "% 8s" % difflineno, - }) +def _prettyprintdifflines(context, lines, blockno, lineidprefix): + for lineno, l in enumerate(lines, 1): + difflineno = "%d.%d" % (blockno, lineno) + if l.startswith('+'): + ltype = "difflineplus" + elif l.startswith('-'): + ltype = "difflineminus" + elif l.startswith('@'): + ltype = "difflineat" + else: + ltype = "diffline" + yield context.process(ltype, { + 'line': l, + 'lineno': lineno, + 'lineid': lineidprefix + "l%s" % difflineno, + 'linenumber': "% 8s" % difflineno, + }) - repo = web.repo +def _diffsgen(context, repo, ctx, basectx, files, style, stripecount, + linerange, lineidprefix): if files: m = match.exact(repo.root, repo.getcwd(), files) else: @@ -540,7 +564,7 @@ diffopts = patch.diffopts(repo.ui, untrusted=True) node1 = basectx.node() node2 = ctx.node() - parity = paritygen(web.stripecount) + parity = paritygen(stripecount) diffhunks = patch.diffhunks(repo, node1, node2, m, opts=diffopts) for blockno, (fctx1, fctx2, header, hunks) in enumerate(diffhunks, 1): @@ -554,70 +578,89 @@ continue lines.extend(hunklines) if lines: - yield web.tmpl.generate('diffblock', { + l = templateutil.mappedgenerator(_prettyprintdifflines, + args=(lines, blockno, + lineidprefix)) + yield { 'parity': next(parity), 'blockno': blockno, - 'lines': prettyprintlines(lines, blockno), - }) + 'lines': l, + } -def compare(tmpl, context, leftlines, rightlines): - '''Generator function that provides side-by-side comparison data.''' +def diffs(web, ctx, basectx, files, style, linerange=None, lineidprefix=''): + args = (web.repo, ctx, basectx, files, style, web.stripecount, + linerange, lineidprefix) + return templateutil.mappinggenerator(_diffsgen, args=args, name='diffblock') - def compline(type, leftlineno, leftline, rightlineno, rightline): - lineid = leftlineno and ("l%d" % leftlineno) or '' - lineid += rightlineno and ("r%d" % rightlineno) or '' - llno = '%d' % leftlineno if leftlineno else '' - rlno = '%d' % rightlineno if rightlineno else '' - return tmpl.generate('comparisonline', { - 'type': type, - 'lineid': lineid, - 'leftlineno': leftlineno, - 'leftlinenumber': "% 6s" % llno, - 'leftline': leftline or '', - 'rightlineno': rightlineno, - 'rightlinenumber': "% 6s" % rlno, - 'rightline': rightline or '', - }) +def _compline(type, leftlineno, leftline, rightlineno, rightline): + lineid = leftlineno and ("l%d" % leftlineno) or '' + lineid += rightlineno and ("r%d" % rightlineno) or '' + llno = '%d' % leftlineno if leftlineno else '' + rlno = '%d' % rightlineno if rightlineno else '' + return { + 'type': type, + 'lineid': lineid, + 'leftlineno': leftlineno, + 'leftlinenumber': "% 6s" % llno, + 'leftline': leftline or '', + 'rightlineno': rightlineno, + 'rightlinenumber': "% 6s" % rlno, + 'rightline': rightline or '', + } - def getblock(opcodes): - for type, llo, lhi, rlo, rhi in opcodes: - len1 = lhi - llo - len2 = rhi - rlo - count = min(len1, len2) - for i in xrange(count): - yield compline(type=type, - leftlineno=llo + i + 1, - leftline=leftlines[llo + i], - rightlineno=rlo + i + 1, - rightline=rightlines[rlo + i]) - if len1 > len2: - for i in xrange(llo + count, lhi): - yield compline(type=type, - leftlineno=i + 1, - leftline=leftlines[i], - rightlineno=None, - rightline=None) - elif len2 > len1: - for i in xrange(rlo + count, rhi): - yield compline(type=type, - leftlineno=None, - leftline=None, - rightlineno=i + 1, - rightline=rightlines[i]) +def _getcompblockgen(context, leftlines, rightlines, opcodes): + for type, llo, lhi, rlo, rhi in opcodes: + len1 = lhi - llo + len2 = rhi - rlo + count = min(len1, len2) + for i in xrange(count): + yield _compline(type=type, + leftlineno=llo + i + 1, + leftline=leftlines[llo + i], + rightlineno=rlo + i + 1, + rightline=rightlines[rlo + i]) + if len1 > len2: + for i in xrange(llo + count, lhi): + yield _compline(type=type, + leftlineno=i + 1, + leftline=leftlines[i], + rightlineno=None, + rightline=None) + elif len2 > len1: + for i in xrange(rlo + count, rhi): + yield _compline(type=type, + leftlineno=None, + leftline=None, + rightlineno=i + 1, + rightline=rightlines[i]) +def _getcompblock(leftlines, rightlines, opcodes): + args = (leftlines, rightlines, opcodes) + return templateutil.mappinggenerator(_getcompblockgen, args=args, + name='comparisonline') + +def _comparegen(context, contextnum, leftlines, rightlines): + '''Generator function that provides side-by-side comparison data.''' s = difflib.SequenceMatcher(None, leftlines, rightlines) - if context < 0: - yield tmpl.generate('comparisonblock', - {'lines': getblock(s.get_opcodes())}) + if contextnum < 0: + l = _getcompblock(leftlines, rightlines, s.get_opcodes()) + yield {'lines': l} else: - for oc in s.get_grouped_opcodes(n=context): - yield tmpl.generate('comparisonblock', {'lines': getblock(oc)}) + for oc in s.get_grouped_opcodes(n=contextnum): + l = _getcompblock(leftlines, rightlines, oc) + yield {'lines': l} -def diffstatgen(ctx, basectx): +def compare(contextnum, leftlines, rightlines): + args = (contextnum, leftlines, rightlines) + return templateutil.mappinggenerator(_comparegen, args=args, + name='comparisonblock') + +def diffstatgen(ui, ctx, basectx): '''Generator function that provides the diffstat data.''' + diffopts = patch.diffopts(ui, {'noprefix': False}) stats = patch.diffstatdata( - util.iterlines(ctx.diff(basectx, noprefix=False))) + util.iterlines(ctx.diff(basectx, opts=diffopts))) maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats) while True: yield stats, maxname, maxtotal, addtotal, removetotal, binary @@ -629,9 +672,7 @@ return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % ( len(stats), addtotal, removetotal) -def diffstat(tmpl, ctx, statgen, parity): - '''Return a diffstat template for each file in the diff.''' - +def _diffstattmplgen(context, ctx, statgen, parity): stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen) files = ctx.files() @@ -645,7 +686,7 @@ template = 'diffstatlink' if filename in files else 'diffstatnolink' total = adds + removes fileno += 1 - yield tmpl.generate(template, { + yield context.process(template, { 'node': ctx.hex(), 'file': filename, 'fileno': fileno, @@ -655,6 +696,11 @@ 'parity': next(parity), }) +def diffstat(ctx, statgen, parity): + '''Return a diffstat template for each file in the diff.''' + args = (ctx, statgen, parity) + return templateutil.mappedgenerator(_diffstattmplgen, args=args) + class sessionvars(templateutil.wrapped): def __init__(self, vars, start='?'): self._start = start @@ -669,6 +715,24 @@ def __copy__(self): return sessionvars(copy.copy(self._vars), self._start) + def contains(self, context, mapping, item): + item = templateutil.unwrapvalue(context, mapping, item) + return item in self._vars + + def getmember(self, context, mapping, key): + key = templateutil.unwrapvalue(context, mapping, key) + return self._vars.get(key) + + def getmin(self, context, mapping): + raise error.ParseError(_('not comparable')) + + def getmax(self, context, mapping): + raise error.ParseError(_('not comparable')) + + def filter(self, context, mapping, select): + # implement if necessary + raise error.ParseError(_('not filterable')) + def itermaps(self, context): separator = self._start for key, value in sorted(self._vars.iteritems()): @@ -685,6 +749,9 @@ def show(self, context, mapping): return self.join(context, '') + def tobool(self, context, mapping): + return bool(self._vars) + def tovalue(self, context, mapping): return self._vars @@ -701,7 +768,7 @@ for key, pattern in websubdefs: # grab the delimiter from the character after the "s" unesc = pattern[1:2] - delim = re.escape(unesc) + delim = stringutil.reescape(unesc) # identify portions of the pattern, taking care to avoid escaped # delimiters. the replace format and flags are optional, but @@ -733,3 +800,7 @@ repo.ui.warn(_("websub: invalid regexp for %s: %s\n") % (key, regexp)) return websubtable + +def getgraphnode(repo, ctx): + return (templatekw.getgraphnodecurrent(repo, ctx) + + templatekw.getgraphnodesymbol(ctx))
--- a/mercurial/hook.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/hook.py Thu Jul 19 13:55:54 2018 -0400 @@ -24,7 +24,7 @@ stringutil, ) -def _pythonhook(ui, repo, htype, hname, funcname, args, throw): +def pythonhook(ui, repo, htype, hname, funcname, args, throw): '''call python hook. hook is callable object, looked up as name in python module. if callable returns "true", hook fails, else passes. if hook raises exception, treated as @@ -120,8 +120,6 @@ return r, False def _exthook(ui, repo, htype, name, cmd, args, throw): - ui.note(_("running hook %s: %s\n") % (name, cmd)) - starttime = util.timer() env = {} @@ -138,9 +136,17 @@ if callable(v): v = v() if isinstance(v, (dict, list)): - v = stringutil.pprint(v, bprefix=False) + v = stringutil.pprint(v) env['HG_' + k.upper()] = v + if ui.configbool('hooks', 'tonative.%s' % name, False): + oldcmd = cmd + cmd = procutil.shelltonative(cmd, env) + if cmd != oldcmd: + ui.note(_('converting hook "%s" to native\n') % name) + + ui.note(_("running hook %s: %s\n") % (name, cmd)) + if repo: cwd = repo.root else: @@ -179,9 +185,11 @@ """return all hooks items ready to be sorted""" hooks = {} for name, cmd in ui.configitems('hooks', untrusted=_untrusted): - if not name.startswith('priority'): - priority = ui.configint('hooks', 'priority.%s' % name, 0) - hooks[name] = (-priority, len(hooks), name, cmd) + if name.startswith('priority.') or name.startswith('tonative.'): + continue + + priority = ui.configint('hooks', 'priority.%s' % name, 0) + hooks[name] = (-priority, len(hooks), name, cmd) return hooks _redirect = False @@ -242,7 +250,7 @@ r = 1 raised = False elif callable(cmd): - r, raised = _pythonhook(ui, repo, htype, hname, cmd, args, + r, raised = pythonhook(ui, repo, htype, hname, cmd, args, throw) elif cmd.startswith('python:'): if cmd.count(':') >= 2: @@ -258,7 +266,7 @@ hookfn = getattr(mod, cmd) else: hookfn = cmd[7:].strip() - r, raised = _pythonhook(ui, repo, htype, hname, hookfn, args, + r, raised = pythonhook(ui, repo, htype, hname, hookfn, args, throw) else: r = _exthook(ui, repo, htype, hname, cmd, args, throw)
--- a/mercurial/httpconnection.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/httpconnection.py Thu Jul 19 13:55:54 2018 -0400 @@ -38,21 +38,21 @@ self.write = self._data.write self.length = os.fstat(self._data.fileno()).st_size self._pos = 0 - self._total = self.length // 1024 * 2 - - def read(self, *args, **kwargs): - ret = self._data.read(*args, **kwargs) - if not ret: - self.ui.progress(_('sending'), None) - return ret - self._pos += len(ret) # We pass double the max for total because we currently have # to send the bundle twice in the case of a server that # requires authentication. Since we can't know until we try # once whether authentication will be required, just lie to # the user and maybe the push succeeds suddenly at 50%. - self.ui.progress(_('sending'), self._pos // 1024, - unit=_('kb'), total=self._total) + self._progress = ui.makeprogress(_('sending'), unit=_('kb'), + total=(self.length // 1024 * 2)) + + def read(self, *args, **kwargs): + ret = self._data.read(*args, **kwargs) + if not ret: + self._progress.complete() + return ret + self._pos += len(ret) + self._progress.update(self._pos // 1024) return ret def __enter__(self):
--- a/mercurial/httppeer.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/httppeer.py Thu Jul 19 13:55:54 2018 -0400 @@ -13,7 +13,6 @@ import os import socket import struct -import tempfile import weakref from .i18n import _ @@ -307,6 +306,7 @@ start = util.timer() + res = None try: res = opener.open(req) except urlerr.httperror as inst: @@ -320,8 +320,9 @@ raise IOError(None, inst) finally: if ui.debugflag and ui.configbool('devel', 'debug.peer-request'): + code = res.code if res else -1 dbg(line % ' finished in %.4f seconds (%d)' - % (util.timer() - start, res.code)) + % (util.timer() - start, code)) # Insert error handlers for common I/O failures. _wraphttpresponse(res) @@ -519,7 +520,7 @@ filename = None try: # dump bundle to disk - fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg") + fd, filename = pycompat.mkstemp(prefix="hg-bundle-", suffix=".hg") fh = os.fdopen(fd, r"wb") d = fp.read(4096) while d:
--- a/mercurial/i18n.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/i18n.py Thu Jul 19 13:55:54 2018 -0400 @@ -23,11 +23,6 @@ else: module = pycompat.fsencode(__file__) -try: - unicode -except NameError: - unicode = str - _languages = None if (pycompat.iswindows and 'LANGUAGE' not in encoding.environ @@ -76,7 +71,7 @@ cache = _msgcache.setdefault(encoding.encoding, {}) if message not in cache: - if type(message) is unicode: + if type(message) is pycompat.unicode: # goofy unicode docstrings in test paragraphs = message.split(u'\n\n') else:
--- a/mercurial/localrepo.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/localrepo.py Thu Jul 19 13:55:54 2018 -0400 @@ -354,6 +354,15 @@ # clients. REVLOGV2_REQUIREMENT = 'exp-revlogv2.0' +# A repository with the sparserevlog feature will have delta chains that +# can spread over a larger span. Sparse reading cuts these large spans into +# pieces, so that each piece isn't too big. +# Without the sparserevlog capability, reading from the repository could use +# huge amounts of memory, because the whole span would be read at once, +# including all the intermediate revisions that aren't pertinent for the chain. +# This is why once a repository has enabled sparse-read, it becomes required. +SPARSEREVLOG_REQUIREMENT = 'sparserevlog' + # Functions receiving (ui, features) that extensions can register to impact # the ability to load repositories with custom requirements. Only # functions defined in loaded extensions are called. @@ -376,6 +385,7 @@ 'generaldelta', 'treemanifest', REVLOGV2_REQUIREMENT, + SPARSEREVLOG_REQUIREMENT, } _basesupported = supportedformats | { 'store', @@ -658,10 +668,9 @@ manifestcachesize = self.ui.configint('format', 'manifestcachesize') if manifestcachesize is not None: self.svfs.options['manifestcachesize'] = manifestcachesize - # experimental config: format.aggressivemergedeltas - aggressivemergedeltas = self.ui.configbool('format', - 'aggressivemergedeltas') - self.svfs.options['aggressivemergedeltas'] = aggressivemergedeltas + deltabothparents = self.ui.configbool('revlog', + 'optimize-delta-parent-choice') + self.svfs.options['deltabothparents'] = deltabothparents self.svfs.options['lazydeltabase'] = not scmutil.gddeltaconfig(self.ui) chainspan = self.ui.configbytes('experimental', 'maxdeltachainspan') if 0 <= chainspan: @@ -678,6 +687,8 @@ self.svfs.options['with-sparse-read'] = withsparseread self.svfs.options['sparse-read-density-threshold'] = srdensitythres self.svfs.options['sparse-read-min-gap-size'] = srmingapsize + sparserevlog = SPARSEREVLOG_REQUIREMENT in self.requirements + self.svfs.options['sparse-revlog'] = sparserevlog for r in self.requirements: if r.startswith('exp-compression-'): @@ -778,6 +789,10 @@ @repofilecache('dirstate') def dirstate(self): + return self._makedirstate() + + def _makedirstate(self): + """Extension point for wrapping the dirstate per-repo.""" sparsematchfn = lambda: sparse.matcher(self) return dirstate.dirstate(self.vfs, self.ui, self.root, @@ -1029,11 +1044,7 @@ def nodebookmarks(self, node): """return the list of bookmarks pointing to the specified node""" - marks = [] - for bookmark, n in self._bookmarks.iteritems(): - if n == node: - marks.append(bookmark) - return sorted(marks) + return self._bookmarks.names(node) def branchmap(self): '''returns a dictionary {branch: [branchheads]} with branchheads @@ -2370,6 +2381,9 @@ requirements.add('generaldelta') if ui.configbool('experimental', 'treemanifest'): requirements.add('treemanifest') + # experimental config: format.sparse-revlog + if ui.configbool('format', 'sparse-revlog'): + requirements.add(SPARSEREVLOG_REQUIREMENT) revlogv2 = ui.config('experimental', 'revlogv2') if revlogv2 == 'enable-unstable-format-and-corrupt-my-data':
--- a/mercurial/lock.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/lock.py Thu Jul 19 13:55:54 2018 -0400 @@ -209,7 +209,7 @@ def __del__(self): if self.held: - warnings.warn("use lock.release instead of del lock", + warnings.warn(r"use lock.release instead of del lock", category=DeprecationWarning, stacklevel=2)
--- a/mercurial/logcmdutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/logcmdutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -76,9 +76,9 @@ if not ui.plain(): width = ui.termwidth() - chunks = patch.diff(repo, node1, node2, match, changes, opts=diffopts, - prefix=prefix, relroot=relroot, - hunksfilterfn=hunksfilterfn) + chunks = repo[node2].diff(repo[node1], match, changes, opts=diffopts, + prefix=prefix, relroot=relroot, + hunksfilterfn=hunksfilterfn) if fp is not None or ui.canwritewithoutlabels(): out = fp or ui @@ -154,7 +154,9 @@ self.repo = repo self.buffered = buffered self._differ = differ or changesetdiffer() - self.diffopts = diffopts or {} + self._diffopts = patch.diffallopts(ui, diffopts) + self._includestat = diffopts and diffopts.get('stat') + self._includediff = diffopts and diffopts.get('patch') self.header = {} self.hunk = {} self.lastheader = None @@ -226,7 +228,7 @@ if self.ui.debugflag and rev is not None: mnode = ctx.manifestnode() - mrev = self.repo.manifestlog._revlog.rev(mnode) + mrev = self.repo.manifestlog.rev(mnode) self.ui.write(columns['manifest'] % scmutil.formatrevnode(self.ui, mrev, mnode), label='ui.debug log.manifest') @@ -298,16 +300,13 @@ ''' def _showpatch(self, ctx): - stat = self.diffopts.get('stat') - diff = self.diffopts.get('patch') - diffopts = patch.diffallopts(self.ui, self.diffopts) - if stat: - self._differ.showdiff(self.ui, ctx, diffopts, stat=True) - if stat and diff: + if self._includestat: + self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True) + if self._includestat and self._includediff: self.ui.write("\n") - if diff: - self._differ.showdiff(self.ui, ctx, diffopts, stat=False) - if stat or diff: + if self._includediff: + self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False) + if self._includestat or self._includediff: self.ui.write("\n") class changesetformatter(changesetprinter): @@ -316,6 +315,7 @@ def __init__(self, ui, repo, fm, differ=None, diffopts=None, buffered=False): changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered) + self._diffopts = patch.difffeatureopts(ui, diffopts, git=True) self._fm = fm def close(self): @@ -367,16 +367,13 @@ fm.data(copies=fm.formatdict(copies, key='name', value='source')) - stat = self.diffopts.get('stat') - diff = self.diffopts.get('patch') - diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True) - if stat: + if self._includestat: self.ui.pushbuffer() - self._differ.showdiff(self.ui, ctx, diffopts, stat=True) + self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True) fm.data(diffstat=self.ui.popbuffer()) - if diff: + if self._includediff: self.ui.pushbuffer() - self._differ.showdiff(self.ui, ctx, diffopts, stat=False) + self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False) fm.data(diff=self.ui.popbuffer()) class changesettemplater(changesetprinter): @@ -868,7 +865,7 @@ for fn in ctx.files(): rename = getrenamed(fn, ctx.rev()) if rename: - copies.append((fn, rename[0])) + copies.append((fn, rename)) edges = edgefn(type, char, state, rev, parents) firstedge = next(edges) width = firstedge[2] @@ -896,7 +893,7 @@ for fn in ctx.files(): rename = getrenamed(fn, rev) if rename: - copies.append((fn, rename[0])) + copies.append((fn, rename)) displayer.show(ctx, copies=copies) displayer.flush(ctx) displayer.close()
--- a/mercurial/logexchange.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/logexchange.py Thu Jul 19 13:55:54 2018 -0400 @@ -112,8 +112,8 @@ # represent the remotepath with user defined path name if exists for path, url in repo.ui.configitems('paths'): # remove auth info from user defined url - url = util.removeauth(url) - if url == rpath: + noauthurl = util.removeauth(url) + if url == rpath or noauthurl == rpath: rpath = path break
--- a/mercurial/mail.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/mail.py Thu Jul 19 13:55:54 2018 -0400 @@ -11,6 +11,8 @@ import email.charset import email.header import email.message +import email.parser +import io import os import smtplib import socket @@ -322,6 +324,23 @@ s, cs = _encode(ui, s, charsets) return mimetextqp(s, 'plain', cs) +if pycompat.ispy3: + def parse(fp): + ep = email.parser.Parser() + # disable the "universal newlines" mode, which isn't binary safe. + # I have no idea if ascii/surrogateescape is correct, but that's + # what the standard Python email parser does. + fp = io.TextIOWrapper(fp, encoding=r'ascii', + errors=r'surrogateescape', newline=chr(10)) + try: + return ep.parse(fp) + finally: + fp.detach() +else: + def parse(fp): + ep = email.parser.Parser() + return ep.parse(fp) + def headdecode(s): '''Decodes RFC-2047 header''' uparts = []
--- a/mercurial/manifest.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/manifest.py Thu Jul 19 13:55:54 2018 -0400 @@ -20,9 +20,14 @@ error, mdiff, policy, + pycompat, + repository, revlog, util, ) +from .utils import ( + interfaceutil, +) parsers = policy.importmod(r'parsers') propertycache = util.propertycache @@ -362,6 +367,7 @@ except AttributeError: pass +@interfaceutil.implementer(repository.imanifestdict) class manifestdict(object): def __init__(self, data=''): self._lm = _lazymanifest(data) @@ -528,7 +534,8 @@ def items(self): return (x[:2] for x in self._lm.iterentries()) - iteritems = items + def iteritems(self): + return (x[:2] for x in self._lm.iterentries()) def iterentries(self): return self._lm.iterentries() @@ -635,7 +642,8 @@ for f in l: if '\n' in f or '\r' in f: raise error.RevlogError( - _("'\\n' and '\\r' disallowed in filenames: %r") % f) + _("'\\n' and '\\r' disallowed in filenames: %r") + % pycompat.bytestr(f)) # apply the changes collected during the bisect loop to our addlist @@ -1260,6 +1268,7 @@ m.setnode(n) return n +@interfaceutil.implementer(repository.imanifestlog) class manifestlog(object): """A collection class representing the collection of manifest snapshots referenced by commits in the repository. @@ -1285,7 +1294,7 @@ self._dirmancache = {} self._dirmancache[''] = util.lrucachedict(cachesize) - self.cachesize = cachesize + self._cachesize = cachesize def __getitem__(self, node): """Retrieves the manifest instance for the given node. Throws a @@ -1331,7 +1340,7 @@ if node != revlog.nullid: mancache = self._dirmancache.get(dir) if not mancache: - mancache = util.lrucachedict(self.cachesize) + mancache = util.lrucachedict(self._cachesize) self._dirmancache[dir] = mancache mancache[node] = m return m @@ -1340,6 +1349,13 @@ self._dirmancache.clear() self._revlog.clearcaches() + def rev(self, node): + return self._revlog.rev(node) + + def addgroup(self, deltas, linkmapper, transaction): + return self._revlog.addgroup(deltas, linkmapper, transaction) + +@interfaceutil.implementer(repository.imanifestrevisionwritable) class memmanifestctx(object): def __init__(self, manifestlog): self._manifestlog = manifestlog @@ -1363,6 +1379,7 @@ return self._revlog().add(self._manifestdict, transaction, link, p1, p2, added, removed) +@interfaceutil.implementer(repository.imanifestrevisionstored) class manifestctx(object): """A class representing a single revision of a manifest, including its contents, its parent revs, and its linkrev. @@ -1439,6 +1456,7 @@ def find(self, key): return self.read().find(key) +@interfaceutil.implementer(repository.imanifestrevisionwritable) class memtreemanifestctx(object): def __init__(self, manifestlog, dir=''): self._manifestlog = manifestlog @@ -1465,6 +1483,7 @@ return self._revlog().add(self._treemanifest, transaction, link, p1, p2, added, removed, readtree=readtree) +@interfaceutil.implementer(repository.imanifestrevisionstored) class treemanifestctx(object): def __init__(self, manifestlog, dir, node): self._manifestlog = manifestlog
--- a/mercurial/match.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/match.py Thu Jul 19 13:55:54 2018 -0400 @@ -40,9 +40,9 @@ except AttributeError: return m.match -def _expandsets(kindpats, ctx, listsubrepos): - '''Returns the kindpats list with the 'set' patterns expanded.''' - fset = set() +def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn): + '''Returns the kindpats list with the 'set' patterns expanded to matchers''' + matchers = [] other = [] for kind, pat, source in kindpats: @@ -50,17 +50,17 @@ if not ctx: raise error.ProgrammingError("fileset expression with no " "context") - s = ctx.getfileset(pat) - fset.update(s) + matchers.append(ctx.matchfileset(pat, badfn=badfn)) if listsubrepos: for subpath in ctx.substate: - s = ctx.sub(subpath).getfileset(pat) - fset.update(subpath + '/' + f for f in s) + sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn) + pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn) + matchers.append(pm) continue other.append((kind, pat, source)) - return fset, other + return matchers, other def _expandsubinclude(kindpats, root): '''Returns the list of subinclude matcher args and the kindpats without the @@ -95,6 +95,23 @@ return False return True +def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None, + listsubrepos=False, badfn=None): + matchers = [] + fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx, + listsubrepos=listsubrepos, badfn=badfn) + if kindpats: + m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos, + badfn=badfn) + matchers.append(m) + if fms: + matchers.extend(fms) + if not matchers: + return nevermatcher(root, cwd, badfn=badfn) + if len(matchers) == 1: + return matchers[0] + return unionmatcher(matchers) + def match(root, cwd, patterns=None, include=None, exclude=None, default='glob', exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None, badfn=None, icasefs=False): @@ -159,8 +176,9 @@ if _kindpatsalwaysmatch(kindpats): m = alwaysmatcher(root, cwd, badfn, relativeuipath=True) else: - m = patternmatcher(root, cwd, kindpats, ctx=ctx, - listsubrepos=listsubrepos, badfn=badfn) + m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats, + ctx=ctx, listsubrepos=listsubrepos, + badfn=badfn) else: # It's a little strange that no patterns means to match everything. # Consider changing this to match nothing (probably using nevermatcher). @@ -168,13 +186,13 @@ if include: kindpats = normalize(include, 'glob', root, cwd, auditor, warn) - im = includematcher(root, cwd, kindpats, ctx=ctx, - listsubrepos=listsubrepos, badfn=None) + im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx, + listsubrepos=listsubrepos, badfn=None) m = intersectmatchers(m, im) if exclude: kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn) - em = includematcher(root, cwd, kindpats, ctx=ctx, - listsubrepos=listsubrepos, badfn=None) + em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx, + listsubrepos=listsubrepos, badfn=None) m = differencematcher(m, em) return m @@ -375,15 +393,28 @@ def __repr__(self): return r'<nevermatcher>' +class predicatematcher(basematcher): + """A matcher adapter for a simple boolean function""" + + def __init__(self, root, cwd, predfn, predrepr=None, badfn=None): + super(predicatematcher, self).__init__(root, cwd, badfn) + self.matchfn = predfn + self._predrepr = predrepr + + @encoding.strmethod + def __repr__(self): + s = (stringutil.buildrepr(self._predrepr) + or pycompat.byterepr(self.matchfn)) + return '<predicatenmatcher pred=%s>' % s + class patternmatcher(basematcher): - def __init__(self, root, cwd, kindpats, ctx=None, listsubrepos=False, - badfn=None): + def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None): super(patternmatcher, self).__init__(root, cwd, badfn) self._files = _explicitfiles(kindpats) self._prefix = _prefix(kindpats) - self._pats, self.matchfn = _buildmatch(ctx, kindpats, '$', listsubrepos, + self._pats, self.matchfn = _buildmatch(kindpats, '$', listsubrepos, root) @propertycache @@ -404,15 +435,14 @@ @encoding.strmethod def __repr__(self): - return ('<patternmatcher patterns=%r>' % self._pats) + return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats)) class includematcher(basematcher): - def __init__(self, root, cwd, kindpats, ctx=None, listsubrepos=False, - badfn=None): + def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None): super(includematcher, self).__init__(root, cwd, badfn) - self._pats, self.matchfn = _buildmatch(ctx, kindpats, '(?:/|$)', + self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)', listsubrepos, root) self._prefix = _prefix(kindpats) roots, dirs = _rootsanddirs(kindpats) @@ -653,6 +683,78 @@ return ('<subdirmatcher path=%r, matcher=%r>' % (self._path, self._matcher)) +class prefixdirmatcher(basematcher): + """Adapt a matcher to work on a parent directory. + + The matcher's non-matching-attributes (root, cwd, bad, explicitdir, + traversedir) are ignored. + + The prefix path should usually be the relative path from the root of + this matcher to the root of the wrapped matcher. + + >>> m1 = match(b'root/d/e', b'f', [b'../a.txt', b'b.txt']) + >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1) + >>> bool(m2(b'a.txt'),) + False + >>> bool(m2(b'd/e/a.txt')) + True + >>> bool(m2(b'd/e/b.txt')) + False + >>> m2.files() + ['d/e/a.txt', 'd/e/f/b.txt'] + >>> m2.exact(b'd/e/a.txt') + True + >>> m2.visitdir(b'd') + True + >>> m2.visitdir(b'd/e') + True + >>> m2.visitdir(b'd/e/f') + True + >>> m2.visitdir(b'd/e/g') + False + >>> m2.visitdir(b'd/ef') + False + """ + + def __init__(self, root, cwd, path, matcher, badfn=None): + super(prefixdirmatcher, self).__init__(root, cwd, badfn) + if not path: + raise error.ProgrammingError('prefix path must not be empty') + self._path = path + self._pathprefix = path + '/' + self._matcher = matcher + + @propertycache + def _files(self): + return [self._pathprefix + f for f in self._matcher._files] + + def matchfn(self, f): + if not f.startswith(self._pathprefix): + return False + return self._matcher.matchfn(f[len(self._pathprefix):]) + + @propertycache + def _pathdirs(self): + return set(util.finddirs(self._path)) | {'.'} + + def visitdir(self, dir): + if dir == self._path: + return self._matcher.visitdir('.') + if dir.startswith(self._pathprefix): + return self._matcher.visitdir(dir[len(self._pathprefix):]) + return dir in self._pathdirs + + def isexact(self): + return self._matcher.isexact() + + def prefix(self): + return self._matcher.prefix() + + @encoding.strmethod + def __repr__(self): + return ('<prefixdirmatcher path=%r, matcher=%r>' + % (pycompat.bytestr(self._path), self._matcher)) + class unionmatcher(basematcher): """A matcher that is the union of several matchers. @@ -714,7 +816,7 @@ >>> bprint(_globre(br'**/a')) (?:.*/)?a >>> bprint(_globre(br'a/**/b')) - a\/(?:.*/)?b + a/(?:.*/)?b >>> bprint(_globre(br'[a*?!^][^b][!c]')) [a*?!^][\^b][^c] >>> bprint(_globre(br'{a,b}')) @@ -725,7 +827,7 @@ i, n = 0, len(pat) res = '' group = 0 - escape = util.re.escape + escape = util.stringutil.reescape def peek(): return i < n and pat[i:i + 1] while i < n: @@ -790,13 +892,13 @@ if kind in ('path', 'relpath'): if pat == '.': return '' - return util.re.escape(pat) + '(?:/|$)' + return util.stringutil.reescape(pat) + '(?:/|$)' if kind == 'rootfilesin': if pat == '.': escaped = '' else: # Pattern is a directory name. - escaped = util.re.escape(pat) + '/' + escaped = util.stringutil.reescape(pat) + '/' # Anything after the pattern must be a non-directory. return escaped + '[^/]+$' if kind == 'relglob': @@ -805,9 +907,11 @@ if pat.startswith('^'): return pat return '.*' + pat - return _globre(pat) + globsuffix + if kind == 'glob': + return _globre(pat) + globsuffix + raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat)) -def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root): +def _buildmatch(kindpats, globsuffix, listsubrepos, root): '''Return regexp string and a matcher function for kindpats. globsuffix is appended to the regexp of globs.''' matchfuncs = [] @@ -828,10 +932,6 @@ return False matchfuncs.append(matchsubinclude) - fset, kindpats = _expandsets(kindpats, ctx, listsubrepos) - if fset: - matchfuncs.append(fset.__contains__) - regex = '' if kindpats: regex, mf = _buildregexmatch(kindpats, globsuffix)
--- a/mercurial/merge.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/merge.py Thu Jul 19 13:55:54 2018 -0400 @@ -903,6 +903,23 @@ return actions def _checkcollision(repo, wmf, actions): + """ + Check for case-folding collisions. + """ + + # If the repo is narrowed, filter out files outside the narrowspec. + narrowmatch = repo.narrowmatch() + if not narrowmatch.always(): + wmf = wmf.matches(narrowmatch) + if actions: + narrowactions = {} + for m, actionsfortype in actions.iteritems(): + narrowactions[m] = [] + for (f, args, msg) in actionsfortype: + if narrowmatch(f): + narrowactions[m].append((f, args, msg)) + actions = narrowactions + # build provisional merged manifest up pmmf = set(wmf) @@ -1072,6 +1089,33 @@ repo.ui.warn(_("%s: is both a file and a directory\n") % p) raise error.Abort(_("destination manifest contains path conflicts")) +def _filternarrowactions(narrowmatch, branchmerge, actions): + """ + Filters out actions that can ignored because the repo is narrowed. + + Raise an exception if the merge cannot be completed because the repo is + narrowed. + """ + nooptypes = set(['k']) # TODO: handle with nonconflicttypes + nonconflicttypes = set('a am c cm f g r e'.split()) + # We mutate the items in the dict during iteration, so iterate + # over a copy. + for f, action in list(actions.items()): + if narrowmatch(f): + pass + elif not branchmerge: + del actions[f] # just updating, ignore changes outside clone + elif action[0] in nooptypes: + del actions[f] # merge does not affect file + elif action[0] in nonconflicttypes: + raise error.Abort(_('merge affects file \'%s\' outside narrow, ' + 'which is not yet supported') % f, + hint=_('merging in the other direction ' + 'may work')) + else: + raise error.Abort(_('conflict in file \'%s\' is outside ' + 'narrow clone') % f) + def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher, acceptremote, followcopies, forcefulldiff=False): """ @@ -1258,6 +1302,11 @@ # If we are merging, look for path conflicts. checkpathconflicts(repo, wctx, p2, actions) + narrowmatch = repo.narrowmatch() + if not narrowmatch.always(): + # Updates "actions" in place + _filternarrowactions(narrowmatch, branchmerge, actions) + return actions, diverge, renamedelete def _resolvetrivial(repo, wctx, mctx, ancestor, actions): @@ -1492,27 +1541,6 @@ return (not self.updatedcount and not self.mergedcount and not self.removedcount and not self.unresolvedcount) - # TODO remove container emulation once consumers switch to new API. - - def __getitem__(self, x): - util.nouideprecwarn('access merge.update() results by name instead of ' - 'index', '4.6', 2) - if x == 0: - return self.updatedcount - elif x == 1: - return self.mergedcount - elif x == 2: - return self.removedcount - elif x == 3: - return self.unresolvedcount - else: - raise IndexError('can only access items 0-3') - - def __len__(self): - util.nouideprecwarn('access merge.update() results by name instead of ' - 'index', '4.6', 2) - return 4 - def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None): """apply the merge action list to the working directory @@ -1558,10 +1586,6 @@ if f1 != f and move: moves.append(f1) - _updating = _('updating') - _files = _('files') - progress = repo.ui.progress - # remove renamed files after safely stored for f in moves: if wctx[f].lexists(): @@ -1571,7 +1595,8 @@ numupdates = sum(len(l) for m, l in actions.items() if m != ACTION_KEEP) - z = 0 + progress = repo.ui.makeprogress(_('updating'), unit=_('files'), + total=numupdates) if [a for a in actions[ACTION_REMOVE] if a[0] == '.hgsubstate']: subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels) @@ -1588,8 +1613,7 @@ s(_("the remote file has been renamed to %s\n") % f1) s(_("resolve manually then use 'hg resolve --mark %s'\n") % f) ms.addpath(f, f1, fo) - z += 1 - progress(_updating, z, item=f, total=numupdates, unit=_files) + progress.increment(item=f) # When merging in-memory, we can't support worker processes, so set the # per-item cost at 0 in that case. @@ -1599,8 +1623,7 @@ prog = worker.worker(repo.ui, cost, batchremove, (repo, wctx), actions[ACTION_REMOVE]) for i, item in prog: - z += i - progress(_updating, z, item=item, total=numupdates, unit=_files) + progress.increment(step=i, item=item) removed = len(actions[ACTION_REMOVE]) # resolve path conflicts (must come before getting) @@ -1612,15 +1635,16 @@ wctx[f].audit() wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags()) wctx[f0].remove() - z += 1 - progress(_updating, z, item=f, total=numupdates, unit=_files) + progress.increment(item=f) - # get in parallel + # get in parallel. + threadsafe = repo.ui.configbool('experimental', + 'worker.wdir-get-thread-safe') prog = worker.worker(repo.ui, cost, batchget, (repo, mctx, wctx), - actions[ACTION_GET]) + actions[ACTION_GET], + threadsafe=threadsafe) for i, item in prog: - z += i - progress(_updating, z, item=item, total=numupdates, unit=_files) + progress.increment(step=i, item=item) updated = len(actions[ACTION_GET]) if [a for a in actions[ACTION_GET] if a[0] == '.hgsubstate']: @@ -1629,20 +1653,17 @@ # forget (manifest only, just log it) (must come first) for f, args, msg in actions[ACTION_FORGET]: repo.ui.debug(" %s: %s -> f\n" % (f, msg)) - z += 1 - progress(_updating, z, item=f, total=numupdates, unit=_files) + progress.increment(item=f) # re-add (manifest only, just log it) for f, args, msg in actions[ACTION_ADD]: repo.ui.debug(" %s: %s -> a\n" % (f, msg)) - z += 1 - progress(_updating, z, item=f, total=numupdates, unit=_files) + progress.increment(item=f) # re-add/mark as modified (manifest only, just log it) for f, args, msg in actions[ACTION_ADD_MODIFIED]: repo.ui.debug(" %s: %s -> am\n" % (f, msg)) - z += 1 - progress(_updating, z, item=f, total=numupdates, unit=_files) + progress.increment(item=f) # keep (noop, just log it) for f, args, msg in actions[ACTION_KEEP]: @@ -1652,8 +1673,7 @@ # directory rename, move local for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]: repo.ui.debug(" %s: %s -> dm\n" % (f, msg)) - z += 1 - progress(_updating, z, item=f, total=numupdates, unit=_files) + progress.increment(item=f) f0, flags = args repo.ui.note(_("moving %s to %s\n") % (f0, f)) wctx[f].audit() @@ -1664,8 +1684,7 @@ # local directory rename, get for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]: repo.ui.debug(" %s: %s -> dg\n" % (f, msg)) - z += 1 - progress(_updating, z, item=f, total=numupdates, unit=_files) + progress.increment(item=f) f0, flags = args repo.ui.note(_("getting %s to %s\n") % (f0, f)) wctx[f].write(mctx.filectx(f0).data(), flags) @@ -1674,8 +1693,7 @@ # exec for f, args, msg in actions[ACTION_EXEC]: repo.ui.debug(" %s: %s -> e\n" % (f, msg)) - z += 1 - progress(_updating, z, item=f, total=numupdates, unit=_files) + progress.increment(item=f) flags, = args wctx[f].audit() wctx[f].setflags('l' in flags, 'x' in flags) @@ -1710,8 +1728,7 @@ tocomplete = [] for f, args, msg in mergeactions: repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg)) - z += 1 - progress(_updating, z, item=f, total=numupdates, unit=_files) + progress.increment(item=f) if f == '.hgsubstate': # subrepo states need updating subrepoutil.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite, labels) @@ -1725,8 +1742,7 @@ # merge for f, args, msg in tocomplete: repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg)) - z += 1 - progress(_updating, z, item=f, total=numupdates, unit=_files) + progress.increment(item=f, total=numupdates) ms.resolve(f, wctx) finally: @@ -1774,7 +1790,7 @@ actions[ACTION_MERGE] = [a for a in actions[ACTION_MERGE] if a[0] in mfiles] - progress(_updating, None, total=numupdates, unit=_files) + progress.complete() return updateresult(updated, merged, removed, unresolved) def recordupdates(repo, actions, branchmerge): @@ -2181,7 +2197,8 @@ error=stats.unresolvedcount) return stats -def graft(repo, ctx, pctx, labels, keepparent=False): +def graft(repo, ctx, pctx, labels, keepparent=False, + keepconflictparent=False): """Do a graft-like merge. This is a merge where the merge ancestor is chosen such that one @@ -2194,6 +2211,7 @@ pctx - merge base, usually ctx.p1() labels - merge labels eg ['local', 'graft'] keepparent - keep second parent if any + keepparent - if unresolved, keep parent used for the merge """ # If we're grafting a descendant onto an ancestor, be sure to pass @@ -2207,11 +2225,15 @@ stats = update(repo, ctx.node(), True, True, pctx.node(), mergeancestor=mergeancestor, labels=labels) - pother = nullid - parents = ctx.parents() - if keepparent and len(parents) == 2 and pctx in parents: - parents.remove(pctx) - pother = parents[0].node() + + if keepconflictparent and stats.unresolvedcount: + pother = ctx.node() + else: + pother = nullid + parents = ctx.parents() + if keepparent and len(parents) == 2 and pctx in parents: + parents.remove(pctx) + pother = parents[0].node() with repo.dirstate.parentchange(): repo.setparents(repo['.'].node(), pother)
--- a/mercurial/minifileset.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/minifileset.py Thu Jul 19 13:55:54 2018 -0400 @@ -11,8 +11,14 @@ from . import ( error, fileset, + pycompat, ) +def _sizep(x): + # i18n: "size" is a keyword + expr = fileset.getstring(x, _("size requires an expression")) + return fileset.sizematcher(expr) + def _compile(tree): if not tree: raise error.ParseError(_("missing argument")) @@ -21,14 +27,15 @@ name = fileset.getpattern(tree, {'path'}, _('invalid file pattern')) if name.startswith('**'): # file extension test, ex. "**.tar.gz" ext = name[2:] - for c in ext: + for c in pycompat.bytestr(ext): if c in '*{}[]?/\\': raise error.ParseError(_('reserved character: %s') % c) return lambda n, s: n.endswith(ext) elif name.startswith('path:'): # directory or full path test p = name[5:] # prefix pl = len(p) - f = lambda n, s: n.startswith(p) and (len(n) == pl or n[pl] == '/') + f = lambda n, s: n.startswith(p) and (len(n) == pl + or n[pl:pl + 1] == '/') return f raise error.ParseError(_("unsupported file pattern: %s") % name, hint=_('paths must be prefixed with "path:"')) @@ -48,7 +55,7 @@ symbols = { 'all': lambda n, s: True, 'none': lambda n, s: False, - 'size': lambda n, s: fileset.sizematcher(tree[2])(s), + 'size': lambda n, s: _sizep(tree[2])(s), } name = fileset.getsymbol(tree[1])
--- a/mercurial/minirst.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/minirst.py Thu Jul 19 13:55:54 2018 -0400 @@ -169,7 +169,7 @@ if not itemre.match(line1): return False if singleline: - return line2 == '' or line2[0] == ' ' or itemre.match(line2) + return line2 == '' or line2[0:1] == ' ' or itemre.match(line2) else: return line2.startswith(' ')
--- a/mercurial/namespaces.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/namespaces.py Thu Jul 19 13:55:54 2018 -0400 @@ -95,21 +95,16 @@ def singlenode(self, repo, name): """ - Return the 'best' node for the given name. Best means the first node - in the first nonempty list returned by a name-to-nodes mapping function - in the defined precedence order. + Return the 'best' node for the given name. What's best is defined + by the namespace's singlenode() function. The first match returned by + a namespace in the defined precedence order is used. Raises a KeyError if there is no such node. """ for ns, v in self._names.iteritems(): - n = v.namemap(repo, name) + n = v.singlenode(repo, name) if n: - # return max revision number - if len(n) > 1: - cl = repo.changelog - maxrev = max(cl.rev(node) for node in n) - return cl.node(maxrev) - return n[0] + return n raise KeyError(_('no such name: %s') % name) class namespace(object): @@ -142,7 +137,7 @@ def __init__(self, name, templatename=None, logname=None, colorname=None, logfmt=None, listnames=None, namemap=None, nodemap=None, - deprecated=None, builtin=False): + deprecated=None, builtin=False, singlenode=None): """create a namespace name: the namespace to be registered (in plural form) @@ -158,6 +153,7 @@ nodemap: function that inputs a node, output name(s) deprecated: set of names to be masked for ordinary use builtin: whether namespace is implemented by core Mercurial + singlenode: function that inputs a name, output best node (or None) """ self.name = name self.templatename = templatename @@ -167,6 +163,8 @@ self.listnames = listnames self.namemap = namemap self.nodemap = nodemap + if singlenode: + self.singlenode = singlenode # if logname is not specified, use the template name as backup if self.logname is None: @@ -199,3 +197,18 @@ """ return sorted(self.namemap(repo, name)) + + def singlenode(self, repo, name): + """returns the best node for the given name + + By default, the best node is the node from nodes() with the highest + revision number. It can be overriden by the namespace.""" + n = self.namemap(repo, name) + if n: + # return max revision number + if len(n) > 1: + cl = repo.changelog + maxrev = max(cl.rev(node) for node in n) + return cl.node(maxrev) + return n[0] + return None
--- a/mercurial/obsolete.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/obsolete.py Thu Jul 19 13:55:54 2018 -0400 @@ -74,11 +74,13 @@ from .i18n import _ from . import ( + encoding, error, node, obsutil, phases, policy, + pycompat, util, ) from .utils import dateutil @@ -526,7 +528,7 @@ # prec: nodeid, predecessors changesets # succs: tuple of nodeid, successor changesets (0-N length) # flag: integer, flag field carrying modifier for the markers (see doc) - # meta: binary blob, encoded metadata dictionary + # meta: binary blob in UTF-8, encoded metadata dictionary # date: (float, int) tuple, date of marker creation # parents: (tuple of nodeid) or None, parents of predecessors # None is used when no data has been recorded @@ -599,6 +601,16 @@ raise ValueError(_('in-marker cycle with %s') % node.hex(prec)) metadata = tuple(sorted(metadata.iteritems())) + for k, v in metadata: + try: + # might be better to reject non-ASCII keys + k.decode('utf-8') + v.decode('utf-8') + except UnicodeDecodeError: + raise error.ProgrammingError( + 'obsstore metadata must be valid UTF-8 sequence ' + '(key = %r, value = %r)' + % (pycompat.bytestr(k), pycompat.bytestr(v))) marker = (bytes(prec), tuple(succs), int(flag), metadata, date, parents) return bool(self.add(transaction, [marker])) @@ -853,7 +865,7 @@ def _mutablerevs(repo): """the set of mutable revision in the repository""" - return repo._phasecache.getrevset(repo, (phases.draft, phases.secret)) + return repo._phasecache.getrevset(repo, phases.mutablephases) @cachefor('obsolete') def _computeobsoleteset(repo): @@ -950,7 +962,8 @@ <relations> must be an iterable of (<old>, (<new>, ...)[,{metadata}]) tuple. `old` and `news` are changectx. metadata is an optional dictionary containing metadata for this marker only. It is merged with the global - metadata specified through the `metadata` argument of this function, + metadata specified through the `metadata` argument of this function. + Any string values in metadata must be UTF-8 bytes. Trying to obsolete a public changeset will raise an exception. @@ -964,11 +977,8 @@ if metadata is None: metadata = {} if 'user' not in metadata: - develuser = repo.ui.config('devel', 'user.obsmarker') - if develuser: - metadata['user'] = develuser - else: - metadata['user'] = repo.ui.username() + luser = repo.ui.config('devel', 'user.obsmarker') or repo.ui.username() + metadata['user'] = encoding.fromlocal(luser) # Operation metadata handling useoperation = repo.ui.configbool('experimental',
--- a/mercurial/obsutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/obsutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -11,11 +11,15 @@ from .i18n import _ from . import ( + diffutil, + encoding, node as nodemod, phases, util, ) -from .utils import dateutil +from .utils import ( + dateutil, +) ### obsolescence marker flag @@ -392,13 +396,13 @@ This is a first and basic implementation, with many shortcoming. """ - + diffopts = diffutil.diffallopts(leftctx.repo().ui, {'git': True}) # Leftctx or right ctx might be filtered, so we need to use the contexts # with an unfiltered repository to safely compute the diff leftunfi = leftctx._repo.unfiltered()[leftctx.rev()] - leftdiff = leftunfi.diff(git=1) + leftdiff = leftunfi.diff(opts=diffopts) rightunfi = rightctx._repo.unfiltered()[rightctx.rev()] - rightdiff = rightunfi.diff(git=1) + rightdiff = rightunfi.diff(opts=diffopts) left, right = (0, 0) while None not in (left, right): @@ -819,7 +823,8 @@ """ Returns a sorted list of markers users without duplicates """ markersmeta = [dict(m[3]) for m in markers] - users = set(meta.get('user') for meta in markersmeta if meta.get('user')) + users = set(encoding.tolocal(meta['user']) for meta in markersmeta + if meta.get('user')) return sorted(users)
--- a/mercurial/patch.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/patch.py Thu Jul 19 13:55:54 2018 -0400 @@ -18,7 +18,6 @@ import posixpath import re import shutil -import tempfile import zlib from .i18n import _ @@ -29,6 +28,7 @@ from . import ( copies, diffhelper, + diffutil, encoding, error, mail, @@ -51,7 +51,7 @@ gitre = re.compile(br'diff --git a/(.*) b/(.*)') tabsplitter = re.compile(br'(\t+|[^\t]+)') wordsplitter = re.compile(br'(\t+| +|[a-zA-Z0-9_\x80-\xff]+|' - '[^ \ta-zA-Z0-9_\x80-\xff])') + b'[^ \ta-zA-Z0-9_\x80-\xff])') PatchError = error.PatchError @@ -113,7 +113,7 @@ cur.append(line) c = chunk(cur) - m = pycompat.emailparser().parse(c) + m = mail.parse(c) if not m.is_multipart(): yield msgfp(m) else: @@ -211,7 +211,7 @@ Any item can be missing from the dictionary. If filename is missing, fileobj did not contain a patch. Caller must unlink filename when done.''' - fd, tmpname = tempfile.mkstemp(prefix='hg-patch-') + fd, tmpname = pycompat.mkstemp(prefix='hg-patch-') tmpfp = os.fdopen(fd, r'wb') try: yield _extract(ui, fileobj, tmpname, tmpfp) @@ -231,7 +231,7 @@ data = {} - msg = pycompat.emailparser().parse(fileobj) + msg = mail.parse(fileobj) subject = msg[r'Subject'] and mail.headdecode(msg[r'Subject']) data['user'] = msg[r'From'] and mail.headdecode(msg[r'From']) @@ -498,7 +498,8 @@ self.opener.setflags(fname, False, True) def unlink(self, fname): - self.opener.unlinkpath(fname, ignoremissing=True) + rmdir = self.ui.configbool('experimental', 'removeemptydirs') + self.opener.unlinkpath(fname, ignoremissing=True, rmdir=rmdir) def writerej(self, fname, failed, total, lines): fname = fname + ".rej" @@ -573,7 +574,7 @@ self.size += len(data) else: if self.opener is None: - root = tempfile.mkdtemp(prefix='hg-patch-') + root = pycompat.mkdtemp(prefix='hg-patch-') self.opener = vfsmod.vfs(root) # Avoid filename issues with these simple names fn = '%d' % self.created @@ -708,7 +709,7 @@ if self.eolmode != 'strict' and eol and eol != '\n': rawlines = [] for l in lines: - if l and l[-1] == '\n': + if l and l.endswith('\n'): l = l[:-1] + eol rawlines.append(l) lines = rawlines @@ -1109,7 +1110,7 @@ all lines of the hunk are removed, then the edit is aborted and the hunk is left unchanged. """) - (patchfd, patchfn) = tempfile.mkstemp(prefix="hg-editor-", + (patchfd, patchfn) = pycompat.mkstemp(prefix="hg-editor-", suffix=".diff") ncpatchfp = None try: @@ -1946,7 +1947,7 @@ """ def deltahead(binchunk): i = 0 - for c in binchunk: + for c in pycompat.bytestr(binchunk): i += 1 if not (ord(c) & 0x80): return i @@ -1958,31 +1959,31 @@ binchunk = binchunk[s:] i = 0 while i < len(binchunk): - cmd = ord(binchunk[i]) + cmd = ord(binchunk[i:i + 1]) i += 1 if (cmd & 0x80): offset = 0 size = 0 if (cmd & 0x01): - offset = ord(binchunk[i]) + offset = ord(binchunk[i:i + 1]) i += 1 if (cmd & 0x02): - offset |= ord(binchunk[i]) << 8 + offset |= ord(binchunk[i:i + 1]) << 8 i += 1 if (cmd & 0x04): - offset |= ord(binchunk[i]) << 16 + offset |= ord(binchunk[i:i + 1]) << 16 i += 1 if (cmd & 0x08): - offset |= ord(binchunk[i]) << 24 + offset |= ord(binchunk[i:i + 1]) << 24 i += 1 if (cmd & 0x10): - size = ord(binchunk[i]) + size = ord(binchunk[i:i + 1]) i += 1 if (cmd & 0x20): - size |= ord(binchunk[i]) << 8 + size |= ord(binchunk[i:i + 1]) << 8 i += 1 if (cmd & 0x40): - size |= ord(binchunk[i]) << 16 + size |= ord(binchunk[i:i + 1]) << 16 i += 1 if size == 0: size = 0x10000 @@ -2113,6 +2114,7 @@ args.append('-d %s' % procutil.shellquote(cwd)) cmd = ('%s %s -p%d < %s' % (patcher, ' '.join(args), strip, procutil.shellquote(patchname))) + ui.debug('Using external patch tool: %s\n' % cmd) fp = procutil.popen(cmd, 'rb') try: for line in util.iterfile(fp): @@ -2231,95 +2233,9 @@ class GitDiffRequired(Exception): pass -def diffallopts(ui, opts=None, untrusted=False, section='diff'): - '''return diffopts with all features supported and parsed''' - return difffeatureopts(ui, opts=opts, untrusted=untrusted, section=section, - git=True, whitespace=True, formatchanging=True) - -diffopts = diffallopts - -def difffeatureopts(ui, opts=None, untrusted=False, section='diff', git=False, - whitespace=False, formatchanging=False): - '''return diffopts with only opted-in features parsed - - Features: - - git: git-style diffs - - whitespace: whitespace options like ignoreblanklines and ignorews - - formatchanging: options that will likely break or cause correctness issues - with most diff parsers - ''' - def get(key, name=None, getter=ui.configbool, forceplain=None): - if opts: - v = opts.get(key) - # diffopts flags are either None-default (which is passed - # through unchanged, so we can identify unset values), or - # some other falsey default (eg --unified, which defaults - # to an empty string). We only want to override the config - # entries from hgrc with command line values if they - # appear to have been set, which is any truthy value, - # True, or False. - if v or isinstance(v, bool): - return v - if forceplain is not None and ui.plain(): - return forceplain - return getter(section, name or key, untrusted=untrusted) - - # core options, expected to be understood by every diff parser - buildopts = { - 'nodates': get('nodates'), - 'showfunc': get('show_function', 'showfunc'), - 'context': get('unified', getter=ui.config), - } - buildopts['worddiff'] = ui.configbool('experimental', 'worddiff') - buildopts['xdiff'] = ui.configbool('experimental', 'xdiff') - - if git: - buildopts['git'] = get('git') - - # since this is in the experimental section, we need to call - # ui.configbool directory - buildopts['showsimilarity'] = ui.configbool('experimental', - 'extendedheader.similarity') - - # need to inspect the ui object instead of using get() since we want to - # test for an int - hconf = ui.config('experimental', 'extendedheader.index') - if hconf is not None: - hlen = None - try: - # the hash config could be an integer (for length of hash) or a - # word (e.g. short, full, none) - hlen = int(hconf) - if hlen < 0 or hlen > 40: - msg = _("invalid length for extendedheader.index: '%d'\n") - ui.warn(msg % hlen) - except ValueError: - # default value - if hconf == 'short' or hconf == '': - hlen = 12 - elif hconf == 'full': - hlen = 40 - elif hconf != 'none': - msg = _("invalid value for extendedheader.index: '%s'\n") - ui.warn(msg % hconf) - finally: - buildopts['index'] = hlen - - if whitespace: - buildopts['ignorews'] = get('ignore_all_space', 'ignorews') - buildopts['ignorewsamount'] = get('ignore_space_change', - 'ignorewsamount') - buildopts['ignoreblanklines'] = get('ignore_blank_lines', - 'ignoreblanklines') - buildopts['ignorewseol'] = get('ignore_space_at_eol', 'ignorewseol') - if formatchanging: - buildopts['text'] = opts and opts.get('text') - binary = None if opts is None else opts.get('binary') - buildopts['nobinary'] = (not binary if binary is not None - else get('nobinary', forceplain=False)) - buildopts['noprefix'] = get('noprefix', forceplain=False) - - return mdiff.diffopts(**pycompat.strkwargs(buildopts)) +diffopts = diffutil.diffallopts +diffallopts = diffutil.diffallopts +difffeatureopts = diffutil.difffeatureopts def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None, losedatafn=None, prefix='', relroot='', copy=None, @@ -2489,17 +2405,17 @@ """yield tokens for a list of lines in a single hunk""" for line in hunklines: # chomp - chompline = line.rstrip('\n') + chompline = line.rstrip('\r\n') # highlight tabs and trailing whitespace stripline = chompline.rstrip() - if line[0] == '-': + if line.startswith('-'): label = 'diff.deleted' - elif line[0] == '+': + elif line.startswith('+'): label = 'diff.inserted' else: raise error.ProgrammingError('unexpected hunk line: %s' % line) for token in tabsplitter.findall(stripline): - if '\t' == token[0]: + if token.startswith('\t'): yield (token, 'diff.tab') else: yield (token, label) @@ -2557,6 +2473,9 @@ isendofline = token.endswith('\n') if isendofline: chomp = token[:-1] # chomp + if chomp.endswith('\r'): + chomp = chomp[:-1] + endofline = token[len(chomp):] token = chomp.rstrip() # detect spaces at the end endspaces = chomp[len(token):] # scan tabs @@ -2572,7 +2491,7 @@ if isendofline: if endspaces: yield (endspaces, 'diff.trailingwhitespace') - yield ('\n', '') + yield (endofline, '') nextisnewline = True def difflabel(func, *args, **kw):
--- a/mercurial/pathutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/pathutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -226,7 +226,7 @@ if cwd != root: canonpath(root, root, myname, auditor) relpath = util.pathto(root, cwd, '') - if relpath[-1] == pycompat.ossep: + if relpath.endswith(pycompat.ossep): relpath = relpath[:-1] hint = (_("consider using '--cwd %s'") % relpath) except error.Abort:
--- a/mercurial/phases.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/phases.py Thu Jul 19 13:55:54 2018 -0400 @@ -126,6 +126,8 @@ allphases = public, draft, secret = range(3) trackedphases = allphases[1:] phasenames = ['public', 'draft', 'secret'] +mutablephases = tuple(allphases[1:]) +remotehiddenphases = tuple(allphases[2:]) def _readroots(repo, phasedefaults=None): """Read phase roots from disk @@ -352,10 +354,14 @@ _trackphasechange(phasetracking, rev, None, revphase) repo.invalidatevolatilesets() - def advanceboundary(self, repo, tr, targetphase, nodes): + def advanceboundary(self, repo, tr, targetphase, nodes, dryrun=None): """Set all 'nodes' to phase 'targetphase' Nodes with a phase lower than 'targetphase' are not affected. + + If dryrun is True, no actions will be performed + + Returns a set of revs whose phase is changed or should be changed """ # Be careful to preserve shallow-copied values: do not update # phaseroots values, replace them. @@ -366,6 +372,7 @@ repo = repo.unfiltered() + changes = set() # set of revisions to be changed delroots = [] # set of root deleted by this path for phase in xrange(targetphase + 1, len(allphases)): # filter nodes that are not in a compatible phase already @@ -377,6 +384,9 @@ olds = self.phaseroots[phase] affected = repo.revs('%ln::%ln', olds, nodes) + changes.update(affected) + if dryrun: + continue for r in affected: _trackphasechange(phasetracking, r, self.phase(repo, r), targetphase) @@ -387,10 +397,12 @@ self._updateroots(phase, roots, tr) # some roots may need to be declared for lower phases delroots.extend(olds - roots) - # declare deleted root in the target phase - if targetphase != 0: - self._retractboundary(repo, tr, targetphase, delroots) - repo.invalidatevolatilesets() + if not dryrun: + # declare deleted root in the target phase + if targetphase != 0: + self._retractboundary(repo, tr, targetphase, delroots) + repo.invalidatevolatilesets() + return changes def retractboundary(self, repo, tr, targetphase, nodes): oldroots = self.phaseroots[:targetphase + 1] @@ -478,16 +490,24 @@ # (see branchmap one) self.invalidate() -def advanceboundary(repo, tr, targetphase, nodes): +def advanceboundary(repo, tr, targetphase, nodes, dryrun=None): """Add nodes to a phase changing other nodes phases if necessary. This function move boundary *forward* this means that all nodes are set in the target phase or kept in a *lower* phase. - Simplify boundary to contains phase roots only.""" + Simplify boundary to contains phase roots only. + + If dryrun is True, no actions will be performed + + Returns a set of revs whose phase is changed or should be changed + """ phcache = repo._phasecache.copy() - phcache.advanceboundary(repo, tr, targetphase, nodes) - repo._phasecache.replace(phcache) + changes = phcache.advanceboundary(repo, tr, targetphase, nodes, + dryrun=dryrun) + if not dryrun: + repo._phasecache.replace(phcache) + return changes def retractboundary(repo, tr, targetphase, nodes): """Set nodes back to a phase changing other nodes phases if @@ -645,10 +665,8 @@ * `heads`: define the first subset * `roots`: define the second we subtract from the first""" repo = repo.unfiltered() - revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))', - heads, roots, roots, heads) - return [c.node() for c in revset] - + revs = repo.revs('heads(::%ln - (%ln::%ln))', heads, roots, heads) + return pycompat.maplist(repo.changelog.node, revs) def newcommitphase(ui): """helper to get the target phase of new commit
--- a/mercurial/policy.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/policy.py Thu Jul 19 13:55:54 2018 -0400 @@ -69,7 +69,7 @@ (r'cext', r'bdiff'): 3, (r'cext', r'mpatch'): 1, (r'cext', r'osutil'): 4, - (r'cext', r'parsers'): 4, + (r'cext', r'parsers'): 5, } # map import request to other package or module
--- a/mercurial/posix.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/posix.py Thu Jul 19 13:55:54 2018 -0400 @@ -216,7 +216,7 @@ # check directly in path and don't leave checkisexec behind checkdir = path checkisexec = None - fh, fn = tempfile.mkstemp(dir=checkdir, prefix='hg-checkexec-') + fh, fn = pycompat.mkstemp(dir=checkdir, prefix='hg-checkexec-') try: os.close(fh) m = os.stat(fn).st_mode @@ -249,16 +249,15 @@ else: checkdir = path cachedir = None - fscheckdir = pycompat.fsdecode(checkdir) - name = tempfile.mktemp(dir=fscheckdir, + name = tempfile.mktemp(dir=pycompat.fsdecode(checkdir), prefix=r'checklink-') name = pycompat.fsencode(name) try: fd = None if cachedir is None: - fd = tempfile.NamedTemporaryFile(dir=fscheckdir, - prefix=r'hg-checklink-') - target = pycompat.fsencode(os.path.basename(fd.name)) + fd = pycompat.namedtempfile(dir=checkdir, + prefix='hg-checklink-') + target = os.path.basename(fd.name) else: # create a fixed file to link to; doesn't matter if it # already exists. @@ -287,7 +286,7 @@ return True except OSError as inst: # link creation might race, try again - if inst[0] == errno.EEXIST: + if inst.errno == errno.EEXIST: continue raise finally: @@ -297,7 +296,7 @@ return False except OSError as inst: # sshfs might report failure while successfully creating the link - if inst[0] == errno.EIO and os.path.exists(name): + if inst.errno == errno.EIO and os.path.exists(name): unlink(name) return False @@ -542,9 +541,9 @@ if uid is None: uid = os.getuid() try: - return pwd.getpwuid(uid)[0] + return pycompat.fsencode(pwd.getpwuid(uid)[0]) except KeyError: - return str(uid) + return b'%d' % uid def groupname(gid=None): """Return the name of the group with the given gid.
--- a/mercurial/profiling.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/profiling.py Thu Jul 19 13:55:54 2018 -0400 @@ -101,7 +101,8 @@ else: ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq) - statprof.start(mechanism='thread') + track = ui.config('profiling', 'time-track') + statprof.start(mechanism='thread', track=track) try: yield
--- a/mercurial/progress.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/progress.py Thu Jul 19 13:55:54 2018 -0400 @@ -264,39 +264,40 @@ self.starttimes[topic] = now - interval def progress(self, topic, pos, item='', unit='', total=None): + if pos is None: + self.closetopic(topic) + return now = time.time() - self._refreshlock.acquire() - try: - if pos is None: - self.starttimes.pop(topic, None) - self.startvals.pop(topic, None) - self.topicstates.pop(topic, None) - # reset the progress bar if this is the outermost topic - if self.topics and self.topics[0] == topic and self.printed: - self.complete() - self.resetstate() - # truncate the list of topics assuming all topics within - # this one are also closed - if topic in self.topics: - self.topics = self.topics[:self.topics.index(topic)] - # reset the last topic to the one we just unwound to, - # so that higher-level topics will be stickier than - # lower-level topics - if self.topics: - self.lasttopic = self.topics[-1] - else: - self.lasttopic = None - else: - if topic not in self.topics: - self.starttimes[topic] = now - self.startvals[topic] = pos - self.topics.append(topic) - self.topicstates[topic] = pos, item, unit, total - self.curtopic = topic - self._calibrateestimate(topic, now, pos) - if now - self.lastprint >= self.refresh and self.topics: - if self._oktoprint(now): - self.lastprint = now - self.show(now, topic, *self.topicstates[topic]) - finally: - self._refreshlock.release() + with self._refreshlock: + if topic not in self.topics: + self.starttimes[topic] = now + self.startvals[topic] = pos + self.topics.append(topic) + self.topicstates[topic] = pos, item, unit, total + self.curtopic = topic + self._calibrateestimate(topic, now, pos) + if now - self.lastprint >= self.refresh and self.topics: + if self._oktoprint(now): + self.lastprint = now + self.show(now, topic, *self.topicstates[topic]) + + def closetopic(self, topic): + with self._refreshlock: + self.starttimes.pop(topic, None) + self.startvals.pop(topic, None) + self.topicstates.pop(topic, None) + # reset the progress bar if this is the outermost topic + if self.topics and self.topics[0] == topic and self.printed: + self.complete() + self.resetstate() + # truncate the list of topics assuming all topics within + # this one are also closed + if topic in self.topics: + self.topics = self.topics[:self.topics.index(topic)] + # reset the last topic to the one we just unwound to, + # so that higher-level topics will be stickier than + # lower-level topics + if self.topics: + self.lasttopic = self.topics[-1] + else: + self.lasttopic = None
--- a/mercurial/pycompat.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/pycompat.py Thu Jul 19 13:55:54 2018 -0400 @@ -15,6 +15,7 @@ import os import shlex import sys +import tempfile ispy3 = (sys.version_info[0] >= 3) ispypy = (r'__pypy__' in sys.builtin_module_names) @@ -23,7 +24,7 @@ import cookielib import cPickle as pickle import httplib - import Queue as _queue + import Queue as queue import SocketServer as socketserver import xmlrpclib @@ -36,19 +37,49 @@ import http.cookiejar as cookielib import http.client as httplib import pickle - import queue as _queue + import queue as queue import socketserver import xmlrpc.client as xmlrpclib def future_set_exception_info(f, exc_info): f.set_exception(exc_info[0]) -empty = _queue.Empty -queue = _queue.Queue - def identity(a): return a +def _rapply(f, xs): + if xs is None: + # assume None means non-value of optional data + return xs + if isinstance(xs, (list, set, tuple)): + return type(xs)(_rapply(f, x) for x in xs) + if isinstance(xs, dict): + return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items()) + return f(xs) + +def rapply(f, xs): + """Apply function recursively to every item preserving the data structure + + >>> def f(x): + ... return 'f(%s)' % x + >>> rapply(f, None) is None + True + >>> rapply(f, 'a') + 'f(a)' + >>> rapply(f, {'a'}) == {'f(a)'} + True + >>> rapply(f, ['a', 'b', None, {'c': 'd'}, []]) + ['f(a)', 'f(b)', None, {'f(c)': 'f(d)'}, []] + + >>> xs = [object()] + >>> rapply(identity, xs) is xs + True + """ + if f is identity: + # fast path mainly for py2 + return xs + return _rapply(f, xs) + if ispy3: import builtins import functools @@ -297,13 +328,10 @@ ret = shlex.split(s.decode('latin-1'), comments, posix) return [a.encode('latin-1') for a in ret] - def emailparser(*args, **kwargs): - import email.parser - return email.parser.BytesParser(*args, **kwargs) - else: import cStringIO + unicode = unicode bytechr = chr byterepr = repr bytestr = str @@ -372,10 +400,6 @@ rawinput = raw_input getargspec = inspect.getargspec - def emailparser(*args, **kwargs): - import email.parser - return email.parser.Parser(*args, **kwargs) - isjython = sysplatform.startswith('java') isdarwin = sysplatform == 'darwin' @@ -387,3 +411,18 @@ def gnugetoptb(args, shortlist, namelist): return _getoptbwrapper(getopt.gnu_getopt, args, shortlist, namelist) + +def mkdtemp(suffix=b'', prefix=b'tmp', dir=None): + return tempfile.mkdtemp(suffix, prefix, dir) + +# text=True is not supported; use util.from/tonativeeol() instead +def mkstemp(suffix=b'', prefix=b'tmp', dir=None): + return tempfile.mkstemp(suffix, prefix, dir) + +# mode must include 'b'ytes as encoding= is not supported +def namedtempfile(mode=b'w+b', bufsize=-1, suffix=b'', prefix=b'tmp', dir=None, + delete=True): + mode = sysstr(mode) + assert r'b' in mode + return tempfile.NamedTemporaryFile(mode, bufsize, suffix=suffix, + prefix=prefix, dir=dir, delete=delete)
--- a/mercurial/registrar.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/registrar.py Thu Jul 19 13:55:54 2018 -0400 @@ -247,10 +247,6 @@ implies 'matchctx.status()' at runtime or not (False, by default). - Optional argument 'callexisting' indicates whether a predicate - implies 'matchctx.existing()' at runtime or not (False, by - default). - 'filesetpredicate' instance in example above can be used to decorate multiple functions. @@ -263,9 +259,8 @@ _getname = _funcregistrarbase._parsefuncdecl _docformat = "``%s``\n %s" - def _extrasetup(self, name, func, callstatus=False, callexisting=False): + def _extrasetup(self, name, func, callstatus=False): func._callstatus = callstatus - func._callexisting = callexisting class _templateregistrarbase(_funcregistrarbase): """Base of decorator to register functions as template specific one @@ -351,7 +346,8 @@ templatefunc = registrar.templatefunc() - @templatefunc('myfunc(arg1, arg2[, arg3])', argspec='arg1 arg2 arg3') + @templatefunc('myfunc(arg1, arg2[, arg3])', argspec='arg1 arg2 arg3', + requires={'ctx'}) def myfuncfunc(context, mapping, args): '''Explanation of this template function .... ''' @@ -363,6 +359,9 @@ a dict of named arguments. Otherwise 'args' is a list of positional arguments. + Optional argument 'requires' should be a collection of resource names + which the template function depends on. + 'templatefunc' instance in example above can be used to decorate multiple functions. @@ -374,8 +373,9 @@ """ _getname = _funcregistrarbase._parsefuncdecl - def _extrasetup(self, name, func, argspec=None): + def _extrasetup(self, name, func, argspec=None, requires=()): func._argspec = argspec + func._requires = requires class internalmerge(_funcregistrarbase): """Decorator to register in-process merge tool
--- a/mercurial/repair.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/repair.py Thu Jul 19 13:55:54 2018 -0400 @@ -186,76 +186,77 @@ tmpbundlefile = backupbundle(repo, savebases, saveheads, node, 'temp', compress=False, obsolescence=False) - try: - with repo.transaction("strip") as tr: - offset = len(tr.entries) + with ui.uninterruptable(): + try: + with repo.transaction("strip") as tr: + offset = len(tr.entries) - tr.startgroup() - cl.strip(striprev, tr) - stripmanifest(repo, striprev, tr, files) - - for fn in files: - repo.file(fn).strip(striprev, tr) - tr.endgroup() + tr.startgroup() + cl.strip(striprev, tr) + stripmanifest(repo, striprev, tr, files) - for i in xrange(offset, len(tr.entries)): - file, troffset, ignore = tr.entries[i] - with repo.svfs(file, 'a', checkambig=True) as fp: - fp.truncate(troffset) - if troffset == 0: - repo.store.markremoved(file) + for fn in files: + repo.file(fn).strip(striprev, tr) + tr.endgroup() - deleteobsmarkers(repo.obsstore, stripobsidx) - del repo.obsstore - repo.invalidatevolatilesets() - repo._phasecache.filterunknown(repo) + for i in xrange(offset, len(tr.entries)): + file, troffset, ignore = tr.entries[i] + with repo.svfs(file, 'a', checkambig=True) as fp: + fp.truncate(troffset) + if troffset == 0: + repo.store.markremoved(file) + + deleteobsmarkers(repo.obsstore, stripobsidx) + del repo.obsstore + repo.invalidatevolatilesets() + repo._phasecache.filterunknown(repo) - if tmpbundlefile: - ui.note(_("adding branch\n")) - f = vfs.open(tmpbundlefile, "rb") - gen = exchange.readbundle(ui, f, tmpbundlefile, vfs) - if not repo.ui.verbose: - # silence internal shuffling chatter - repo.ui.pushbuffer() - tmpbundleurl = 'bundle:' + vfs.join(tmpbundlefile) - txnname = 'strip' - if not isinstance(gen, bundle2.unbundle20): - txnname = "strip\n%s" % util.hidepassword(tmpbundleurl) - with repo.transaction(txnname) as tr: - bundle2.applybundle(repo, gen, tr, source='strip', - url=tmpbundleurl) - if not repo.ui.verbose: - repo.ui.popbuffer() - f.close() + if tmpbundlefile: + ui.note(_("adding branch\n")) + f = vfs.open(tmpbundlefile, "rb") + gen = exchange.readbundle(ui, f, tmpbundlefile, vfs) + if not repo.ui.verbose: + # silence internal shuffling chatter + repo.ui.pushbuffer() + tmpbundleurl = 'bundle:' + vfs.join(tmpbundlefile) + txnname = 'strip' + if not isinstance(gen, bundle2.unbundle20): + txnname = "strip\n%s" % util.hidepassword(tmpbundleurl) + with repo.transaction(txnname) as tr: + bundle2.applybundle(repo, gen, tr, source='strip', + url=tmpbundleurl) + if not repo.ui.verbose: + repo.ui.popbuffer() + f.close() - with repo.transaction('repair') as tr: - bmchanges = [(m, repo[newbmtarget].node()) for m in updatebm] - bm.applychanges(repo, tr, bmchanges) + with repo.transaction('repair') as tr: + bmchanges = [(m, repo[newbmtarget].node()) for m in updatebm] + bm.applychanges(repo, tr, bmchanges) - # remove undo files - for undovfs, undofile in repo.undofiles(): - try: - undovfs.unlink(undofile) - except OSError as e: - if e.errno != errno.ENOENT: - ui.warn(_('error removing %s: %s\n') % - (undovfs.join(undofile), - stringutil.forcebytestr(e))) + # remove undo files + for undovfs, undofile in repo.undofiles(): + try: + undovfs.unlink(undofile) + except OSError as e: + if e.errno != errno.ENOENT: + ui.warn(_('error removing %s: %s\n') % + (undovfs.join(undofile), + stringutil.forcebytestr(e))) - except: # re-raises - if backupfile: - ui.warn(_("strip failed, backup bundle stored in '%s'\n") - % vfs.join(backupfile)) - if tmpbundlefile: - ui.warn(_("strip failed, unrecovered changes stored in '%s'\n") - % vfs.join(tmpbundlefile)) - ui.warn(_("(fix the problem, then recover the changesets with " - "\"hg unbundle '%s'\")\n") % vfs.join(tmpbundlefile)) - raise - else: - if tmpbundlefile: - # Remove temporary bundle only if there were no exceptions - vfs.unlink(tmpbundlefile) + except: # re-raises + if backupfile: + ui.warn(_("strip failed, backup bundle stored in '%s'\n") + % vfs.join(backupfile)) + if tmpbundlefile: + ui.warn(_("strip failed, unrecovered changes stored in '%s'\n") + % vfs.join(tmpbundlefile)) + ui.warn(_("(fix the problem, then recover the changesets with " + "\"hg unbundle '%s'\")\n") % vfs.join(tmpbundlefile)) + raise + else: + if tmpbundlefile: + # Remove temporary bundle only if there were no exceptions + vfs.unlink(tmpbundlefile) repo.destroyed() # return the backup file path (or None if 'backup' was False) so @@ -355,10 +356,10 @@ newentries = set() seenfiles = set() - repolen = len(repo) + progress = ui.makeprogress(_('rebuilding'), unit=_('changesets'), + total=len(repo)) for rev in repo: - ui.progress(_('rebuilding'), rev, total=repolen, - unit=_('changesets')) + progress.update(rev) ctx = repo[rev] for f in ctx.files(): @@ -375,7 +376,7 @@ if repo.store._exists(d): newentries.add(d) - ui.progress(_('rebuilding'), None) + progress.complete() if 'treemanifest' in repo.requirements: # safe but unnecessary otherwise for dir in util.dirs(seenfiles): @@ -405,18 +406,6 @@ else: ui.write(_('fncache already up to date\n')) -def stripbmrevset(repo, mark): - """ - The revset to strip when strip is called with -B mark - - Needs to live here so extensions can use it and wrap it even when strip is - not enabled or not present on a box. - """ - return repo.revs("ancestors(bookmark(%s)) - " - "ancestors(head() and not bookmark(%s)) - " - "ancestors(bookmark() and not bookmark(%s))", - mark, mark, mark) - def deleteobsmarkers(obsstore, indices): """Delete some obsmarkers from obsstore and return how many were deleted
--- a/mercurial/repository.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/repository.py Thu Jul 19 13:55:54 2018 -0400 @@ -642,6 +642,308 @@ TODO this is used by verify and it should not be part of the interface. """ +class idirs(interfaceutil.Interface): + """Interface representing a collection of directories from paths. + + This interface is essentially a derived data structure representing + directories from a collection of paths. + """ + + def addpath(path): + """Add a path to the collection. + + All directories in the path will be added to the collection. + """ + + def delpath(path): + """Remove a path from the collection. + + If the removal was the last path in a particular directory, the + directory is removed from the collection. + """ + + def __iter__(): + """Iterate over the directories in this collection of paths.""" + + def __contains__(path): + """Whether a specific directory is in this collection.""" + +class imanifestdict(interfaceutil.Interface): + """Interface representing a manifest data structure. + + A manifest is effectively a dict mapping paths to entries. Each entry + consists of a binary node and extra flags affecting that entry. + """ + + def __getitem__(path): + """Returns the binary node value for a path in the manifest. + + Raises ``KeyError`` if the path does not exist in the manifest. + + Equivalent to ``self.find(path)[0]``. + """ + + def find(path): + """Returns the entry for a path in the manifest. + + Returns a 2-tuple of (node, flags). + + Raises ``KeyError`` if the path does not exist in the manifest. + """ + + def __len__(): + """Return the number of entries in the manifest.""" + + def __nonzero__(): + """Returns True if the manifest has entries, False otherwise.""" + + __bool__ = __nonzero__ + + def __setitem__(path, node): + """Define the node value for a path in the manifest. + + If the path is already in the manifest, its flags will be copied to + the new entry. + """ + + def __contains__(path): + """Whether a path exists in the manifest.""" + + def __delitem__(path): + """Remove a path from the manifest. + + Raises ``KeyError`` if the path is not in the manifest. + """ + + def __iter__(): + """Iterate over paths in the manifest.""" + + def iterkeys(): + """Iterate over paths in the manifest.""" + + def keys(): + """Obtain a list of paths in the manifest.""" + + def filesnotin(other, match=None): + """Obtain the set of paths in this manifest but not in another. + + ``match`` is an optional matcher function to be applied to both + manifests. + + Returns a set of paths. + """ + + def dirs(): + """Returns an object implementing the ``idirs`` interface.""" + + def hasdir(dir): + """Returns a bool indicating if a directory is in this manifest.""" + + def matches(match): + """Generate a new manifest filtered through a matcher. + + Returns an object conforming to the ``imanifestdict`` interface. + """ + + def walk(match): + """Generator of paths in manifest satisfying a matcher. + + This is equivalent to ``self.matches(match).iterkeys()`` except a new + manifest object is not created. + + If the matcher has explicit files listed and they don't exist in + the manifest, ``match.bad()`` is called for each missing file. + """ + + def diff(other, match=None, clean=False): + """Find differences between this manifest and another. + + This manifest is compared to ``other``. + + If ``match`` is provided, the two manifests are filtered against this + matcher and only entries satisfying the matcher are compared. + + If ``clean`` is True, unchanged files are included in the returned + object. + + Returns a dict with paths as keys and values of 2-tuples of 2-tuples of + the form ``((node1, flag1), (node2, flag2))`` where ``(node1, flag1)`` + represents the node and flags for this manifest and ``(node2, flag2)`` + are the same for the other manifest. + """ + + def setflag(path, flag): + """Set the flag value for a given path. + + Raises ``KeyError`` if the path is not already in the manifest. + """ + + def get(path, default=None): + """Obtain the node value for a path or a default value if missing.""" + + def flags(path, default=''): + """Return the flags value for a path or a default value if missing.""" + + def copy(): + """Return a copy of this manifest.""" + + def items(): + """Returns an iterable of (path, node) for items in this manifest.""" + + def iteritems(): + """Identical to items().""" + + def iterentries(): + """Returns an iterable of (path, node, flags) for this manifest. + + Similar to ``iteritems()`` except items are a 3-tuple and include + flags. + """ + + def text(): + """Obtain the raw data representation for this manifest. + + Result is used to create a manifest revision. + """ + + def fastdelta(base, changes): + """Obtain a delta between this manifest and another given changes. + + ``base`` in the raw data representation for another manifest. + + ``changes`` is an iterable of ``(path, to_delete)``. + + Returns a 2-tuple containing ``bytearray(self.text())`` and the + delta between ``base`` and this manifest. + """ + +class imanifestrevisionbase(interfaceutil.Interface): + """Base interface representing a single revision of a manifest. + + Should not be used as a primary interface: should always be inherited + as part of a larger interface. + """ + + def new(): + """Obtain a new manifest instance. + + Returns an object conforming to the ``imanifestrevisionwritable`` + interface. The instance will be associated with the same + ``imanifestlog`` collection as this instance. + """ + + def copy(): + """Obtain a copy of this manifest instance. + + Returns an object conforming to the ``imanifestrevisionwritable`` + interface. The instance will be associated with the same + ``imanifestlog`` collection as this instance. + """ + + def read(): + """Obtain the parsed manifest data structure. + + The returned object conforms to the ``imanifestdict`` interface. + """ + +class imanifestrevisionstored(imanifestrevisionbase): + """Interface representing a manifest revision committed to storage.""" + + def node(): + """The binary node for this manifest.""" + + parents = interfaceutil.Attribute( + """List of binary nodes that are parents for this manifest revision.""" + ) + + def readdelta(shallow=False): + """Obtain the manifest data structure representing changes from parent. + + This manifest is compared to its 1st parent. A new manifest representing + those differences is constructed. + + The returned object conforms to the ``imanifestdict`` interface. + """ + + def readfast(shallow=False): + """Calls either ``read()`` or ``readdelta()``. + + The faster of the two options is called. + """ + + def find(key): + """Calls self.read().find(key)``. + + Returns a 2-tuple of ``(node, flags)`` or raises ``KeyError``. + """ + +class imanifestrevisionwritable(imanifestrevisionbase): + """Interface representing a manifest revision that can be committed.""" + + def write(transaction, linkrev, p1node, p2node, added, removed): + """Add this revision to storage. + + Takes a transaction object, the changeset revision number it will + be associated with, its parent nodes, and lists of added and + removed paths. + + Returns the binary node of the created revision. + """ + +class imanifestlog(interfaceutil.Interface): + """Interface representing a collection of manifest snapshots.""" + + def __getitem__(node): + """Obtain a manifest instance for a given binary node. + + Equivalent to calling ``self.get('', node)``. + + The returned object conforms to the ``imanifestrevisionstored`` + interface. + """ + + def get(dir, node, verify=True): + """Retrieve the manifest instance for a given directory and binary node. + + ``node`` always refers to the node of the root manifest (which will be + the only manifest if flat manifests are being used). + + If ``dir`` is the empty string, the root manifest is returned. Otherwise + the manifest for the specified directory will be returned (requires + tree manifests). + + If ``verify`` is True, ``LookupError`` is raised if the node is not + known. + + The returned object conforms to the ``imanifestrevisionstored`` + interface. + """ + + def clearcaches(): + """Clear caches associated with this collection.""" + + def rev(node): + """Obtain the revision number for a binary node. + + Raises ``error.LookupError`` if the node is not known. + """ + + def addgroup(deltas, linkmapper, transaction): + """Process a series of deltas for storage. + + ``deltas`` is an iterable of 7-tuples of + (node, p1, p2, linknode, deltabase, delta, flags) defining revisions + to add. + + The ``delta`` field contains ``mpatch`` data to apply to a base + revision, identified by ``deltabase``. The base node can be + ``nullid``, in which case the header from the delta can be ignored + and the delta used as the fulltext. + + Returns a list of nodes that were processed. A node will be in the list + even if it existed in the store previously. + """ + class completelocalrepository(interfaceutil.Interface): """Monolithic interface for local repositories. @@ -757,7 +1059,10 @@ """A handle on the changelog revlog.""") manifestlog = interfaceutil.Attribute( - """A handle on the root manifest revlog.""") + """An instance conforming to the ``imanifestlog`` interface. + + Provides access to manifests for the repository. + """) dirstate = interfaceutil.Attribute( """Working directory state.""") @@ -863,7 +1168,10 @@ """Calls self.vfs.reljoin(self.root, f, *insidef)""" def file(f): - """Obtain a filelog for a tracked path.""" + """Obtain a filelog for a tracked path. + + The returned type conforms to the ``ifilestorage`` interface. + """ def setparents(p1, p2): """Set the parent nodes of the working directory."""
--- a/mercurial/repoview.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/repoview.py Thu Jul 19 13:55:54 2018 -0400 @@ -77,8 +77,7 @@ if visibilityexceptions: hidden -= visibilityexceptions pfunc = repo.changelog.parentrevs - mutablephases = (phases.draft, phases.secret) - mutable = repo._phasecache.getrevset(repo, mutablephases) + mutable = repo._phasecache.getrevset(repo, phases.mutablephases) visible = mutable - hidden _revealancestors(pfunc, hidden, visible) @@ -92,13 +91,8 @@ # fast path in simple case to avoid impact of non optimised code hiddens = filterrevs(repo, 'visible') if phases.hassecret(repo): - cl = repo.changelog - secret = phases.secret - getphase = repo._phasecache.phase - first = min(cl.rev(n) for n in repo._phasecache.phaseroots[secret]) - revs = cl.revs(start=first) - secrets = set(r for r in revs if getphase(repo, r) >= secret) - return frozenset(hiddens | secrets) + secrets = repo._phasecache.getrevset(repo, phases.remotehiddenphases) + return frozenset(hiddens | frozenset(secrets)) else: return hiddens
--- a/mercurial/revlog.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/revlog.py Thu Jul 19 13:55:54 2018 -0400 @@ -196,8 +196,66 @@ s.update(text) return s.digest() +class _testrevlog(object): + """minimalist fake revlog to use in doctests""" + + def __init__(self, data, density=0.5, mingap=0): + """data is an list of revision payload boundaries""" + self._data = data + self._srdensitythreshold = density + self._srmingapsize = mingap + + def start(self, rev): + if rev == 0: + return 0 + return self._data[rev - 1] + + def end(self, rev): + return self._data[rev] + + def length(self, rev): + return self.end(rev) - self.start(rev) + + def __len__(self): + return len(self._data) + def _trimchunk(revlog, revs, startidx, endidx=None): """returns revs[startidx:endidx] without empty trailing revs + + Doctest Setup + >>> revlog = _testrevlog([ + ... 5, #0 + ... 10, #1 + ... 12, #2 + ... 12, #3 (empty) + ... 17, #4 + ... 21, #5 + ... 21, #6 (empty) + ... ]) + + Contiguous cases: + >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0) + [0, 1, 2, 3, 4, 5] + >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0, 5) + [0, 1, 2, 3, 4] + >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 0, 4) + [0, 1, 2] + >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 2, 4) + [2] + >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 3) + [3, 4, 5] + >>> _trimchunk(revlog, [0, 1, 2, 3, 4, 5, 6], 3, 5) + [3, 4] + + Discontiguous cases: + >>> _trimchunk(revlog, [1, 3, 5, 6], 0) + [1, 3, 5] + >>> _trimchunk(revlog, [1, 3, 5, 6], 0, 2) + [1] + >>> _trimchunk(revlog, [1, 3, 5, 6], 1, 3) + [3, 5] + >>> _trimchunk(revlog, [1, 3, 5, 6], 1) + [3, 5] """ length = revlog.length @@ -210,11 +268,231 @@ return revs[startidx:endidx] -def _slicechunk(revlog, revs): +def _segmentspan(revlog, revs): + """Get the byte span of a segment of revisions + + revs is a sorted array of revision numbers + + >>> revlog = _testrevlog([ + ... 5, #0 + ... 10, #1 + ... 12, #2 + ... 12, #3 (empty) + ... 17, #4 + ... ]) + + >>> _segmentspan(revlog, [0, 1, 2, 3, 4]) + 17 + >>> _segmentspan(revlog, [0, 4]) + 17 + >>> _segmentspan(revlog, [3, 4]) + 5 + >>> _segmentspan(revlog, [1, 2, 3,]) + 7 + >>> _segmentspan(revlog, [1, 3]) + 7 + """ + if not revs: + return 0 + return revlog.end(revs[-1]) - revlog.start(revs[0]) + +def _slicechunk(revlog, revs, deltainfo=None, targetsize=None): """slice revs to reduce the amount of unrelated data to be read from disk. ``revs`` is sliced into groups that should be read in one time. Assume that revs are sorted. + + The initial chunk is sliced until the overall density (payload/chunks-span + ratio) is above `revlog._srdensitythreshold`. No gap smaller than + `revlog._srmingapsize` is skipped. + + If `targetsize` is set, no chunk larger than `targetsize` will be yield. + For consistency with other slicing choice, this limit won't go lower than + `revlog._srmingapsize`. + + If individual revisions chunk are larger than this limit, they will still + be raised individually. + + >>> revlog = _testrevlog([ + ... 5, #00 (5) + ... 10, #01 (5) + ... 12, #02 (2) + ... 12, #03 (empty) + ... 27, #04 (15) + ... 31, #05 (4) + ... 31, #06 (empty) + ... 42, #07 (11) + ... 47, #08 (5) + ... 47, #09 (empty) + ... 48, #10 (1) + ... 51, #11 (3) + ... 74, #12 (23) + ... 85, #13 (11) + ... 86, #14 (1) + ... 91, #15 (5) + ... ]) + + >>> list(_slicechunk(revlog, list(range(16)))) + [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]] + >>> list(_slicechunk(revlog, [0, 15])) + [[0], [15]] + >>> list(_slicechunk(revlog, [0, 11, 15])) + [[0], [11], [15]] + >>> list(_slicechunk(revlog, [0, 11, 13, 15])) + [[0], [11, 13, 15]] + >>> list(_slicechunk(revlog, [1, 2, 3, 5, 8, 10, 11, 14])) + [[1, 2], [5, 8, 10, 11], [14]] + + Slicing with a maximum chunk size + >>> list(_slicechunk(revlog, [0, 11, 13, 15], targetsize=15)) + [[0], [11], [13], [15]] + >>> list(_slicechunk(revlog, [0, 11, 13, 15], targetsize=20)) + [[0], [11], [13, 15]] + """ + if targetsize is not None: + targetsize = max(targetsize, revlog._srmingapsize) + # targetsize should not be specified when evaluating delta candidates: + # * targetsize is used to ensure we stay within specification when reading, + # * deltainfo is used to pick are good delta chain when writing. + if not (deltainfo is None or targetsize is None): + msg = 'cannot use `targetsize` with a `deltainfo`' + raise error.ProgrammingError(msg) + for chunk in _slicechunktodensity(revlog, revs, + deltainfo, + revlog._srdensitythreshold, + revlog._srmingapsize): + for subchunk in _slicechunktosize(revlog, chunk, targetsize): + yield subchunk + +def _slicechunktosize(revlog, revs, targetsize=None): + """slice revs to match the target size + + This is intended to be used on chunk that density slicing selected by that + are still too large compared to the read garantee of revlog. This might + happens when "minimal gap size" interrupted the slicing or when chain are + built in a way that create large blocks next to each other. + + >>> revlog = _testrevlog([ + ... 3, #0 (3) + ... 5, #1 (2) + ... 6, #2 (1) + ... 8, #3 (2) + ... 8, #4 (empty) + ... 11, #5 (3) + ... 12, #6 (1) + ... 13, #7 (1) + ... 14, #8 (1) + ... ]) + + Cases where chunk is already small enough + >>> list(_slicechunktosize(revlog, [0], 3)) + [[0]] + >>> list(_slicechunktosize(revlog, [6, 7], 3)) + [[6, 7]] + >>> list(_slicechunktosize(revlog, [0], None)) + [[0]] + >>> list(_slicechunktosize(revlog, [6, 7], None)) + [[6, 7]] + + cases where we need actual slicing + >>> list(_slicechunktosize(revlog, [0, 1], 3)) + [[0], [1]] + >>> list(_slicechunktosize(revlog, [1, 3], 3)) + [[1], [3]] + >>> list(_slicechunktosize(revlog, [1, 2, 3], 3)) + [[1, 2], [3]] + >>> list(_slicechunktosize(revlog, [3, 5], 3)) + [[3], [5]] + >>> list(_slicechunktosize(revlog, [3, 4, 5], 3)) + [[3], [5]] + >>> list(_slicechunktosize(revlog, [5, 6, 7, 8], 3)) + [[5], [6, 7, 8]] + >>> list(_slicechunktosize(revlog, [0, 1, 2, 3, 4, 5, 6, 7, 8], 3)) + [[0], [1, 2], [3], [5], [6, 7, 8]] + + Case with too large individual chunk (must return valid chunk) + >>> list(_slicechunktosize(revlog, [0, 1], 2)) + [[0], [1]] + >>> list(_slicechunktosize(revlog, [1, 3], 1)) + [[1], [3]] + >>> list(_slicechunktosize(revlog, [3, 4, 5], 2)) + [[3], [5]] + """ + assert targetsize is None or 0 <= targetsize + if targetsize is None or _segmentspan(revlog, revs) <= targetsize: + yield revs + return + + startrevidx = 0 + startdata = revlog.start(revs[0]) + endrevidx = 0 + iterrevs = enumerate(revs) + next(iterrevs) # skip first rev. + for idx, r in iterrevs: + span = revlog.end(r) - startdata + if span <= targetsize: + endrevidx = idx + else: + chunk = _trimchunk(revlog, revs, startrevidx, endrevidx + 1) + if chunk: + yield chunk + startrevidx = idx + startdata = revlog.start(r) + endrevidx = idx + yield _trimchunk(revlog, revs, startrevidx) + +def _slicechunktodensity(revlog, revs, deltainfo=None, targetdensity=0.5, + mingapsize=0): + """slice revs to reduce the amount of unrelated data to be read from disk. + + ``revs`` is sliced into groups that should be read in one time. + Assume that revs are sorted. + + ``deltainfo`` is a _deltainfo instance of a revision that we would append + to the top of the revlog. + + The initial chunk is sliced until the overall density (payload/chunks-span + ratio) is above `targetdensity`. No gap smaller than `mingapsize` is + skipped. + + >>> revlog = _testrevlog([ + ... 5, #00 (5) + ... 10, #01 (5) + ... 12, #02 (2) + ... 12, #03 (empty) + ... 27, #04 (15) + ... 31, #05 (4) + ... 31, #06 (empty) + ... 42, #07 (11) + ... 47, #08 (5) + ... 47, #09 (empty) + ... 48, #10 (1) + ... 51, #11 (3) + ... 74, #12 (23) + ... 85, #13 (11) + ... 86, #14 (1) + ... 91, #15 (5) + ... ]) + + >>> list(_slicechunktodensity(revlog, list(range(16)))) + [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]] + >>> list(_slicechunktodensity(revlog, [0, 15])) + [[0], [15]] + >>> list(_slicechunktodensity(revlog, [0, 11, 15])) + [[0], [11], [15]] + >>> list(_slicechunktodensity(revlog, [0, 11, 13, 15])) + [[0], [11, 13, 15]] + >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14])) + [[1, 2], [5, 8, 10, 11], [14]] + >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14], + ... mingapsize=20)) + [[1, 2, 3, 5, 8, 10, 11], [14]] + >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14], + ... targetdensity=0.95)) + [[1, 2], [5], [8, 10, 11], [14]] + >>> list(_slicechunktodensity(revlog, [1, 2, 3, 5, 8, 10, 11, 14], + ... targetdensity=0.95, mingapsize=12)) + [[1, 2], [5, 8, 10, 11], [14]] """ start = revlog.start length = revlog.length @@ -223,24 +501,46 @@ yield revs return - startbyte = start(revs[0]) - endbyte = start(revs[-1]) + length(revs[-1]) - readdata = deltachainspan = endbyte - startbyte - - chainpayload = sum(length(r) for r in revs) + nextrev = len(revlog) + nextoffset = revlog.end(nextrev - 1) + + if deltainfo is None: + deltachainspan = _segmentspan(revlog, revs) + chainpayload = sum(length(r) for r in revs) + else: + deltachainspan = deltainfo.distance + chainpayload = deltainfo.compresseddeltalen + + if deltachainspan < mingapsize: + yield revs + return + + readdata = deltachainspan if deltachainspan: density = chainpayload / float(deltachainspan) else: density = 1.0 + if density >= targetdensity: + yield revs + return + + if deltainfo is not None: + revs = list(revs) + revs.append(nextrev) + # Store the gaps in a heap to have them sorted by decreasing size gapsheap = [] heapq.heapify(gapsheap) prevend = None for i, rev in enumerate(revs): - revstart = start(rev) - revlen = length(rev) + if rev < nextrev: + revstart = start(rev) + revlen = length(rev) + else: + revstart = nextoffset + revlen = deltainfo.deltalen # Skip empty revisions to form larger holes if revlen == 0: @@ -249,7 +549,7 @@ if prevend is not None: gapsize = revstart - prevend # only consider holes that are large enough - if gapsize > revlog._srmingapsize: + if gapsize > mingapsize: heapq.heappush(gapsheap, (-gapsize, i)) prevend = revstart + revlen @@ -257,7 +557,7 @@ # Collect the indices of the largest holes until the density is acceptable indicesheap = [] heapq.heapify(indicesheap) - while gapsheap and density < revlog._srdensitythreshold: + while gapsheap and density < targetdensity: oppgapsize, gapidx = heapq.heappop(gapsheap) heapq.heappush(indicesheap, gapidx) @@ -305,6 +605,7 @@ grouped by level of easiness. """ revlog = self.revlog + gdelta = revlog._generaldelta curr = len(revlog) prev = curr - 1 p1r, p2r = revlog.rev(p1), revlog.rev(p2) @@ -316,27 +617,35 @@ # changegroup data into a generaldelta repo. The only time it # isn't true is if this is the first revision in a delta chain # or if ``format.generaldelta=true`` disabled ``lazydeltabase``. - if cachedelta and revlog._generaldelta and revlog._lazydeltabase: + if cachedelta and gdelta and revlog._lazydeltabase: # Assume what we received from the server is a good choice # build delta will reuse the cache yield (cachedelta[0],) tested.add(cachedelta[0]) - if revlog._generaldelta: + if gdelta: # exclude already lazy tested base if any parents = [p for p in (p1r, p2r) if p != nullrev and p not in tested] - if parents and not revlog._aggressivemergedeltas: - # Pick whichever parent is closer to us (to minimize the - # chance of having to build a fulltext). - parents = [max(parents)] - tested.update(parents) - yield parents + + if not revlog._deltabothparents and len(parents) == 2: + parents.sort() + # To minimize the chance of having to build a fulltext, + # pick first whichever parent is closest to us (max rev) + yield (parents[1],) + # then the other one (min rev) if the first did not fit + yield (parents[0],) + tested.update(parents) + elif len(parents) > 0: + # Test all parents (1 or 2), and keep the best candidate + yield parents + tested.update(parents) if prev not in tested: # other approach failed try against prev to hopefully save us a # fulltext. yield (prev,) + tested.add(prev) def buildtext(self, revinfo, fh): """Builds a fulltext version of a revision @@ -441,7 +750,7 @@ if revlog.flags(candidaterev) & REVIDX_RAWTEXT_CHANGING_FLAGS: continue candidatedelta = self._builddeltainfo(revinfo, candidaterev, fh) - if revlog._isgooddeltainfo(candidatedelta, revinfo.textlen): + if revlog._isgooddeltainfo(candidatedelta, revinfo): nominateddeltas.append(candidatedelta) if nominateddeltas: deltainfo = min(nominateddeltas, key=lambda x: x.deltalen) @@ -606,7 +915,7 @@ # How much data to read and cache into the raw revlog data cache. self._chunkcachesize = 65536 self._maxchainlen = None - self._aggressivemergedeltas = False + self._deltabothparents = True self.index = [] # Mapping of partial identifiers to full nodes. self._pcache = {} @@ -616,7 +925,8 @@ self._compengine = 'zlib' self._maxdeltachainspan = -1 self._withsparseread = False - self._srdensitythreshold = 0.25 + self._sparserevlog = False + self._srdensitythreshold = 0.50 self._srmingapsize = 262144 mmapindexthreshold = None @@ -635,8 +945,8 @@ self._chunkcachesize = opts['chunkcachesize'] if 'maxchainlen' in opts: self._maxchainlen = opts['maxchainlen'] - if 'aggressivemergedeltas' in opts: - self._aggressivemergedeltas = opts['aggressivemergedeltas'] + if 'deltabothparents' in opts: + self._deltabothparents = opts['deltabothparents'] self._lazydeltabase = bool(opts.get('lazydeltabase', False)) if 'compengine' in opts: self._compengine = opts['compengine'] @@ -644,7 +954,10 @@ self._maxdeltachainspan = opts['maxdeltachainspan'] if mmaplargeindex and 'mmapindexthreshold' in opts: mmapindexthreshold = opts['mmapindexthreshold'] - self._withsparseread = bool(opts.get('with-sparse-read', False)) + self._sparserevlog = bool(opts.get('sparse-revlog', False)) + withsparseread = bool(opts.get('with-sparse-read', False)) + # sparse-revlog forces sparse-read + self._withsparseread = self._sparserevlog or withsparseread if 'sparse-read-density-threshold' in opts: self._srdensitythreshold = opts['sparse-read-density-threshold'] if 'sparse-read-min-gap-size' in opts: @@ -868,10 +1181,11 @@ return base index = self.index - base = index[rev][3] - while base != rev: - rev = base - base = index[rev][3] + iterrev = rev + base = index[iterrev][3] + while base != iterrev: + iterrev = base + base = index[iterrev][3] self._chainbasecache[rev] = base return base @@ -1365,31 +1679,46 @@ c.append(self.node(r)) return c - def descendant(self, start, end): - if start == nullrev: - return True - for i in self.descendants([start]): - if i == end: - return True - elif i > end: - break - return False - def commonancestorsheads(self, a, b): """calculate all the heads of the common ancestors of nodes a and b""" a, b = self.rev(a), self.rev(b) + ancs = self._commonancestorsheads(a, b) + return pycompat.maplist(self.node, ancs) + + def _commonancestorsheads(self, *revs): + """calculate all the heads of the common ancestors of revs""" try: - ancs = self.index.commonancestorsheads(a, b) + ancs = self.index.commonancestorsheads(*revs) except (AttributeError, OverflowError): # C implementation failed - ancs = ancestor.commonancestorsheads(self.parentrevs, a, b) - return pycompat.maplist(self.node, ancs) + ancs = ancestor.commonancestorsheads(self.parentrevs, *revs) + return ancs def isancestor(self, a, b): """return True if node a is an ancestor of node b + A revision is considered an ancestor of itself.""" + a, b = self.rev(a), self.rev(b) + return self.isancestorrev(a, b) + + def descendant(self, a, b): + msg = (b'revlog.descendant is deprecated, use revlog.isancestorrev') + self._repo.ui.deprecwarn(msg, b'4.7') + return self.isancestorrev(a, b) + + def isancestorrev(self, a, b): + """return True if revision a is an ancestor of revision b + + A revision is considered an ancestor of itself. + The implementation of this is trivial but the use of commonancestorsheads is not.""" - return a in self.commonancestorsheads(a, b) + if a == nullrev: + return True + elif a == b: + return True + elif a > b: + return False + return a in self._commonancestorsheads(a, b) def ancestor(self, a, b): """calculate the "best" common ancestor of nodes a and b""" @@ -1502,42 +1831,51 @@ def shortest(self, node, minlength=1): """Find the shortest unambiguous prefix that matches node.""" - def isvalid(test): + def isvalid(prefix): try: - if self._partialmatch(test) is None: - return False - - try: - i = int(test) - # if we are a pure int, then starting with zero will not be - # confused as a rev; or, obviously, if the int is larger - # than the value of the tip rev - if test[0] == '0' or i > len(self): - return True - return False - except ValueError: - return True + node = self._partialmatch(prefix) except error.RevlogError: return False except error.WdirUnsupported: # single 'ff...' match return True + if node is None: + raise LookupError(node, self.indexfile, _('no node')) + return True + + def maybewdir(prefix): + return all(c == 'f' for c in prefix) hexnode = hex(node) - shortest = hexnode - startlength = max(6, minlength) - length = startlength - while True: - test = hexnode[:length] - if isvalid(test): - shortest = test - if length == minlength or length > startlength: - return shortest - length -= 1 - else: - length += 1 - if len(shortest) <= length: - return shortest + + def disambiguate(hexnode, minlength): + """Disambiguate against wdirid.""" + for length in range(minlength, 41): + prefix = hexnode[:length] + if not maybewdir(prefix): + return prefix + + if not getattr(self, 'filteredrevs', None): + try: + length = max(self.index.shortest(node), minlength) + return disambiguate(hexnode, length) + except RevlogError: + if node != wdirid: + raise LookupError(node, self.indexfile, _('no node')) + except AttributeError: + # Fall through to pure code + pass + + if node == wdirid: + for length in range(minlength, 41): + prefix = hexnode[:length] + if isvalid(prefix): + return prefix + + for length in range(minlength, 41): + prefix = hexnode[:length] + if isvalid(prefix): + return disambiguate(hexnode, length) def cmp(self, node, text): """compare text with a given file revision @@ -1654,7 +1992,7 @@ """ return self.decompress(self._getsegmentforrevs(rev, rev, df=df)[1]) - def _chunks(self, revs, df=None): + def _chunks(self, revs, df=None, targetsize=None): """Obtain decompressed chunks for the specified revisions. Accepts an iterable of numeric revisions that are assumed to be in @@ -1681,7 +2019,7 @@ if not self._withsparseread: slicedchunks = (revs,) else: - slicedchunks = _slicechunk(self, revs) + slicedchunks = _slicechunk(self, revs, targetsize=targetsize) for revschunk in slicedchunks: firstrev = revschunk[0] @@ -1784,7 +2122,12 @@ # drop cache to save memory self._cache = None - bins = self._chunks(chain, df=_df) + targetsize = None + rawsize = self.index[rev][2] + if 0 <= rawsize: + targetsize = 4 * rawsize + + bins = self._chunks(chain, df=_df, targetsize=targetsize) if rawtext is None: rawtext = bytes(bins[0]) bins = bins[1:] @@ -2076,26 +2419,49 @@ return compressor.decompress(data) - def _isgooddeltainfo(self, d, textlen): + def _isgooddeltainfo(self, deltainfo, revinfo): """Returns True if the given delta is good. Good means that it is within the disk span, disk size, and chain length bounds that we know to be performant.""" - if d is None: + if deltainfo is None: return False - # - 'd.distance' is the distance from the base revision -- bounding it - # limits the amount of I/O we need to do. - # - 'd.compresseddeltalen' is the sum of the total size of deltas we - # need to apply -- bounding it limits the amount of CPU we consume. - + # - 'deltainfo.distance' is the distance from the base revision -- + # bounding it limits the amount of I/O we need to do. + # - 'deltainfo.compresseddeltalen' is the sum of the total size of + # deltas we need to apply -- bounding it limits the amount of CPU + # we consume. + + if self._sparserevlog: + # As sparse-read will be used, we can consider that the distance, + # instead of being the span of the whole chunk, + # is the span of the largest read chunk + base = deltainfo.base + + if base != nullrev: + deltachain = self._deltachain(base)[0] + else: + deltachain = [] + + chunks = _slicechunk(self, deltachain, deltainfo) + distance = max(map(lambda revs:_segmentspan(self, revs), chunks)) + else: + distance = deltainfo.distance + + textlen = revinfo.textlen defaultmax = textlen * 4 maxdist = self._maxdeltachainspan if not maxdist: - maxdist = d.distance # ensure the conditional pass + maxdist = distance # ensure the conditional pass maxdist = max(maxdist, defaultmax) - if (d.distance > maxdist or d.deltalen > textlen or - d.compresseddeltalen > textlen * 2 or - (self._maxchainlen and d.chainlen > self._maxchainlen)): + if self._sparserevlog and maxdist < self._srmingapsize: + # In multiple place, we are ignoring irrelevant data range below a + # certain size. Be also apply this tradeoff here and relax span + # constraint for small enought content. + maxdist = self._srmingapsize + if (distance > maxdist or deltainfo.deltalen > textlen or + deltainfo.compresseddeltalen > textlen * 2 or + (self._maxchainlen and deltainfo.chainlen > self._maxchainlen)): return False return True @@ -2477,7 +2843,7 @@ DELTAREUSEALL = {'always', 'samerevs', 'never', 'fulladd'} def clone(self, tr, destrevlog, addrevisioncb=None, - deltareuse=DELTAREUSESAMEREVS, aggressivemergedeltas=None): + deltareuse=DELTAREUSESAMEREVS, deltabothparents=None): """Copy this revlog to another, possibly with format changes. The destination revlog will contain the same revisions and nodes. @@ -2511,7 +2877,7 @@ deltas will be recomputed if the delta's parent isn't a parent of the revision. - In addition to the delta policy, the ``aggressivemergedeltas`` argument + In addition to the delta policy, the ``deltabothparents`` argument controls whether to compute deltas against both parents for merges. By default, the current default is used. """ @@ -2528,7 +2894,7 @@ # lazydeltabase controls whether to reuse a cached delta, if possible. oldlazydeltabase = destrevlog._lazydeltabase - oldamd = destrevlog._aggressivemergedeltas + oldamd = destrevlog._deltabothparents try: if deltareuse == self.DELTAREUSEALWAYS: @@ -2536,7 +2902,7 @@ elif deltareuse == self.DELTAREUSESAMEREVS: destrevlog._lazydeltabase = False - destrevlog._aggressivemergedeltas = aggressivemergedeltas or oldamd + destrevlog._deltabothparents = deltabothparents or oldamd populatecachedelta = deltareuse in (self.DELTAREUSEALWAYS, self.DELTAREUSESAMEREVS) @@ -2591,4 +2957,4 @@ addrevisioncb(self, rev, node) finally: destrevlog._lazydeltabase = oldlazydeltabase - destrevlog._aggressivemergedeltas = oldamd + destrevlog._deltabothparents = oldamd
--- a/mercurial/revset.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/revset.py Thu Jul 19 13:55:54 2018 -0400 @@ -13,6 +13,7 @@ from . import ( dagop, destutil, + diffutil, encoding, error, hbisect, @@ -111,7 +112,7 @@ return None def _sortedb(xs): - return sorted(util.rapply(pycompat.maybebytestr, xs)) + return sorted(pycompat.rapply(pycompat.maybebytestr, xs)) # operator methods @@ -203,6 +204,8 @@ def orset(repo, subset, x, order): xs = getlist(x) + if not xs: + return baseset() if order == followorder: # slow path to take the subset order return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder) @@ -309,21 +312,17 @@ Will return empty list when passed no args. Greatest common ancestor of a single changeset is that changeset. """ - # i18n: "ancestor" is a keyword - l = getlist(x) - rl = fullreposet(repo) - anc = None + reviter = iter(orset(repo, fullreposet(repo), x, order=anyorder)) + try: + anc = repo[next(reviter)] + except StopIteration: + return baseset() + for r in reviter: + anc = anc.ancestor(repo[r]) - # (getset(repo, rl, i) for i in l) generates a list of lists - for revs in (getset(repo, rl, i) for i in l): - for r in revs: - if anc is None: - anc = repo[r] - else: - anc = anc.ancestor(repo[r]) - - if anc is not None and anc.rev() in subset: - return baseset([anc.rev()]) + r = scmutil.intrev(anc) + if r in subset: + return baseset([r]) return baseset() def _ancestors(repo, subset, x, followfirst=False, startdepth=None, @@ -609,6 +608,38 @@ return subset.filter(lambda r: repo[r].closesbranch(), condrepr='<branch closed>') +# for internal use +@predicate('_commonancestorheads(set)', safe=True) +def _commonancestorheads(repo, subset, x): + # This is an internal method is for quickly calculating "heads(::x and + # ::y)" + + # These greatest common ancestors are the same ones that the consesus bid + # merge will find. + h = heads(repo, fullreposet(repo), x, anyorder) + + ancs = repo.changelog._commonancestorsheads(*list(h)) + return subset & baseset(ancs) + +@predicate('commonancestors(set)', safe=True) +def commonancestors(repo, subset, x): + """Returns all common ancestors of the set. + + This method is for calculating "::x and ::y" (i.e. all the ancestors that + are common to both x and y) in an easy and optimized way. We can't quite + use "::head()" because that revset returns "::x + ::y + ..." for each head + in the repo (whereas we want "::x *and* ::y"). + + """ + # only wants the heads of the set passed in + h = heads(repo, fullreposet(repo), x, anyorder) + if not h: + return baseset() + for r in h: + subset &= dagop.revancestors(repo, baseset([r])) + + return subset + @predicate('contains(pattern)', weight=100) def contains(repo, subset, x): """The revision's manifest contains a file matching pattern (but might not @@ -1129,11 +1160,14 @@ hs.update(cl.rev(h) for h in ls) return subset & baseset(hs) -@predicate('heads(set)', safe=True) -def heads(repo, subset, x): +@predicate('heads(set)', safe=True, takeorder=True) +def heads(repo, subset, x, order): """Members of set with no children in set. """ - s = getset(repo, subset, x) + # argument set should never define order + if order == defineorder: + order = followorder + s = getset(repo, subset, x, order=order) ps = parents(repo, subset, x) return s - ps @@ -1333,9 +1367,11 @@ else: rn = None try: - pm = repo.changelog._partialmatch(n) + pm = scmutil.resolvehexnodeidprefix(repo, n) if pm is not None: rn = repo.changelog.rev(pm) + except LookupError: + pass except error.WdirUnsupported: rn = node.wdirrev @@ -1344,6 +1380,14 @@ result = baseset([rn]) return result & subset +@predicate('none()', safe=True) +def none(repo, subset, x): + """No changesets. + """ + # i18n: "none" is a keyword + getargs(x, 0, 0, _("none takes no arguments")) + return baseset() + @predicate('obsolete()', safe=True) def obsolete(repo, subset, x): """Mutable changeset with a newer version.""" @@ -1792,7 +1836,8 @@ 'phase': lambda r: repo[r].phase(), 'substate': lambda r: repo[r].substate, 'summary': lambda r: repo[r].description().splitlines()[0], - 'diff': lambda r: list(repo[r].diff(git=True),) + 'diff': lambda r: list(repo[r].diff( + opts=diffutil.diffallopts(repo.ui, {'git': True}))), } for info in fields: getfield = _funcs.get(info, None)
--- a/mercurial/revsetlang.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/revsetlang.py Thu Jul 19 13:55:54 2018 -0400 @@ -459,6 +459,12 @@ f = getsymbol(x[1]) wa, ta = _optimize(x[2]) w = getattr(symbols.get(f), '_weight', 1) + m = _match('commonancestors(_)', ta) + + # Optimize heads(commonancestors(_)) because we have a fast version + if f == 'heads' and m: + return w + wa, _build('_commonancestorheads(_)', m[1]) + return w + wa, (op, x[1], ta) raise ValueError('invalid operator %r' % op)
--- a/mercurial/scmutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/scmutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -104,8 +104,9 @@ return self[6] def __repr__(self, *args, **kwargs): - return (('<status modified=%r, added=%r, removed=%r, deleted=%r, ' - 'unknown=%r, ignored=%r, clean=%r>') % self) + return ((r'<status modified=%s, added=%s, removed=%s, deleted=%s, ' + r'unknown=%s, ignored=%s, clean=%s>') % + tuple(pycompat.sysstr(stringutil.pprint(v)) for v in self)) def itersubrepos(ctx1, ctx2): """find subrepos in ctx1 or ctx2""" @@ -200,7 +201,7 @@ elif not msg: ui.warn(_(" empty string\n")) else: - ui.warn("\n%r\n" % stringutil.ellipsis(msg)) + ui.warn("\n%r\n" % pycompat.bytestr(stringutil.ellipsis(msg))) except error.CensoredNodeError as inst: ui.warn(_("abort: file censored %s!\n") % inst) except error.RevlogError as inst: @@ -232,7 +233,7 @@ except (AttributeError, IndexError): # it might be anything, for example a string reason = inst.reason - if isinstance(reason, unicode): + if isinstance(reason, pycompat.unicode): # SSLError of Python 2.7.9 contains a unicode reason = encoding.unitolocal(reason) ui.warn(_("abort: error: %s\n") % reason) @@ -286,7 +287,8 @@ def checkfilename(f): '''Check that the filename f is an acceptable filename for a tracked file''' if '\r' in f or '\n' in f: - raise error.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f) + raise error.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") + % pycompat.bytestr(f)) def checkportable(ui, f): '''Check if filename f is portable and warn or abort depending on config''' @@ -448,7 +450,32 @@ # _partialmatch() of filtered changelog could take O(len(repo)) time, # which would be unacceptably slow. so we look for hash collision in # unfiltered space, which means some hashes may be slightly longer. - return repo.unfiltered().changelog.shortest(node, minlength) + cl = repo.unfiltered().changelog + + def isrev(prefix): + try: + i = int(prefix) + # if we are a pure int, then starting with zero will not be + # confused as a rev; or, obviously, if the int is larger + # than the value of the tip rev + if prefix[0:1] == b'0' or i > len(cl): + return False + return True + except ValueError: + return False + + def disambiguate(prefix): + """Disambiguate against revnums.""" + hexnode = hex(node) + for length in range(len(prefix), len(hexnode) + 1): + prefix = hexnode[:length] + if not isrev(prefix): + return prefix + + try: + return disambiguate(cl.shortest(node, minlength)) + except error.LookupError: + raise error.RepoLookupError() def isrevsymbol(repo, symbol): """Checks if a symbol exists in the repo. @@ -561,11 +588,6 @@ tree = revsetlang.parse(revspec) return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall') -def revpairnodes(repo, revs): - repo.ui.deprecwarn("revpairnodes is deprecated, please use revpair", "4.6") - ctx1, ctx2 = revpair(repo, revs) - return ctx1.node(), ctx2.node() - def revpair(repo, revs): if not revs: return repo['.'], repo[None] @@ -757,7 +779,8 @@ def __contains__(self, node): return self._revcontains(self._torev(node)) -def cleanupnodes(repo, replacements, operation, moves=None, metadata=None): +def cleanupnodes(repo, replacements, operation, moves=None, metadata=None, + fixphase=False, targetphase=None): """do common cleanups when old nodes are replaced by new nodes That includes writing obsmarkers or stripping nodes, and moving bookmarks. @@ -773,6 +796,7 @@ metadata is dictionary containing metadata to be stored in obsmarker if obsolescence is enabled. """ + assert fixphase or targetphase is None if not replacements and not moves: return @@ -803,18 +827,45 @@ newnode = newnodes[0] moves[oldnode] = newnode + allnewnodes = [n for ns in replacements.values() for n in ns] + toretract = {} + toadvance = {} + if fixphase: + precursors = {} + for oldnode, newnodes in replacements.items(): + for newnode in newnodes: + precursors.setdefault(newnode, []).append(oldnode) + + allnewnodes.sort(key=lambda n: unfi[n].rev()) + newphases = {} + def phase(ctx): + return newphases.get(ctx.node(), ctx.phase()) + for newnode in allnewnodes: + ctx = unfi[newnode] + parentphase = max(phase(p) for p in ctx.parents()) + if targetphase is None: + oldphase = max(unfi[oldnode].phase() + for oldnode in precursors[newnode]) + newphase = max(oldphase, parentphase) + else: + newphase = max(targetphase, parentphase) + newphases[newnode] = newphase + if newphase > ctx.phase(): + toretract.setdefault(newphase, []).append(newnode) + elif newphase < ctx.phase(): + toadvance.setdefault(newphase, []).append(newnode) + with repo.transaction('cleanup') as tr: # Move bookmarks bmarks = repo._bookmarks bmarkchanges = [] - allnewnodes = [n for ns in replacements.values() for n in ns] for oldnode, newnode in moves.items(): oldbmarks = repo.nodebookmarks(oldnode) if not oldbmarks: continue from . import bookmarks # avoid import cycle repo.ui.debug('moving bookmarks %r from %s to %s\n' % - (util.rapply(pycompat.maybebytestr, oldbmarks), + (pycompat.rapply(pycompat.maybebytestr, oldbmarks), hex(oldnode), hex(newnode))) # Delete divergent bookmarks being parents of related newnodes deleterevs = repo.revs('parents(roots(%ln & (::%n))) - parents(%n)', @@ -828,6 +879,11 @@ if bmarkchanges: bmarks.applychanges(repo, tr, bmarkchanges) + for phase, nodes in toretract.items(): + phases.retractboundary(repo, tr, phase, nodes) + for phase, nodes in toadvance.items(): + phases.advanceboundary(repo, tr, phase, nodes) + # Obsolete or strip nodes if obsolete.isenabled(repo, obsolete.createmarkersopt): # If a node is already obsoleted, and we want to obsolete it @@ -1110,21 +1166,32 @@ entry.refresh() class filecache(object): - '''A property like decorator that tracks files under .hg/ for updates. + """A property like decorator that tracks files under .hg/ for updates. - Records stat info when called in _filecache. + On first access, the files defined as arguments are stat()ed and the + results cached. The decorated function is called. The results are stashed + away in a ``_filecache`` dict on the object whose method is decorated. - On subsequent calls, compares old stat info with new info, and recreates the - object when any of the files changes, updating the new stat info in - _filecache. + On subsequent access, the cached result is returned. + + On external property set operations, stat() calls are performed and the new + value is cached. + + On property delete operations, cached data is removed. - Mercurial either atomic renames or appends for files under .hg, - so to ensure the cache is reliable we need the filesystem to be able - to tell us if a file has been replaced. If it can't, we fallback to - recreating the object on every call (essentially the same behavior as - propertycache). + When using the property API, cached data is always returned, if available: + no stat() is performed to check if the file has changed and if the function + needs to be called to reflect file changes. - ''' + Others can muck about with the state of the ``_filecache`` dict. e.g. they + can populate an entry before the property's getter is called. In this case, + entries in ``_filecache`` will be used during property operations, + if available. If the underlying file changes, it is up to external callers + to reflect this by e.g. calling ``delattr(obj, attr)`` to remove the cached + method result as well as possibly calling ``del obj._filecache[attr]`` to + remove the ``filecacheentry``. + """ + def __init__(self, *paths): self.paths = paths @@ -1139,7 +1206,8 @@ def __call__(self, func): self.func = func - self.name = func.__name__.encode('ascii') + self.sname = func.__name__ + self.name = pycompat.sysbytes(self.sname) return self def __get__(self, obj, type=None): @@ -1147,9 +1215,9 @@ if obj is None: return self # do we need to check if the file changed? - if self.name in obj.__dict__: + if self.sname in obj.__dict__: assert self.name in obj._filecache, self.name - return obj.__dict__[self.name] + return obj.__dict__[self.sname] entry = obj._filecache.get(self.name) @@ -1166,7 +1234,7 @@ obj._filecache[self.name] = entry - obj.__dict__[self.name] = entry.obj + obj.__dict__[self.sname] = entry.obj return entry.obj def __set__(self, obj, value): @@ -1180,13 +1248,13 @@ ce = obj._filecache[self.name] ce.obj = value # update cached copy - obj.__dict__[self.name] = value # update copy returned by obj.x + obj.__dict__[self.sname] = value # update copy returned by obj.x def __delete__(self, obj): try: - del obj.__dict__[self.name] + del obj.__dict__[self.sname] except KeyError: - raise AttributeError(self.name) + raise AttributeError(self.sname) def extdatasource(repo, source): """Gather a map of rev -> value dict from the specified source @@ -1262,6 +1330,37 @@ return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args, **kwargs) +class progress(object): + def __init__(self, ui, topic, unit="", total=None): + self.ui = ui + self.pos = 0 + self.topic = topic + self.unit = unit + self.total = total + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_tb): + self.complete() + + def update(self, pos, item="", total=None): + assert pos is not None + if total: + self.total = total + self.pos = pos + self._print(item) + + def increment(self, step=1, item="", total=None): + self.update(self.pos + step, item, total) + + def complete(self): + self.ui.progress(self.topic, None) + + def _print(self, item): + self.ui.progress(self.topic, self.pos, item, self.unit, + self.total) + def gdinitconfig(ui): """helper function to know if a repo should be created as general delta """ @@ -1434,9 +1533,9 @@ for instability, revset in instabilitytypes: delta = (newinstabilitycounts[instability] - oldinstabilitycounts[instability]) - if delta > 0: - repo.ui.warn(_('%i new %s changesets\n') % - (delta, instability)) + msg = getinstabilitymessage(delta, instability) + if msg: + repo.ui.warn(msg) if txmatch(_reportnewcssource): @reportsummary @@ -1460,6 +1559,32 @@ revrange = '%s:%s' % (minrev, maxrev) repo.ui.status(_('new changesets %s\n') % revrange) + @reportsummary + def reportphasechanges(repo, tr): + """Report statistics of phase changes for changesets pre-existing + pull/unbundle. + """ + newrevs = tr.changes.get('revs', xrange(0, 0)) + phasetracking = tr.changes.get('phases', {}) + if not phasetracking: + return + published = [ + rev for rev, (old, new) in phasetracking.iteritems() + if new == phases.public and rev not in newrevs + ] + if not published: + return + repo.ui.status(_('%d local changesets published\n') + % len(published)) + +def getinstabilitymessage(delta, instability): + """function to return the message to show warning about new instabilities + + exists as a separate function so that extension can wrap to show more + information like how to fix instabilities""" + if delta > 0: + return _('%i new %s changesets\n') % (delta, instability) + def nodesummaries(repo, nodes, maxnumnodes=4): if len(nodes) <= maxnumnodes or repo.ui.verbose: return ' '.join(short(h) for h in nodes) @@ -1538,7 +1663,6 @@ unficl = unfi.changelog cl = repo.changelog tiprev = len(unficl) - pmatch = unficl._partialmatch allowrevnums = repo.ui.configbool('experimental', 'directaccess.revnums') for s in symbols: try: @@ -1554,7 +1678,7 @@ pass try: - s = pmatch(s) + s = resolvehexnodeidprefix(unfi, s) except (error.LookupError, error.WdirUnsupported): s = None @@ -1564,3 +1688,12 @@ revs.add(rev) return revs + +def bookmarkrevs(repo, mark): + """ + Select revisions reachable by a given bookmark + """ + return repo.revs("ancestors(bookmark(%s)) - " + "ancestors(head() and not bookmark(%s)) - " + "ancestors(bookmark() and not bookmark(%s))", + mark, mark, mark)
--- a/mercurial/server.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/server.py Thu Jul 19 13:55:54 2018 -0400 @@ -8,7 +8,6 @@ from __future__ import absolute_import import os -import tempfile from .i18n import _ @@ -72,7 +71,7 @@ if opts['daemon'] and not opts['daemon_postexec']: # Signal child process startup with file removal - lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-') + lockfd, lockpath = pycompat.mkstemp(prefix='hg-service-') os.close(lockfd) try: if not runargs:
--- a/mercurial/setdiscovery.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/setdiscovery.py Thu Jul 19 13:55:54 2018 -0400 @@ -197,6 +197,7 @@ missing = set() full = False + progress = ui.makeprogress(_('searching'), unit=_('queries')) while undecided: if sample: @@ -226,7 +227,7 @@ sample = samplefunc(dag, undecided, targetsize) roundtrips += 1 - ui.progress(_('searching'), roundtrips, unit=_('queries')) + progress.update(roundtrips) ui.debug("query %i; still undecided: %i, sample size is: %i\n" % (roundtrips, len(undecided), len(sample))) # indices between sample and externalized version must match @@ -251,7 +252,7 @@ # return any heads in that case, so discard that result.discard(nullrev) elapsed = util.timer() - start - ui.progress(_('searching'), None) + progress.complete() ui.debug("%d total queries in %.4fs\n" % (roundtrips, elapsed)) msg = ('found %d common and %d unknown server heads,' ' %d roundtrips in %.4fs\n')
--- a/mercurial/similar.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/similar.py Thu Jul 19 13:55:54 2018 -0400 @@ -18,14 +18,14 @@ Takes a list of new filectxs and a list of removed filectxs, and yields (before, after) tuples of exact matches. ''' - numfiles = len(added) + len(removed) - # Build table of removed files: {hash(fctx.data()): [fctx, ...]}. # We use hash() to discard fctx.data() from memory. hashes = {} - for i, fctx in enumerate(removed): - repo.ui.progress(_('searching for exact renames'), i, total=numfiles, - unit=_('files')) + progress = repo.ui.makeprogress(_('searching for exact renames'), + total=(len(added) + len(removed)), + unit=_('files')) + for fctx in removed: + progress.increment() h = hash(fctx.data()) if h not in hashes: hashes[h] = [fctx] @@ -33,9 +33,8 @@ hashes[h].append(fctx) # For each added file, see if it corresponds to a removed file. - for i, fctx in enumerate(added): - repo.ui.progress(_('searching for exact renames'), i + len(removed), - total=numfiles, unit=_('files')) + for fctx in added: + progress.increment() adata = fctx.data() h = hash(adata) for rfctx in hashes.get(h, []): @@ -45,7 +44,7 @@ break # Done - repo.ui.progress(_('searching for exact renames'), None) + progress.complete() def _ctxdata(fctx): # lazily load text @@ -76,10 +75,10 @@ (before, after, score) tuples of partial matches. ''' copies = {} - for i, r in enumerate(removed): - repo.ui.progress(_('searching for similar files'), i, - total=len(removed), unit=_('files')) - + progress = repo.ui.makeprogress(_('searching for similar files'), + unit=_('files'), total=len(removed)) + for r in removed: + progress.increment() data = None for a in added: bestscore = copies.get(a, (None, threshold))[1] @@ -88,7 +87,7 @@ myscore = _score(a, data) if myscore > bestscore: copies[a] = (r, myscore) - repo.ui.progress(_('searching'), None) + progress.complete() for dest, v in copies.iteritems(): source, bscore = v
--- a/mercurial/smartset.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/smartset.py Thu Jul 19 13:55:54 2018 -0400 @@ -13,29 +13,9 @@ pycompat, util, ) - -def _formatsetrepr(r): - """Format an optional printable representation of a set - - ======== ================================= - type(r) example - ======== ================================= - tuple ('<not %r>', other) - bytes '<branch closed>' - callable lambda: '<branch %r>' % sorted(b) - object other - ======== ================================= - """ - if r is None: - return '' - elif isinstance(r, tuple): - return r[0] % util.rapply(pycompat.maybebytestr, r[1:]) - elif isinstance(r, bytes): - return r - elif callable(r): - return r() - else: - return pycompat.byterepr(r) +from .utils import ( + stringutil, +) def _typename(o): return pycompat.sysbytes(type(o).__name__).lstrip('_') @@ -392,7 +372,7 @@ @encoding.strmethod def __repr__(self): d = {None: '', False: '-', True: '+'}[self._ascending] - s = _formatsetrepr(self._datarepr) + s = stringutil.buildrepr(self._datarepr) if not s: l = self._list # if _list has been built from a set, it might have a different @@ -514,7 +494,7 @@ @encoding.strmethod def __repr__(self): xs = [pycompat.byterepr(self._subset)] - s = _formatsetrepr(self._condrepr) + s = stringutil.buildrepr(self._condrepr) if s: xs.append(s) return '<%s %s>' % (_typename(self), ', '.join(xs)) @@ -1129,17 +1109,3 @@ other.sort(reverse=self.isdescending()) return other - -def prettyformat(revs): - lines = [] - rs = pycompat.byterepr(revs) - p = 0 - while p < len(rs): - q = rs.find('<', p + 1) - if q < 0: - q = len(rs) - l = rs.count('<', 0, p) - rs.count('>', 0, p) - assert l >= 0 - lines.append((l, rs[p:q].rstrip())) - p = q - return '\n'.join(' ' * l + s for l, s in lines)
--- a/mercurial/sshpeer.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/sshpeer.py Thu Jul 19 13:55:54 2018 -0400 @@ -22,6 +22,7 @@ ) from .utils import ( procutil, + stringutil, ) def _serverquote(s): @@ -98,6 +99,17 @@ _forwardoutput(self._ui, self._side) return r + def unbufferedread(self, size): + r = self._call('unbufferedread', size) + if size != 0 and not r: + # We've observed a condition that indicates the + # stdout closed unexpectedly. Check stderr one + # more time and snag anything that's there before + # letting anyone know the main part of the pipe + # closed prematurely. + _forwardoutput(self._ui, self._side) + return r + def readline(self): return self._call('readline') @@ -273,7 +285,7 @@ # Assume version 1 of wire protocol by default. protoname = wireprototypes.SSHV1 - reupgraded = re.compile(b'^upgraded %s (.*)$' % re.escape(token)) + reupgraded = re.compile(b'^upgraded %s (.*)$' % stringutil.reescape(token)) lines = ['', 'dummy'] max_noise = 500
--- a/mercurial/sslutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/sslutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -618,14 +618,14 @@ # The client SHOULD NOT attempt to match a presented identifier # where the wildcard character is embedded within an A-label or # U-label of an internationalized domain name. - pats.append(re.escape(leftmost)) + pats.append(stringutil.reescape(leftmost)) else: # Otherwise, '*' matches any dotless string, e.g. www* - pats.append(re.escape(leftmost).replace(br'\*', '[^.]*')) + pats.append(stringutil.reescape(leftmost).replace(br'\*', '[^.]*')) # add the remaining fragments, ignore any wildcards for frag in remainder: - pats.append(re.escape(frag)) + pats.append(stringutil.reescape(frag)) pat = re.compile(br'\A' + br'\.'.join(pats) + br'\Z', re.IGNORECASE) return pat.match(hostname) is not None @@ -640,9 +640,9 @@ return _('no certificate received') dnsnames = [] - san = cert.get('subjectAltName', []) + san = cert.get(r'subjectAltName', []) for key, value in san: - if key == 'DNS': + if key == r'DNS': try: if _dnsnamematch(value, hostname): return @@ -672,6 +672,7 @@ dnsnames.append(value) + dnsnames = [pycompat.bytesurl(d) for d in dnsnames] if len(dnsnames) > 1: return _('certificate is for %s') % ', '.join(dnsnames) elif len(dnsnames) == 1:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/state.py Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,84 @@ +# state.py - writing and reading state files in Mercurial +# +# Copyright 2018 Pulkit Goyal <pulkitmgoyal@gmail.com> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +""" +This file contains class to wrap the state for commands and other +related logic. + +All the data related to the command state is stored as dictionary in the object. +The class has methods using which the data can be stored to disk in a file under +.hg/ directory. + +We store the data on disk in cbor, for which we use the third party cbor library +to serialize and deserialize data. +""" + +from __future__ import absolute_import + +from .thirdparty import cbor + +from . import ( + error, + util, +) + +class cmdstate(object): + """a wrapper class to store the state of commands like `rebase`, `graft`, + `histedit`, `shelve` etc. Extensions can also use this to write state files. + + All the data for the state is stored in the form of key-value pairs in a + dictionary. + + The class object can write all the data to a file in .hg/ directory and + can populate the object data reading that file. + + Uses cbor to serialize and deserialize data while writing and reading from + disk. + """ + + def __init__(self, repo, fname): + """ repo is the repo object + fname is the file name in which data should be stored in .hg directory + """ + self._repo = repo + self.fname = fname + + def read(self): + """read the existing state file and return a dict of data stored""" + return self._read() + + def save(self, version, data): + """write all the state data stored to .hg/<filename> file + + we use third-party library cbor to serialize data to write in the file. + """ + if not isinstance(version, int): + raise error.ProgrammingError("version of state file should be" + " an integer") + + with self._repo.vfs(self.fname, 'wb', atomictemp=True) as fp: + fp.write('%d\n' % version) + cbor.dump(data, fp, canonical=True) + + def _read(self): + """reads the state file and returns a dictionary which contain + data in the same format as it was before storing""" + with self._repo.vfs(self.fname, 'rb') as fp: + try: + int(fp.readline()) + except ValueError: + raise error.CorruptedState("unknown version of state file" + " found") + return cbor.load(fp) + + def delete(self): + """drop the state file if exists""" + util.unlinkpath(self._repo.vfs.join(self.fname), ignoremissing=True) + + def exists(self): + """check whether the state file exists or not""" + return self._repo.vfs.exists(self.fname)
--- a/mercurial/statprof.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/statprof.py Thu Jul 19 13:55:54 2018 -0400 @@ -112,7 +112,6 @@ import os import signal import sys -import tempfile import threading import time @@ -140,7 +139,7 @@ def clock(): times = os.times() - return times[0] + times[1] + return (times[0] + times[1], times[4]) ########################################################################### @@ -149,10 +148,11 @@ class ProfileState(object): def __init__(self, frequency=None): self.reset(frequency) + self.track = 'cpu' def reset(self, frequency=None): # total so far - self.accumulated_time = 0.0 + self.accumulated_time = (0.0, 0.0) # start_time when timer is active self.last_start_time = None # a float @@ -171,10 +171,23 @@ self.samples = [] def accumulate_time(self, stop_time): - self.accumulated_time += stop_time - self.last_start_time + increment = ( + stop_time[0] - self.last_start_time[0], + stop_time[1] - self.last_start_time[1], + ) + self.accumulated_time = ( + self.accumulated_time[0] + increment[0], + self.accumulated_time[1] + increment[1], + ) def seconds_per_sample(self): - return self.accumulated_time / len(self.samples) + return self.accumulated_time[self.timeidx] / len(self.samples) + + @property + def timeidx(self): + if self.track == 'real': + return 1 + return 0 state = ProfileState() @@ -262,7 +275,8 @@ now = clock() state.accumulate_time(now) - state.samples.append(Sample.from_frame(frame, state.accumulated_time)) + timestamp = state.accumulated_time[state.timeidx] + state.samples.append(Sample.from_frame(frame, timestamp)) signal.setitimer(signal.ITIMER_PROF, state.sample_interval, 0.0) @@ -275,7 +289,9 @@ state.accumulate_time(now) frame = sys._current_frames()[tid] - state.samples.append(Sample.from_frame(frame, state.accumulated_time)) + + timestamp = state.accumulated_time[state.timeidx] + state.samples.append(Sample.from_frame(frame, timestamp)) state.last_start_time = now time.sleep(state.sample_interval) @@ -289,8 +305,9 @@ return state.profile_level > 0 lastmechanism = None -def start(mechanism='thread'): +def start(mechanism='thread', track='cpu'): '''Install the profiling signal handler, and start profiling.''' + state.track = track # note: nesting different mode won't work state.profile_level += 1 if state.profile_level == 1: state.last_start_time = clock() @@ -333,7 +350,7 @@ def save_data(path): with open(path, 'w+') as file: - file.write(str(state.accumulated_time) + '\n') + file.write("%f %f\n" % state.accumulated_time) for sample in state.samples: time = str(sample.time) stack = sample.stack @@ -344,7 +361,7 @@ def load_data(path): lines = open(path, 'r').read().splitlines() - state.accumulated_time = float(lines[0]) + state.accumulated_time = [float(value) for value in lines[0].split()] state.samples = [] for line in lines[1:]: parts = line.split('\0') @@ -437,7 +454,8 @@ def display(fp=None, format=3, data=None, **kwargs): '''Print statistics, either to stdout or the given file object.''' - data = data or state + if data is None: + data = state if fp is None: import sys @@ -466,7 +484,8 @@ if format not in (DisplayFormats.Json, DisplayFormats.Chrome): print('---', file=fp) print('Sample count: %d' % len(data.samples), file=fp) - print('Total time: %f seconds' % data.accumulated_time, file=fp) + print('Total time: %f seconds (%f wall)' % data.accumulated_time, + file=fp) def display_by_line(data, fp): '''Print the profiler data with each sample line represented @@ -691,7 +710,7 @@ file=fp) return - fd, path = tempfile.mkstemp() + fd, path = pycompat.mkstemp() file = open(path, "w+")
--- a/mercurial/store.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/store.py Thu Jul 19 13:55:54 2018 -0400 @@ -449,6 +449,7 @@ def write(self, tr): if self._dirty: + assert self.entries is not None tr.addbackup('fncache') fp = self.vfs('fncache', mode='wb', atomictemp=True) if self.entries: @@ -489,10 +490,20 @@ self.encode = encode def __call__(self, path, mode='r', *args, **kw): + encoded = self.encode(path) if mode not in ('r', 'rb') and (path.startswith('data/') or path.startswith('meta/')): - self.fncache.add(path) - return self.vfs(self.encode(path), mode, *args, **kw) + # do not trigger a fncache load when adding a file that already is + # known to exist. + notload = self.fncache.entries is None and self.vfs.exists(encoded) + if notload and 'a' in mode and not self.vfs.stat(encoded).st_size: + # when appending to an existing file, if the file has size zero, + # it should be considered as missing. Such zero-size files are + # the result of truncation when a transaction is aborted. + notload = False + if not notload: + self.fncache.add(path) + return self.vfs(encoded, mode, *args, **kw) def join(self, path): if path:
--- a/mercurial/streamclone.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/streamclone.py Thu Jul 19 13:55:54 2018 -0400 @@ -10,7 +10,6 @@ import contextlib import os import struct -import tempfile import warnings from .i18n import _ @@ -19,6 +18,7 @@ cacheutil, error, phases, + pycompat, store, util, ) @@ -313,16 +313,15 @@ # This is where we'll add compression in the future. assert compression == 'UN' - seen = 0 - repo.ui.progress(_('bundle'), 0, total=bytecount, unit=_('bytes')) + progress = repo.ui.makeprogress(_('bundle'), total=bytecount, + unit=_('bytes')) + progress.update(0) for chunk in it: - seen += len(chunk) - repo.ui.progress(_('bundle'), seen, total=bytecount, - unit=_('bytes')) + progress.increment(step=len(chunk)) yield chunk - repo.ui.progress(_('bundle'), None) + progress.complete() return requirements, gen() @@ -338,8 +337,9 @@ with repo.lock(): repo.ui.status(_('%d files to transfer, %s of data\n') % (filecount, util.bytecount(bytecount))) - handled_bytes = 0 - repo.ui.progress(_('clone'), 0, total=bytecount, unit=_('bytes')) + progress = repo.ui.makeprogress(_('clone'), total=bytecount, + unit=_('bytes')) + progress.update(0) start = util.timer() # TODO: get rid of (potential) inconsistency @@ -374,9 +374,7 @@ path = store.decodedir(name) with repo.svfs(path, 'w', backgroundclose=True) as ofp: for chunk in util.filechunkiter(fp, limit=size): - handled_bytes += len(chunk) - repo.ui.progress(_('clone'), handled_bytes, - total=bytecount, unit=_('bytes')) + progress.increment(step=len(chunk)) ofp.write(chunk) # force @filecache properties to be reloaded from @@ -386,7 +384,7 @@ elapsed = util.timer() - start if elapsed <= 0: elapsed = 0.001 - repo.ui.progress(_('clone'), None) + progress.complete() repo.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') % (util.bytecount(bytecount), elapsed, util.bytecount(bytecount / elapsed))) @@ -469,7 +467,7 @@ files = [] try: def copy(src): - fd, dst = tempfile.mkstemp() + fd, dst = pycompat.mkstemp() os.close(fd) files.append(dst) util.copyfiles(src, dst, hardlink=True) @@ -494,41 +492,38 @@ def _emit2(repo, entries, totalfilesize): """actually emit the stream bundle""" vfsmap = _makemap(repo) - progress = repo.ui.progress - progress(_('bundle'), 0, total=totalfilesize, unit=_('bytes')) - with maketempcopies() as copy: - try: - # copy is delayed until we are in the try - entries = [_filterfull(e, copy, vfsmap) for e in entries] - yield None # this release the lock on the repository - seen = 0 + progress = repo.ui.makeprogress(_('bundle'), total=totalfilesize, + unit=_('bytes')) + progress.update(0) + with maketempcopies() as copy, progress: + # copy is delayed until we are in the try + entries = [_filterfull(e, copy, vfsmap) for e in entries] + yield None # this release the lock on the repository + seen = 0 - for src, name, ftype, data in entries: - vfs = vfsmap[src] - yield src - yield util.uvarintencode(len(name)) - if ftype == _fileappend: - fp = vfs(name) - size = data - elif ftype == _filefull: - fp = open(data, 'rb') - size = util.fstat(fp).st_size - try: - yield util.uvarintencode(size) - yield name - if size <= 65536: - chunks = (fp.read(size),) - else: - chunks = util.filechunkiter(fp, limit=size) - for chunk in chunks: - seen += len(chunk) - progress(_('bundle'), seen, total=totalfilesize, - unit=_('bytes')) - yield chunk - finally: - fp.close() - finally: - progress(_('bundle'), None) + for src, name, ftype, data in entries: + vfs = vfsmap[src] + yield src + yield util.uvarintencode(len(name)) + if ftype == _fileappend: + fp = vfs(name) + size = data + elif ftype == _filefull: + fp = open(data, 'rb') + size = util.fstat(fp).st_size + try: + yield util.uvarintencode(size) + yield name + if size <= 65536: + chunks = (fp.read(size),) + else: + chunks = util.filechunkiter(fp, limit=size) + for chunk in chunks: + seen += len(chunk) + progress.update(seen) + yield chunk + finally: + fp.close() def generatev2(repo): """Emit content for version 2 of a streaming clone. @@ -589,10 +584,9 @@ (filecount, util.bytecount(filesize))) start = util.timer() - handledbytes = 0 - progress = repo.ui.progress - - progress(_('clone'), handledbytes, total=filesize, unit=_('bytes')) + progress = repo.ui.makeprogress(_('clone'), total=filesize, + unit=_('bytes')) + progress.update(0) vfsmap = _makemap(repo) @@ -614,9 +608,7 @@ with vfs(name, 'w') as ofp: for chunk in util.filechunkiter(fp, limit=datalen): - handledbytes += len(chunk) - progress(_('clone'), handledbytes, total=filesize, - unit=_('bytes')) + progress.increment(step=len(chunk)) ofp.write(chunk) # force @filecache properties to be reloaded from @@ -626,10 +618,10 @@ elapsed = util.timer() - start if elapsed <= 0: elapsed = 0.001 - progress(_('clone'), None) repo.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') % - (util.bytecount(handledbytes), elapsed, - util.bytecount(handledbytes / elapsed))) + (util.bytecount(progress.pos), elapsed, + util.bytecount(progress.pos / elapsed))) + progress.complete() def applybundlev2(repo, fp, filecount, filesize, requirements): missingreqs = [r for r in requirements if r not in repo.supported]
--- a/mercurial/subrepo.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/subrepo.py Thu Jul 19 13:55:54 2018 -0400 @@ -318,9 +318,9 @@ """return file flags""" return '' - def getfileset(self, expr): + def matchfileset(self, expr, badfn=None): """Resolve the fileset expression for this repo""" - return set() + return matchmod.nevermatcher(self.wvfs.base, '', badfn=badfn) def printfiles(self, ui, m, fm, fmt, subrepos): """handle the files command for this subrepo""" @@ -333,17 +333,17 @@ files = self.files() total = len(files) relpath = subrelpath(self) - self.ui.progress(_('archiving (%s)') % relpath, 0, - unit=_('files'), total=total) - for i, name in enumerate(files): + progress = self.ui.makeprogress(_('archiving (%s)') % relpath, + unit=_('files'), total=total) + progress.update(0) + for name in files: flags = self.fileflags(name) mode = 'x' in flags and 0o755 or 0o644 symlink = 'l' in flags archiver.addfile(prefix + self._path + '/' + name, mode, symlink, self.filedata(name, decode)) - self.ui.progress(_('archiving (%s)') % relpath, i + 1, - unit=_('files'), total=total) - self.ui.progress(_('archiving (%s)') % relpath, None) + progress.increment() + progress.complete() return total def walk(self, match): @@ -792,24 +792,30 @@ return cmdutil.files(ui, ctx, m, fm, fmt, subrepos) @annotatesubrepoerror - def getfileset(self, expr): + def matchfileset(self, expr, badfn=None): + repo = self._repo if self._ctx.rev() is None: - ctx = self._repo[None] + ctx = repo[None] else: rev = self._state[1] - ctx = self._repo[rev] + ctx = repo[rev] - files = ctx.getfileset(expr) + matchers = [ctx.matchfileset(expr, badfn=badfn)] for subpath in ctx.substate: sub = ctx.sub(subpath) try: - files.extend(subpath + '/' + f for f in sub.getfileset(expr)) + sm = sub.matchfileset(expr, badfn=badfn) + pm = matchmod.prefixdirmatcher(repo.root, repo.getcwd(), + subpath, sm, badfn=badfn) + matchers.append(pm) except error.LookupError: self.ui.status(_("skipping missing subrepository: %s\n") % self.wvfs.reljoin(reporelpath(self), subpath)) - return files + if len(matchers) == 1: + return matchers[0] + return matchmod.unionmatcher(matchers) def walk(self, match): ctx = self._repo[None] @@ -1640,8 +1646,10 @@ tarstream = self._gitcommand(['archive', revision], stream=True) tar = tarfile.open(fileobj=tarstream, mode=r'r|') relpath = subrelpath(self) - self.ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files')) - for i, info in enumerate(tar): + progress = self.ui.makeprogress(_('archiving (%s)') % relpath, + unit=_('files')) + progress.update(0) + for info in tar: if info.isdir(): continue if match and not match(info.name): @@ -1653,9 +1661,8 @@ archiver.addfile(prefix + self._path + '/' + info.name, info.mode, info.issym(), data) total += 1 - self.ui.progress(_('archiving (%s)') % relpath, i + 1, - unit=_('files')) - self.ui.progress(_('archiving (%s)') % relpath, None) + progress.increment() + progress.complete() return total @@ -1695,7 +1702,7 @@ tab = line.find('\t') if tab == -1: continue - status, f = line[tab - 1], line[tab + 1:] + status, f = line[tab - 1:tab], line[tab + 1:] if status == 'M': modified.append(f) elif status == 'A':
--- a/mercurial/templatefilters.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templatefilters.py Thu Jul 19 13:55:54 2018 -0400 @@ -99,6 +99,45 @@ """ return os.path.basename(path) +@templatefilter('commondir') +def commondir(filelist): + """List of text. Treats each list item as file name with / + as path separator and returns the longest common directory + prefix shared by all list items. + Returns the empty string if no common prefix exists. + + The list items are not normalized, i.e. "foo/../bar" is handled as + file "bar" in the directory "foo/..". Leading slashes are ignored. + + For example, ["foo/bar/baz", "foo/baz/bar"] becomes "foo" and + ["foo/bar", "baz"] becomes "". + """ + def common(a, b): + if len(a) > len(b): + a = b[:len(a)] + elif len(b) > len(a): + b = b[:len(a)] + if a == b: + return a + for i in xrange(len(a)): + if a[i] != b[i]: + return a[:i] + return a + try: + if not filelist: + return "" + dirlist = [f.lstrip('/').split('/')[:-1] for f in filelist] + if len(dirlist) == 1: + return '/'.join(dirlist[0]) + a = min(dirlist) + b = max(dirlist) + # The common prefix of a and b is shared with all + # elements of the list since Python sorts lexicographical + # and [1, x] after [1]. + return '/'.join(common(a, b)) + except TypeError: + raise error.ParseError(_('argument is not a list of text')) + @templatefilter('count') def count(i): """List or text. Returns the length as an integer.""" @@ -238,6 +277,7 @@ @templatefilter('json') def json(obj, paranoid=True): + """Any object. Serializes the object to a JSON formatted text.""" if obj is None: return 'null' elif obj is False: @@ -248,13 +288,9 @@ return pycompat.bytestr(obj) elif isinstance(obj, bytes): return '"%s"' % encoding.jsonescape(obj, paranoid=paranoid) - elif isinstance(obj, str): - # This branch is unreachable on Python 2, because bytes == str - # and we'll return in the next-earlier block in the elif - # ladder. On Python 3, this helps us catch bugs before they - # hurt someone. + elif isinstance(obj, type(u'')): raise error.ProgrammingError( - 'Mercurial only does output with bytes on Python 3: %r' % obj) + 'Mercurial only does output with bytes: %r' % obj) elif util.safehasattr(obj, 'keys'): out = ['"%s": %s' % (encoding.jsonescape(k, paranoid=paranoid), json(v, paranoid))
--- a/mercurial/templatefuncs.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templatefuncs.py Thu Jul 19 13:55:54 2018 -0400 @@ -12,6 +12,7 @@ from .i18n import _ from .node import ( bin, + wdirid, ) from . import ( color, @@ -19,7 +20,6 @@ error, minirst, obsutil, - pycompat, registrar, revset as revsetmod, revsetlang, @@ -35,6 +35,7 @@ ) evalrawexp = templateutil.evalrawexp +evalwrapped = templateutil.evalwrapped evalfuncarg = templateutil.evalfuncarg evalboolean = templateutil.evalboolean evaldate = templateutil.evaldate @@ -84,7 +85,7 @@ for k, v in args['kwargs'].iteritems()) return templateutil.hybriddict(data) -@templatefunc('diff([includepattern [, excludepattern]])') +@templatefunc('diff([includepattern [, excludepattern]])', requires={'ctx'}) def diff(context, mapping, args): """Show a diff, optionally specifying files to include or exclude.""" @@ -104,7 +105,7 @@ return ''.join(chunks) -@templatefunc('extdata(source)', argspec='source') +@templatefunc('extdata(source)', argspec='source', requires={'ctx', 'cache'}) def extdata(context, mapping, args): """Show a text read from the specified extdata source. (EXPERIMENTAL)""" if 'source' not in args: @@ -112,6 +113,13 @@ raise error.ParseError(_('extdata expects one argument')) source = evalstring(context, mapping, args['source']) + if not source: + sym = templateutil.findsymbolicname(args['source']) + if sym: + raise error.ParseError(_('empty data source specified'), + hint=_("did you mean extdata('%s')?") % sym) + else: + raise error.ParseError(_('empty data source specified')) cache = context.resource(mapping, 'cache').setdefault('extdata', {}) ctx = context.resource(mapping, 'ctx') if source in cache: @@ -120,7 +128,7 @@ data = cache[source] = scmutil.extdatasource(ctx.repo(), source) return data.get(ctx.rev(), '') -@templatefunc('files(pattern)') +@templatefunc('files(pattern)', requires={'ctx'}) def files(context, mapping, args): """All files of the current changeset matching the pattern. See :hg:`help patterns`.""" @@ -158,7 +166,26 @@ return templatefilters.fill(text, width, initindent, hangindent) -@templatefunc('formatnode(node)') +@templatefunc('filter(iterable[, expr])') +def filter_(context, mapping, args): + """Remove empty elements from a list or a dict. If expr specified, it's + applied to each element to test emptiness.""" + if not (1 <= len(args) <= 2): + # i18n: "filter" is a keyword + raise error.ParseError(_("filter expects one or two arguments")) + iterable = evalwrapped(context, mapping, args[0]) + if len(args) == 1: + def select(w): + return w.tobool(context, mapping) + else: + def select(w): + if not isinstance(w, templateutil.mappable): + raise error.ParseError(_("not filterable by expression")) + lm = context.overlaymap(mapping, w.tomap(context)) + return evalboolean(context, lm, args[1]) + return iterable.filter(context, mapping, select) + +@templatefunc('formatnode(node)', requires={'ui'}) def formatnode(context, mapping, args): """Obtain the preferred form of a changeset hash. (DEPRECATED)""" if len(args) != 1: @@ -171,7 +198,7 @@ return node return templatefilters.short(node) -@templatefunc('mailmap(author)') +@templatefunc('mailmap(author)', requires={'repo', 'cache'}) def mailmap(context, mapping, args): """Return the author, updated according to the value set in the .mailmap file""" @@ -252,13 +279,14 @@ # i18n: "get" is a keyword raise error.ParseError(_("get() expects two arguments")) - dictarg = evalfuncarg(context, mapping, args[0]) - if not util.safehasattr(dictarg, 'get'): + dictarg = evalwrapped(context, mapping, args[0]) + key = evalrawexp(context, mapping, args[1]) + try: + return dictarg.getmember(context, mapping, key) + except error.ParseError as err: # i18n: "get" is a keyword - raise error.ParseError(_("get() expects a dict as first argument")) - - key = evalfuncarg(context, mapping, args[1]) - return templateutil.getdictitem(dictarg, key) + hint = _("get() expects a dict as first argument") + raise error.ParseError(bytes(err), hint=hint) @templatefunc('if(expr, then[, else])') def if_(context, mapping, args): @@ -282,13 +310,10 @@ # i18n: "ifcontains" is a keyword raise error.ParseError(_("ifcontains expects three or four arguments")) - haystack = evalfuncarg(context, mapping, args[1]) - keytype = getattr(haystack, 'keytype', None) + haystack = evalwrapped(context, mapping, args[1]) try: needle = evalrawexp(context, mapping, args[0]) - needle = templateutil.unwrapastype(context, mapping, needle, - keytype or bytes) - found = (needle in haystack) + found = haystack.contains(context, mapping, needle) except error.ParseError: found = False @@ -319,18 +344,13 @@ # i18n: "join" is a keyword raise error.ParseError(_("join expects one or two arguments")) - joinset = evalrawexp(context, mapping, args[0]) + joinset = evalwrapped(context, mapping, args[0]) joiner = " " if len(args) > 1: joiner = evalstring(context, mapping, args[1]) - if isinstance(joinset, templateutil.wrapped): - return joinset.join(context, mapping, joiner) - # TODO: perhaps a generator should be stringify()-ed here, but we can't - # because hgweb abuses it as a keyword that returns a list of dicts. - joinset = templateutil.unwrapvalue(context, mapping, joinset) - return templateutil.joinitems(pycompat.maybebytestr(joinset), joiner) + return joinset.join(context, mapping, joiner) -@templatefunc('label(label, expr)') +@templatefunc('label(label, expr)', requires={'ui'}) def label(context, mapping, args): """Apply a label to generated content. Content with a label applied can result in additional post-processing, such as @@ -352,7 +372,9 @@ """The global tags matching the given pattern on the most recent globally tagged ancestor of this changeset. If no such tags exist, the "{tag}" template resolves to - the string "null".""" + the string "null". See :hg:`help revisions.patterns` for the pattern + syntax. + """ if len(args) > 1: # i18n: "latesttag" is a keyword raise error.ParseError(_("latesttag expects at most one argument")) @@ -388,7 +410,7 @@ raise error.ParseError(_("localdate expects a timezone")) else: tzoffset = dateutil.makedate()[1] - return (date[0], tzoffset) + return templateutil.date((date[0], tzoffset)) @templatefunc('max(iterable)') def max_(context, mapping, args, **kwargs): @@ -397,13 +419,13 @@ # i18n: "max" is a keyword raise error.ParseError(_("max expects one argument")) - iterable = evalfuncarg(context, mapping, args[0]) + iterable = evalwrapped(context, mapping, args[0]) try: - x = max(pycompat.maybebytestr(iterable)) - except (TypeError, ValueError): + return iterable.getmax(context, mapping) + except error.ParseError as err: # i18n: "max" is a keyword - raise error.ParseError(_("max first argument should be an iterable")) - return templateutil.wraphybridvalue(iterable, x, x) + hint = _("max first argument should be an iterable") + raise error.ParseError(bytes(err), hint=hint) @templatefunc('min(iterable)') def min_(context, mapping, args, **kwargs): @@ -412,13 +434,13 @@ # i18n: "min" is a keyword raise error.ParseError(_("min expects one argument")) - iterable = evalfuncarg(context, mapping, args[0]) + iterable = evalwrapped(context, mapping, args[0]) try: - x = min(pycompat.maybebytestr(iterable)) - except (TypeError, ValueError): + return iterable.getmin(context, mapping) + except error.ParseError as err: # i18n: "min" is a keyword - raise error.ParseError(_("min first argument should be an iterable")) - return templateutil.wraphybridvalue(iterable, x, x) + hint = _("min first argument should be an iterable") + raise error.ParseError(bytes(err), hint=hint) @templatefunc('mod(a, b)') def mod(context, mapping, args): @@ -458,6 +480,7 @@ markers = evalfuncarg(context, mapping, args[0]) try: + # TODO: maybe this has to be a wrapped list of date wrappers? data = obsutil.markersdates(markers) return templateutil.hybridlist(data, name='date', fmt='%d %d') except (TypeError, KeyError): @@ -500,7 +523,7 @@ errmsg = _("obsfateverb first argument should be countable") raise error.ParseError(errmsg) -@templatefunc('relpath(path)') +@templatefunc('relpath(path)', requires={'repo'}) def relpath(context, mapping, args): """Convert a repository-absolute path into a filesystem path relative to the current working directory.""" @@ -508,11 +531,11 @@ # i18n: "relpath" is a keyword raise error.ParseError(_("relpath expects one argument")) - repo = context.resource(mapping, 'ctx').repo() + repo = context.resource(mapping, 'repo') path = evalstring(context, mapping, args[0]) return repo.pathto(path) -@templatefunc('revset(query[, formatargs...])') +@templatefunc('revset(query[, formatargs...])', requires={'repo', 'cache'}) def revset(context, mapping, args): """Execute a revision set query. See :hg:`help revset`.""" @@ -521,8 +544,7 @@ raise error.ParseError(_("revset expects one or more arguments")) raw = evalstring(context, mapping, args[0]) - ctx = context.resource(mapping, 'ctx') - repo = ctx.repo() + repo = context.resource(mapping, 'repo') def query(expr): m = revsetmod.match(repo.ui, expr, lookup=revsetmod.lookupfn(repo)) @@ -574,7 +596,7 @@ yield sep yield argstr -@templatefunc('shortest(node, minlength=4)') +@templatefunc('shortest(node, minlength=4)', requires={'repo'}) def shortest(context, mapping, args): """Obtain the shortest representation of a node.""" @@ -590,7 +612,7 @@ # i18n: "shortest" is a keyword _("shortest() expects an integer minlength")) - repo = context.resource(mapping, 'ctx')._repo + repo = context.resource(mapping, 'repo') if len(hexnode) > 40: return hexnode elif len(hexnode) == 40: @@ -601,11 +623,16 @@ else: try: node = scmutil.resolvehexnodeidprefix(repo, hexnode) - except (error.LookupError, error.WdirUnsupported): + except error.WdirUnsupported: + node = wdirid + except error.LookupError: return hexnode if not node: return hexnode - return scmutil.shortesthexnodeidprefix(repo, node, minlength) + try: + return scmutil.shortesthexnodeidprefix(repo, node, minlength) + except error.RepoLookupError: + return hexnode @templatefunc('strip(text[, chars])') def strip(context, mapping, args):
--- a/mercurial/templatekw.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templatekw.py Thu Jul 19 13:55:54 2018 -0400 @@ -14,6 +14,7 @@ ) from . import ( + diffutil, encoding, error, hbisect, @@ -31,41 +32,12 @@ ) _hybrid = templateutil.hybrid -_mappable = templateutil.mappable hybriddict = templateutil.hybriddict hybridlist = templateutil.hybridlist compatdict = templateutil.compatdict compatlist = templateutil.compatlist _showcompatlist = templateutil._showcompatlist -def _showlist(name, values, templ, mapping, plural=None, separator=' '): - ui = mapping.get('ui') - if ui: - ui.deprecwarn("templatekw._showlist() is deprecated, use " - "templateutil._showcompatlist()", '4.6') - context = templ # this is actually a template context, not a templater - return _showcompatlist(context, mapping, name, values, plural, separator) - -def showdict(name, data, mapping, plural=None, key='key', value='value', - fmt=None, separator=' '): - ui = mapping.get('ui') - if ui: - ui.deprecwarn("templatekw.showdict() is deprecated, use " - "templateutil.compatdict()", '4.6') - c = [{key: k, value: v} for k, v in data.iteritems()] - f = _showlist(name, c, mapping['templ'], mapping, plural, separator) - return hybriddict(data, key=key, value=value, fmt=fmt, gen=f) - -def showlist(name, values, mapping, plural=None, element=None, separator=' '): - ui = mapping.get('ui') - if ui: - ui.deprecwarn("templatekw.showlist() is deprecated, use " - "templateutil.compatlist()", '4.6') - if not element: - element = name - f = _showlist(name, values, mapping['templ'], mapping, plural, separator) - return hybridlist(values, name=element, gen=f) - def getlatesttags(context, mapping, pattern=None): '''return date, distance and name for the latest tag of rev''' repo = context.resource(mapping, 'repo') @@ -139,7 +111,7 @@ for i in fl: lr = fl.linkrev(i) renamed = fl.renamed(fl.node(i)) - rcache[fn][lr] = renamed + rcache[fn][lr] = renamed and renamed[0] if lr >= endrev: break if rev in rcache[fn]: @@ -148,7 +120,8 @@ # If linkrev != rev (i.e. rev not found in rcache) fallback to # filectx logic. try: - return repo[rev][fn].renamed() + renamed = repo[rev][fn].renamed() + return renamed and renamed[0] except error.LookupError: return None @@ -268,7 +241,9 @@ def showdate(context, mapping): """Date information. The date when the changeset was committed.""" ctx = context.resource(mapping, 'ctx') - return ctx.date() + # the default string format is '<float(unixtime)><tzoffset>' because + # python-hglib splits date at decimal separator. + return templateutil.date(ctx.date(), showfmt='%d.0%d') @templatekeyword('desc', requires={'ctx'}) def showdescription(context, mapping): @@ -278,16 +253,21 @@ if isinstance(s, encoding.localstr): # try hard to preserve utf-8 bytes return encoding.tolocal(encoding.fromlocal(s).strip()) + elif isinstance(s, encoding.safelocalstr): + return encoding.safelocalstr(s.strip()) else: return s.strip() -@templatekeyword('diffstat', requires={'ctx'}) +@templatekeyword('diffstat', requires={'ui', 'ctx'}) def showdiffstat(context, mapping): """String. Statistics of changes with the following format: "modified files: +added/-removed lines" """ + ui = context.resource(mapping, 'ui') ctx = context.resource(mapping, 'ctx') - stats = patch.diffstatdata(util.iterlines(ctx.diff(noprefix=False))) + diffopts = diffutil.diffallopts(ui, {'noprefix': False}) + diff = ctx.diff(opts=diffopts) + stats = patch.diffstatdata(util.iterlines(diff)) maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats) return '%d: +%d/-%d' % (len(stats), adds, removes) @@ -344,7 +324,7 @@ for fn in ctx.files(): rename = getrenamed(fn, ctx.rev()) if rename: - copies.append((fn, rename[0])) + copies.append((fn, rename)) copies = util.sortdict(copies) return compatdict(context, mapping, 'file_copy', copies, @@ -392,12 +372,19 @@ return getgraphnode(repo, ctx) def getgraphnode(repo, ctx): + return getgraphnodecurrent(repo, ctx) or getgraphnodesymbol(ctx) + +def getgraphnodecurrent(repo, ctx): wpnodes = repo.dirstate.parents() if wpnodes[1] == nullid: wpnodes = wpnodes[:1] if ctx.node() in wpnodes: return '@' - elif ctx.obsolete(): + else: + return '' + +def getgraphnodesymbol(ctx): + if ctx.obsolete(): return 'x' elif ctx.isunstable(): return '*' @@ -481,13 +468,14 @@ if mnode is None: # just avoid crash, we might want to use the 'ff...' hash in future return - mrev = repo.manifestlog._revlog.rev(mnode) + mrev = repo.manifestlog.rev(mnode) mhex = hex(mnode) mapping = context.overlaymap(mapping, {'rev': mrev, 'node': mhex}) f = context.process('manifest', mapping) # TODO: perhaps 'ctx' should be dropped from mapping because manifest # rev and node are completely different from changeset's. - return _mappable(f, None, f, lambda x: {'rev': mrev, 'node': mhex}) + return templateutil.hybriditem(f, None, f, + lambda x: {'rev': mrev, 'node': mhex}) @templatekeyword('obsfate', requires={'ui', 'repo', 'ctx'}) def showobsfate(context, mapping): @@ -583,7 +571,7 @@ repo = context.resource(mapping, 'repo') ctx = context.resource(mapping, 'ctx') predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node())) - predecessors = map(hex, predecessors) + predecessors = pycompat.maplist(hex, predecessors) return _hybrid(None, predecessors, lambda x: {'ctx': repo[x]},
--- a/mercurial/templater.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templater.py Thu Jul 19 13:55:54 2018 -0400 @@ -26,23 +26,23 @@ values of any printable types, and will be folded by ``stringify()`` or ``flatten()``. - BUG: hgweb overloads this type for mappings (i.e. some hgweb keywords - returns a generator of dicts.) - None sometimes represents an empty value, which can be stringified to ''. True, False, int, float can be stringified as such. -date tuple - a (unixtime, offset) tuple, which produces no meaningful output by itself. +wrappedbytes, wrappedvalue + a wrapper for the above printable types. + +date + represents a (unixtime, offset) tuple. hybrid represents a list/dict of printable values, which can also be converted to mappings by % operator. -mappable +hybriditem represents a scalar printable value, also supports % operator. mappinggenerator, mappinglist @@ -253,7 +253,8 @@ p = parser.parser(elements) try: while pos < stop: - n = min((tmpl.find(c, pos, stop) for c in sepchars), + n = min((tmpl.find(c, pos, stop) + for c in pycompat.bytestr(sepchars)), key=lambda n: (n < 0, n)) if n < 0: yield ('string', unescape(tmpl[pos:stop]), pos) @@ -596,8 +597,7 @@ filter uses function to transform value. syntax is {key|filter1|filter2|...}.''' - def __init__(self, loader, filters=None, defaults=None, resources=None, - aliases=()): + def __init__(self, loader, filters=None, defaults=None, resources=None): self._loader = loader if filters is None: filters = {} @@ -609,7 +609,6 @@ resources = nullresourcemapper() self._defaults = defaults self._resources = resources - self._aliasmap = _aliasrules.buildmap(aliases) self._cache = {} # key: (func, data) self._tmplcache = {} # literal template: (func, data) @@ -664,12 +663,10 @@ def _load(self, t): '''load, parse, and cache a template''' if t not in self._cache: + x = self._loader(t) # put poison to cut recursion while compiling 't' self._cache[t] = (_runrecursivesymbol, t) try: - x = parse(self._loader(t)) - if self._aliasmap: - x = _aliasrules.expand(self._aliasmap, x) self._cache[t] = compileexp(x, self, methods) except: # re-raises del self._cache[t] @@ -717,8 +714,6 @@ mapping = extramapping return templateutil.flatten(self, mapping, func(self, mapping, data)) -engines = {'default': engine} - def stylelist(): paths = templatepaths() if not paths: @@ -776,13 +771,81 @@ conf.source('templates', key)) cache[key] = unquotestring(val) elif key != '__base__': - val = 'default', val - if ':' in val[1]: - val = val[1].split(':', 1) - tmap[key] = val[0], os.path.join(base, val[1]) + tmap[key] = os.path.join(base, val) aliases.extend(conf['templatealias'].items()) return cache, tmap, aliases +class loader(object): + """Load template fragments optionally from a map file""" + + def __init__(self, cache, aliases): + if cache is None: + cache = {} + self.cache = cache.copy() + self._map = {} + self._aliasmap = _aliasrules.buildmap(aliases) + + def __contains__(self, key): + return key in self.cache or key in self._map + + def load(self, t): + """Get parsed tree for the given template name. Use a local cache.""" + if t not in self.cache: + try: + self.cache[t] = util.readfile(self._map[t]) + except KeyError as inst: + raise templateutil.TemplateNotFound( + _('"%s" not in template map') % inst.args[0]) + except IOError as inst: + reason = (_('template file %s: %s') + % (self._map[t], + stringutil.forcebytestr(inst.args[1]))) + raise IOError(inst.args[0], encoding.strfromlocal(reason)) + return self._parse(self.cache[t]) + + def _parse(self, tmpl): + x = parse(tmpl) + if self._aliasmap: + x = _aliasrules.expand(self._aliasmap, x) + return x + + def _findsymbolsused(self, tree, syms): + if not tree: + return + op = tree[0] + if op == 'symbol': + s = tree[1] + if s in syms[0]: + return # avoid recursion: s -> cache[s] -> s + syms[0].add(s) + if s in self.cache or s in self._map: + # s may be a reference for named template + self._findsymbolsused(self.load(s), syms) + return + if op in {'integer', 'string'}: + return + # '{arg|func}' == '{func(arg)}' + if op == '|': + syms[1].add(getsymbol(tree[2])) + self._findsymbolsused(tree[1], syms) + return + if op == 'func': + syms[1].add(getsymbol(tree[1])) + self._findsymbolsused(tree[2], syms) + return + for x in tree[1:]: + self._findsymbolsused(x, syms) + + def symbolsused(self, t): + """Look up (keywords, filters/functions) referenced from the name + template 't' + + This may load additional templates from the map file. + """ + syms = (set(), set()) + self._findsymbolsused(self.load(t), syms) + return syms + class templater(object): def __init__(self, filters=None, defaults=None, resources=None, @@ -800,21 +863,12 @@ self.cache may be updated later to register additional template fragments. """ - if filters is None: - filters = {} - if defaults is None: - defaults = {} - if cache is None: - cache = {} - self.cache = cache.copy() - self.map = {} - self.filters = templatefilters.filters.copy() - self.filters.update(filters) - self.defaults = defaults - self._resources = resources - self._aliases = aliases - self.minchunk, self.maxchunk = minchunk, maxchunk - self.ecache = {} + allfilters = templatefilters.filters.copy() + if filters: + allfilters.update(filters) + self._loader = loader(cache, aliases) + self._proc = engine(self._loader.load, allfilters, defaults, resources) + self._minchunk, self._maxchunk = minchunk, maxchunk @classmethod def frommapfile(cls, mapfile, filters=None, defaults=None, resources=None, @@ -822,28 +876,46 @@ """Create templater from the specified map file""" t = cls(filters, defaults, resources, cache, [], minchunk, maxchunk) cache, tmap, aliases = _readmapfile(mapfile) - t.cache.update(cache) - t.map = tmap - t._aliases = aliases + t._loader.cache.update(cache) + t._loader._map = tmap + t._loader._aliasmap = _aliasrules.buildmap(aliases) return t def __contains__(self, key): - return key in self.cache or key in self.map + return key in self._loader + + @property + def cache(self): + return self._loader.cache + + # for highlight extension to insert one-time 'colorize' filter + @property + def _filters(self): + return self._proc._filters + + @property + def defaults(self): + return self._proc._defaults def load(self, t): - '''Get the template for the given template name. Use a local cache.''' - if t not in self.cache: - try: - self.cache[t] = util.readfile(self.map[t][1]) - except KeyError as inst: - raise templateutil.TemplateNotFound( - _('"%s" not in template map') % inst.args[0]) - except IOError as inst: - reason = (_('template file %s: %s') - % (self.map[t][1], - stringutil.forcebytestr(inst.args[1]))) - raise IOError(inst.args[0], encoding.strfromlocal(reason)) - return self.cache[t] + """Get parsed tree for the given template name. Use a local cache.""" + return self._loader.load(t) + + def symbolsuseddefault(self): + """Look up (keywords, filters/functions) referenced from the default + unnamed template + + This may load additional templates from the map file. + """ + return self.symbolsused('') + + def symbolsused(self, t): + """Look up (keywords, filters/functions) referenced from the name + template 't' + + This may load additional templates from the map file. + """ + return self._loader.symbolsused(t) def renderdefault(self, mapping): """Render the default unnamed template and return result as string""" @@ -856,20 +928,10 @@ def generate(self, t, mapping): """Return a generator that renders the specified named template and yields chunks""" - ttype = t in self.map and self.map[t][0] or 'default' - if ttype not in self.ecache: - try: - ecls = engines[ttype] - except KeyError: - raise error.Abort(_('invalid template engine: %s') % ttype) - self.ecache[ttype] = ecls(self.load, self.filters, self.defaults, - self._resources, self._aliases) - proc = self.ecache[ttype] - - stream = proc.process(t, mapping) - if self.minchunk: - stream = util.increasingchunks(stream, min=self.minchunk, - max=self.maxchunk) + stream = self._proc.process(t, mapping) + if self._minchunk: + stream = util.increasingchunks(stream, min=self._minchunk, + max=self._maxchunk) return stream def templatepaths():
--- a/mercurial/templates/gitweb/graph.tmpl Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/gitweb/graph.tmpl Thu Jul 19 13:55:54 2018 -0400 @@ -21,7 +21,7 @@ <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a> | <a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a> | <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a> | -<a href="{url|urlescape}file/{symrev}{sessionvars%urlparameter}">files</a> | +<a href="{url|urlescape}file/{symrev}{sessionvars%urlparameter}">files</a>{archives%archiveentry} | <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a> <br/> <a href="{url|urlescape}graph/{symrev}{lessvars%urlparameter}">less</a>
--- a/mercurial/templates/gitweb/manifest.tmpl Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/gitweb/manifest.tmpl Thu Jul 19 13:55:54 2018 -0400 @@ -30,13 +30,7 @@ <div class="title">{path|escape} {alltags}</div> <table cellspacing="0"> -<tr class="parity{upparity}"> -<td style="font-family:monospace">drwxr-xr-x</td> -<td style="font-family:monospace"></td> -<td style="font-family:monospace"></td> -<td><a href="{url|urlescape}file/{symrev}{up|urlescape}{sessionvars%urlparameter}">[up]</a></td> -<td class="link"> </td> -</tr> +{ifeq(path, up, '', updirentry)} {dentries%direntry} {fentries%fileentry} </table>
--- a/mercurial/templates/gitweb/map Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/gitweb/map Thu Jul 19 13:55:54 2018 -0400 @@ -59,6 +59,16 @@ changelogentry = changelogentry.tmpl changeset = changeset.tmpl manifest = manifest.tmpl +updirentry = ' + <tr class="parity{upparity}"> + <td style="font-family:monospace">drwxr-xr-x</td> + <td style="font-family:monospace"></td> + <td style="font-family:monospace"></td> + <td> + <a href="{url|urlescape}file/{symrev}{up|urlescape}{sessionvars%urlparameter}">[up]</a> + </td> + <td class="link"> </td> + </tr>' direntry = ' <tr class="parity{parity}"> <td style="font-family:monospace">drwxr-xr-x</td>
--- a/mercurial/templates/map-cmdline.show Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/map-cmdline.show Thu Jul 19 13:55:54 2018 -0400 @@ -15,7 +15,11 @@ # Treat branch and tags specially so we don't display "default" or "tip" cset_namespace = '{ifeq(namespace, "branches", names_branches, ifeq(namespace, "tags", names_tags, names_others))}' names_branches = '{ifeq(branch, "default", "", " ({label('log.{colorname}', branch)})")}' -names_tags = '{if(names % "{ifeq(name, 'tip', '', name)}", " ({label('log.{colorname}', join(names % "{ifeq(name, 'tip', '', name)}", ' '))})")}' +names_tags = '{if(filter_tags(names), + " ({label('log.{colorname}', join(filter_tags(names), ' '))})")}' names_others = '{if(names, " ({label('log.{colorname}', join(names, ' '))})")}' cset_shortdesc = '{label("log.description", desc|firstline)}' + +[templatealias] +filter_tags(names) = filter(names, ifeq(name, 'tip', '', name))
--- a/mercurial/templates/monoblue/graph.tmpl Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/monoblue/graph.tmpl Thu Jul 19 13:55:54 2018 -0400 @@ -20,6 +20,7 @@ <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li> <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li> <li><a href="{url|urlescape}file/{symrev}{sessionvars%urlparameter}">files</a></li> + {archives%archiveentry} <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li> </ul> </div>
--- a/mercurial/templates/monoblue/manifest.tmpl Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/monoblue/manifest.tmpl Thu Jul 19 13:55:54 2018 -0400 @@ -33,13 +33,7 @@ <p class="files">{path|escape} {alltags}</p> <table> - <tr class="parity{upparity}"> - <td>drwxr-xr-x</td> - <td></td> - <td></td> - <td><a href="{url|urlescape}file/{symrev}{up|urlescape}{sessionvars%urlparameter}">[up]</a></td> - <td class="link"> </td> - </tr> + {ifeq(path, up, '', updirentry)} {dentries%direntry} {fentries%fileentry} </table>
--- a/mercurial/templates/monoblue/map Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/monoblue/map Thu Jul 19 13:55:54 2018 -0400 @@ -59,6 +59,16 @@ changelogentry = changelogentry.tmpl changeset = changeset.tmpl manifest = manifest.tmpl +updirentry = ' + <tr class="parity{upparity}"> + <td>drwxr-xr-x</td> + <td></td> + <td></td> + <td> + <a href="{url|urlescape}file/{symrev}{up|urlescape}{sessionvars%urlparameter}">[up]</a> + </td> + <td class="link"> </td> + </tr>' direntry = ' <tr class="parity{parity}"> <td>drwxr-xr-x</td>
--- a/mercurial/templates/paper/graph.tmpl Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/paper/graph.tmpl Thu Jul 19 13:55:54 2018 -0400 @@ -25,6 +25,9 @@ <li><a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">browse</a></li> </ul> <ul> +{archives%archiveentry} +</ul> +<ul> <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li> </ul> <div class="atom-logo">
--- a/mercurial/templates/paper/manifest.tmpl Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/paper/manifest.tmpl Thu Jul 19 13:55:54 2018 -0400 @@ -46,11 +46,7 @@ </tr> </thead> <tbody class="stripes2"> -<tr class="fileline"> - <td class="name"><a href="{url|urlescape}file/{symrev}{up|urlescape}{sessionvars%urlparameter}">[up]</a></td> - <td class="size"></td> - <td class="permissions">drwxr-xr-x</td> -</tr> +{ifeq(path, up, '', updirentry)} {dentries%direntry} {fentries%fileentry} </tbody>
--- a/mercurial/templates/paper/map Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/paper/map Thu Jul 19 13:55:54 2018 -0400 @@ -41,6 +41,15 @@ navgraph = '{before%navgraphentry}{after%navgraphentry}' filenav = '{before%filenaventry}{after%filenaventry}' +updirentry = ' + <tr class="fileline"> + <td class="name"> + <a href="{url|urlescape}file/{symrev}{up|urlescape}{sessionvars%urlparameter}">[up]</a> + </td> + <td class="size"></td> + <td class="permissions">drwxr-xr-x</td> + </tr>' + direntry = ' <tr class="fileline"> <td class="name">
--- a/mercurial/templates/spartan/manifest.tmpl Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/spartan/manifest.tmpl Thu Jul 19 13:55:54 2018 -0400 @@ -17,12 +17,7 @@ <h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / files for changeset <a href="{url|urlescape}rev/{node|short}">{node|short}</a>: {path|escape}</h2> <table cellpadding="0" cellspacing="0"> -<tr class="parity{upparity}"> - <td><tt>drwxr-xr-x</tt> - <td> - <td> - <td><a href="{url|urlescape}file/{symrev}{up|urlescape}{sessionvars%urlparameter}">[up]</a> -</tr> +{ifeq(path, up, '', updirentry)} {dentries%direntry} {fentries%fileentry} </table>
--- a/mercurial/templates/spartan/map Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/spartan/map Thu Jul 19 13:55:54 2018 -0400 @@ -25,23 +25,36 @@ navgraph = '{before%navgraphentry}{after%navgraphentry}' filenav = '{before%filenaventry}{after%filenaventry}' +updirentry = ' + <tr class="parity{upparity}"> + <td><tt>drwxr-xr-x</tt> </td> + <td> </td> + <td> </td> + <td> + <a href="{url|urlescape}file/{symrev}{up|urlescape}{sessionvars%urlparameter}">[up]</a> + </td> + </tr> ' + direntry = ' <tr class="parity{parity}"> - <td><tt>drwxr-xr-x</tt> - <td> - <td> + <td><tt>drwxr-xr-x</tt> </td> + <td> </td> + <td> </td> <td> <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">{basename|escape}/</a> <a href="{url|urlescape}file/{symrev}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}"> {emptydirs|urlescape} - </a>' + </a> + </td> + </tr>' fileentry = ' <tr class="parity{parity}"> - <td><tt>{permissions|permissions}</tt> - <td align=right><tt class="date">{date|isodate}</tt> - <td align=right><tt>{size}</tt> - <td><a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>' + <td><tt>{permissions|permissions}</tt> </td> + <td align=right><tt class="date">{date|isodate}</tt> </td> + <td align=right><tt>{size}</tt> </td> + <td><a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">{basename|escape}</a></td> + </tr>' filerevision = filerevision.tmpl fileannotate = fileannotate.tmpl
--- a/mercurial/templates/static/style-gitweb.css Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/static/style-gitweb.css Thu Jul 19 13:55:54 2018 -0400 @@ -74,6 +74,7 @@ background: #ffc; border: 1px solid yellow; border-radius: 5px; + z-index: 15; } #searchform:hover div#hint { display: block; }
--- a/mercurial/templates/static/style-paper.css Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templates/static/style-paper.css Thu Jul 19 13:55:54 2018 -0400 @@ -90,6 +90,7 @@ font-size: 70%; border: 1px solid yellow; border-radius: 5px; + z-index: 15; } form.search:hover div#hint { display: block; }
--- a/mercurial/templateutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/templateutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -31,13 +31,46 @@ """Object requiring extra conversion prior to displaying or processing as value - Use unwrapvalue(), unwrapastype(), or unwraphybrid() to obtain the inner - object. + Use unwrapvalue() or unwrapastype() to obtain the inner object. """ __metaclass__ = abc.ABCMeta @abc.abstractmethod + def contains(self, context, mapping, item): + """Test if the specified item is in self + + The item argument may be a wrapped object. + """ + + @abc.abstractmethod + def getmember(self, context, mapping, key): + """Return a member item for the specified key + + The key argument may be a wrapped object. + A returned object may be either a wrapped object or a pure value + depending on the self type. + """ + + @abc.abstractmethod + def getmin(self, context, mapping): + """Return the smallest item, which may be either a wrapped or a pure + value depending on the self type""" + + @abc.abstractmethod + def getmax(self, context, mapping): + """Return the largest item, which may be either a wrapped or a pure + value depending on the self type""" + + @abc.abstractmethod + def filter(self, context, mapping, select): + """Return new container of the same type which includes only the + selected elements + + select() takes each item as a wrapped object and returns True/False. + """ + + @abc.abstractmethod def itermaps(self, context): """Yield each template mapping""" @@ -60,16 +93,153 @@ """ @abc.abstractmethod + def tobool(self, context, mapping): + """Return a boolean representation of the inner value""" + + @abc.abstractmethod def tovalue(self, context, mapping): """Move the inner value object out or create a value representation A returned value must be serializable by templaterfilters.json(). """ -# stub for representing a date type; may be a real date type that can -# provide a readable string value -class date(object): - pass +class mappable(object): + """Object which can be converted to a single template mapping""" + + def itermaps(self, context): + yield self.tomap(context) + + @abc.abstractmethod + def tomap(self, context): + """Create a single template mapping representing this""" + +class wrappedbytes(wrapped): + """Wrapper for byte string""" + + def __init__(self, value): + self._value = value + + def contains(self, context, mapping, item): + item = stringify(context, mapping, item) + return item in self._value + + def getmember(self, context, mapping, key): + raise error.ParseError(_('%r is not a dictionary') + % pycompat.bytestr(self._value)) + + def getmin(self, context, mapping): + return self._getby(context, mapping, min) + + def getmax(self, context, mapping): + return self._getby(context, mapping, max) + + def _getby(self, context, mapping, func): + if not self._value: + raise error.ParseError(_('empty string')) + return func(pycompat.iterbytestr(self._value)) + + def filter(self, context, mapping, select): + raise error.ParseError(_('%r is not filterable') + % pycompat.bytestr(self._value)) + + def itermaps(self, context): + raise error.ParseError(_('%r is not iterable of mappings') + % pycompat.bytestr(self._value)) + + def join(self, context, mapping, sep): + return joinitems(pycompat.iterbytestr(self._value), sep) + + def show(self, context, mapping): + return self._value + + def tobool(self, context, mapping): + return bool(self._value) + + def tovalue(self, context, mapping): + return self._value + +class wrappedvalue(wrapped): + """Generic wrapper for pure non-list/dict/bytes value""" + + def __init__(self, value): + self._value = value + + def contains(self, context, mapping, item): + raise error.ParseError(_("%r is not iterable") % self._value) + + def getmember(self, context, mapping, key): + raise error.ParseError(_('%r is not a dictionary') % self._value) + + def getmin(self, context, mapping): + raise error.ParseError(_("%r is not iterable") % self._value) + + def getmax(self, context, mapping): + raise error.ParseError(_("%r is not iterable") % self._value) + + def filter(self, context, mapping, select): + raise error.ParseError(_("%r is not iterable") % self._value) + + def itermaps(self, context): + raise error.ParseError(_('%r is not iterable of mappings') + % self._value) + + def join(self, context, mapping, sep): + raise error.ParseError(_('%r is not iterable') % self._value) + + def show(self, context, mapping): + if self._value is None: + return b'' + return pycompat.bytestr(self._value) + + def tobool(self, context, mapping): + if self._value is None: + return False + if isinstance(self._value, bool): + return self._value + # otherwise evaluate as string, which means 0 is True + return bool(pycompat.bytestr(self._value)) + + def tovalue(self, context, mapping): + return self._value + +class date(mappable, wrapped): + """Wrapper for date tuple""" + + def __init__(self, value, showfmt='%d %d'): + # value may be (float, int), but public interface shouldn't support + # floating-point timestamp + self._unixtime, self._tzoffset = map(int, value) + self._showfmt = showfmt + + def contains(self, context, mapping, item): + raise error.ParseError(_('date is not iterable')) + + def getmember(self, context, mapping, key): + raise error.ParseError(_('date is not a dictionary')) + + def getmin(self, context, mapping): + raise error.ParseError(_('date is not iterable')) + + def getmax(self, context, mapping): + raise error.ParseError(_('date is not iterable')) + + def filter(self, context, mapping, select): + raise error.ParseError(_('date is not iterable')) + + def join(self, context, mapping, sep): + raise error.ParseError(_("date is not iterable")) + + def show(self, context, mapping): + return self._showfmt % (self._unixtime, self._tzoffset) + + def tomap(self, context): + return {'unixtime': self._unixtime, 'tzoffset': self._tzoffset} + + def tobool(self, context, mapping): + return True + + def tovalue(self, context, mapping): + return (self._unixtime, self._tzoffset) class hybrid(wrapped): """Wrapper for list or dict to support legacy template @@ -88,7 +258,46 @@ self._values = values self._makemap = makemap self._joinfmt = joinfmt - self.keytype = keytype # hint for 'x in y' where type(x) is unresolved + self._keytype = keytype # hint for 'x in y' where type(x) is unresolved + + def contains(self, context, mapping, item): + item = unwrapastype(context, mapping, item, self._keytype) + return item in self._values + + def getmember(self, context, mapping, key): + # TODO: maybe split hybrid list/dict types? + if not util.safehasattr(self._values, 'get'): + raise error.ParseError(_('not a dictionary')) + key = unwrapastype(context, mapping, key, self._keytype) + return self._wrapvalue(key, self._values.get(key)) + + def getmin(self, context, mapping): + return self._getby(context, mapping, min) + + def getmax(self, context, mapping): + return self._getby(context, mapping, max) + + def _getby(self, context, mapping, func): + if not self._values: + raise error.ParseError(_('empty sequence')) + val = func(self._values) + return self._wrapvalue(val, val) + + def _wrapvalue(self, key, val): + if val is None: + return + if util.safehasattr(val, '_makemap'): + # a nested hybrid list/dict, which has its own way of map operation + return val + return hybriditem(None, key, val, self._makemap) + + def filter(self, context, mapping, select): + if util.safehasattr(self._values, 'get'): + values = {k: v for k, v in self._values.iteritems() + if select(self._wrapvalue(k, v))} + else: + values = [v for v in self._values if select(self._wrapvalue(v, v))] + return hybrid(None, values, self._makemap, self._joinfmt, self._keytype) def itermaps(self, context): makemap = self._makemap @@ -108,34 +317,24 @@ return gen() return gen - def tovalue(self, context, mapping): - # TODO: return self._values and get rid of proxy methods - return self + def tobool(self, context, mapping): + return bool(self._values) - def __contains__(self, x): - return x in self._values - def __getitem__(self, key): - return self._values[key] - def __len__(self): - return len(self._values) - def __iter__(self): - return iter(self._values) - def __getattr__(self, name): - if name not in (r'get', r'items', r'iteritems', r'iterkeys', - r'itervalues', r'keys', r'values'): - raise AttributeError(name) - return getattr(self._values, name) + def tovalue(self, context, mapping): + # TODO: make it non-recursive for trivial lists/dicts + xs = self._values + if util.safehasattr(xs, 'get'): + return {k: unwrapvalue(context, mapping, v) + for k, v in xs.iteritems()} + return [unwrapvalue(context, mapping, x) for x in xs] -class mappable(wrapped): +class hybriditem(mappable, wrapped): """Wrapper for non-list/dict object to support map operation This class allows us to handle both: - "{manifest}" - "{manifest % '{rev}:{node}'}" - "{manifest.rev}" - - Unlike a hybrid, this does not simulate the behavior of the underling - value. """ def __init__(self, gen, key, value, makemap): @@ -144,19 +343,32 @@ self._value = value # may be generator of strings self._makemap = makemap - def tomap(self): + def tomap(self, context): return self._makemap(self._key) - def itermaps(self, context): - yield self.tomap() + def contains(self, context, mapping, item): + w = makewrapped(context, mapping, self._value) + return w.contains(context, mapping, item) + + def getmember(self, context, mapping, key): + w = makewrapped(context, mapping, self._value) + return w.getmember(context, mapping, key) + + def getmin(self, context, mapping): + w = makewrapped(context, mapping, self._value) + return w.getmin(context, mapping) + + def getmax(self, context, mapping): + w = makewrapped(context, mapping, self._value) + return w.getmax(context, mapping) + + def filter(self, context, mapping, select): + w = makewrapped(context, mapping, self._value) + return w.filter(context, mapping, select) def join(self, context, mapping, sep): - # TODO: just copies the old behavior where a value was a generator - # yielding one item, but reconsider about it. join() over a string - # has no consistent result because a string may be a bytes, or a - # generator yielding an item, or a generator yielding multiple items. - # Preserving all of the current behaviors wouldn't make any sense. - return self.show(context, mapping) + w = makewrapped(context, mapping, self._value) + return w.join(context, mapping, sep) def show(self, context, mapping): # TODO: switch gen to (context, mapping) API? @@ -167,6 +379,10 @@ return gen() return gen + def tobool(self, context, mapping): + w = makewrapped(context, mapping, self._value) + return w.tobool(context, mapping) + def tovalue(self, context, mapping): return _unthunk(context, mapping, self._value) @@ -186,6 +402,22 @@ self._tmpl = tmpl self._defaultsep = sep + def contains(self, context, mapping, item): + raise error.ParseError(_('not comparable')) + + def getmember(self, context, mapping, key): + raise error.ParseError(_('not a dictionary')) + + def getmin(self, context, mapping): + raise error.ParseError(_('not comparable')) + + def getmax(self, context, mapping): + raise error.ParseError(_('not comparable')) + + def filter(self, context, mapping, select): + # implement if necessary; we'll need a wrapped type for a mapping dict + raise error.ParseError(_('not filterable without template')) + def join(self, context, mapping, sep): mapsiter = _iteroverlaymaps(context, mapping, self.itermaps(context)) if self._name: @@ -224,6 +456,9 @@ def itermaps(self, context): return self._make(context, *self._args) + def tobool(self, context, mapping): + return _nonempty(self.itermaps(context)) + class mappinglist(_mappingsequence): """Wrapper for list of template mappings""" @@ -234,6 +469,9 @@ def itermaps(self, context): return iter(self._mappings) + def tobool(self, context, mapping): + return bool(self._mappings) + class mappedgenerator(wrapped): """Wrapper for generator of strings which acts as a list @@ -246,9 +484,39 @@ self._make = make self._args = args + def contains(self, context, mapping, item): + item = stringify(context, mapping, item) + return item in self.tovalue(context, mapping) + def _gen(self, context): return self._make(context, *self._args) + def getmember(self, context, mapping, key): + raise error.ParseError(_('not a dictionary')) + + def getmin(self, context, mapping): + return self._getby(context, mapping, min) + + def getmax(self, context, mapping): + return self._getby(context, mapping, max) + + def _getby(self, context, mapping, func): + xs = self.tovalue(context, mapping) + if not xs: + raise error.ParseError(_('empty sequence')) + return func(xs) + + @staticmethod + def _filteredgen(context, mapping, make, args, select): + for x in make(context, *args): + s = stringify(context, mapping, x) + if select(wrappedbytes(s)): + yield s + + def filter(self, context, mapping, select): + args = (mapping, self._make, self._args, select) + return mappedgenerator(self._filteredgen, args) + def itermaps(self, context): raise error.ParseError(_('list of strings is not mappable')) @@ -258,6 +526,9 @@ def show(self, context, mapping): return self.join(context, mapping, '') + def tobool(self, context, mapping): + return _nonempty(self._gen(context)) + def tovalue(self, context, mapping): return [stringify(context, mapping, x) for x in self._gen(context)] @@ -278,33 +549,6 @@ prefmt = pycompat.bytestr return hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % prefmt(x)) -def unwraphybrid(context, mapping, thing): - """Return an object which can be stringified possibly by using a legacy - template""" - if not isinstance(thing, wrapped): - return thing - return thing.show(context, mapping) - -def unwrapvalue(context, mapping, thing): - """Move the inner value object out of the wrapper""" - if not isinstance(thing, wrapped): - return thing - return thing.tovalue(context, mapping) - -def wraphybridvalue(container, key, value): - """Wrap an element of hybrid container to be mappable - - The key is passed to the makemap function of the given container, which - should be an item generated by iter(container). - """ - makemap = getattr(container, '_makemap', None) - if makemap is None: - return value - if util.safehasattr(value, '_makemap'): - # a nested hybrid list/dict, which has its own way of map operation - return value - return mappable(None, key, value, makemap) - def compatdict(context, mapping, name, data, key='key', value='value', fmt=None, plural=None, separator=' '): """Wrap data like hybriddict(), but also supports old-style list template @@ -398,7 +642,8 @@ def flatten(context, mapping, thing): """Yield a single stream from a possibly nested set of iterators""" - thing = unwraphybrid(context, mapping, thing) + if isinstance(thing, wrapped): + thing = thing.show(context, mapping) if isinstance(thing, bytes): yield thing elif isinstance(thing, str): @@ -412,7 +657,8 @@ yield pycompat.bytestr(thing) else: for i in thing: - i = unwraphybrid(context, mapping, i) + if isinstance(i, wrapped): + i = i.show(context, mapping) if isinstance(i, bytes): yield i elif i is None: @@ -441,6 +687,13 @@ else: return None +def _nonempty(xiter): + try: + next(xiter) + return True + except StopIteration: + return False + def _unthunk(context, mapping, thing): """Evaluate a lazy byte string into value""" if not isinstance(thing, types.GeneratorType): @@ -453,15 +706,28 @@ func, data = arg return func(context, mapping, data) +def evalwrapped(context, mapping, arg): + """Evaluate given argument to wrapped object""" + thing = evalrawexp(context, mapping, arg) + return makewrapped(context, mapping, thing) + +def makewrapped(context, mapping, thing): + """Lift object to a wrapped type""" + if isinstance(thing, wrapped): + return thing + thing = _unthunk(context, mapping, thing) + if isinstance(thing, bytes): + return wrappedbytes(thing) + return wrappedvalue(thing) + def evalfuncarg(context, mapping, arg): """Evaluate given argument as value type""" - return _unwrapvalue(context, mapping, evalrawexp(context, mapping, arg)) + return unwrapvalue(context, mapping, evalrawexp(context, mapping, arg)) -# TODO: unify this with unwrapvalue() once the bug of templatefunc.join() -# is fixed. we can't do that right now because join() has to take a generator -# of byte strings as it is, not a lazy byte string. -def _unwrapvalue(context, mapping, thing): - thing = unwrapvalue(context, mapping, thing) +def unwrapvalue(context, mapping, thing): + """Move the inner value object out of the wrapper""" + if isinstance(thing, wrapped): + return thing.tovalue(context, mapping) # evalrawexp() may return string, generator of strings or arbitrary object # such as date tuple, but filter does not want generator. return _unthunk(context, mapping, thing) @@ -476,12 +742,7 @@ thing = stringutil.parsebool(data) else: thing = func(context, mapping, data) - thing = unwrapvalue(context, mapping, thing) - if isinstance(thing, bool): - return thing - # other objects are evaluated as strings, which means 0 is True, but - # empty dict/list should be False as they are expected to be '' - return bool(stringify(context, mapping, thing)) + return makewrapped(context, mapping, thing).tobool(context, mapping) def evaldate(context, mapping, arg, err=None): """Evaluate given argument as a date tuple or a date string; returns @@ -490,7 +751,10 @@ return unwrapdate(context, mapping, thing, err) def unwrapdate(context, mapping, thing, err=None): - thing = _unwrapvalue(context, mapping, thing) + if isinstance(thing, date): + return thing.tovalue(context, mapping) + # TODO: update hgweb to not return bare tuple; then just stringify 'thing' + thing = unwrapvalue(context, mapping, thing) try: return dateutil.parsedate(thing) except AttributeError: @@ -505,7 +769,7 @@ return unwrapinteger(context, mapping, thing, err) def unwrapinteger(context, mapping, thing, err=None): - thing = _unwrapvalue(context, mapping, thing) + thing = unwrapvalue(context, mapping, thing) try: return int(thing) except (TypeError, ValueError): @@ -525,7 +789,7 @@ return stringify(context, mapping, thing) _unwrapfuncbytype = { - None: _unwrapvalue, + None: unwrapvalue, bytes: stringify, date: unwrapdate, int: unwrapinteger, @@ -601,20 +865,6 @@ return (_("template filter '%s' is not compatible with keyword '%s'") % (fn, sym)) -def _checkeditermaps(darg, d): - try: - for v in d: - if not isinstance(v, dict): - raise TypeError - yield v - except TypeError: - sym = findsymbolicname(darg) - if sym: - raise error.ParseError(_("keyword '%s' is not iterable of mappings") - % sym) - else: - raise error.ParseError(_("%r is not iterable of mappings") % d) - def _iteroverlaymaps(context, origmapping, newmappings): """Generate combined mappings from the original mapping and an iterable of partial mappings to override the original""" @@ -623,38 +873,37 @@ lm['index'] = i yield lm -def _applymap(context, mapping, diter, targ): +def _applymap(context, mapping, d, darg, targ): + try: + diter = d.itermaps(context) + except error.ParseError as err: + sym = findsymbolicname(darg) + if not sym: + raise + hint = _("keyword '%s' does not support map operation") % sym + raise error.ParseError(bytes(err), hint=hint) for lm in _iteroverlaymaps(context, mapping, diter): yield evalrawexp(context, lm, targ) def runmap(context, mapping, data): darg, targ = data - d = evalrawexp(context, mapping, darg) - # TODO: a generator should be rejected because it is a thunk of lazy - # string, but we can't because hgweb abuses generator as a keyword - # that returns a list of dicts. - # TODO: drop _checkeditermaps() and pass 'd' to mappedgenerator so it - # can be restarted. - if isinstance(d, wrapped): - diter = d.itermaps(context) - else: - diter = _checkeditermaps(darg, d) - return mappedgenerator(_applymap, args=(mapping, diter, targ)) + d = evalwrapped(context, mapping, darg) + return mappedgenerator(_applymap, args=(mapping, d, darg, targ)) def runmember(context, mapping, data): darg, memb = data - d = evalrawexp(context, mapping, darg) - if util.safehasattr(d, 'tomap'): - lm = context.overlaymap(mapping, d.tomap()) + d = evalwrapped(context, mapping, darg) + if isinstance(d, mappable): + lm = context.overlaymap(mapping, d.tomap(context)) return runsymbol(context, lm, memb) - if util.safehasattr(d, 'get'): - return getdictitem(d, memb) - - sym = findsymbolicname(darg) - if sym: - raise error.ParseError(_("keyword '%s' has no member") % sym) - else: - raise error.ParseError(_("%r has no member") % pycompat.bytestr(d)) + try: + return d.getmember(context, mapping, memb) + except error.ParseError as err: + sym = findsymbolicname(darg) + if not sym: + raise + hint = _("keyword '%s' does not support member operation") % sym + raise error.ParseError(bytes(err), hint=hint) def runnegate(context, mapping, data): data = evalinteger(context, mapping, data, @@ -672,12 +921,6 @@ except ZeroDivisionError: raise error.Abort(_('division by zero is not defined')) -def getdictitem(dictarg, key): - val = dictarg.get(key) - if val is None: - return - return wraphybridvalue(dictarg, key, val) - def joinitems(itemiter, sep): """Join items with the separator; Returns generator of bytes""" first = True
--- a/mercurial/treediscovery.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/treediscovery.py Thu Jul 19 13:55:54 2018 -0400 @@ -61,6 +61,7 @@ req = set(unknown) reqcnt = 0 + progress = repo.ui.makeprogress(_('searching'), unit=_('queries')) # search through remote branches # a 'branch' here is a linear segment of history, with four parts: @@ -107,7 +108,7 @@ if r: reqcnt += 1 - repo.ui.progress(_('searching'), reqcnt, unit=_('queries')) + progress.increment() repo.ui.debug("request %d: %s\n" % (reqcnt, " ".join(map(short, r)))) for p in xrange(0, len(r), 10): @@ -125,7 +126,7 @@ while search: newsearch = [] reqcnt += 1 - repo.ui.progress(_('searching'), reqcnt, unit=_('queries')) + progress.increment() with remote.commandexecutor() as e: between = e.callcommand('between', {'pairs': search}).result() @@ -166,7 +167,7 @@ repo.ui.debug("found new changesets starting at " + " ".join([short(f) for f in fetch]) + "\n") - repo.ui.progress(_('searching'), None) + progress.complete() repo.ui.debug("%d total queries\n" % reqcnt) return base, list(fetch), heads
--- a/mercurial/ui.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/ui.py Thu Jul 19 13:55:54 2018 -0400 @@ -18,7 +18,6 @@ import socket import subprocess import sys -import tempfile import traceback from .i18n import _ @@ -60,18 +59,21 @@ interface = curses [commands] +# Grep working directory by default. +grep.all-files = True # Make `hg status` emit cwd-relative paths by default. status.relative = yes # Refuse to perform an `hg update` that would cause a file content merge update.check = noconflict # Show conflicts information in `hg status` status.verbose = True -# Skip the bisect state in conflicts information in `hg status` -status.skipstates = bisect +# Collapse entire directories that contain only unknown files +status.terse = u [diff] git = 1 showfunc = 1 +word-diff = 1 """ samplehgrcs = { @@ -157,10 +159,10 @@ } def _maybestrurl(maybebytes): - return util.rapply(pycompat.strurl, maybebytes) + return pycompat.rapply(pycompat.strurl, maybebytes) def _maybebytesurl(maybestr): - return util.rapply(pycompat.bytesurl, maybestr) + return pycompat.rapply(pycompat.bytesurl, maybestr) class httppasswordmgrdbproxy(object): """Delays loading urllib2 until it's needed.""" @@ -225,6 +227,7 @@ self._colormode = None self._terminfoparams = {} self._styles = {} + self._uninterruptible = False if src: self.fout = src.fout @@ -335,6 +338,37 @@ self._blockedtimes[key + '_blocked'] += \ (util.timer() - starttime) * 1000 + @contextlib.contextmanager + def uninterruptable(self): + """Mark an operation as unsafe. + + Most operations on a repository are safe to interrupt, but a + few are risky (for example repair.strip). This context manager + lets you advise Mercurial that something risky is happening so + that control-C etc can be blocked if desired. + """ + enabled = self.configbool('experimental', 'nointerrupt') + if (enabled and + self.configbool('experimental', 'nointerrupt-interactiveonly')): + enabled = self.interactive() + if self._uninterruptible or not enabled: + # if nointerrupt support is turned off, the process isn't + # interactive, or we're already in an uninterruptable + # block, do nothing. + yield + return + def warn(): + self.warn(_("shutting down cleanly\n")) + self.warn( + _("press ^C again to terminate immediately (dangerous)\n")) + return True + with procutil.uninterruptable(warn): + try: + self._uninterruptible = True + yield + finally: + self._uninterruptible = False + def formatter(self, topic, opts): return formatter.formatter(self, self, topic, opts) @@ -1180,7 +1214,7 @@ "Feature %s does not handle all default interfaces" % feature) - if self.plain(): + if self.plain() or encoding.environ.get('TERM') == 'dumb': return "text" # Default interface for all the features @@ -1446,7 +1480,7 @@ rdir = None if self.configbool('experimental', 'editortmpinhg'): rdir = repopath - (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-', + (fd, name) = pycompat.mkstemp(prefix='hg-' + extra['prefix'] + '-', suffix=suffix, dir=rdir) try: @@ -1597,6 +1631,10 @@ else: self.debug('%s:%s %d%s\n' % (topic, item, pos, unit)) + def makeprogress(self, topic, unit="", total=None): + '''exists only so low-level modules won't need to import scmutil''' + return scmutil.progress(self, topic, unit, total) + def log(self, service, *msg, **opts): '''hook for logging facility extensions
--- a/mercurial/unionrepo.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/unionrepo.py Thu Jul 19 13:55:54 2018 -0400 @@ -49,7 +49,7 @@ for rev2 in self.revlog2: rev = self.revlog2.index[rev2] # rev numbers - in revlog2, very different from self.rev - _start, _csize, _rsize, base, linkrev, p1rev, p2rev, node = rev + _start, _csize, rsize, base, linkrev, p1rev, p2rev, node = rev flags = _start & 0xFFFF if linkmapper is None: # link is to same revlog @@ -69,7 +69,9 @@ p1node = self.revlog2.node(p1rev) p2node = self.revlog2.node(p2rev) - e = (flags, None, None, base, + # TODO: it's probably wrong to set compressed length to None, but + # I have no idea if csize is valid in the base revlog context. + e = (flags, None, rsize, base, link, self.rev(p1node), self.rev(p2node), node) self.index.insert(-1, e) self.nodemap[node] = n
--- a/mercurial/upgrade.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/upgrade.py Thu Jul 19 13:55:54 2018 -0400 @@ -8,7 +8,6 @@ from __future__ import absolute_import import stat -import tempfile from .i18n import _ from . import ( @@ -18,6 +17,7 @@ hg, localrepo, manifest, + pycompat, revlog, scmutil, util, @@ -61,7 +61,9 @@ the dropped requirement must appear in the returned set for the upgrade to be allowed. """ - return set() + return { + localrepo.SPARSEREVLOG_REQUIREMENT, + } def supporteddestrequirements(repo): """Obtain requirements that upgrade supports in the destination. @@ -77,6 +79,7 @@ 'generaldelta', 'revlogv1', 'store', + localrepo.SPARSEREVLOG_REQUIREMENT, } def allowednewrequirements(repo): @@ -93,6 +96,7 @@ 'dotencode', 'fncache', 'generaldelta', + localrepo.SPARSEREVLOG_REQUIREMENT, } def preservedrequirements(repo): @@ -259,6 +263,28 @@ 'faster') @registerformatvariant +class sparserevlog(requirementformatvariant): + name = 'sparserevlog' + + _requirement = localrepo.SPARSEREVLOG_REQUIREMENT + + default = False + + description = _('in order to limit disk reading and memory usage on older ' + 'version, the span of a delta chain from its root to its ' + 'end is limited, whatever the relevant data in this span. ' + 'This can severly limit Mercurial ability to build good ' + 'chain of delta resulting is much more storage space being ' + 'taken and limit reusability of on disk delta during ' + 'exchange.' + ) + + upgrademessage = _('Revlog supports delta chain with more unused data ' + 'between payload. These gaps will be skipped at read ' + 'time. This allows for better delta chains, making a ' + 'better compression and faster exchange with server.') + +@registerformatvariant class removecldeltachain(formatvariant): name = 'plain-cl-delta' @@ -429,7 +455,7 @@ #reverse of "/".join(("data", path + ".i")) return filelog.filelog(repo.svfs, path[5:-2]) -def _copyrevlogs(ui, srcrepo, dstrepo, tr, deltareuse, aggressivemergedeltas): +def _copyrevlogs(ui, srcrepo, dstrepo, tr, deltareuse, deltabothparents): """Copy revlogs between 2 repos.""" revcount = 0 srcsize = 0 @@ -498,10 +524,9 @@ (util.bytecount(srcsize), util.bytecount(srcrawsize)))) # Used to keep track of progress. - progress = [] + progress = None def oncopiedrevision(rl, rev, node): - progress[1] += 1 - srcrepo.ui.progress(progress[0], progress[1], total=progress[2]) + progress.increment() # Do the actual copying. # FUTURE this operation can be farmed off to worker processes. @@ -523,7 +548,8 @@ (crevcount, util.bytecount(csrcsize), util.bytecount(crawsize))) seen.add('c') - progress[:] = [_('changelog revisions'), 0, crevcount] + progress = srcrepo.ui.makeprogress(_('changelog revisions'), + total=crevcount) elif isinstance(oldrl, manifest.manifestrevlog) and 'm' not in seen: ui.write(_('finished migrating %d filelog revisions across %d ' 'filelogs; change in size: %s\n') % @@ -534,21 +560,26 @@ (mcount, mrevcount, util.bytecount(msrcsize), util.bytecount(mrawsize))) seen.add('m') - progress[:] = [_('manifest revisions'), 0, mrevcount] + if progress: + progress.complete() + progress = srcrepo.ui.makeprogress(_('manifest revisions'), + total=mrevcount) elif 'f' not in seen: ui.write(_('migrating %d filelogs containing %d revisions ' '(%s in store; %s tracked data)\n') % (fcount, frevcount, util.bytecount(fsrcsize), util.bytecount(frawsize))) seen.add('f') - progress[:] = [_('file revisions'), 0, frevcount] + if progress: + progress.complete() + progress = srcrepo.ui.makeprogress(_('file revisions'), + total=frevcount) - ui.progress(progress[0], progress[1], total=progress[2]) ui.note(_('cloning %d revisions from %s\n') % (len(oldrl), unencoded)) oldrl.clone(tr, newrl, addrevisioncb=oncopiedrevision, deltareuse=deltareuse, - aggressivemergedeltas=aggressivemergedeltas) + deltabothparents=deltabothparents) datasize = 0 idx = newrl.index @@ -564,7 +595,7 @@ else: fdstsize += datasize - ui.progress(progress[0], None) + progress.complete() ui.write(_('finished migrating %d changelog revisions; change in size: ' '%s\n') % (crevcount, util.bytecount(cdstsize - csrcsize))) @@ -657,7 +688,7 @@ ui.write(_('data fully migrated to temporary repository\n')) - backuppath = tempfile.mkdtemp(prefix='upgradebackup.', dir=srcrepo.path) + backuppath = pycompat.mkdtemp(prefix='upgradebackup.', dir=srcrepo.path) backupvfs = vfsmod.vfs(backuppath) # Make a backup of requires file first, as it is the first to be modified. @@ -842,7 +873,7 @@ # data. There are less heavyweight ways to do this, but it is easier # to create a new repo object than to instantiate all the components # (like the store) separately. - tmppath = tempfile.mkdtemp(prefix='upgrade.', dir=repo.path) + tmppath = pycompat.mkdtemp(prefix='upgrade.', dir=repo.path) backuppath = None try: ui.write(_('creating temporary repository to stage migrated '
--- a/mercurial/util.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/util.py Thu Jul 19 13:55:54 2018 -0400 @@ -31,7 +31,6 @@ import socket import stat import sys -import tempfile import time import traceback import warnings @@ -47,7 +46,6 @@ urllibcompat, ) from .utils import ( - dateutil, procutil, stringutil, ) @@ -60,10 +58,8 @@ b85encode = base85.b85encode cookielib = pycompat.cookielib -empty = pycompat.empty httplib = pycompat.httplib pickle = pycompat.pickle -queue = pycompat.queue safehasattr = pycompat.safehasattr socketserver = pycompat.socketserver bytesio = pycompat.bytesio @@ -135,39 +131,6 @@ _notset = object() -def _rapply(f, xs): - if xs is None: - # assume None means non-value of optional data - return xs - if isinstance(xs, (list, set, tuple)): - return type(xs)(_rapply(f, x) for x in xs) - if isinstance(xs, dict): - return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items()) - return f(xs) - -def rapply(f, xs): - """Apply function recursively to every item preserving the data structure - - >>> def f(x): - ... return 'f(%s)' % x - >>> rapply(f, None) is None - True - >>> rapply(f, 'a') - 'f(a)' - >>> rapply(f, {'a'}) == {'f(a)'} - True - >>> rapply(f, ['a', 'b', None, {'c': 'd'}, []]) - ['f(a)', 'f(b)', None, {'f(c)': 'f(d)'}, []] - - >>> xs = [object()] - >>> rapply(pycompat.identity, xs) is xs - True - """ - if f is pycompat.identity: - # fast path mainly for py2 - return xs - return _rapply(f, xs) - def bitsfrom(container): bits = 0 for bit in container: @@ -359,6 +322,11 @@ self._fillbuffer() return self._frombuffer(size) + def unbufferedread(self, size): + if not self._eof and self._lenbuf == 0: + self._fillbuffer(max(size, _chunksize)) + return self._frombuffer(min(self._lenbuf, size)) + def readline(self, *args, **kwargs): if 1 < len(self._buffer): # this should not happen because both read and readline end with a @@ -400,9 +368,9 @@ self._lenbuf = 0 return data - def _fillbuffer(self): + def _fillbuffer(self, size=_chunksize): """read data to the buffer""" - data = os.read(self._input.fileno(), _chunksize) + data = os.read(self._input.fileno(), size) if not data: self._eof = True else: @@ -786,6 +754,13 @@ if res is None: res = '' + if size == -1 and res == '': + # Suppress pointless read(-1) calls that return + # nothing. These happen _a lot_ on Python 3, and there + # doesn't seem to be a better workaround to have matching + # Python 2 and 3 behavior. :( + return + if self.logdataapis: self.fh.write('%s> read(%d) -> %d' % (self.name, size, len(res))) @@ -1628,31 +1603,30 @@ except shutil.Error as inst: raise error.Abort(str(inst)) -def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None): +def copyfiles(src, dst, hardlink=None, progress=None): """Copy a directory tree using hardlinks if possible.""" num = 0 - gettopic = lambda: hardlink and _('linking') or _('copying') + def settopic(): + if progress: + progress.topic = _('linking') if hardlink else _('copying') if os.path.isdir(src): if hardlink is None: hardlink = (os.stat(src).st_dev == os.stat(os.path.dirname(dst)).st_dev) - topic = gettopic() + settopic() os.mkdir(dst) for name, kind in listdir(src): srcname = os.path.join(src, name) dstname = os.path.join(dst, name) - def nprog(t, pos): - if pos is not None: - return progress(t, pos + num) - hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog) + hardlink, n = copyfiles(srcname, dstname, hardlink, progress) num += n else: if hardlink is None: hardlink = (os.stat(os.path.dirname(src)).st_dev == os.stat(os.path.dirname(dst)).st_dev) - topic = gettopic() + settopic() if hardlink: try: @@ -1663,8 +1637,8 @@ else: shutil.copy(src, dst) num += 1 - progress(topic, num) - progress(topic, None) + if progress: + progress.increment() return hardlink, num @@ -1896,7 +1870,7 @@ # work around issue2543 (or testfile may get lost on Samba shares) f1, f2, fp = None, None, None try: - fd, f1 = tempfile.mkstemp(prefix='.%s-' % os.path.basename(testfile), + fd, f1 = pycompat.mkstemp(prefix='.%s-' % os.path.basename(testfile), suffix='1~', dir=os.path.dirname(testfile)) os.close(fd) f2 = '%s2~' % f1[:-2] @@ -1942,7 +1916,7 @@ Returns the name of the temporary file. """ d, fn = os.path.split(name) - fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, suffix='~', dir=d) + fd, temp = pycompat.mkstemp(prefix='.%s-' % fn, suffix='~', dir=d) os.close(fd) # Temporary files are created with mode 0600, which is usually not # what we want. If the original file already exists, just copy @@ -2137,17 +2111,18 @@ else: self.close() -def unlinkpath(f, ignoremissing=False): +def unlinkpath(f, ignoremissing=False, rmdir=True): """unlink and remove the directory if it is empty""" if ignoremissing: tryunlink(f) else: unlink(f) - # try removing directories that might now be empty - try: - removedirs(os.path.dirname(f)) - except OSError: - pass + if rmdir: + # try removing directories that might now be empty + try: + removedirs(os.path.dirname(f)) + except OSError: + pass def tryunlink(f): """Attempt to remove a file, ignoring ENOENT errors.""" @@ -2719,7 +2694,7 @@ 'query', 'fragment'): v = getattr(self, a) if v is not None: - attrs.append('%s: %r' % (a, v)) + attrs.append('%s: %r' % (a, pycompat.bytestr(v))) return '<url %s>' % ', '.join(attrs) def __bytes__(self): @@ -2921,6 +2896,7 @@ finally: elapsed = timer() - start _timenesting[0] -= indent + stderr = procutil.stderr stderr.write('%s%s: %s\n' % (' ' * _timenesting[0], func.__name__, timecount(elapsed))) @@ -3331,6 +3307,104 @@ """ raise NotImplementedError() +class _CompressedStreamReader(object): + def __init__(self, fh): + if safehasattr(fh, 'unbufferedread'): + self._reader = fh.unbufferedread + else: + self._reader = fh.read + self._pending = [] + self._pos = 0 + self._eof = False + + def _decompress(self, chunk): + raise NotImplementedError() + + def read(self, l): + buf = [] + while True: + while self._pending: + if len(self._pending[0]) > l + self._pos: + newbuf = self._pending[0] + buf.append(newbuf[self._pos:self._pos + l]) + self._pos += l + return ''.join(buf) + + newbuf = self._pending.pop(0) + if self._pos: + buf.append(newbuf[self._pos:]) + l -= len(newbuf) - self._pos + else: + buf.append(newbuf) + l -= len(newbuf) + self._pos = 0 + + if self._eof: + return ''.join(buf) + chunk = self._reader(65536) + self._decompress(chunk) + +class _GzipCompressedStreamReader(_CompressedStreamReader): + def __init__(self, fh): + super(_GzipCompressedStreamReader, self).__init__(fh) + self._decompobj = zlib.decompressobj() + def _decompress(self, chunk): + newbuf = self._decompobj.decompress(chunk) + if newbuf: + self._pending.append(newbuf) + d = self._decompobj.copy() + try: + d.decompress('x') + d.flush() + if d.unused_data == 'x': + self._eof = True + except zlib.error: + pass + +class _BZ2CompressedStreamReader(_CompressedStreamReader): + def __init__(self, fh): + super(_BZ2CompressedStreamReader, self).__init__(fh) + self._decompobj = bz2.BZ2Decompressor() + def _decompress(self, chunk): + newbuf = self._decompobj.decompress(chunk) + if newbuf: + self._pending.append(newbuf) + try: + while True: + newbuf = self._decompobj.decompress('') + if newbuf: + self._pending.append(newbuf) + else: + break + except EOFError: + self._eof = True + +class _TruncatedBZ2CompressedStreamReader(_BZ2CompressedStreamReader): + def __init__(self, fh): + super(_TruncatedBZ2CompressedStreamReader, self).__init__(fh) + newbuf = self._decompobj.decompress('BZ') + if newbuf: + self._pending.append(newbuf) + +class _ZstdCompressedStreamReader(_CompressedStreamReader): + def __init__(self, fh, zstd): + super(_ZstdCompressedStreamReader, self).__init__(fh) + self._zstd = zstd + self._decompobj = zstd.ZstdDecompressor().decompressobj() + def _decompress(self, chunk): + newbuf = self._decompobj.decompress(chunk) + if newbuf: + self._pending.append(newbuf) + try: + while True: + newbuf = self._decompobj.decompress('') + if newbuf: + self._pending.append(newbuf) + else: + break + except self._zstd.ZstdError: + self._eof = True + class _zlibengine(compressionengine): def name(self): return 'zlib' @@ -3364,15 +3438,7 @@ yield z.flush() def decompressorreader(self, fh): - def gen(): - d = zlib.decompressobj() - for chunk in filechunkiter(fh): - while chunk: - # Limit output size to limit memory. - yield d.decompress(chunk, 2 ** 18) - chunk = d.unconsumed_tail - - return chunkbuffer(gen()) + return _GzipCompressedStreamReader(fh) class zlibrevlogcompressor(object): def compress(self, data): @@ -3452,12 +3518,7 @@ yield z.flush() def decompressorreader(self, fh): - def gen(): - d = bz2.BZ2Decompressor() - for chunk in filechunkiter(fh): - yield d.decompress(chunk) - - return chunkbuffer(gen()) + return _BZ2CompressedStreamReader(fh) compengines.register(_bz2engine()) @@ -3471,14 +3532,7 @@ # We don't implement compressstream because it is hackily handled elsewhere. def decompressorreader(self, fh): - def gen(): - # The input stream doesn't have the 'BZ' header. So add it back. - d = bz2.BZ2Decompressor() - d.decompress('BZ') - for chunk in filechunkiter(fh): - yield d.decompress(chunk) - - return chunkbuffer(gen()) + return _TruncatedBZ2CompressedStreamReader(fh) compengines.register(_truncatedbz2engine()) @@ -3573,9 +3627,7 @@ yield z.flush() def decompressorreader(self, fh): - zstd = self._module - dctx = zstd.ZstdDecompressor() - return chunkbuffer(dctx.read_from(fh)) + return _ZstdCompressedStreamReader(fh, self._module) class zstdrevlogcompressor(object): def __init__(self, zstd, level=3): @@ -3784,93 +3836,3 @@ if not (byte & 0x80): return result shift += 7 - -### -# Deprecation warnings for util.py splitting -### - -def _deprecatedfunc(func, version, modname=None): - def wrapped(*args, **kwargs): - fn = pycompat.sysbytes(func.__name__) - mn = modname or pycompat.sysbytes(func.__module__)[len('mercurial.'):] - msg = "'util.%s' is deprecated, use '%s.%s'" % (fn, mn, fn) - nouideprecwarn(msg, version, stacklevel=2) - return func(*args, **kwargs) - wrapped.__name__ = func.__name__ - return wrapped - -defaultdateformats = dateutil.defaultdateformats -extendeddateformats = dateutil.extendeddateformats -makedate = _deprecatedfunc(dateutil.makedate, '4.6') -datestr = _deprecatedfunc(dateutil.datestr, '4.6') -shortdate = _deprecatedfunc(dateutil.shortdate, '4.6') -parsetimezone = _deprecatedfunc(dateutil.parsetimezone, '4.6') -strdate = _deprecatedfunc(dateutil.strdate, '4.6') -parsedate = _deprecatedfunc(dateutil.parsedate, '4.6') -matchdate = _deprecatedfunc(dateutil.matchdate, '4.6') - -stderr = procutil.stderr -stdin = procutil.stdin -stdout = procutil.stdout -explainexit = _deprecatedfunc(procutil.explainexit, '4.6', - modname='utils.procutil') -findexe = _deprecatedfunc(procutil.findexe, '4.6', modname='utils.procutil') -getuser = _deprecatedfunc(procutil.getuser, '4.6', modname='utils.procutil') -getpid = _deprecatedfunc(procutil.getpid, '4.6', modname='utils.procutil') -hidewindow = _deprecatedfunc(procutil.hidewindow, '4.6', - modname='utils.procutil') -popen = _deprecatedfunc(procutil.popen, '4.6', modname='utils.procutil') -quotecommand = _deprecatedfunc(procutil.quotecommand, '4.6', - modname='utils.procutil') -readpipe = _deprecatedfunc(procutil.readpipe, '4.6', modname='utils.procutil') -setbinary = _deprecatedfunc(procutil.setbinary, '4.6', modname='utils.procutil') -setsignalhandler = _deprecatedfunc(procutil.setsignalhandler, '4.6', - modname='utils.procutil') -shellquote = _deprecatedfunc(procutil.shellquote, '4.6', - modname='utils.procutil') -shellsplit = _deprecatedfunc(procutil.shellsplit, '4.6', - modname='utils.procutil') -spawndetached = _deprecatedfunc(procutil.spawndetached, '4.6', - modname='utils.procutil') -sshargs = _deprecatedfunc(procutil.sshargs, '4.6', modname='utils.procutil') -testpid = _deprecatedfunc(procutil.testpid, '4.6', modname='utils.procutil') -try: - setprocname = _deprecatedfunc(procutil.setprocname, '4.6', - modname='utils.procutil') -except AttributeError: - pass -try: - unblocksignal = _deprecatedfunc(procutil.unblocksignal, '4.6', - modname='utils.procutil') -except AttributeError: - pass -closefds = procutil.closefds -isatty = _deprecatedfunc(procutil.isatty, '4.6') -popen2 = _deprecatedfunc(procutil.popen2, '4.6') -popen3 = _deprecatedfunc(procutil.popen3, '4.6') -popen4 = _deprecatedfunc(procutil.popen4, '4.6') -pipefilter = _deprecatedfunc(procutil.pipefilter, '4.6') -tempfilter = _deprecatedfunc(procutil.tempfilter, '4.6') -filter = _deprecatedfunc(procutil.filter, '4.6') -mainfrozen = _deprecatedfunc(procutil.mainfrozen, '4.6') -hgexecutable = _deprecatedfunc(procutil.hgexecutable, '4.6') -isstdin = _deprecatedfunc(procutil.isstdin, '4.6') -isstdout = _deprecatedfunc(procutil.isstdout, '4.6') -shellenviron = _deprecatedfunc(procutil.shellenviron, '4.6') -system = _deprecatedfunc(procutil.system, '4.6') -gui = _deprecatedfunc(procutil.gui, '4.6') -hgcmd = _deprecatedfunc(procutil.hgcmd, '4.6') -rundetached = _deprecatedfunc(procutil.rundetached, '4.6') - -binary = _deprecatedfunc(stringutil.binary, '4.6') -stringmatcher = _deprecatedfunc(stringutil.stringmatcher, '4.6') -shortuser = _deprecatedfunc(stringutil.shortuser, '4.6') -emailuser = _deprecatedfunc(stringutil.emailuser, '4.6') -email = _deprecatedfunc(stringutil.email, '4.6') -ellipsis = _deprecatedfunc(stringutil.ellipsis, '4.6') -escapestr = _deprecatedfunc(stringutil.escapestr, '4.6') -unescapestr = _deprecatedfunc(stringutil.unescapestr, '4.6') -forcebytestr = _deprecatedfunc(stringutil.forcebytestr, '4.6') -uirepr = _deprecatedfunc(stringutil.uirepr, '4.6') -wrap = _deprecatedfunc(stringutil.wrap, '4.6') -parsebool = _deprecatedfunc(stringutil.parsebool, '4.6')
--- a/mercurial/utils/cborutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/utils/cborutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -140,12 +140,15 @@ yield BREAK +def _mixedtypesortkey(v): + return type(v).__name__, v + def streamencodeset(s): # https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml defines # semantic tag 258 for finite sets. yield encodelength(MAJOR_TYPE_SEMANTIC, 258) - for chunk in streamencodearray(sorted(s)): + for chunk in streamencodearray(sorted(s, key=_mixedtypesortkey)): yield chunk def streamencodemap(d): @@ -155,7 +158,8 @@ """ yield encodelength(MAJOR_TYPE_MAP, len(d)) - for key, value in sorted(d.iteritems()): + for key, value in sorted(d.iteritems(), + key=lambda x: _mixedtypesortkey(x[0])): for chunk in streamencode(key): yield chunk for chunk in streamencode(value):
--- a/mercurial/utils/procutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/utils/procutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -16,7 +16,6 @@ import signal import subprocess import sys -import tempfile import time from ..i18n import _ @@ -168,11 +167,11 @@ the temporary files generated.''' inname, outname = None, None try: - infd, inname = tempfile.mkstemp(prefix='hg-filter-in-') + infd, inname = pycompat.mkstemp(prefix='hg-filter-in-') fp = os.fdopen(infd, r'wb') fp.write(s) fp.close() - outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-') + outfd, outname = pycompat.mkstemp(prefix='hg-filter-out-') os.close(outfd) cmd = cmd.replace('INFILE', inname) cmd = cmd.replace('OUTFILE', outname) @@ -318,6 +317,13 @@ env['HG'] = hgexecutable() return env +if pycompat.iswindows: + def shelltonative(cmd, env): + return platform.shelltocmdexe(cmd, shellenviron(env)) +else: + def shelltonative(cmd, env): + return cmd + def system(cmd, environ=None, cwd=None, out=None): '''enhanced shell command execution. run with environment maybe modified, maybe in different dir. @@ -409,3 +415,36 @@ finally: if prevhandler is not None: signal.signal(signal.SIGCHLD, prevhandler) + +@contextlib.contextmanager +def uninterruptable(warn): + """Inhibit SIGINT handling on a region of code. + + Note that if this is called in a non-main thread, it turns into a no-op. + + Args: + warn: A callable which takes no arguments, and returns True if the + previous signal handling should be restored. + """ + + oldsiginthandler = [signal.getsignal(signal.SIGINT)] + shouldbail = [] + + def disabledsiginthandler(*args): + if warn(): + signal.signal(signal.SIGINT, oldsiginthandler[0]) + del oldsiginthandler[0] + shouldbail.append(True) + + try: + try: + signal.signal(signal.SIGINT, disabledsiginthandler) + except ValueError: + # wrong thread, oh well, we tried + del oldsiginthandler[0] + yield + finally: + if oldsiginthandler: + signal.signal(signal.SIGINT, oldsiginthandler[0]) + if shouldbail: + raise KeyboardInterrupt
--- a/mercurial/utils/stringutil.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/utils/stringutil.py Thu Jul 19 13:55:54 2018 -0400 @@ -23,7 +23,26 @@ pycompat, ) -def pprint(o, bprefix=True): +# regex special chars pulled from https://bugs.python.org/issue29995 +# which was part of Python 3.7. +_respecial = pycompat.bytestr(b'()[]{}?*+-|^$\\.&~# \t\n\r\v\f') +_regexescapemap = {ord(i): (b'\\' + i).decode('latin1') for i in _respecial} + +def reescape(pat): + """Drop-in replacement for re.escape.""" + # NOTE: it is intentional that this works on unicodes and not + # bytes, as it's only possible to do the escaping with + # unicode.translate, not bytes.translate. Sigh. + wantuni = True + if isinstance(pat, bytes): + wantuni = False + pat = pat.decode('latin1') + pat = pat.translate(_regexescapemap) + if wantuni: + return pat + return pat.encode('latin1') + +def pprint(o, bprefix=False): """Pretty print an object.""" if isinstance(o, bytes): if bprefix: @@ -40,16 +59,59 @@ '%s: %s' % (pprint(k, bprefix=bprefix), pprint(v, bprefix=bprefix)) for k, v in sorted(o.items()))) - elif isinstance(o, bool): - return b'True' if o else b'False' - elif isinstance(o, int): - return '%d' % o - elif isinstance(o, float): - return '%f' % o - elif o is None: - return b'None' + elif isinstance(o, tuple): + return '(%s)' % (b', '.join(pprint(a, bprefix=bprefix) for a in o)) else: - raise error.ProgrammingError('do not know how to format %r' % o) + return pycompat.byterepr(o) + +def prettyrepr(o): + """Pretty print a representation of a possibly-nested object""" + lines = [] + rs = pycompat.byterepr(o) + p0 = p1 = 0 + while p0 < len(rs): + # '... field=<type ... field=<type ...' + # ~~~~~~~~~~~~~~~~ + # p0 p1 q0 q1 + q0 = -1 + q1 = rs.find('<', p1 + 1) + if q1 < 0: + q1 = len(rs) + elif q1 > p1 + 1 and rs.startswith('=', q1 - 1): + # backtrack for ' field=<' + q0 = rs.rfind(' ', p1 + 1, q1 - 1) + if q0 < 0: + q0 = q1 + else: + q0 += 1 # skip ' ' + l = rs.count('<', 0, p0) - rs.count('>', 0, p0) + assert l >= 0 + lines.append((l, rs[p0:q0].rstrip())) + p0, p1 = q0, q1 + return '\n'.join(' ' * l + s for l, s in lines) + +def buildrepr(r): + """Format an optional printable representation from unexpanded bits + + ======== ================================= + type(r) example + ======== ================================= + tuple ('<not %r>', other) + bytes '<branch closed>' + callable lambda: '<branch %r>' % sorted(b) + object other + ======== ================================= + """ + if r is None: + return '' + elif isinstance(r, tuple): + return r[0] % pycompat.rapply(pycompat.maybebytestr, r[1:]) + elif isinstance(r, bytes): + return r + elif callable(r): + return r() + else: + return pycompat.byterepr(r) def binary(s): """return true if a string is binary data"""
--- a/mercurial/verify.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/verify.py Thu Jul 19 13:55:54 2018 -0400 @@ -178,9 +178,10 @@ filelinkrevs = {} seen = {} self.checklog(cl, "changelog", 0) - total = len(repo) + progress = ui.makeprogress(_('checking'), unit=_('changesets'), + total=len(repo)) for i in repo: - ui.progress(_('checking'), i, total=total, unit=_('changesets')) + progress.update(i) n = cl.node(i) self.checkentry(cl, i, n, seen, [i], "changelog") @@ -195,11 +196,11 @@ except Exception as inst: self.refersmf = True self.exc(i, _("unpacking changeset %s") % short(n), inst) - ui.progress(_('checking'), None) + progress.complete() return mflinkrevs, filelinkrevs def _verifymanifest(self, mflinkrevs, dir="", storefiles=None, - progress=None): + subdirprogress=None): repo = self.repo ui = self.ui match = self.match @@ -217,16 +218,17 @@ label = dir revlogfiles = mf.files() storefiles.difference_update(revlogfiles) - if progress: # should be true since we're in a subdirectory - progress() + if subdirprogress: # should be true since we're in a subdirectory + subdirprogress.increment() if self.refersmf: # Do not check manifest if there are only changelog entries with # null manifests. self.checklog(mf, label, 0) - total = len(mf) + progress = ui.makeprogress(_('checking'), unit=_('manifests'), + total=len(mf)) for i in mf: if not dir: - ui.progress(_('checking'), i, total=total, unit=_('manifests')) + progress.update(i) n = mf.node(i) lr = self.checkentry(mf, i, n, seen, mflinkrevs.get(n, []), label) if n in mflinkrevs: @@ -257,7 +259,7 @@ except Exception as inst: self.exc(lr, _("reading delta %s") % short(n), inst, label) if not dir: - ui.progress(_('checking'), None) + progress.complete() if self.havemf: for c, m in sorted([(c, m) for m in mflinkrevs @@ -280,21 +282,17 @@ elif (size > 0 or not revlogv1) and f.startswith('meta/'): storefiles.add(_normpath(f)) subdirs.add(os.path.dirname(f)) - subdircount = len(subdirs) - currentsubdir = [0] - def progress(): - currentsubdir[0] += 1 - ui.progress(_('checking'), currentsubdir[0], total=subdircount, - unit=_('manifests')) + subdirprogress = ui.makeprogress(_('checking'), unit=_('manifests'), + total=len(subdirs)) for subdir, linkrevs in subdirnodes.iteritems(): subdirfilenodes = self._verifymanifest(linkrevs, subdir, storefiles, - progress) + subdirprogress) for f, onefilenodes in subdirfilenodes.iteritems(): filenodes.setdefault(f, {}).update(onefilenodes) if not dir and subdirnodes: - ui.progress(_('checking'), None) + subdirprogress.complete() if self.warnorphanstorefiles: for f in sorted(storefiles): self.warn(_("warning: orphan data file '%s'") % f) @@ -307,19 +305,17 @@ ui.status(_("crosschecking files in changesets and manifests\n")) total = len(filelinkrevs) + len(filenodes) - count = 0 + progress = ui.makeprogress(_('crosschecking'), total=total) if self.havemf: for f in sorted(filelinkrevs): - count += 1 - ui.progress(_('crosschecking'), count, total=total) + progress.increment() if f not in filenodes: lr = filelinkrevs[f][0] self.err(lr, _("in changeset but not in manifest"), f) if self.havecl: for f in sorted(filenodes): - count += 1 - ui.progress(_('crosschecking'), count, total=total) + progress.increment() if f not in filelinkrevs: try: fl = repo.file(f) @@ -328,7 +324,7 @@ lr = None self.err(lr, _("in manifest but not in changeset"), f) - ui.progress(_('crosschecking'), None) + progress.complete() def _verifyfiles(self, filenodes, filelinkrevs): repo = self.repo @@ -346,10 +342,11 @@ storefiles.add(_normpath(f)) files = sorted(set(filenodes) | set(filelinkrevs)) - total = len(files) revisions = 0 + progress = ui.makeprogress(_('checking'), unit=_('files'), + total=len(files)) for i, f in enumerate(files): - ui.progress(_('checking'), i, item=f, total=total, unit=_('files')) + progress.update(i, item=f) try: linkrevs = filelinkrevs[f] except KeyError: @@ -483,7 +480,7 @@ for lr, node in sorted(fns): self.err(lr, _("manifest refers to unknown revision %s") % short(node), f) - ui.progress(_('checking'), None) + progress.complete() if self.warnorphanstorefiles: for f in sorted(storefiles):
--- a/mercurial/vfs.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/vfs.py Thu Jul 19 13:55:54 2018 -0400 @@ -11,7 +11,6 @@ import os import shutil import stat -import tempfile import threading from .i18n import _ @@ -171,7 +170,7 @@ return os.mkdir(self.join(path)) def mkstemp(self, suffix='', prefix='tmp', dir=None): - fd, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, + fd, name = pycompat.mkstemp(suffix=suffix, prefix=prefix, dir=self.join(dir)) dname, fname = util.split(name) if dir: @@ -247,8 +246,9 @@ """Attempt to remove a file, ignoring missing file errors.""" util.tryunlink(self.join(path)) - def unlinkpath(self, path=None, ignoremissing=False): - return util.unlinkpath(self.join(path), ignoremissing=ignoremissing) + def unlinkpath(self, path=None, ignoremissing=False, rmdir=True): + return util.unlinkpath(self.join(path), ignoremissing=ignoremissing, + rmdir=rmdir) def utime(self, path=None, t=None): return os.utime(self.join(path), t) @@ -568,7 +568,7 @@ ui.debug('starting %d threads for background file closing\n' % threadcount) - self._queue = util.queue(maxsize=maxqueue) + self._queue = pycompat.queue.Queue(maxsize=maxqueue) self._running = True for i in range(threadcount): @@ -600,7 +600,7 @@ except Exception as e: # Stash so can re-raise from main thread later. self._threadexception = e - except util.empty: + except pycompat.queue.Empty: if not self._running: break
--- a/mercurial/windows.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/windows.py Thu Jul 19 13:55:54 2018 -0400 @@ -12,6 +12,7 @@ import os import re import stat +import string import sys from .i18n import _ @@ -253,6 +254,113 @@ def samestat(s1, s2): return False +def shelltocmdexe(path, env): + r"""Convert shell variables in the form $var and ${var} inside ``path`` + to %var% form. Existing Windows style variables are left unchanged. + + The variables are limited to the given environment. Unknown variables are + left unchanged. + + >>> e = {b'var1': b'v1', b'var2': b'v2', b'var3': b'v3'} + >>> # Only valid values are expanded + >>> shelltocmdexe(b'cmd $var1 ${var2} %var3% $missing ${missing} %missing%', + ... e) + 'cmd %var1% %var2% %var3% $missing ${missing} %missing%' + >>> # Single quote prevents expansion, as does \$ escaping + >>> shelltocmdexe(b"cmd '$var1 ${var2} %var3%' \$var1 \${var2} \\", e) + 'cmd "$var1 ${var2} %var3%" $var1 ${var2} \\' + >>> # $$ is not special. %% is not special either, but can be the end and + >>> # start of consecutive variables + >>> shelltocmdexe(b"cmd $$ %% %var1%%var2%", e) + 'cmd $$ %% %var1%%var2%' + >>> # No double substitution + >>> shelltocmdexe(b"$var1 %var1%", {b'var1': b'%var2%', b'var2': b'boom'}) + '%var1% %var1%' + >>> # Tilde expansion + >>> shelltocmdexe(b"~/dir ~\dir2 ~tmpfile \~/", {}) + '%USERPROFILE%/dir %USERPROFILE%\\dir2 ~tmpfile ~/' + """ + if not any(c in path for c in b"$'~"): + return path + + varchars = pycompat.sysbytes(string.ascii_letters + string.digits) + b'_-' + + res = b'' + index = 0 + pathlen = len(path) + while index < pathlen: + c = path[index] + if c == b'\'': # no expansion within single quotes + path = path[index + 1:] + pathlen = len(path) + try: + index = path.index(b'\'') + res += b'"' + path[:index] + b'"' + except ValueError: + res += c + path + index = pathlen - 1 + elif c == b'%': # variable + path = path[index + 1:] + pathlen = len(path) + try: + index = path.index(b'%') + except ValueError: + res += b'%' + path + index = pathlen - 1 + else: + var = path[:index] + res += b'%' + var + b'%' + elif c == b'$': # variable + if path[index + 1:index + 2] == b'{': + path = path[index + 2:] + pathlen = len(path) + try: + index = path.index(b'}') + var = path[:index] + + # See below for why empty variables are handled specially + if env.get(var, '') != '': + res += b'%' + var + b'%' + else: + res += b'${' + var + b'}' + except ValueError: + res += b'${' + path + index = pathlen - 1 + else: + var = b'' + index += 1 + c = path[index:index + 1] + while c != b'' and c in varchars: + var += c + index += 1 + c = path[index:index + 1] + # Some variables (like HG_OLDNODE) may be defined, but have an + # empty value. Those need to be skipped because when spawning + # cmd.exe to run the hook, it doesn't replace %VAR% for an empty + # VAR, and that really confuses things like revset expressions. + # OTOH, if it's left in Unix format and the hook runs sh.exe, it + # will substitute to an empty string, and everything is happy. + if env.get(var, '') != '': + res += b'%' + var + b'%' + else: + res += b'$' + var + + if c != '': + index -= 1 + elif (c == b'~' and index + 1 < pathlen + and path[index + 1] in (b'\\', b'/')): + res += "%USERPROFILE%" + elif (c == b'\\' and index + 1 < pathlen + and path[index + 1] in (b'$', b'~')): + # Skip '\', but only if it is escaping $ or ~ + res += path[index + 1] + index += 1 + else: + res += c + + index += 1 + return res + # A sequence of backslashes is special iff it precedes a double quote: # - if there's an even number of backslashes, the double quote is not # quoted (i.e. it ends the quoted region)
--- a/mercurial/wireprotoserver.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/wireprotoserver.py Thu Jul 19 13:55:54 2018 -0400 @@ -18,7 +18,6 @@ from . import ( encoding, error, - hook, pycompat, util, wireprototypes, @@ -785,8 +784,7 @@ def __init__(self, ui, repo, logfh=None): self._ui = ui self._repo = repo - self._fin = ui.fin - self._fout = ui.fout + self._fin, self._fout = procutil.protectstdio(ui.fin, ui.fout) # Log write I/O to stdout and stderr if configured. if logfh: @@ -795,15 +793,10 @@ ui.ferr = util.makeloggingfileobject( logfh, ui.ferr, 'e', logdata=True) - hook.redirect(True) - ui.fout = repo.ui.fout = ui.ferr - - # Prevent insertion/deletion of CRs - procutil.setbinary(self._fin) - procutil.setbinary(self._fout) - def serve_forever(self): self.serveuntil(threading.Event()) + procutil.restorestdio(self._ui.fin, self._ui.fout, + self._fin, self._fout) sys.exit(0) def serveuntil(self, ev):
--- a/mercurial/wireprotov1peer.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/wireprotov1peer.py Thu Jul 19 13:55:54 2018 -0400 @@ -355,7 +355,7 @@ yield {'nodes': wireprototypes.encodelist(nodes)}, f d = f.value try: - yield [bool(int(b)) for b in d] + yield [bool(int(b)) for b in pycompat.iterbytestr(d)] except ValueError: self._abort(error.ResponseError(_("unexpected response:"), d))
--- a/mercurial/wireprotov1server.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/wireprotov1server.py Thu Jul 19 13:55:54 2018 -0400 @@ -8,7 +8,6 @@ from __future__ import absolute_import import os -import tempfile from .i18n import _ from .node import ( @@ -354,7 +353,9 @@ common_anc = cl.ancestors([cl.rev(rev) for rev in common], inclusive=True) compformats = clientcompressionsupport(proto) for entry in res: - if 'COMPRESSION' in entry and entry['COMPRESSION'] not in compformats: + comp = entry.get('COMPRESSION') + altcomp = util.compengines._bundlenames.get(comp) + if comp and comp not in compformats and altcomp not in compformats: continue # No test yet for VERSION, since V2 is supported by any client # that advertises partial pulls @@ -568,7 +569,7 @@ fp.close() if tempname: os.unlink(tempname) - fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') + fd, tempname = pycompat.mkstemp(prefix='hg-unbundle-') repo.ui.debug('redirecting incoming bundle to %s\n' % tempname) fp = os.fdopen(fd, pycompat.sysstr('wb+'))
--- a/mercurial/worker.py Sun Jul 01 23:36:53 2018 +0900 +++ b/mercurial/worker.py Thu Jul 19 13:55:54 2018 -0400 @@ -14,6 +14,12 @@ import threading import time +try: + import selectors + selectors.BaseSelector +except ImportError: + from .thirdparty import selectors2 as selectors + from .i18n import _ from . import ( encoding, @@ -56,19 +62,28 @@ return min(max(countcpus(), 4), 32) if pycompat.isposix or pycompat.iswindows: - _startupcost = 0.01 + _STARTUP_COST = 0.01 + # The Windows worker is thread based. If tasks are CPU bound, threads + # in the presence of the GIL result in excessive context switching and + # this overhead can slow down execution. + _DISALLOW_THREAD_UNSAFE = pycompat.iswindows else: - _startupcost = 1e30 + _STARTUP_COST = 1e30 + _DISALLOW_THREAD_UNSAFE = False -def worthwhile(ui, costperop, nops): +def worthwhile(ui, costperop, nops, threadsafe=True): '''try to determine whether the benefit of multiple processes can outweigh the cost of starting them''' + + if not threadsafe and _DISALLOW_THREAD_UNSAFE: + return False + linear = costperop * nops workers = _numworkers(ui) - benefit = linear - (_startupcost * workers + linear / workers) + benefit = linear - (_STARTUP_COST * workers + linear / workers) return benefit >= 0.15 -def worker(ui, costperarg, func, staticargs, args): +def worker(ui, costperarg, func, staticargs, args, threadsafe=True): '''run a function, possibly in parallel in multiple worker processes. @@ -82,14 +97,17 @@ args - arguments to split into chunks, to pass to individual workers + + threadsafe - whether work items are thread safe and can be executed using + a thread-based worker. Should be disabled for CPU heavy tasks that don't + release the GIL. ''' enabled = ui.configbool('worker', 'enabled') - if enabled and worthwhile(ui, costperarg, len(args)): + if enabled and worthwhile(ui, costperarg, len(args), threadsafe=threadsafe): return _platformworker(ui, func, staticargs, args) return func(*staticargs + (args,)) def _posixworker(ui, func, staticargs, args): - rfd, wfd = os.pipe() workers = _numworkers(ui) oldhandler = signal.getsignal(signal.SIGINT) signal.signal(signal.SIGINT, signal.SIG_IGN) @@ -138,7 +156,15 @@ oldchldhandler = signal.signal(signal.SIGCHLD, sigchldhandler) ui.flush() parentpid = os.getpid() + pipes = [] for pargs in partition(args, workers): + # Every worker gets its own pipe to send results on, so we don't have to + # implement atomic writes larger than PIPE_BUF. Each forked process has + # its own pipe's descriptors in the local variables, and the parent + # process has the full list of pipe descriptors (and it doesn't really + # care what order they're in). + rfd, wfd = os.pipe() + pipes.append((rfd, wfd)) # make sure we use os._exit in all worker code paths. otherwise the # worker may do some clean-ups which could cause surprises like # deadlock. see sshpeer.cleanup for example. @@ -154,9 +180,12 @@ signal.signal(signal.SIGCHLD, oldchldhandler) def workerfunc(): + for r, w in pipes[:-1]: + os.close(r) + os.close(w) os.close(rfd) - for i, item in func(*(staticargs + (pargs,))): - os.write(wfd, '%d %s\n' % (i, item)) + for result in func(*(staticargs + (pargs,))): + os.write(wfd, util.pickle.dumps(result)) return 0 ret = scmutil.callcatch(ui, workerfunc) @@ -175,8 +204,10 @@ finally: os._exit(ret & 255) pids.add(pid) - os.close(wfd) - fp = os.fdopen(rfd, r'rb', 0) + selector = selectors.DefaultSelector() + for rfd, wfd in pipes: + os.close(wfd) + selector.register(os.fdopen(rfd, r'rb', 0), selectors.EVENT_READ) def cleanup(): signal.signal(signal.SIGINT, oldhandler) waitforworkers() @@ -187,9 +218,19 @@ os.kill(os.getpid(), -status) sys.exit(status) try: - for line in util.iterfile(fp): - l = line.split(' ', 1) - yield int(l[0]), l[1][:-1] + openpipes = len(pipes) + while openpipes > 0: + for key, events in selector.select(): + try: + yield util.pickle.load(key.fileobj) + except EOFError: + selector.unregister(key.fileobj) + key.fileobj.close() + openpipes -= 1 + except IOError as e: + if e.errno == errno.EINTR: + continue + raise except: # re-raises killworkers() cleanup() @@ -235,7 +276,7 @@ # iteration. if self._interrupted: return - except util.empty: + except pycompat.queue.Empty: break except Exception as e: # store the exception such that the main thread can resurface @@ -262,8 +303,8 @@ return workers = _numworkers(ui) - resultqueue = util.queue() - taskqueue = util.queue() + resultqueue = pycompat.queue.Queue() + taskqueue = pycompat.queue.Queue() # partition work to more pieces than workers to minimize the chance # of uneven distribution of large tasks between the workers for pargs in partition(args, workers * 20):
--- a/setup.py Sun Jul 01 23:36:53 2018 +0900 +++ b/setup.py Thu Jul 19 13:55:54 2018 -0400 @@ -74,7 +74,7 @@ badpython = True # Allow Python 3 from source checkouts. - if os.path.isdir('.hg'): + if os.path.isdir('.hg') or 'HGPYTHON3' in os.environ: badpython = False if badpython: @@ -360,7 +360,7 @@ write_if_changed('mercurial/__version__.py', b''.join([ b'# this file is autogenerated by setup.py\n' - b'version = "%s"\n' % versionb, + b'version = b"%s"\n' % versionb, ])) try: @@ -369,7 +369,7 @@ from mercurial import __version__ version = __version__.version except ImportError: - version = 'unknown' + version = b'unknown' finally: if oldpolicy is None: del os.environ['HGMODULEPOLICY'] @@ -1066,16 +1066,24 @@ package_data=packagedata, cmdclass=cmdclass, distclass=hgdist, - options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email', - # implicitly imported per module policy - # (cffi wouldn't be used as a frozen exe) - 'mercurial.cext', - #'mercurial.cffi', - 'mercurial.pure']}, - 'bdist_mpkg': {'zipdist': False, - 'license': 'COPYING', - 'readme': 'contrib/macosx/Readme.html', - 'welcome': 'contrib/macosx/Welcome.html', - }, - }, + options={ + 'py2exe': { + 'packages': [ + 'hgdemandimport', + 'hgext', + 'email', + # implicitly imported per module policy + # (cffi wouldn't be used as a frozen exe) + 'mercurial.cext', + #'mercurial.cffi', + 'mercurial.pure', + ], + }, + 'bdist_mpkg': { + 'zipdist': False, + 'license': 'COPYING', + 'readme': 'contrib/packaging/macosx/Readme.html', + 'welcome': 'contrib/packaging/macosx/Welcome.html', + }, + }, **extra)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/basic_test_result.py Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,52 @@ +from __future__ import absolute_import, print_function + +import unittest + +class TestResult(unittest._TextTestResult): + + def __init__(self, options, *args, **kwargs): + super(TestResult, self).__init__(*args, **kwargs) + self._options = options + + # unittest.TestResult didn't have skipped until 2.7. We need to + # polyfill it. + self.skipped = [] + + # We have a custom "ignored" result that isn't present in any Python + # unittest implementation. It is very similar to skipped. It may make + # sense to map it into skip some day. + self.ignored = [] + + self.times = [] + self._firststarttime = None + # Data stored for the benefit of generating xunit reports. + self.successes = [] + self.faildata = {} + + def addFailure(self, test, reason): + print("FAILURE!", test, reason) + + def addSuccess(self, test): + print("SUCCESS!", test) + + def addError(self, test, err): + print("ERR!", test, err) + + # Polyfill. + def addSkip(self, test, reason): + print("SKIP!", test, reason) + + def addIgnore(self, test, reason): + print("IGNORE!", test, reason) + + def onStart(self, test): + print("ON_START!", test) + + def onEnd(self): + print("ON_END!") + + def addOutputMismatch(self, test, ret, got, expected): + return False + + def stopTest(self, test, interrupted=False): + super(TestResult, self).stopTest(test)
--- a/tests/common-pattern.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/common-pattern.py Thu Jul 19 13:55:54 2018 -0400 @@ -71,6 +71,20 @@ # (replacement patterns) br'$USUAL_BUNDLE2_CAPS_SERVER$' ), + ( + br'bundle2=HG20%0A' + br'bookmarks%0A' + br'changegroup%3D01%2C02%0A' + br'digests%3Dmd5%2Csha1%2Csha512%0A' + br'error%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0A' + br'hgtagsfnodes%0A' + br'listkeys%0A' + br'pushkey%0A' + br'remote-changegroup%3Dhttp%2Chttps%0A' + br'rev-branch-cache', + # (replacement patterns) + br'$USUAL_BUNDLE2_CAPS_NO_PHASES$' + ), # HTTP access log dates (br' - - \[\d\d/.../2\d\d\d \d\d:\d\d:\d\d] "(GET|PUT|POST)', lambda m: br' - - [$LOGDATE$] "' + m.group(1)
--- a/tests/dumbhttp.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/dumbhttp.py Thu Jul 19 13:55:54 2018 -0400 @@ -13,6 +13,7 @@ import sys from mercurial import ( + encoding, pycompat, server, util, @@ -66,7 +67,8 @@ opts = {b'pid_file': options.pid, b'daemon': not options.foreground, - b'daemon_postexec': options.daemon_postexec} + b'daemon_postexec': pycompat.rapply(encoding.strtolocal, + options.daemon_postexec)} service = simplehttpservice(options.host, options.port) runargs = [sys.executable, __file__] + sys.argv[1:] runargs = [pycompat.fsencode(a) for a in runargs]
--- a/tests/f Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/f Thu Jul 19 13:55:54 2018 -0400 @@ -84,7 +84,7 @@ if opts.mode and not islink: facts.append(b'mode=%o' % (stat.st_mode & 0o777)) if opts.links: - facts.append(b'links=%s' % stat.st_nlink) + facts.append(b'links=%d' % stat.st_nlink) if opts.newer: # mtime might be in whole seconds so newer file might be same if stat.st_mtime >= os.stat(opts.newer).st_mtime:
--- a/tests/hghave.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/hghave.py Thu Jul 19 13:55:54 2018 -0400 @@ -452,8 +452,9 @@ @check("clang-format", "clang-format C code formatter") def has_clang_format(): - return matchoutput("clang-format --help", - br"^OVERVIEW: A tool to format C/C\+\+[^ ]+ code.") + m = matchoutput('clang-format --version', br'clang-format version (\d)') + # style changed somewhere between 4.x and 6.x + return m and int(m.group(1)) >= 6 @check("jshint", "JSHint static code analysis tool") def has_jshint(): @@ -616,7 +617,7 @@ "debian build dependencies (run dpkg-checkbuilddeps in contrib/)") def has_debdeps(): # just check exit status (ignoring output) - path = '%s/../contrib/debian/control' % os.environ['TESTDIR'] + path = '%s/../contrib/packaging/debian/control' % os.environ['TESTDIR'] return matchoutput('dpkg-checkbuilddeps %s' % path, br'') @check("demandimport", "demandimport enabled") @@ -709,6 +710,10 @@ return int(mat.group(1)) > 5 return False +@check("clang-6.0", "clang 6.0 with version suffix (libfuzzer included)") +def has_clang60(): + return matchoutput('clang-6.0 --version', b'clang version 6\.') + @check("xdiff", "xdiff algorithm") def has_xdiff(): try:
--- a/tests/killdaemons.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/killdaemons.py Thu Jul 19 13:55:54 2018 -0400 @@ -124,4 +124,4 @@ else: path = os.environ["DAEMON_PIDS"] - killdaemons(path) + killdaemons(path, remove=True)
--- a/tests/printenv.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/printenv.py Thu Jul 19 13:55:54 2018 -0400 @@ -26,6 +26,7 @@ exitcode = 0 out = sys.stdout +out = getattr(out, 'buffer', out) name = sys.argv[1] if len(sys.argv) > 2: @@ -39,14 +40,15 @@ if k.startswith("HG_") and v] env.sort() -out.write("%s hook: " % name) +out.write(b"%s hook: " % name.encode('ascii')) if os.name == 'nt': filter = lambda x: x.replace('\\', '/') else: filter = lambda x: x -vars = ["%s=%s" % (k, filter(v)) for k, v in env] -out.write(" ".join(vars)) -out.write("\n") +vars = [b"%s=%s" % (k.encode('ascii'), filter(v).encode('ascii')) + for k, v in env] +out.write(b" ".join(vars)) +out.write(b"\n") out.close() sys.exit(exitcode)
--- a/tests/run-tests.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/run-tests.py Thu Jul 19 13:55:54 2018 -0400 @@ -120,7 +120,7 @@ } class TestRunnerLexer(lexer.RegexLexer): - testpattern = r'[\w-]+\.(t|py)( \(case [\w-]+\))?' + testpattern = r'[\w-]+\.(t|py)(#[a-zA-Z0-9_\-\.]+)?' tokens = { 'root': [ (r'^Skipped', token.Generic.Skipped, 'skipped'), @@ -1247,7 +1247,7 @@ self._allcases = parsettestcases(path) super(TTest, self).__init__(path, *args, **kwds) if case: - self.name = '%s (case %s)' % (self.name, _strpath(case)) + self.name = '%s#%s' % (self.name, _strpath(case)) self.errpath = b'%s.%s.err' % (self.errpath[:-4], case) self._tmpname += b'-%s' % case self._have = {} @@ -1480,20 +1480,17 @@ if expected.get(pos, None): els = expected[pos] - i = 0 optional = [] - while i < len(els): - el = els[i] - - r = self.linematch(el, lout) + for i, el in enumerate(els): + r = False + if el: + r, exact = self.linematch(el, lout) if isinstance(r, str): if r == '-glob': lout = ''.join(el.rsplit(' (glob)', 1)) r = '' # Warn only this line. elif r == "retry": postout.append(b' ' + el) - els.pop(i) - break else: log('\ninfo, unknown linematch result: %r\n' % r) r = False @@ -1511,8 +1508,11 @@ if not self._iftest(conditions): optional.append(i) - - i += 1 + if exact: + # Don't allow line to be matches against a later + # line in the output + els.pop(i) + break if r: if r == "retry": @@ -1612,42 +1612,41 @@ return TTest.rematch(res, l) def linematch(self, el, l): + if el == l: # perfect match (fast) + return True, True retry = False - if el == l: # perfect match (fast) - return True - if el: - if el.endswith(b" (?)\n"): - retry = "retry" - el = el[:-5] + b"\n" + if el.endswith(b" (?)\n"): + retry = "retry" + el = el[:-5] + b"\n" + else: + m = optline.match(el) + if m: + conditions = [c for c in m.group(2).split(b' ')] + + el = m.group(1) + b"\n" + if not self._iftest(conditions): + retry = "retry" # Not required by listed features + + if el.endswith(b" (esc)\n"): + if PYTHON3: + el = el[:-7].decode('unicode_escape') + '\n' + el = el.encode('utf-8') else: - m = optline.match(el) - if m: - conditions = [c for c in m.group(2).split(b' ')] - - el = m.group(1) + b"\n" - if not self._iftest(conditions): - retry = "retry" # Not required by listed features - - if el.endswith(b" (esc)\n"): - if PYTHON3: - el = el[:-7].decode('unicode_escape') + '\n' - el = el.encode('utf-8') - else: - el = el[:-7].decode('string-escape') + '\n' - if el == l or os.name == 'nt' and el[:-1] + b'\r\n' == l: - return True - if el.endswith(b" (re)\n"): - return TTest.rematch(el[:-6], l) or retry - if el.endswith(b" (glob)\n"): - # ignore '(glob)' added to l by 'replacements' - if l.endswith(b" (glob)\n"): - l = l[:-8] + b"\n" - return TTest.globmatch(el[:-8], l) or retry - if os.altsep: - _l = l.replace(b'\\', b'/') - if el == _l or os.name == 'nt' and el[:-1] + b'\r\n' == _l: - return True - return retry + el = el[:-7].decode('string-escape') + '\n' + if el == l or os.name == 'nt' and el[:-1] + b'\r\n' == l: + return True, True + if el.endswith(b" (re)\n"): + return (TTest.rematch(el[:-6], l) or retry), False + if el.endswith(b" (glob)\n"): + # ignore '(glob)' added to l by 'replacements' + if l.endswith(b" (glob)\n"): + l = l[:-8] + b"\n" + return (TTest.globmatch(el[:-8], l) or retry), False + if os.altsep: + _l = l.replace(b'\\', b'/') + if el == _l or os.name == 'nt' and el[:-1] + b'\r\n' == _l: + return True, True + return retry, True @staticmethod def parsehghaveoutput(lines): @@ -1711,6 +1710,14 @@ else: # 'always', for testing purposes self.color = pygmentspresent + def onStart(self, test): + """ Can be overriden by custom TestResult + """ + + def onEnd(self): + """ Can be overriden by custom TestResult + """ + def addFailure(self, test, reason): self.failures.append((test, reason)) @@ -1852,6 +1859,16 @@ self.stream.writeln('INTERRUPTED: %s (after %d seconds)' % ( test.name, self.times[-1][3])) +def getTestResult(): + """ + Returns the relevant test result + """ + if "CUSTOM_TEST_RESULT" in os.environ: + testresultmodule = __import__(os.environ["CUSTOM_TEST_RESULT"]) + return testresultmodule.TestResult + else: + return TestResult + class TestSuite(unittest.TestSuite): """Custom unittest TestSuite that knows how to execute Mercurial tests.""" @@ -2090,71 +2107,74 @@ self._runner = runner + self._result = getTestResult()(self._runner.options, self.stream, + self.descriptions, self.verbosity) + def listtests(self, test): - result = TestResult(self._runner.options, self.stream, - self.descriptions, 0) test = sorted(test, key=lambda t: t.name) + + self._result.onStart(test) + for t in test: print(t.name) - result.addSuccess(t) + self._result.addSuccess(t) if self._runner.options.xunit: with open(self._runner.options.xunit, "wb") as xuf: - self._writexunit(result, xuf) + self._writexunit(self._result, xuf) if self._runner.options.json: jsonpath = os.path.join(self._runner._outputdir, b'report.json') with open(jsonpath, 'w') as fp: - self._writejson(result, fp) - - return result + self._writejson(self._result, fp) + + return self._result def run(self, test): - result = TestResult(self._runner.options, self.stream, - self.descriptions, self.verbosity) - - test(result) - - failed = len(result.failures) - skipped = len(result.skipped) - ignored = len(result.ignored) + self._result.onStart(test) + test(self._result) + + failed = len(self._result.failures) + skipped = len(self._result.skipped) + ignored = len(self._result.ignored) with iolock: self.stream.writeln('') if not self._runner.options.noskips: - for test, msg in result.skipped: + for test, msg in self._result.skipped: formatted = 'Skipped %s: %s\n' % (test.name, msg) - self.stream.write(highlightmsg(formatted, result.color)) - for test, msg in result.failures: + msg = highlightmsg(formatted, self._result.color) + self.stream.write(msg) + for test, msg in self._result.failures: formatted = 'Failed %s: %s\n' % (test.name, msg) - self.stream.write(highlightmsg(formatted, result.color)) - for test, msg in result.errors: + self.stream.write(highlightmsg(formatted, self._result.color)) + for test, msg in self._result.errors: self.stream.writeln('Errored %s: %s' % (test.name, msg)) if self._runner.options.xunit: with open(self._runner.options.xunit, "wb") as xuf: - self._writexunit(result, xuf) + self._writexunit(self._result, xuf) if self._runner.options.json: jsonpath = os.path.join(self._runner._outputdir, b'report.json') with open(jsonpath, 'w') as fp: - self._writejson(result, fp) + self._writejson(self._result, fp) self._runner._checkhglib('Tested') - savetimes(self._runner._outputdir, result) + savetimes(self._runner._outputdir, self._result) if failed and self._runner.options.known_good_rev: - self._bisecttests(t for t, m in result.failures) + self._bisecttests(t for t, m in self._result.failures) self.stream.writeln( '# Ran %d tests, %d skipped, %d failed.' - % (result.testsRun, skipped + ignored, failed)) + % (self._result.testsRun, skipped + ignored, failed)) if failed: self.stream.writeln('python hash seed: %s' % os.environ['PYTHONHASHSEED']) if self._runner.options.time: - self.printtimes(result.times) + self.printtimes(self._result.times) if self._runner.options.exceptions: exceptions = aggregateexceptions( @@ -2177,7 +2197,7 @@ self.stream.flush() - return result + return self._result def _bisecttests(self, tests): bisectcmd = ['hg', 'bisect'] @@ -2646,16 +2666,31 @@ expanded_args.append(arg) args = expanded_args + testcasepattern = re.compile(br'([\w-]+\.t|py)(#([a-zA-Z0-9_\-\.]+))') tests = [] for t in args: + case = None + if not (os.path.basename(t).startswith(b'test-') and (t.endswith(b'.py') or t.endswith(b'.t'))): - continue + + m = testcasepattern.match(t) + if m is not None: + t, _, case = m.groups() + else: + continue + if t.endswith(b'.t'): # .t file may contain multiple test cases cases = sorted(parsettestcases(t)) if cases: - tests += [{'path': t, 'case': c} for c in sorted(cases)] + if case is not None and case in cases: + tests += [{'path': t, 'case': case}] + elif case is not None and case not in cases: + # Ignore invalid cases + pass + else: + tests += [{'path': t, 'case': c} for c in sorted(cases)] else: tests.append({'path': t}) else: @@ -2707,7 +2742,9 @@ showchannels=self.options.showchannels, tests=tests, loadtest=_reloadtest) verbosity = 1 - if self.options.verbose: + if self.options.list_tests: + verbosity = 0 + elif self.options.verbose: verbosity = 2 runner = TextTestRunner(self, verbosity=verbosity) @@ -2728,6 +2765,8 @@ if result.failures: failed = True + result.onEnd() + if self.options.anycoverage: self._outputcoverage() except KeyboardInterrupt:
--- a/tests/simplestorerepo.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/simplestorerepo.py Thu Jul 19 13:55:54 2018 -0400 @@ -24,9 +24,6 @@ from mercurial.thirdparty import ( cbor, ) -from mercurial.thirdparty.zope import ( - interface as zi, -) from mercurial import ( ancestor, bundlerepo, @@ -40,6 +37,9 @@ store, verify, ) +from mercurial.utils import ( + interfaceutil, +) # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -60,7 +60,7 @@ if not isinstance(rev, int): raise ValueError('expected int') -@zi.implementer(repository.ifilestorage) +@interfaceutil.implementer(repository.ifilestorage) class filestorage(object): """Implements storage for a tracked path.
--- a/tests/test-acl.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-acl.t Thu Jul 19 13:55:54 2018 -0400 @@ -15,7 +15,7 @@ > # LOGNAME=$user hg --cws a --debug push ../b > # fails with "This variable is read only." > # Use env to work around this. - > env LOGNAME=$user hg --cwd a --debug push ../b + > env LOGNAME=$user hg --cwd a --debug push ../b $* > hg --cwd b rollback > hg --cwd b --quiet tip > echo @@ -47,6 +47,7 @@ > cat > $config <<EOF > [hooks] > pretxnchangegroup.acl = python:hgext.acl.hook + > prepushkey.acl = python:hgext.acl.hook > [acl] > sources = push > [extensions] @@ -148,6 +149,7 @@ $ echo '[hooks]' >> $config $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config + $ echo 'prepushkey.acl = python:hgext.acl.hook' >> $config Extension disabled for lack of acl.sources @@ -156,6 +158,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook """ pushing to ../b query 1; heads @@ -220,6 +223,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push """ @@ -295,6 +299,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [acl.allow] @@ -362,6 +367,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [acl.allow] @@ -434,6 +440,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [acl.allow] @@ -503,6 +510,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [acl.allow] @@ -577,6 +585,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [acl.allow] @@ -649,6 +658,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [acl.allow] @@ -712,6 +722,178 @@ 0:6675d58eff77 +fred is not blocked from moving bookmarks + + $ hg -R a book -q moving-bookmark -r 1 + $ hg -R b book -q moving-bookmark -r 0 + $ cp $config normalconfig + $ do_push fred -r 1 + Pushing as user fred + hgrc = """ + [hooks] + pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook + [acl] + sources = push + [acl.allow] + foo/** = fred + [acl.deny] + foo/bar/** = fred + foo/Bar/** = fred + """ + pushing to ../b + query 1; heads + searching for changes + all remote heads known locally + listing keys for "phases" + checking for updated bookmarks + listing keys for "bookmarks" + listing keys for "bookmarks" + 1 changesets found + list of changesets: + ef1ea85a6374b77d6da9dcda9541f498f2d17df7 + bundle2-output-bundle: "HG20", 7 parts total + bundle2-output-part: "replycaps" 205 bytes payload + bundle2-output-part: "check:bookmarks" 37 bytes payload + bundle2-output-part: "check:phases" 24 bytes payload + bundle2-output-part: "check:heads" streamed payload + bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload + bundle2-output-part: "phase-heads" 24 bytes payload + bundle2-output-part: "bookmarks" 37 bytes payload + bundle2-input-bundle: with-transaction + bundle2-input-part: "replycaps" supported + bundle2-input-part: total payload size 205 + bundle2-input-part: "check:bookmarks" supported + bundle2-input-part: total payload size 37 + bundle2-input-part: "check:phases" supported + bundle2-input-part: total payload size 24 + bundle2-input-part: "check:heads" supported + bundle2-input-part: total payload size 20 + bundle2-input-part: "changegroup" (params: 1 mandatory) supported + adding changesets + add changeset ef1ea85a6374 + adding manifests + adding file changes + adding foo/file.txt revisions + added 1 changesets with 1 changes to 1 files + calling hook pretxnchangegroup.acl: hgext.acl.hook + acl: checking access for user "fred" + acl: acl.allow.branches not enabled + acl: acl.deny.branches not enabled + acl: acl.allow enabled, 1 entries for user fred + acl: acl.deny enabled, 2 entries for user fred + acl: branch access granted: "ef1ea85a6374" on branch "default" + acl: path access granted: "ef1ea85a6374" + bundle2-input-part: total payload size 520 + bundle2-input-part: "phase-heads" supported + bundle2-input-part: total payload size 24 + bundle2-input-part: "bookmarks" supported + bundle2-input-part: total payload size 37 + calling hook prepushkey.acl: hgext.acl.hook + acl: checking access for user "fred" + acl: acl.allow.bookmarks not enabled + acl: acl.deny.bookmarks not enabled + acl: bookmark access granted: "ef1ea85a6374b77d6da9dcda9541f498f2d17df7" on bookmark "moving-bookmark" + bundle2-input-bundle: 6 parts total + updating the branch cache + bundle2-output-bundle: "HG20", 1 parts total + bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload + bundle2-input-bundle: no-transaction + bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported + bundle2-input-bundle: 0 parts total + updating bookmark moving-bookmark + listing keys for "phases" + repository tip rolled back to revision 0 (undo push) + 0:6675d58eff77 + + +fred is not allowed to move bookmarks + + $ echo '[acl.deny.bookmarks]' >> $config + $ echo '* = fred' >> $config + $ do_push fred -r 1 + Pushing as user fred + hgrc = """ + [hooks] + pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook + [acl] + sources = push + [acl.allow] + foo/** = fred + [acl.deny] + foo/bar/** = fred + foo/Bar/** = fred + [acl.deny.bookmarks] + * = fred + """ + pushing to ../b + query 1; heads + searching for changes + all remote heads known locally + listing keys for "phases" + checking for updated bookmarks + listing keys for "bookmarks" + listing keys for "bookmarks" + 1 changesets found + list of changesets: + ef1ea85a6374b77d6da9dcda9541f498f2d17df7 + bundle2-output-bundle: "HG20", 7 parts total + bundle2-output-part: "replycaps" 205 bytes payload + bundle2-output-part: "check:bookmarks" 37 bytes payload + bundle2-output-part: "check:phases" 24 bytes payload + bundle2-output-part: "check:heads" streamed payload + bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload + bundle2-output-part: "phase-heads" 24 bytes payload + bundle2-output-part: "bookmarks" 37 bytes payload + bundle2-input-bundle: with-transaction + bundle2-input-part: "replycaps" supported + bundle2-input-part: total payload size 205 + bundle2-input-part: "check:bookmarks" supported + bundle2-input-part: total payload size 37 + bundle2-input-part: "check:phases" supported + bundle2-input-part: total payload size 24 + bundle2-input-part: "check:heads" supported + bundle2-input-part: total payload size 20 + bundle2-input-part: "changegroup" (params: 1 mandatory) supported + adding changesets + add changeset ef1ea85a6374 + adding manifests + adding file changes + adding foo/file.txt revisions + added 1 changesets with 1 changes to 1 files + calling hook pretxnchangegroup.acl: hgext.acl.hook + acl: checking access for user "fred" + acl: acl.allow.branches not enabled + acl: acl.deny.branches not enabled + acl: acl.allow enabled, 1 entries for user fred + acl: acl.deny enabled, 2 entries for user fred + acl: branch access granted: "ef1ea85a6374" on branch "default" + acl: path access granted: "ef1ea85a6374" + bundle2-input-part: total payload size 520 + bundle2-input-part: "phase-heads" supported + bundle2-input-part: total payload size 24 + bundle2-input-part: "bookmarks" supported + bundle2-input-part: total payload size 37 + calling hook prepushkey.acl: hgext.acl.hook + acl: checking access for user "fred" + acl: acl.allow.bookmarks not enabled + acl: acl.deny.bookmarks enabled, 1 entries for user fred + error: prepushkey.acl hook failed: acl: user "fred" denied on bookmark "moving-bookmark" (changeset "ef1ea85a6374b77d6da9dcda9541f498f2d17df7") + bundle2-input-bundle: 6 parts total + transaction abort! + rollback completed + abort: acl: user "fred" denied on bookmark "moving-bookmark" (changeset "ef1ea85a6374b77d6da9dcda9541f498f2d17df7") + no rollback information available + 0:6675d58eff77 + + +cleanup bookmark stuff + + $ hg book -R a -d moving-bookmark + $ hg book -R b -d moving-bookmark + $ cp normalconfig $config + barney is allowed everywhere $ echo '[acl.allow]' >> $config @@ -721,6 +903,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [acl.allow] @@ -803,6 +986,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [acl.allow] @@ -882,6 +1066,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [acl.allow] @@ -954,6 +1139,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [acl.allow] @@ -1039,6 +1225,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [acl.allow] @@ -1138,6 +1325,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -1219,6 +1407,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -1301,6 +1490,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -1383,6 +1573,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -1507,6 +1698,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -1590,6 +1782,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -1669,6 +1862,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -1742,6 +1936,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -1810,6 +2005,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -1899,6 +2095,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -1987,6 +2184,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -2062,6 +2260,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions] @@ -2145,6 +2344,7 @@ hgrc = """ [hooks] pretxnchangegroup.acl = python:hgext.acl.hook + prepushkey.acl = python:hgext.acl.hook [acl] sources = push [extensions]
--- a/tests/test-annotate.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-annotate.t Thu Jul 19 13:55:54 2018 -0400 @@ -76,6 +76,14 @@ $ hg annotate -T'{lines % "{rev} {node|shortest}: {line}"}' a 0 8435: a +'{line_number}' field should be populated as necessary + + $ hg annotate -T'{lines % "{rev}:{line_number}: {line}"}' a + 0:1: a + $ hg annotate -Ta a \ + > --config templates.a='"{lines % "{rev}:{line_number}: {line}"}"' + 0:1: a + $ cat <<EOF >>a > a > a
--- a/tests/test-archive.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-archive.t Thu Jul 19 13:55:54 2018 -0400 @@ -94,7 +94,7 @@ $ echo "archivesubrepos = True" >> .hg/hgrc $ cp .hg/hgrc .hg/hgrc-base > test_archtype() { - > echo "allow_archive = $1" >> .hg/hgrc + > echo "allow-archive = $1" >> .hg/hgrc > test_archtype_run "$@" > } > test_archtype_deprecated() { @@ -293,7 +293,7 @@ body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352 - $ echo "allow_archive = gz bz2 zip" >> .hg/hgrc + $ echo "allow-archive = gz bz2 zip" >> .hg/hgrc $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log $ cat hg.pid >> $DAEMON_PIDS
--- a/tests/test-bad-extension.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-bad-extension.t Thu Jul 19 13:55:54 2018 -0400 @@ -72,10 +72,9 @@ $ hg --config extensions.badexts=showbadexts.py showbadexts 2>&1 | grep '^BADEXTS' BADEXTS: badext badext2 -show traceback for ImportError of hgext.name if debug is set -(note that --debug option isn't applied yet when loading extensions) +show traceback for ImportError of hgext.name if devel.debug.extensions is set - $ (hg -q help help --traceback --config ui.debug=True 2>&1) \ + $ (hg help help --traceback --debug --config devel.debug.extensions=yes 2>&1) \ > | grep -v '^ ' \ > | egrep 'extension..[^p]|^Exception|Traceback|ImportError|not import' *** failed to import extension badext from $TESTTMP/badext.py: bit bucket overflow
--- a/tests/test-bad-pull.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-bad-pull.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,4 +1,4 @@ -#require serve killdaemons +#require serve $ hg clone http://localhost:$HGPORT/ copy abort: * (glob)
--- a/tests/test-basic.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-basic.t Thu Jul 19 13:55:54 2018 -0400 @@ -64,7 +64,7 @@ > from mercurial import ui, hg, commands > myui = ui.ui.load() > repo = hg.repository(myui, path=b'.') - > commands.update(myui, repo, rev=0) + > commands.update(myui, repo, rev=b"0") > EOF $ hg up null 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
--- a/tests/test-blackbox.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-blackbox.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,13 +1,29 @@ setup + + $ cat > myextension.py <<EOF + > from mercurial import error, registrar + > cmdtable = {} + > command = registrar.command(cmdtable) + > @command(b'crash', [], b'hg crash') + > def crash(ui, *args, **kwargs): + > raise Exception("oops") + > @command(b'abort', [], b'hg abort') + > def abort(ui, *args, **kwargs): + > raise error.Abort(b"oops") + > EOF + $ abspath=`pwd`/myextension.py + $ cat >> $HGRCPATH <<EOF > [extensions] > blackbox= > mock=$TESTDIR/mockblackbox.py > mq= + > myextension=$TESTTMP/myextension.py > [alias] > confuse = log --limit 3 > so-confusing = confuse --style compact > EOF + $ hg init blackboxtest $ cd blackboxtest @@ -21,6 +37,32 @@ 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a exited 0 after * seconds (glob) 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000+ (5000)> blackbox --config *blackbox.dirty=True* (glob) +failure exit code + $ rm ./.hg/blackbox.log + $ hg add non-existent + non-existent: $ENOENT$ + [1] + $ hg blackbox + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add non-existent + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add non-existent exited 1 after * seconds (glob) + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox + +abort exit code + $ rm ./.hg/blackbox.log + $ hg abort 2> /dev/null + [255] + $ hg blackbox -l 2 + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> abort exited 255 after * seconds (glob) + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox -l 2 + +unhandled exception + $ rm ./.hg/blackbox.log + $ hg crash 2> /dev/null + [1] + $ hg blackbox -l 2 + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> crash exited 1 after * seconds (glob) + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox -l 2 + alias expansion is logged $ rm ./.hg/blackbox.log $ hg confuse @@ -206,7 +248,7 @@ committing changelog updating the branch cache committed changeset 0:0e46349438790c460c5c9f7546bfcd39b267bbd2 - result: None + result: 0 running: --debug commit -m commit2 -d 2000-01-02 foo committing files: foo @@ -214,7 +256,7 @@ committing changelog updating the branch cache committed changeset 1:45589e459b2edfbf3dbde7e01f611d2c1e7453d7 - result: None + result: 0 running: --debug log -r 0 changeset: 0:0e46349438790c460c5c9f7546bfcd39b267bbd2 phase: draft @@ -229,7 +271,7 @@ commit1 - result: None + result: 0 running: --debug log -r tip changeset: 1:45589e459b2edfbf3dbde7e01f611d2c1e7453d7 tag: tip @@ -245,7 +287,7 @@ commit2 - result: None + result: 0 $ hg blackbox 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> updating the branch cache 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> updated served branch cache in * seconds (glob)
--- a/tests/test-bookmarks-pushpull.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-bookmarks-pushpull.t Thu Jul 19 13:55:54 2018 -0400 @@ -1034,7 +1034,8 @@ =============================================== #if b2-binary - >>> open('longname', 'w').write('wat' * 100) + >>> with open('longname', 'w') as f: + ... f.write('wat' * 100) and None $ hg book `cat longname` $ hg push -B `cat longname` ../unchanged-b pushing to ../unchanged-b
--- a/tests/test-bookmarks.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-bookmarks.t Thu Jul 19 13:55:54 2018 -0400 @@ -68,12 +68,20 @@ X 0:f7b1eb17ad24 * X2 0:f7b1eb17ad24 Y -1:000000000000 + $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}' + 0 X + 0 X2 $ echo b > b $ hg add b $ hg commit -m 1 --config "$TESTHOOK" test-hook-bookmark: X2: f7b1eb17ad24730a1651fccd46c43826d1bbc2ac -> 925d80f479bb026b0fb3deb27503780b13f74123 + $ hg bookmarks -T '{rev}:{node|shortest} {bookmark} {desc|firstline}\n' + 0:f7b1 X 0 + 1:925d X2 1 + -1:0000 Y + $ hg bookmarks -Tjson [ { @@ -299,6 +307,11 @@ Y 2:db815d6d32e6 Z 0:f7b1eb17ad24 * x y 2:db815d6d32e6 + $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}' + 2 Y + 2 x y + 1 X2 + 0 Z look up stripped bookmark name @@ -445,6 +458,11 @@ Y 2:db815d6d32e6 * Z 2:db815d6d32e6 x y 2:db815d6d32e6 + $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}' + 2 Y + 2 Z + 2 x y + 1 X2 revision but no bookmark name
--- a/tests/test-bugzilla.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-bugzilla.t Thu Jul 19 13:55:54 2018 -0400 @@ -8,15 +8,15 @@ > configtable = {} > configitem = registrar.configitem(configtable) > - > configitem('bugzilla', 'mocklog', + > configitem(b'bugzilla', b'mocklog', > default=None, > ) > def extsetup(ui): - > bugzilla = extensions.find('bugzilla') + > bugzilla = extensions.find(b'bugzilla') > class bzmock(bugzilla.bzaccess): > def __init__(self, ui): > super(bzmock, self).__init__(ui) - > self._logfile = ui.config('bugzilla', 'mocklog') + > self._logfile = ui.config(b'bugzilla', b'mocklog') > def updatebug(self, bugid, newstate, text, committer): > with open(self._logfile, 'a') as f: > f.write('update bugid=%r, newstate=%r, committer=%r\n' @@ -26,7 +26,7 @@ > with open(self._logfile, 'a') as f: > f.write('notify bugs=%r, committer=%r\n' > % (bugs, committer)) - > bugzilla.bugzilla._versions['mock'] = bzmock + > bugzilla.bugzilla._versions[b'mock'] = bzmock > EOF set up mock repository:
--- a/tests/test-bundle-r.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-bundle-r.t Thu Jul 19 13:55:54 2018 -0400 @@ -156,6 +156,7 @@ adding file changes added 4 changesets with 2 changes to 3 files (+1 heads) new changesets c70afb1ee985:faa2e4234c7a + 1 local changesets published (run 'hg heads' to see heads, 'hg merge' to merge) $ hg verify checking changesets
--- a/tests/test-bundle.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-bundle.t Thu Jul 19 13:55:54 2018 -0400 @@ -339,20 +339,20 @@ > from __future__ import absolute_import > > def showtip(ui, repo, hooktype, **kwargs): - > ui.warn('%s: %s\n' % (hooktype, repo['tip'].hex()[:12])) + > ui.warn(b'%s: %s\n' % (hooktype, repo[b'tip'].hex()[:12])) > > def reposetup(ui, repo): > # this confirms (and ensures) that (empty) 00changelog.i > # before streamclone is already cached as repo.changelog - > ui.setconfig('hooks', 'pretxnopen.showtip', showtip) + > ui.setconfig(b'hooks', b'pretxnopen.showtip', showtip) > > # this confirms that streamclone-ed changes are visible to > # in-process procedures before closing transaction - > ui.setconfig('hooks', 'pretxnclose.showtip', showtip) + > ui.setconfig(b'hooks', b'pretxnclose.showtip', showtip) > > # this confirms that streamclone-ed changes are still visible > # after closing transaction - > ui.setconfig('hooks', 'txnclose.showtip', showtip) + > ui.setconfig(b'hooks', b'txnclose.showtip', showtip) > EOF $ cat >> $HGRCPATH <<EOF > [extensions]
--- a/tests/test-bundle2-exchange.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-bundle2-exchange.t Thu Jul 19 13:55:54 2018 -0400 @@ -150,6 +150,7 @@ pulling from $TESTTMP/main no changes found pre-close-tip:24b6387c8c8c public + 1 local changesets published postclose-tip:24b6387c8c8c public txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=0 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob) @@ -935,11 +936,11 @@ > import os > from mercurial import extensions, bundle2, error > def checklock(orig, repo, *args, **kwargs): - > if repo.svfs.lexists("lock"): - > raise error.Abort("Lock should not be taken") + > if repo.svfs.lexists(b"lock"): + > raise error.Abort(b"Lock should not be taken") > return orig(repo, *args, **kwargs) > def extsetup(ui): - > extensions.wrapfunction(bundle2, 'processbundle', checklock) + > extensions.wrapfunction(bundle2, b'processbundle', checklock) > EOF $ hg init lazylock
--- a/tests/test-bundle2-format.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-bundle2-format.t Thu Jul 19 13:55:54 2018 -0400 @@ -91,8 +91,8 @@ > p = p.split(b'=', 1) > try: > bundler.addparam(*p) - > except ValueError as exc: - > raise error.Abort('%s' % exc) + > except error.ProgrammingError as exc: + > raise error.Abort(b'%s' % exc) > > if opts['compress']: > bundler.setcompression(opts['compress']) @@ -184,9 +184,9 @@ > op = bundle2.processbundle(repo, unbundler, lambda: tr) > tr.close() > except error.BundleValueError as exc: - > raise error.Abort('missing support for %s' % exc) + > raise error.Abort(b'missing support for %s' % exc) > except error.PushRaced as exc: - > raise error.Abort('push race: %s' % exc) + > raise error.Abort(b'push race: %s' % exc) > finally: > if tr is not None: > tr.release()
--- a/tests/test-bundle2-pushback.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-bundle2-pushback.t Thu Jul 19 13:55:54 2018 -0400 @@ -20,18 +20,18 @@ > It issues an additional pushkey part to send a new > bookmark back to the client""" > result = bundle2.handlechangegroup(op, inpart) - > if 'pushback' in op.reply.capabilities: - > params = {'namespace': 'bookmarks', - > 'key': 'new-server-mark', - > 'old': '', - > 'new': 'tip'} + > if b'pushback' in op.reply.capabilities: + > params = {b'namespace': b'bookmarks', + > b'key': b'new-server-mark', + > b'old': b'', + > b'new': b'tip'} > encodedparams = [(k, pushkey.encode(v)) for (k,v) in params.items()] - > op.reply.newpart('pushkey', mandatoryparams=encodedparams) + > op.reply.newpart(b'pushkey', mandatoryparams=encodedparams) > else: - > op.reply.newpart('output', data='pushback not enabled') + > op.reply.newpart(b'output', data=b'pushback not enabled') > return result > _newhandlechangegroup.params = bundle2.handlechangegroup.params - > bundle2.parthandlermapping['changegroup'] = _newhandlechangegroup + > bundle2.parthandlermapping[b'changegroup'] = _newhandlechangegroup > EOF $ cat >> $HGRCPATH <<EOF
--- a/tests/test-bundle2-remote-changegroup.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-bundle2-remote-changegroup.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - #testcases sshv1 sshv2 #if sshv2 @@ -18,7 +16,14 @@ > Current bundle2 implementation doesn't provide a way to generate those > parts, so they must be created by extensions. > """ - > from mercurial import bundle2, changegroup, discovery, exchange, util + > from mercurial import ( + > bundle2, + > changegroup, + > discovery, + > exchange, + > pycompat, + > util, + > ) > > def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None, > b2caps=None, heads=None, common=None, @@ -42,45 +47,45 @@ > Python expression as parameters. The Python expression is > evaluated with eval, and is expected to be a dict. > """ - > def newpart(name, data=''): + > def newpart(name, data=b''): > """wrapper around bundler.newpart adding an extra part making the > client output information about each processed part""" - > bundler.newpart('output', data=name) + > bundler.newpart(b'output', data=name) > part = bundler.newpart(name, data=data) > return part > - > for line in open(repo.vfs.join('bundle2maker'), 'r'): + > for line in open(repo.vfs.join(b'bundle2maker'), 'rb'): > line = line.strip() > try: > verb, args = line.split(None, 1) > except ValueError: - > verb, args = line, '' - > if verb == 'remote-changegroup': + > verb, args = line, b'' + > if verb == b'remote-changegroup': > url, file = args.split() > bundledata = open(file, 'rb').read() - > digest = util.digester.preferred(b2caps['digests']) + > digest = util.digester.preferred(b2caps[b'digests']) > d = util.digester([digest], bundledata) - > part = newpart('remote-changegroup') - > part.addparam('url', url) - > part.addparam('size', str(len(bundledata))) - > part.addparam('digests', digest) - > part.addparam('digest:%s' % digest, d[digest]) - > elif verb == 'raw-remote-changegroup': - > part = newpart('remote-changegroup') + > part = newpart(b'remote-changegroup') + > part.addparam(b'url', url) + > part.addparam(b'size', b'%d' % len(bundledata)) + > part.addparam(b'digests', digest) + > part.addparam(b'digest:%s' % digest, d[digest]) + > elif verb == b'raw-remote-changegroup': + > part = newpart(b'remote-changegroup') > for k, v in eval(args).items(): - > part.addparam(k, str(v)) - > elif verb == 'changegroup': + > part.addparam(pycompat.sysbytes(k), pycompat.bytestr(v)) + > elif verb == b'changegroup': > _common, heads = args.split() > common.extend(repo[r].node() for r in repo.revs(_common)) > heads = [repo[r].node() for r in repo.revs(heads)] > outgoing = discovery.outgoing(repo, common, heads) - > cg = changegroup.makechangegroup(repo, outgoing, '01', - > 'changegroup') - > newpart('changegroup', cg.getchunks()) + > cg = changegroup.makechangegroup(repo, outgoing, b'01', + > b'changegroup') + > newpart(b'changegroup', cg.getchunks()) > else: > raise Exception('unknown verb') > - > exchange.getbundle2partsmapping['changegroup'] = _getbundlechangegrouppart + > exchange.getbundle2partsmapping[b'changegroup'] = _getbundlechangegrouppart > EOF Start a simple HTTP server to serve bundles
--- a/tests/test-cat.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-cat.t Thu Jul 19 13:55:54 2018 -0400 @@ -65,10 +65,10 @@ Test template output - $ hg --cwd tmp cat ../b ../c -T '== {path} ({abspath}) ==\n{data}' - == ../b (b) == + $ hg --cwd tmp cat ../b ../c -T '== {path} ({abspath}) r{rev} ==\n{data}' + == ../b (b) r2 == 1 - == ../c (c) == + == ../c (c) r2 == 3 $ hg cat b c -Tjson --output -
--- a/tests/test-cbor.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-cbor.py Thu Jul 19 13:55:54 2018 -0400 @@ -69,7 +69,7 @@ dest = b''.join(cborutil.streamencodeindefinitebytestring( source, chunksize=42)) - self.assertEqual(cbor.loads(dest), b''.join(source)) + self.assertEqual(cbor.loads(dest), source) def testreadtoiter(self): source = io.BytesIO(b'\x5f\x44\xaa\xbb\xcc\xdd\x43\xee\xff\x99\xff')
--- a/tests/test-check-interfaces.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-check-interfaces.py Thu Jul 19 13:55:54 2018 -0400 @@ -25,6 +25,8 @@ filelog, httppeer, localrepo, + manifest, + pycompat, repository, sshpeer, statichttprepo, @@ -37,7 +39,8 @@ wireprotov2server, ) -rootdir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +rootdir = pycompat.fsencode( + os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))) def checkzobject(o, allowextra=False): """Verify an object with a zope interface.""" @@ -106,7 +109,7 @@ httppeer.httpv2peer) ziverify.verifyClass(repository.ipeercapabilities, httppeer.httpv2peer) - checkzobject(httppeer.httpv2peer(None, '', None, None, None, None)) + checkzobject(httppeer.httpv2peer(None, b'', b'', None, None, None)) ziverify.verifyClass(repository.ipeerbase, localrepo.localpeer) @@ -121,11 +124,11 @@ checkzobject(wireprotov1peer.peerexecutor(None)) ziverify.verifyClass(repository.ipeerbase, sshpeer.sshv1peer) - checkzobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, dummypipe(), + checkzobject(sshpeer.sshv1peer(ui, b'ssh://localhost/foo', b'', dummypipe(), dummypipe(), None, None)) ziverify.verifyClass(repository.ipeerbase, sshpeer.sshv2peer) - checkzobject(sshpeer.sshv2peer(ui, 'ssh://localhost/foo', None, dummypipe(), + checkzobject(sshpeer.sshv2peer(ui, b'ssh://localhost/foo', b'', dummypipe(), dummypipe(), None, None)) ziverify.verifyClass(repository.ipeerbase, bundlerepo.bundlepeer) @@ -162,9 +165,35 @@ checkzobject(httpv2) ziverify.verifyClass(repository.ifilestorage, filelog.filelog) + ziverify.verifyClass(repository.imanifestdict, manifest.manifestdict) + ziverify.verifyClass(repository.imanifestrevisionstored, + manifest.manifestctx) + ziverify.verifyClass(repository.imanifestrevisionwritable, + manifest.memmanifestctx) + ziverify.verifyClass(repository.imanifestrevisionstored, + manifest.treemanifestctx) + ziverify.verifyClass(repository.imanifestrevisionwritable, + manifest.memtreemanifestctx) + ziverify.verifyClass(repository.imanifestlog, manifest.manifestlog) - vfs = vfsmod.vfs('.') - fl = filelog.filelog(vfs, 'dummy.i') + vfs = vfsmod.vfs(b'.') + fl = filelog.filelog(vfs, b'dummy.i') checkzobject(fl, allowextra=True) + # Conforms to imanifestlog. + ml = manifest.manifestlog(vfs, repo) + checkzobject(ml) + checkzobject(repo.manifestlog) + + # Conforms to imanifestrevision. + mctx = ml[repo[0].manifestnode()] + checkzobject(mctx) + + # Conforms to imanifestrevisionwritable. + checkzobject(mctx.new()) + checkzobject(mctx.copy()) + + # Conforms to imanifestdict. + checkzobject(mctx.read()) + main()
--- a/tests/test-check-module-imports.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-check-module-imports.t Thu Jul 19 13:55:54 2018 -0400 @@ -20,6 +20,7 @@ > -X setup.py \ > -X contrib/debugshell.py \ > -X contrib/hgweb.fcgi \ + > -X contrib/packaging/hg-docker \ > -X contrib/python-zstandard/ \ > -X contrib/win32/hgwebdir_wsgi.py \ > -X doc/gendoc.py \
--- a/tests/test-clone-uncompressed.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-clone-uncompressed.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,4 +1,4 @@ -#require serve no-reposimplestore +#require serve no-reposimplestore no-chg #testcases stream-legacy stream-bundle2
--- a/tests/test-clonebundles.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-clonebundles.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,4 +1,4 @@ -#require no-reposimplestore +#require no-reposimplestore no-chg Set up a server @@ -129,6 +129,7 @@ adding file changes added 1 changesets with 1 changes to 1 files new changesets aaff8d2ffbbf + 1 local changesets published Incremental pull doesn't fetch bundle @@ -201,6 +202,7 @@ finished applying clone bundle searching for changes no changes found + 2 local changesets published Feature works over SSH @@ -213,6 +215,7 @@ finished applying clone bundle searching for changes no changes found + 2 local changesets published Entry with unknown BUNDLESPEC is filtered and not used @@ -232,6 +235,7 @@ finished applying clone bundle searching for changes no changes found + 2 local changesets published Automatic fallback when all entries are filtered @@ -269,6 +273,7 @@ finished applying clone bundle searching for changes no changes found + 2 local changesets published #else Python <2.7.9 will filter SNI URLs @@ -373,6 +378,7 @@ finished applying clone bundle searching for changes no changes found + 2 local changesets published Preferring bz2 type will download first entry of that type @@ -385,6 +391,7 @@ finished applying clone bundle searching for changes no changes found + 2 local changesets published Preferring multiple values of an option works @@ -397,6 +404,7 @@ finished applying clone bundle searching for changes no changes found + 2 local changesets published Sorting multiple values should get us back to original first entry @@ -409,6 +417,7 @@ finished applying clone bundle searching for changes no changes found + 2 local changesets published Preferring multiple attributes has correct order @@ -421,6 +430,7 @@ finished applying clone bundle searching for changes no changes found + 2 local changesets published Test where attribute is missing from some entries @@ -440,6 +450,7 @@ finished applying clone bundle searching for changes no changes found + 2 local changesets published Test interaction between clone bundles and --stream @@ -545,3 +556,4 @@ finished applying clone bundle searching for changes no changes found + 2 local changesets published
--- a/tests/test-command-template.t Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4884 +0,0 @@ - $ hg init a - $ cd a - $ echo a > a - $ hg add a - $ echo line 1 > b - $ echo line 2 >> b - $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>' - - $ hg add b - $ echo other 1 > c - $ echo other 2 >> c - $ echo >> c - $ echo other 3 >> c - $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>' - - $ hg add c - $ hg commit -m 'no person' -d '1200000 0' -u 'other@place' - $ echo c >> c - $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person' - - $ echo foo > .hg/branch - $ hg commit -m 'new branch' -d '1400000 0' -u 'person' - - $ hg co -q 3 - $ echo other 4 >> d - $ hg add d - $ hg commit -m 'new head' -d '1500000 0' -u 'person' - - $ hg merge -q foo - $ hg commit -m 'merge' -d '1500001 0' -u 'person' - -Test arithmetic operators have the right precedence: - - $ hg log -l 1 -T '{date(date, "%Y") + 5 * 10} {date(date, "%Y") - 2 * 3}\n' - 2020 1964 - $ hg log -l 1 -T '{date(date, "%Y") * 5 + 10} {date(date, "%Y") * 3 - 2}\n' - 9860 5908 - -Test division: - - $ hg debugtemplate -r0 -v '{5 / 2} {mod(5, 2)}\n' - (template - (/ - (integer '5') - (integer '2')) - (string ' ') - (func - (symbol 'mod') - (list - (integer '5') - (integer '2'))) - (string '\n')) - 2 1 - $ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n' - (template - (/ - (integer '5') - (negate - (integer '2'))) - (string ' ') - (func - (symbol 'mod') - (list - (integer '5') - (negate - (integer '2')))) - (string '\n')) - -3 -1 - $ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n' - (template - (/ - (negate - (integer '5')) - (integer '2')) - (string ' ') - (func - (symbol 'mod') - (list - (negate - (integer '5')) - (integer '2'))) - (string '\n')) - -3 1 - $ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n' - (template - (/ - (negate - (integer '5')) - (negate - (integer '2'))) - (string ' ') - (func - (symbol 'mod') - (list - (negate - (integer '5')) - (negate - (integer '2')))) - (string '\n')) - 2 -1 - -Filters bind closer than arithmetic: - - $ hg debugtemplate -r0 -v '{revset(".")|count - 1}\n' - (template - (- - (| - (func - (symbol 'revset') - (string '.')) - (symbol 'count')) - (integer '1')) - (string '\n')) - 0 - -But negate binds closer still: - - $ hg debugtemplate -r0 -v '{1-3|stringify}\n' - (template - (- - (integer '1') - (| - (integer '3') - (symbol 'stringify'))) - (string '\n')) - hg: parse error: arithmetic only defined on integers - [255] - $ hg debugtemplate -r0 -v '{-3|stringify}\n' - (template - (| - (negate - (integer '3')) - (symbol 'stringify')) - (string '\n')) - -3 - -Filters bind as close as map operator: - - $ hg debugtemplate -r0 -v '{desc|splitlines % "{line}\n"}' - (template - (% - (| - (symbol 'desc') - (symbol 'splitlines')) - (template - (symbol 'line') - (string '\n')))) - line 1 - line 2 - -Keyword arguments: - - $ hg debugtemplate -r0 -v '{foo=bar|baz}' - (template - (keyvalue - (symbol 'foo') - (| - (symbol 'bar') - (symbol 'baz')))) - hg: parse error: can't use a key-value pair in this context - [255] - - $ hg debugtemplate '{pad("foo", width=10, left=true)}\n' - foo - -Call function which takes named arguments by filter syntax: - - $ hg debugtemplate '{" "|separate}' - $ hg debugtemplate '{("not", "an", "argument", "list")|separate}' - hg: parse error: unknown method 'list' - [255] - -Second branch starting at nullrev: - - $ hg update null - 0 files updated, 0 files merged, 4 files removed, 0 files unresolved - $ echo second > second - $ hg add second - $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>' - created new head - - $ echo third > third - $ hg add third - $ hg mv second fourth - $ hg commit -m third -d "2020-01-01 10:01" - - $ hg log --template '{join(file_copies, ",\n")}\n' -r . - fourth (second) - $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r . - second -> fourth - $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7 - 8 t - 7 f - -Working-directory revision has special identifiers, though they are still -experimental: - - $ hg log -r 'wdir()' -T '{rev}:{node}\n' - 2147483647:ffffffffffffffffffffffffffffffffffffffff - -Some keywords are invalid for working-directory revision, but they should -never cause crash: - - $ hg log -r 'wdir()' -T '{manifest}\n' - - -Internal resources shouldn't be exposed (issue5699): - - $ hg log -r. -T '{cache}{ctx}{repo}{revcache}{templ}{ui}' - -Never crash on internal resource not available: - - $ hg --cwd .. debugtemplate '{"c0bebeef"|shortest}\n' - abort: template resource not available: ctx - [255] - - $ hg config -T '{author}' - -Quoting for ui.logtemplate - - $ hg tip --config "ui.logtemplate={rev}\n" - 8 - $ hg tip --config "ui.logtemplate='{rev}\n'" - 8 - $ hg tip --config 'ui.logtemplate="{rev}\n"' - 8 - $ hg tip --config 'ui.logtemplate=n{rev}\n' - n8 - -Make sure user/global hgrc does not affect tests - - $ echo '[ui]' > .hg/hgrc - $ echo 'logtemplate =' >> .hg/hgrc - $ echo 'style =' >> .hg/hgrc - -Add some simple styles to settings - - $ cat <<'EOF' >> .hg/hgrc - > [templates] - > simple = "{rev}\n" - > simple2 = {rev}\n - > rev = "should not precede {rev} keyword\n" - > EOF - - $ hg log -l1 -Tsimple - 8 - $ hg log -l1 -Tsimple2 - 8 - $ hg log -l1 -Trev - should not precede 8 keyword - $ hg log -l1 -T '{simple}' - 8 - -Map file shouldn't see user templates: - - $ cat <<EOF > tmpl - > changeset = 'nothing expanded:{simple}\n' - > EOF - $ hg log -l1 --style ./tmpl - nothing expanded: - -Test templates and style maps in files: - - $ echo "{rev}" > tmpl - $ hg log -l1 -T./tmpl - 8 - $ hg log -l1 -Tblah/blah - blah/blah (no-eol) - - $ printf 'changeset = "{rev}\\n"\n' > map-simple - $ hg log -l1 -T./map-simple - 8 - - a map file may have [templates] and [templatealias] sections: - - $ cat <<'EOF' > map-simple - > [templates] - > changeset = "{a}\n" - > [templatealias] - > a = rev - > EOF - $ hg log -l1 -T./map-simple - 8 - - so it can be included in hgrc - - $ cat <<EOF > myhgrc - > %include $HGRCPATH - > %include map-simple - > [templates] - > foo = "{changeset}" - > EOF - $ HGRCPATH=./myhgrc hg log -l1 -Tfoo - 8 - $ HGRCPATH=./myhgrc hg log -l1 -T'{a}\n' - 8 - -Test template map inheritance - - $ echo "__base__ = map-cmdline.default" > map-simple - $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple - $ hg log -l1 -T./map-simple - changeset: ***8*** - tag: tip - user: test - date: Wed Jan 01 10:01:00 2020 +0000 - summary: third - - -Test docheader, docfooter and separator in template map - - $ cat <<'EOF' > map-myjson - > docheader = '\{\n' - > docfooter = '\n}\n' - > separator = ',\n' - > changeset = ' {dict(rev, node|short)|json}' - > EOF - $ hg log -l2 -T./map-myjson - { - {"node": "95c24699272e", "rev": 8}, - {"node": "29114dbae42b", "rev": 7} - } - -Test docheader, docfooter and separator in [templates] section - - $ cat <<'EOF' >> .hg/hgrc - > [templates] - > myjson = ' {dict(rev, node|short)|json}' - > myjson:docheader = '\{\n' - > myjson:docfooter = '\n}\n' - > myjson:separator = ',\n' - > :docheader = 'should not be selected as a docheader for literal templates\n' - > EOF - $ hg log -l2 -Tmyjson - { - {"node": "95c24699272e", "rev": 8}, - {"node": "29114dbae42b", "rev": 7} - } - $ hg log -l1 -T'{rev}\n' - 8 - -Template should precede style option - - $ hg log -l1 --style default -T '{rev}\n' - 8 - -Add a commit with empty description, to ensure that the templates -below will omit the description line. - - $ echo c >> c - $ hg add c - $ hg commit -qm ' ' - -Default style is like normal output. Phases style should be the same -as default style, except for extra phase lines. - - $ hg log > log.out - $ hg log --style default > style.out - $ cmp log.out style.out || diff -u log.out style.out - $ hg log -T phases > phases.out - $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@' - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - - $ hg log -v > log.out - $ hg log -v --style default > style.out - $ cmp log.out style.out || diff -u log.out style.out - $ hg log -v -T phases > phases.out - $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@' - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - +phase: draft - - $ hg log -q > log.out - $ hg log -q --style default > style.out - $ cmp log.out style.out || diff -u log.out style.out - $ hg log -q -T phases > phases.out - $ cmp log.out phases.out || diff -u log.out phases.out - - $ hg log --debug > log.out - $ hg log --debug --style default > style.out - $ cmp log.out style.out || diff -u log.out style.out - $ hg log --debug -T phases > phases.out - $ cmp log.out phases.out || diff -u log.out phases.out - -Default style of working-directory revision should also be the same (but -date may change while running tests): - - $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out - $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out - $ cmp log.out style.out || diff -u log.out style.out - - $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out - $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out - $ cmp log.out style.out || diff -u log.out style.out - - $ hg log -r 'wdir()' -q > log.out - $ hg log -r 'wdir()' -q --style default > style.out - $ cmp log.out style.out || diff -u log.out style.out - - $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out - $ hg log -r 'wdir()' --debug --style default \ - > | sed 's|^date:.*|date:|' > style.out - $ cmp log.out style.out || diff -u log.out style.out - -Default style should also preserve color information (issue2866): - - $ cp $HGRCPATH $HGRCPATH-bak - $ cat <<EOF >> $HGRCPATH - > [extensions] - > color= - > EOF - - $ hg --color=debug log > log.out - $ hg --color=debug log --style default > style.out - $ cmp log.out style.out || diff -u log.out style.out - $ hg --color=debug log -T phases > phases.out - $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@' - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - - $ hg --color=debug -v log > log.out - $ hg --color=debug -v log --style default > style.out - $ cmp log.out style.out || diff -u log.out style.out - $ hg --color=debug -v log -T phases > phases.out - $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@' - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - +[log.phase|phase: draft] - - $ hg --color=debug -q log > log.out - $ hg --color=debug -q log --style default > style.out - $ cmp log.out style.out || diff -u log.out style.out - $ hg --color=debug -q log -T phases > phases.out - $ cmp log.out phases.out || diff -u log.out phases.out - - $ hg --color=debug --debug log > log.out - $ hg --color=debug --debug log --style default > style.out - $ cmp log.out style.out || diff -u log.out style.out - $ hg --color=debug --debug log -T phases > phases.out - $ cmp log.out phases.out || diff -u log.out phases.out - - $ mv $HGRCPATH-bak $HGRCPATH - -Remove commit with empty commit message, so as to not pollute further -tests. - - $ hg --config extensions.strip= strip -q . - -Revision with no copies (used to print a traceback): - - $ hg tip -v --template '\n' - - -Compact style works: - - $ hg log -Tcompact - 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test - third - - 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user - second - - 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person - merge - - 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person - new head - - 4 bbe44766e73d 1970-01-17 04:53 +0000 person - new branch - - 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person - no user, no domain - - 2 97054abb4ab8 1970-01-14 21:20 +0000 other - no person - - 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other - other 1 - - 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user - line 1 - - - $ hg log -v --style compact - 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test - third - - 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname> - second - - 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person - merge - - 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person - new head - - 4 bbe44766e73d 1970-01-17 04:53 +0000 person - new branch - - 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person - no user, no domain - - 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place - no person - - 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place> - other 1 - other 2 - - other 3 - - 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname> - line 1 - line 2 - - - $ hg log --debug --style compact - 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test - third - - 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname> - second - - 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person - merge - - 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person - new head - - 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person - new branch - - 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person - no user, no domain - - 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place - no person - - 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place> - other 1 - other 2 - - other 3 - - 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname> - line 1 - line 2 - - -Test xml styles: - - $ hg log --style xml -r 'not all()' - <?xml version="1.0"?> - <log> - </log> - - $ hg log --style xml - <?xml version="1.0"?> - <log> - <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a"> - <tag>tip</tag> - <author email="test">test</author> - <date>2020-01-01T10:01:00+00:00</date> - <msg xml:space="preserve">third</msg> - </logentry> - <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453"> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <author email="user@hostname">User Name</author> - <date>1970-01-12T13:46:40+00:00</date> - <msg xml:space="preserve">second</msg> - </logentry> - <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b"> - <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" /> - <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" /> - <author email="person">person</author> - <date>1970-01-18T08:40:01+00:00</date> - <msg xml:space="preserve">merge</msg> - </logentry> - <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f"> - <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" /> - <author email="person">person</author> - <date>1970-01-18T08:40:00+00:00</date> - <msg xml:space="preserve">new head</msg> - </logentry> - <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74"> - <branch>foo</branch> - <author email="person">person</author> - <date>1970-01-17T04:53:20+00:00</date> - <msg xml:space="preserve">new branch</msg> - </logentry> - <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47"> - <author email="person">person</author> - <date>1970-01-16T01:06:40+00:00</date> - <msg xml:space="preserve">no user, no domain</msg> - </logentry> - <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465"> - <author email="other@place">other</author> - <date>1970-01-14T21:20:00+00:00</date> - <msg xml:space="preserve">no person</msg> - </logentry> - <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965"> - <author email="other@place">A. N. Other</author> - <date>1970-01-13T17:33:20+00:00</date> - <msg xml:space="preserve">other 1 - other 2 - - other 3</msg> - </logentry> - <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f"> - <author email="user@hostname">User Name</author> - <date>1970-01-12T13:46:40+00:00</date> - <msg xml:space="preserve">line 1 - line 2</msg> - </logentry> - </log> - - $ hg log -v --style xml - <?xml version="1.0"?> - <log> - <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a"> - <tag>tip</tag> - <author email="test">test</author> - <date>2020-01-01T10:01:00+00:00</date> - <msg xml:space="preserve">third</msg> - <paths> - <path action="A">fourth</path> - <path action="A">third</path> - <path action="R">second</path> - </paths> - <copies> - <copy source="second">fourth</copy> - </copies> - </logentry> - <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453"> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <author email="user@hostname">User Name</author> - <date>1970-01-12T13:46:40+00:00</date> - <msg xml:space="preserve">second</msg> - <paths> - <path action="A">second</path> - </paths> - </logentry> - <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b"> - <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" /> - <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" /> - <author email="person">person</author> - <date>1970-01-18T08:40:01+00:00</date> - <msg xml:space="preserve">merge</msg> - <paths> - </paths> - </logentry> - <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f"> - <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" /> - <author email="person">person</author> - <date>1970-01-18T08:40:00+00:00</date> - <msg xml:space="preserve">new head</msg> - <paths> - <path action="A">d</path> - </paths> - </logentry> - <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74"> - <branch>foo</branch> - <author email="person">person</author> - <date>1970-01-17T04:53:20+00:00</date> - <msg xml:space="preserve">new branch</msg> - <paths> - </paths> - </logentry> - <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47"> - <author email="person">person</author> - <date>1970-01-16T01:06:40+00:00</date> - <msg xml:space="preserve">no user, no domain</msg> - <paths> - <path action="M">c</path> - </paths> - </logentry> - <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465"> - <author email="other@place">other</author> - <date>1970-01-14T21:20:00+00:00</date> - <msg xml:space="preserve">no person</msg> - <paths> - <path action="A">c</path> - </paths> - </logentry> - <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965"> - <author email="other@place">A. N. Other</author> - <date>1970-01-13T17:33:20+00:00</date> - <msg xml:space="preserve">other 1 - other 2 - - other 3</msg> - <paths> - <path action="A">b</path> - </paths> - </logentry> - <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f"> - <author email="user@hostname">User Name</author> - <date>1970-01-12T13:46:40+00:00</date> - <msg xml:space="preserve">line 1 - line 2</msg> - <paths> - <path action="A">a</path> - </paths> - </logentry> - </log> - - $ hg log --debug --style xml - <?xml version="1.0"?> - <log> - <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a"> - <tag>tip</tag> - <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" /> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <author email="test">test</author> - <date>2020-01-01T10:01:00+00:00</date> - <msg xml:space="preserve">third</msg> - <paths> - <path action="A">fourth</path> - <path action="A">third</path> - <path action="R">second</path> - </paths> - <copies> - <copy source="second">fourth</copy> - </copies> - <extra key="branch">default</extra> - </logentry> - <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453"> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <author email="user@hostname">User Name</author> - <date>1970-01-12T13:46:40+00:00</date> - <msg xml:space="preserve">second</msg> - <paths> - <path action="A">second</path> - </paths> - <extra key="branch">default</extra> - </logentry> - <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b"> - <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" /> - <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" /> - <author email="person">person</author> - <date>1970-01-18T08:40:01+00:00</date> - <msg xml:space="preserve">merge</msg> - <paths> - </paths> - <extra key="branch">default</extra> - </logentry> - <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f"> - <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" /> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <author email="person">person</author> - <date>1970-01-18T08:40:00+00:00</date> - <msg xml:space="preserve">new head</msg> - <paths> - <path action="A">d</path> - </paths> - <extra key="branch">default</extra> - </logentry> - <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74"> - <branch>foo</branch> - <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" /> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <author email="person">person</author> - <date>1970-01-17T04:53:20+00:00</date> - <msg xml:space="preserve">new branch</msg> - <paths> - </paths> - <extra key="branch">foo</extra> - </logentry> - <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47"> - <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" /> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <author email="person">person</author> - <date>1970-01-16T01:06:40+00:00</date> - <msg xml:space="preserve">no user, no domain</msg> - <paths> - <path action="M">c</path> - </paths> - <extra key="branch">default</extra> - </logentry> - <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465"> - <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" /> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <author email="other@place">other</author> - <date>1970-01-14T21:20:00+00:00</date> - <msg xml:space="preserve">no person</msg> - <paths> - <path action="A">c</path> - </paths> - <extra key="branch">default</extra> - </logentry> - <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965"> - <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" /> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <author email="other@place">A. N. Other</author> - <date>1970-01-13T17:33:20+00:00</date> - <msg xml:space="preserve">other 1 - other 2 - - other 3</msg> - <paths> - <path action="A">b</path> - </paths> - <extra key="branch">default</extra> - </logentry> - <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f"> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <parent revision="-1" node="0000000000000000000000000000000000000000" /> - <author email="user@hostname">User Name</author> - <date>1970-01-12T13:46:40+00:00</date> - <msg xml:space="preserve">line 1 - line 2</msg> - <paths> - <path action="A">a</path> - </paths> - <extra key="branch">default</extra> - </logentry> - </log> - - -Test JSON style: - - $ hg log -k nosuch -Tjson - [ - ] - - $ hg log -qr . -Tjson - [ - { - "node": "95c24699272ef57d062b8bccc32c878bf841784a", - "rev": 8 - } - ] - - $ hg log -vpr . -Tjson --stat - [ - { - "bookmarks": [], - "branch": "default", - "date": [1577872860, 0], - "desc": "third", - "diff": "diff -r 29114dbae42b -r 95c24699272e fourth\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/fourth\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+second\ndiff -r 29114dbae42b -r 95c24699272e second\n--- a/second\tMon Jan 12 13:46:40 1970 +0000\n+++ /dev/null\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +0,0 @@\n-second\ndiff -r 29114dbae42b -r 95c24699272e third\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/third\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+third\n", - "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n", - "files": ["fourth", "second", "third"], - "node": "95c24699272ef57d062b8bccc32c878bf841784a", - "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], - "phase": "draft", - "rev": 8, - "tags": ["tip"], - "user": "test" - } - ] - -honor --git but not format-breaking diffopts - $ hg --config diff.noprefix=True log --git -vpr . -Tjson - [ - { - "bookmarks": [], - "branch": "default", - "date": [1577872860, 0], - "desc": "third", - "diff": "diff --git a/second b/fourth\nrename from second\nrename to fourth\ndiff --git a/third b/third\nnew file mode 100644\n--- /dev/null\n+++ b/third\n@@ -0,0 +1,1 @@\n+third\n", - "files": ["fourth", "second", "third"], - "node": "95c24699272ef57d062b8bccc32c878bf841784a", - "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], - "phase": "draft", - "rev": 8, - "tags": ["tip"], - "user": "test" - } - ] - - $ hg log -T json - [ - { - "bookmarks": [], - "branch": "default", - "date": [1577872860, 0], - "desc": "third", - "node": "95c24699272ef57d062b8bccc32c878bf841784a", - "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], - "phase": "draft", - "rev": 8, - "tags": ["tip"], - "user": "test" - }, - { - "bookmarks": [], - "branch": "default", - "date": [1000000, 0], - "desc": "second", - "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453", - "parents": ["0000000000000000000000000000000000000000"], - "phase": "draft", - "rev": 7, - "tags": [], - "user": "User Name <user@hostname>" - }, - { - "bookmarks": [], - "branch": "default", - "date": [1500001, 0], - "desc": "merge", - "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b", - "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"], - "phase": "draft", - "rev": 6, - "tags": [], - "user": "person" - }, - { - "bookmarks": [], - "branch": "default", - "date": [1500000, 0], - "desc": "new head", - "node": "13207e5a10d9fd28ec424934298e176197f2c67f", - "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"], - "phase": "draft", - "rev": 5, - "tags": [], - "user": "person" - }, - { - "bookmarks": [], - "branch": "foo", - "date": [1400000, 0], - "desc": "new branch", - "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74", - "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"], - "phase": "draft", - "rev": 4, - "tags": [], - "user": "person" - }, - { - "bookmarks": [], - "branch": "default", - "date": [1300000, 0], - "desc": "no user, no domain", - "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47", - "parents": ["97054abb4ab824450e9164180baf491ae0078465"], - "phase": "draft", - "rev": 3, - "tags": [], - "user": "person" - }, - { - "bookmarks": [], - "branch": "default", - "date": [1200000, 0], - "desc": "no person", - "node": "97054abb4ab824450e9164180baf491ae0078465", - "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"], - "phase": "draft", - "rev": 2, - "tags": [], - "user": "other@place" - }, - { - "bookmarks": [], - "branch": "default", - "date": [1100000, 0], - "desc": "other 1\nother 2\n\nother 3", - "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965", - "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"], - "phase": "draft", - "rev": 1, - "tags": [], - "user": "A. N. Other <other@place>" - }, - { - "bookmarks": [], - "branch": "default", - "date": [1000000, 0], - "desc": "line 1\nline 2", - "node": "1e4e1b8f71e05681d422154f5421e385fec3454f", - "parents": ["0000000000000000000000000000000000000000"], - "phase": "draft", - "rev": 0, - "tags": [], - "user": "User Name <user@hostname>" - } - ] - - $ hg heads -v -Tjson - [ - { - "bookmarks": [], - "branch": "default", - "date": [1577872860, 0], - "desc": "third", - "files": ["fourth", "second", "third"], - "node": "95c24699272ef57d062b8bccc32c878bf841784a", - "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], - "phase": "draft", - "rev": 8, - "tags": ["tip"], - "user": "test" - }, - { - "bookmarks": [], - "branch": "default", - "date": [1500001, 0], - "desc": "merge", - "files": [], - "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b", - "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"], - "phase": "draft", - "rev": 6, - "tags": [], - "user": "person" - }, - { - "bookmarks": [], - "branch": "foo", - "date": [1400000, 0], - "desc": "new branch", - "files": [], - "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74", - "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"], - "phase": "draft", - "rev": 4, - "tags": [], - "user": "person" - } - ] - - $ hg log --debug -Tjson - [ - { - "added": ["fourth", "third"], - "bookmarks": [], - "branch": "default", - "date": [1577872860, 0], - "desc": "third", - "extra": {"branch": "default"}, - "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64", - "modified": [], - "node": "95c24699272ef57d062b8bccc32c878bf841784a", - "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], - "phase": "draft", - "removed": ["second"], - "rev": 8, - "tags": ["tip"], - "user": "test" - }, - { - "added": ["second"], - "bookmarks": [], - "branch": "default", - "date": [1000000, 0], - "desc": "second", - "extra": {"branch": "default"}, - "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf", - "modified": [], - "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453", - "parents": ["0000000000000000000000000000000000000000"], - "phase": "draft", - "removed": [], - "rev": 7, - "tags": [], - "user": "User Name <user@hostname>" - }, - { - "added": [], - "bookmarks": [], - "branch": "default", - "date": [1500001, 0], - "desc": "merge", - "extra": {"branch": "default"}, - "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216", - "modified": [], - "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b", - "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"], - "phase": "draft", - "removed": [], - "rev": 6, - "tags": [], - "user": "person" - }, - { - "added": ["d"], - "bookmarks": [], - "branch": "default", - "date": [1500000, 0], - "desc": "new head", - "extra": {"branch": "default"}, - "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216", - "modified": [], - "node": "13207e5a10d9fd28ec424934298e176197f2c67f", - "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"], - "phase": "draft", - "removed": [], - "rev": 5, - "tags": [], - "user": "person" - }, - { - "added": [], - "bookmarks": [], - "branch": "foo", - "date": [1400000, 0], - "desc": "new branch", - "extra": {"branch": "foo"}, - "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc", - "modified": [], - "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74", - "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"], - "phase": "draft", - "removed": [], - "rev": 4, - "tags": [], - "user": "person" - }, - { - "added": [], - "bookmarks": [], - "branch": "default", - "date": [1300000, 0], - "desc": "no user, no domain", - "extra": {"branch": "default"}, - "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc", - "modified": ["c"], - "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47", - "parents": ["97054abb4ab824450e9164180baf491ae0078465"], - "phase": "draft", - "removed": [], - "rev": 3, - "tags": [], - "user": "person" - }, - { - "added": ["c"], - "bookmarks": [], - "branch": "default", - "date": [1200000, 0], - "desc": "no person", - "extra": {"branch": "default"}, - "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1", - "modified": [], - "node": "97054abb4ab824450e9164180baf491ae0078465", - "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"], - "phase": "draft", - "removed": [], - "rev": 2, - "tags": [], - "user": "other@place" - }, - { - "added": ["b"], - "bookmarks": [], - "branch": "default", - "date": [1100000, 0], - "desc": "other 1\nother 2\n\nother 3", - "extra": {"branch": "default"}, - "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55", - "modified": [], - "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965", - "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"], - "phase": "draft", - "removed": [], - "rev": 1, - "tags": [], - "user": "A. N. Other <other@place>" - }, - { - "added": ["a"], - "bookmarks": [], - "branch": "default", - "date": [1000000, 0], - "desc": "line 1\nline 2", - "extra": {"branch": "default"}, - "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0", - "modified": [], - "node": "1e4e1b8f71e05681d422154f5421e385fec3454f", - "parents": ["0000000000000000000000000000000000000000"], - "phase": "draft", - "removed": [], - "rev": 0, - "tags": [], - "user": "User Name <user@hostname>" - } - ] - -Error if style not readable: - -#if unix-permissions no-root - $ touch q - $ chmod 0 q - $ hg log --style ./q - abort: Permission denied: ./q - [255] -#endif - -Error if no style: - - $ hg log --style notexist - abort: style 'notexist' not found - (available styles: bisect, changelog, compact, default, phases, show, status, xml) - [255] - - $ hg log -T list - available styles: bisect, changelog, compact, default, phases, show, status, xml - abort: specify a template - [255] - -Error if style missing key: - - $ echo 'q = q' > t - $ hg log --style ./t - abort: "changeset" not in template map - [255] - -Error if style missing value: - - $ echo 'changeset =' > t - $ hg log --style t - hg: parse error at t:1: missing value - [255] - -Error if include fails: - - $ echo 'changeset = q' >> t -#if unix-permissions no-root - $ hg log --style ./t - abort: template file ./q: Permission denied - [255] - $ rm -f q -#endif - -Include works: - - $ echo '{rev}' > q - $ hg log --style ./t - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0 - -Check that recursive reference does not fall into RuntimeError (issue4758): - - common mistake: - - $ cat << EOF > issue4758 - > changeset = '{changeset}\n' - > EOF - $ hg log --style ./issue4758 - abort: recursive reference 'changeset' in template - [255] - - circular reference: - - $ cat << EOF > issue4758 - > changeset = '{foo}' - > foo = '{changeset}' - > EOF - $ hg log --style ./issue4758 - abort: recursive reference 'foo' in template - [255] - - buildmap() -> gettemplate(), where no thunk was made: - - $ cat << EOF > issue4758 - > changeset = '{files % changeset}\n' - > EOF - $ hg log --style ./issue4758 - abort: recursive reference 'changeset' in template - [255] - - not a recursion if a keyword of the same name exists: - - $ cat << EOF > issue4758 - > changeset = '{tags % rev}' - > rev = '{rev} {tag}\n' - > EOF - $ hg log --style ./issue4758 -r tip - 8 tip - -Check that {phase} works correctly on parents: - - $ cat << EOF > parentphase - > changeset_debug = '{rev} ({phase}):{parents}\n' - > parent = ' {rev} ({phase})' - > EOF - $ hg phase -r 5 --public - $ hg phase -r 7 --secret --force - $ hg log --debug -G --style ./parentphase - @ 8 (secret): 7 (secret) -1 (public) - | - o 7 (secret): -1 (public) -1 (public) - - o 6 (draft): 5 (public) 4 (draft) - |\ - | o 5 (public): 3 (public) -1 (public) - | | - o | 4 (draft): 3 (public) -1 (public) - |/ - o 3 (public): 2 (public) -1 (public) - | - o 2 (public): 1 (public) -1 (public) - | - o 1 (public): 0 (public) -1 (public) - | - o 0 (public): -1 (public) -1 (public) - - -Missing non-standard names give no error (backward compatibility): - - $ echo "changeset = '{c}'" > t - $ hg log --style ./t - -Defining non-standard name works: - - $ cat <<EOF > t - > changeset = '{c}' - > c = q - > EOF - $ hg log --style ./t - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0 - -ui.style works: - - $ echo '[ui]' > .hg/hgrc - $ echo 'style = t' >> .hg/hgrc - $ hg log - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0 - - -Issue338: - - $ hg log --style=changelog > changelog - - $ cat changelog - 2020-01-01 test <test> - - * fourth, second, third: - third - [95c24699272e] [tip] - - 1970-01-12 User Name <user@hostname> - - * second: - second - [29114dbae42b] - - 1970-01-18 person <person> - - * merge - [d41e714fe50d] - - * d: - new head - [13207e5a10d9] - - 1970-01-17 person <person> - - * new branch - [bbe44766e73d] <foo> - - 1970-01-16 person <person> - - * c: - no user, no domain - [10e46f2dcbf4] - - 1970-01-14 other <other@place> - - * c: - no person - [97054abb4ab8] - - 1970-01-13 A. N. Other <other@place> - - * b: - other 1 other 2 - - other 3 - [b608e9d1a3f0] - - 1970-01-12 User Name <user@hostname> - - * a: - line 1 line 2 - [1e4e1b8f71e0] - - -Issue2130: xml output for 'hg heads' is malformed - - $ hg heads --style changelog - 2020-01-01 test <test> - - * fourth, second, third: - third - [95c24699272e] [tip] - - 1970-01-18 person <person> - - * merge - [d41e714fe50d] - - 1970-01-17 person <person> - - * new branch - [bbe44766e73d] <foo> - - -Keys work: - - $ for key in author branch branches date desc file_adds file_dels file_mods \ - > file_copies file_copies_switch files \ - > manifest node parents rev tags diffstat extras \ - > p1rev p2rev p1node p2node; do - > for mode in '' --verbose --debug; do - > hg log $mode --template "$key$mode: {$key}\n" - > done - > done - author: test - author: User Name <user@hostname> - author: person - author: person - author: person - author: person - author: other@place - author: A. N. Other <other@place> - author: User Name <user@hostname> - author--verbose: test - author--verbose: User Name <user@hostname> - author--verbose: person - author--verbose: person - author--verbose: person - author--verbose: person - author--verbose: other@place - author--verbose: A. N. Other <other@place> - author--verbose: User Name <user@hostname> - author--debug: test - author--debug: User Name <user@hostname> - author--debug: person - author--debug: person - author--debug: person - author--debug: person - author--debug: other@place - author--debug: A. N. Other <other@place> - author--debug: User Name <user@hostname> - branch: default - branch: default - branch: default - branch: default - branch: foo - branch: default - branch: default - branch: default - branch: default - branch--verbose: default - branch--verbose: default - branch--verbose: default - branch--verbose: default - branch--verbose: foo - branch--verbose: default - branch--verbose: default - branch--verbose: default - branch--verbose: default - branch--debug: default - branch--debug: default - branch--debug: default - branch--debug: default - branch--debug: foo - branch--debug: default - branch--debug: default - branch--debug: default - branch--debug: default - branches: - branches: - branches: - branches: - branches: foo - branches: - branches: - branches: - branches: - branches--verbose: - branches--verbose: - branches--verbose: - branches--verbose: - branches--verbose: foo - branches--verbose: - branches--verbose: - branches--verbose: - branches--verbose: - branches--debug: - branches--debug: - branches--debug: - branches--debug: - branches--debug: foo - branches--debug: - branches--debug: - branches--debug: - branches--debug: - date: 1577872860.00 - date: 1000000.00 - date: 1500001.00 - date: 1500000.00 - date: 1400000.00 - date: 1300000.00 - date: 1200000.00 - date: 1100000.00 - date: 1000000.00 - date--verbose: 1577872860.00 - date--verbose: 1000000.00 - date--verbose: 1500001.00 - date--verbose: 1500000.00 - date--verbose: 1400000.00 - date--verbose: 1300000.00 - date--verbose: 1200000.00 - date--verbose: 1100000.00 - date--verbose: 1000000.00 - date--debug: 1577872860.00 - date--debug: 1000000.00 - date--debug: 1500001.00 - date--debug: 1500000.00 - date--debug: 1400000.00 - date--debug: 1300000.00 - date--debug: 1200000.00 - date--debug: 1100000.00 - date--debug: 1000000.00 - desc: third - desc: second - desc: merge - desc: new head - desc: new branch - desc: no user, no domain - desc: no person - desc: other 1 - other 2 - - other 3 - desc: line 1 - line 2 - desc--verbose: third - desc--verbose: second - desc--verbose: merge - desc--verbose: new head - desc--verbose: new branch - desc--verbose: no user, no domain - desc--verbose: no person - desc--verbose: other 1 - other 2 - - other 3 - desc--verbose: line 1 - line 2 - desc--debug: third - desc--debug: second - desc--debug: merge - desc--debug: new head - desc--debug: new branch - desc--debug: no user, no domain - desc--debug: no person - desc--debug: other 1 - other 2 - - other 3 - desc--debug: line 1 - line 2 - file_adds: fourth third - file_adds: second - file_adds: - file_adds: d - file_adds: - file_adds: - file_adds: c - file_adds: b - file_adds: a - file_adds--verbose: fourth third - file_adds--verbose: second - file_adds--verbose: - file_adds--verbose: d - file_adds--verbose: - file_adds--verbose: - file_adds--verbose: c - file_adds--verbose: b - file_adds--verbose: a - file_adds--debug: fourth third - file_adds--debug: second - file_adds--debug: - file_adds--debug: d - file_adds--debug: - file_adds--debug: - file_adds--debug: c - file_adds--debug: b - file_adds--debug: a - file_dels: second - file_dels: - file_dels: - file_dels: - file_dels: - file_dels: - file_dels: - file_dels: - file_dels: - file_dels--verbose: second - file_dels--verbose: - file_dels--verbose: - file_dels--verbose: - file_dels--verbose: - file_dels--verbose: - file_dels--verbose: - file_dels--verbose: - file_dels--verbose: - file_dels--debug: second - file_dels--debug: - file_dels--debug: - file_dels--debug: - file_dels--debug: - file_dels--debug: - file_dels--debug: - file_dels--debug: - file_dels--debug: - file_mods: - file_mods: - file_mods: - file_mods: - file_mods: - file_mods: c - file_mods: - file_mods: - file_mods: - file_mods--verbose: - file_mods--verbose: - file_mods--verbose: - file_mods--verbose: - file_mods--verbose: - file_mods--verbose: c - file_mods--verbose: - file_mods--verbose: - file_mods--verbose: - file_mods--debug: - file_mods--debug: - file_mods--debug: - file_mods--debug: - file_mods--debug: - file_mods--debug: c - file_mods--debug: - file_mods--debug: - file_mods--debug: - file_copies: fourth (second) - file_copies: - file_copies: - file_copies: - file_copies: - file_copies: - file_copies: - file_copies: - file_copies: - file_copies--verbose: fourth (second) - file_copies--verbose: - file_copies--verbose: - file_copies--verbose: - file_copies--verbose: - file_copies--verbose: - file_copies--verbose: - file_copies--verbose: - file_copies--verbose: - file_copies--debug: fourth (second) - file_copies--debug: - file_copies--debug: - file_copies--debug: - file_copies--debug: - file_copies--debug: - file_copies--debug: - file_copies--debug: - file_copies--debug: - file_copies_switch: - file_copies_switch: - file_copies_switch: - file_copies_switch: - file_copies_switch: - file_copies_switch: - file_copies_switch: - file_copies_switch: - file_copies_switch: - file_copies_switch--verbose: - file_copies_switch--verbose: - file_copies_switch--verbose: - file_copies_switch--verbose: - file_copies_switch--verbose: - file_copies_switch--verbose: - file_copies_switch--verbose: - file_copies_switch--verbose: - file_copies_switch--verbose: - file_copies_switch--debug: - file_copies_switch--debug: - file_copies_switch--debug: - file_copies_switch--debug: - file_copies_switch--debug: - file_copies_switch--debug: - file_copies_switch--debug: - file_copies_switch--debug: - file_copies_switch--debug: - files: fourth second third - files: second - files: - files: d - files: - files: c - files: c - files: b - files: a - files--verbose: fourth second third - files--verbose: second - files--verbose: - files--verbose: d - files--verbose: - files--verbose: c - files--verbose: c - files--verbose: b - files--verbose: a - files--debug: fourth second third - files--debug: second - files--debug: - files--debug: d - files--debug: - files--debug: c - files--debug: c - files--debug: b - files--debug: a - manifest: 6:94961b75a2da - manifest: 5:f2dbc354b94e - manifest: 4:4dc3def4f9b4 - manifest: 4:4dc3def4f9b4 - manifest: 3:cb5a1327723b - manifest: 3:cb5a1327723b - manifest: 2:6e0e82995c35 - manifest: 1:4e8d705b1e53 - manifest: 0:a0c8bcbbb45c - manifest--verbose: 6:94961b75a2da - manifest--verbose: 5:f2dbc354b94e - manifest--verbose: 4:4dc3def4f9b4 - manifest--verbose: 4:4dc3def4f9b4 - manifest--verbose: 3:cb5a1327723b - manifest--verbose: 3:cb5a1327723b - manifest--verbose: 2:6e0e82995c35 - manifest--verbose: 1:4e8d705b1e53 - manifest--verbose: 0:a0c8bcbbb45c - manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64 - manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf - manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216 - manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216 - manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc - manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc - manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1 - manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55 - manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 - node: 95c24699272ef57d062b8bccc32c878bf841784a - node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 - node: d41e714fe50d9e4a5f11b4d595d543481b5f980b - node: 13207e5a10d9fd28ec424934298e176197f2c67f - node: bbe44766e73d5f11ed2177f1838de10c53ef3e74 - node: 10e46f2dcbf4823578cf180f33ecf0b957964c47 - node: 97054abb4ab824450e9164180baf491ae0078465 - node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 - node: 1e4e1b8f71e05681d422154f5421e385fec3454f - node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a - node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 - node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b - node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f - node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74 - node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47 - node--verbose: 97054abb4ab824450e9164180baf491ae0078465 - node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 - node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f - node--debug: 95c24699272ef57d062b8bccc32c878bf841784a - node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 - node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b - node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f - node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74 - node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47 - node--debug: 97054abb4ab824450e9164180baf491ae0078465 - node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 - node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f - parents: - parents: -1:000000000000 - parents: 5:13207e5a10d9 4:bbe44766e73d - parents: 3:10e46f2dcbf4 - parents: - parents: - parents: - parents: - parents: - parents--verbose: - parents--verbose: -1:000000000000 - parents--verbose: 5:13207e5a10d9 4:bbe44766e73d - parents--verbose: 3:10e46f2dcbf4 - parents--verbose: - parents--verbose: - parents--verbose: - parents--verbose: - parents--verbose: - parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000 - parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000 - parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74 - parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000 - parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000 - parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000 - parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000 - parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000 - parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000 - rev: 8 - rev: 7 - rev: 6 - rev: 5 - rev: 4 - rev: 3 - rev: 2 - rev: 1 - rev: 0 - rev--verbose: 8 - rev--verbose: 7 - rev--verbose: 6 - rev--verbose: 5 - rev--verbose: 4 - rev--verbose: 3 - rev--verbose: 2 - rev--verbose: 1 - rev--verbose: 0 - rev--debug: 8 - rev--debug: 7 - rev--debug: 6 - rev--debug: 5 - rev--debug: 4 - rev--debug: 3 - rev--debug: 2 - rev--debug: 1 - rev--debug: 0 - tags: tip - tags: - tags: - tags: - tags: - tags: - tags: - tags: - tags: - tags--verbose: tip - tags--verbose: - tags--verbose: - tags--verbose: - tags--verbose: - tags--verbose: - tags--verbose: - tags--verbose: - tags--verbose: - tags--debug: tip - tags--debug: - tags--debug: - tags--debug: - tags--debug: - tags--debug: - tags--debug: - tags--debug: - tags--debug: - diffstat: 3: +2/-1 - diffstat: 1: +1/-0 - diffstat: 0: +0/-0 - diffstat: 1: +1/-0 - diffstat: 0: +0/-0 - diffstat: 1: +1/-0 - diffstat: 1: +4/-0 - diffstat: 1: +2/-0 - diffstat: 1: +1/-0 - diffstat--verbose: 3: +2/-1 - diffstat--verbose: 1: +1/-0 - diffstat--verbose: 0: +0/-0 - diffstat--verbose: 1: +1/-0 - diffstat--verbose: 0: +0/-0 - diffstat--verbose: 1: +1/-0 - diffstat--verbose: 1: +4/-0 - diffstat--verbose: 1: +2/-0 - diffstat--verbose: 1: +1/-0 - diffstat--debug: 3: +2/-1 - diffstat--debug: 1: +1/-0 - diffstat--debug: 0: +0/-0 - diffstat--debug: 1: +1/-0 - diffstat--debug: 0: +0/-0 - diffstat--debug: 1: +1/-0 - diffstat--debug: 1: +4/-0 - diffstat--debug: 1: +2/-0 - diffstat--debug: 1: +1/-0 - extras: branch=default - extras: branch=default - extras: branch=default - extras: branch=default - extras: branch=foo - extras: branch=default - extras: branch=default - extras: branch=default - extras: branch=default - extras--verbose: branch=default - extras--verbose: branch=default - extras--verbose: branch=default - extras--verbose: branch=default - extras--verbose: branch=foo - extras--verbose: branch=default - extras--verbose: branch=default - extras--verbose: branch=default - extras--verbose: branch=default - extras--debug: branch=default - extras--debug: branch=default - extras--debug: branch=default - extras--debug: branch=default - extras--debug: branch=foo - extras--debug: branch=default - extras--debug: branch=default - extras--debug: branch=default - extras--debug: branch=default - p1rev: 7 - p1rev: -1 - p1rev: 5 - p1rev: 3 - p1rev: 3 - p1rev: 2 - p1rev: 1 - p1rev: 0 - p1rev: -1 - p1rev--verbose: 7 - p1rev--verbose: -1 - p1rev--verbose: 5 - p1rev--verbose: 3 - p1rev--verbose: 3 - p1rev--verbose: 2 - p1rev--verbose: 1 - p1rev--verbose: 0 - p1rev--verbose: -1 - p1rev--debug: 7 - p1rev--debug: -1 - p1rev--debug: 5 - p1rev--debug: 3 - p1rev--debug: 3 - p1rev--debug: 2 - p1rev--debug: 1 - p1rev--debug: 0 - p1rev--debug: -1 - p2rev: -1 - p2rev: -1 - p2rev: 4 - p2rev: -1 - p2rev: -1 - p2rev: -1 - p2rev: -1 - p2rev: -1 - p2rev: -1 - p2rev--verbose: -1 - p2rev--verbose: -1 - p2rev--verbose: 4 - p2rev--verbose: -1 - p2rev--verbose: -1 - p2rev--verbose: -1 - p2rev--verbose: -1 - p2rev--verbose: -1 - p2rev--verbose: -1 - p2rev--debug: -1 - p2rev--debug: -1 - p2rev--debug: 4 - p2rev--debug: -1 - p2rev--debug: -1 - p2rev--debug: -1 - p2rev--debug: -1 - p2rev--debug: -1 - p2rev--debug: -1 - p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 - p1node: 0000000000000000000000000000000000000000 - p1node: 13207e5a10d9fd28ec424934298e176197f2c67f - p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47 - p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47 - p1node: 97054abb4ab824450e9164180baf491ae0078465 - p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 - p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f - p1node: 0000000000000000000000000000000000000000 - p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 - p1node--verbose: 0000000000000000000000000000000000000000 - p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f - p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47 - p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47 - p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465 - p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 - p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f - p1node--verbose: 0000000000000000000000000000000000000000 - p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 - p1node--debug: 0000000000000000000000000000000000000000 - p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f - p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47 - p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47 - p1node--debug: 97054abb4ab824450e9164180baf491ae0078465 - p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 - p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f - p1node--debug: 0000000000000000000000000000000000000000 - p2node: 0000000000000000000000000000000000000000 - p2node: 0000000000000000000000000000000000000000 - p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74 - p2node: 0000000000000000000000000000000000000000 - p2node: 0000000000000000000000000000000000000000 - p2node: 0000000000000000000000000000000000000000 - p2node: 0000000000000000000000000000000000000000 - p2node: 0000000000000000000000000000000000000000 - p2node: 0000000000000000000000000000000000000000 - p2node--verbose: 0000000000000000000000000000000000000000 - p2node--verbose: 0000000000000000000000000000000000000000 - p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74 - p2node--verbose: 0000000000000000000000000000000000000000 - p2node--verbose: 0000000000000000000000000000000000000000 - p2node--verbose: 0000000000000000000000000000000000000000 - p2node--verbose: 0000000000000000000000000000000000000000 - p2node--verbose: 0000000000000000000000000000000000000000 - p2node--verbose: 0000000000000000000000000000000000000000 - p2node--debug: 0000000000000000000000000000000000000000 - p2node--debug: 0000000000000000000000000000000000000000 - p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74 - p2node--debug: 0000000000000000000000000000000000000000 - p2node--debug: 0000000000000000000000000000000000000000 - p2node--debug: 0000000000000000000000000000000000000000 - p2node--debug: 0000000000000000000000000000000000000000 - p2node--debug: 0000000000000000000000000000000000000000 - p2node--debug: 0000000000000000000000000000000000000000 - -Filters work: - - $ hg log --template '{author|domain}\n' - - hostname - - - - - place - place - hostname - - $ hg log --template '{author|person}\n' - test - User Name - person - person - person - person - other - A. N. Other - User Name - - $ hg log --template '{author|user}\n' - test - user - person - person - person - person - other - other - user - - $ hg log --template '{date|date}\n' - Wed Jan 01 10:01:00 2020 +0000 - Mon Jan 12 13:46:40 1970 +0000 - Sun Jan 18 08:40:01 1970 +0000 - Sun Jan 18 08:40:00 1970 +0000 - Sat Jan 17 04:53:20 1970 +0000 - Fri Jan 16 01:06:40 1970 +0000 - Wed Jan 14 21:20:00 1970 +0000 - Tue Jan 13 17:33:20 1970 +0000 - Mon Jan 12 13:46:40 1970 +0000 - - $ hg log --template '{date|isodate}\n' - 2020-01-01 10:01 +0000 - 1970-01-12 13:46 +0000 - 1970-01-18 08:40 +0000 - 1970-01-18 08:40 +0000 - 1970-01-17 04:53 +0000 - 1970-01-16 01:06 +0000 - 1970-01-14 21:20 +0000 - 1970-01-13 17:33 +0000 - 1970-01-12 13:46 +0000 - - $ hg log --template '{date|isodatesec}\n' - 2020-01-01 10:01:00 +0000 - 1970-01-12 13:46:40 +0000 - 1970-01-18 08:40:01 +0000 - 1970-01-18 08:40:00 +0000 - 1970-01-17 04:53:20 +0000 - 1970-01-16 01:06:40 +0000 - 1970-01-14 21:20:00 +0000 - 1970-01-13 17:33:20 +0000 - 1970-01-12 13:46:40 +0000 - - $ hg log --template '{date|rfc822date}\n' - Wed, 01 Jan 2020 10:01:00 +0000 - Mon, 12 Jan 1970 13:46:40 +0000 - Sun, 18 Jan 1970 08:40:01 +0000 - Sun, 18 Jan 1970 08:40:00 +0000 - Sat, 17 Jan 1970 04:53:20 +0000 - Fri, 16 Jan 1970 01:06:40 +0000 - Wed, 14 Jan 1970 21:20:00 +0000 - Tue, 13 Jan 1970 17:33:20 +0000 - Mon, 12 Jan 1970 13:46:40 +0000 - - $ hg log --template '{desc|firstline}\n' - third - second - merge - new head - new branch - no user, no domain - no person - other 1 - line 1 - - $ hg log --template '{node|short}\n' - 95c24699272e - 29114dbae42b - d41e714fe50d - 13207e5a10d9 - bbe44766e73d - 10e46f2dcbf4 - 97054abb4ab8 - b608e9d1a3f0 - 1e4e1b8f71e0 - - $ hg log --template '<changeset author="{author|xmlescape}"/>\n' - <changeset author="test"/> - <changeset author="User Name <user@hostname>"/> - <changeset author="person"/> - <changeset author="person"/> - <changeset author="person"/> - <changeset author="person"/> - <changeset author="other@place"/> - <changeset author="A. N. Other <other@place>"/> - <changeset author="User Name <user@hostname>"/> - - $ hg log --template '{rev}: {children}\n' - 8: - 7: 8:95c24699272e - 6: - 5: 6:d41e714fe50d - 4: 6:d41e714fe50d - 3: 4:bbe44766e73d 5:13207e5a10d9 - 2: 3:10e46f2dcbf4 - 1: 2:97054abb4ab8 - 0: 1:b608e9d1a3f0 - -Formatnode filter works: - - $ hg -q log -r 0 --template '{node|formatnode}\n' - 1e4e1b8f71e0 - - $ hg log -r 0 --template '{node|formatnode}\n' - 1e4e1b8f71e0 - - $ hg -v log -r 0 --template '{node|formatnode}\n' - 1e4e1b8f71e0 - - $ hg --debug log -r 0 --template '{node|formatnode}\n' - 1e4e1b8f71e05681d422154f5421e385fec3454f - -Age filter: - - $ hg init unstable-hash - $ cd unstable-hash - $ hg log --template '{date|age}\n' > /dev/null || exit 1 - - >>> from __future__ import absolute_import - >>> import datetime - >>> fp = open('a', 'wb') - >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7) - >>> fp.write(b'%d-%d-%d 00:00' % (n.year, n.month, n.day)) and None - >>> fp.close() - $ hg add a - $ hg commit -m future -d "`cat a`" - - $ hg log -l1 --template '{date|age}\n' - 7 years from now - - $ cd .. - $ rm -rf unstable-hash - -Filename filters: - - $ hg debugtemplate '{"foo/bar"|basename}|{"foo/"|basename}|{"foo"|basename}|\n' - bar||foo| - $ hg debugtemplate '{"foo/bar"|dirname}|{"foo/"|dirname}|{"foo"|dirname}|\n' - foo|foo|| - $ hg debugtemplate '{"foo/bar"|stripdir}|{"foo/"|stripdir}|{"foo"|stripdir}|\n' - foo|foo|foo| - -Add a dummy commit to make up for the instability of the above: - - $ echo a > a - $ hg add a - $ hg ci -m future - -Count filter: - - $ hg log -l1 --template '{node|count} {node|short|count}\n' - 40 12 - - $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n' - 0 1 4 - - $ hg log -G --template '{rev}: children: {children|count}, \ - > tags: {tags|count}, file_adds: {file_adds|count}, \ - > ancestors: {revset("ancestors(%s)", rev)|count}' - @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3 - | - o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2 - | - o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1 - - o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7 - |\ - | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5 - | | - o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5 - |/ - o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4 - | - o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3 - | - o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2 - | - o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1 - - - $ hg log -l1 -T '{termwidth|count}\n' - hg: parse error: not countable - (template filter 'count' is not compatible with keyword 'termwidth') - [255] - -Upper/lower filters: - - $ hg log -r0 --template '{branch|upper}\n' - DEFAULT - $ hg log -r0 --template '{author|lower}\n' - user name <user@hostname> - $ hg log -r0 --template '{date|upper}\n' - 1000000.00 - -Add a commit that does all possible modifications at once - - $ echo modify >> third - $ touch b - $ hg add b - $ hg mv fourth fifth - $ hg rm a - $ hg ci -m "Modify, add, remove, rename" - -Check the status template - - $ cat <<EOF >> $HGRCPATH - > [extensions] - > color= - > EOF - - $ hg log -T status -r 10 - changeset: 10:0f9759ec227a - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: Modify, add, remove, rename - files: - M third - A b - A fifth - R a - R fourth - - $ hg log -T status -C -r 10 - changeset: 10:0f9759ec227a - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: Modify, add, remove, rename - files: - M third - A b - A fifth - fourth - R a - R fourth - - $ hg log -T status -C -r 10 -v - changeset: 10:0f9759ec227a - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - description: - Modify, add, remove, rename - - files: - M third - A b - A fifth - fourth - R a - R fourth - - $ hg log -T status -C -r 10 --debug - changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c - tag: tip - phase: secret - parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066 - parent: -1:0000000000000000000000000000000000000000 - manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - extra: branch=default - description: - Modify, add, remove, rename - - files: - M third - A b - A fifth - fourth - R a - R fourth - - $ hg log -T status -C -r 10 --quiet - 10:0f9759ec227a - $ hg --color=debug log -T status -r 10 - [log.changeset changeset.secret|changeset: 10:0f9759ec227a] - [log.tag|tag: tip] - [log.user|user: test] - [log.date|date: Thu Jan 01 00:00:00 1970 +0000] - [log.summary|summary: Modify, add, remove, rename] - [ui.note log.files|files:] - [status.modified|M third] - [status.added|A b] - [status.added|A fifth] - [status.removed|R a] - [status.removed|R fourth] - - $ hg --color=debug log -T status -C -r 10 - [log.changeset changeset.secret|changeset: 10:0f9759ec227a] - [log.tag|tag: tip] - [log.user|user: test] - [log.date|date: Thu Jan 01 00:00:00 1970 +0000] - [log.summary|summary: Modify, add, remove, rename] - [ui.note log.files|files:] - [status.modified|M third] - [status.added|A b] - [status.added|A fifth] - [status.copied| fourth] - [status.removed|R a] - [status.removed|R fourth] - - $ hg --color=debug log -T status -C -r 10 -v - [log.changeset changeset.secret|changeset: 10:0f9759ec227a] - [log.tag|tag: tip] - [log.user|user: test] - [log.date|date: Thu Jan 01 00:00:00 1970 +0000] - [ui.note log.description|description:] - [ui.note log.description|Modify, add, remove, rename] - - [ui.note log.files|files:] - [status.modified|M third] - [status.added|A b] - [status.added|A fifth] - [status.copied| fourth] - [status.removed|R a] - [status.removed|R fourth] - - $ hg --color=debug log -T status -C -r 10 --debug - [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c] - [log.tag|tag: tip] - [log.phase|phase: secret] - [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066] - [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] - [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567] - [log.user|user: test] - [log.date|date: Thu Jan 01 00:00:00 1970 +0000] - [ui.debug log.extra|extra: branch=default] - [ui.note log.description|description:] - [ui.note log.description|Modify, add, remove, rename] - - [ui.note log.files|files:] - [status.modified|M third] - [status.added|A b] - [status.added|A fifth] - [status.copied| fourth] - [status.removed|R a] - [status.removed|R fourth] - - $ hg --color=debug log -T status -C -r 10 --quiet - [log.node|10:0f9759ec227a] - -Check the bisect template - - $ hg bisect -g 1 - $ hg bisect -b 3 --noupdate - Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests) - $ hg log -T bisect -r 0:4 - changeset: 0:1e4e1b8f71e0 - bisect: good (implicit) - user: User Name <user@hostname> - date: Mon Jan 12 13:46:40 1970 +0000 - summary: line 1 - - changeset: 1:b608e9d1a3f0 - bisect: good - user: A. N. Other <other@place> - date: Tue Jan 13 17:33:20 1970 +0000 - summary: other 1 - - changeset: 2:97054abb4ab8 - bisect: untested - user: other@place - date: Wed Jan 14 21:20:00 1970 +0000 - summary: no person - - changeset: 3:10e46f2dcbf4 - bisect: bad - user: person - date: Fri Jan 16 01:06:40 1970 +0000 - summary: no user, no domain - - changeset: 4:bbe44766e73d - bisect: bad (implicit) - branch: foo - user: person - date: Sat Jan 17 04:53:20 1970 +0000 - summary: new branch - - $ hg log --debug -T bisect -r 0:4 - changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f - bisect: good (implicit) - phase: public - parent: -1:0000000000000000000000000000000000000000 - parent: -1:0000000000000000000000000000000000000000 - manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 - user: User Name <user@hostname> - date: Mon Jan 12 13:46:40 1970 +0000 - files+: a - extra: branch=default - description: - line 1 - line 2 - - - changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 - bisect: good - phase: public - parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f - parent: -1:0000000000000000000000000000000000000000 - manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55 - user: A. N. Other <other@place> - date: Tue Jan 13 17:33:20 1970 +0000 - files+: b - extra: branch=default - description: - other 1 - other 2 - - other 3 - - - changeset: 2:97054abb4ab824450e9164180baf491ae0078465 - bisect: untested - phase: public - parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 - parent: -1:0000000000000000000000000000000000000000 - manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1 - user: other@place - date: Wed Jan 14 21:20:00 1970 +0000 - files+: c - extra: branch=default - description: - no person - - - changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 - bisect: bad - phase: public - parent: 2:97054abb4ab824450e9164180baf491ae0078465 - parent: -1:0000000000000000000000000000000000000000 - manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc - user: person - date: Fri Jan 16 01:06:40 1970 +0000 - files: c - extra: branch=default - description: - no user, no domain - - - changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74 - bisect: bad (implicit) - branch: foo - phase: draft - parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 - parent: -1:0000000000000000000000000000000000000000 - manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc - user: person - date: Sat Jan 17 04:53:20 1970 +0000 - extra: branch=foo - description: - new branch - - - $ hg log -v -T bisect -r 0:4 - changeset: 0:1e4e1b8f71e0 - bisect: good (implicit) - user: User Name <user@hostname> - date: Mon Jan 12 13:46:40 1970 +0000 - files: a - description: - line 1 - line 2 - - - changeset: 1:b608e9d1a3f0 - bisect: good - user: A. N. Other <other@place> - date: Tue Jan 13 17:33:20 1970 +0000 - files: b - description: - other 1 - other 2 - - other 3 - - - changeset: 2:97054abb4ab8 - bisect: untested - user: other@place - date: Wed Jan 14 21:20:00 1970 +0000 - files: c - description: - no person - - - changeset: 3:10e46f2dcbf4 - bisect: bad - user: person - date: Fri Jan 16 01:06:40 1970 +0000 - files: c - description: - no user, no domain - - - changeset: 4:bbe44766e73d - bisect: bad (implicit) - branch: foo - user: person - date: Sat Jan 17 04:53:20 1970 +0000 - description: - new branch - - - $ hg --color=debug log -T bisect -r 0:4 - [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0] - [log.bisect bisect.good|bisect: good (implicit)] - [log.user|user: User Name <user@hostname>] - [log.date|date: Mon Jan 12 13:46:40 1970 +0000] - [log.summary|summary: line 1] - - [log.changeset changeset.public|changeset: 1:b608e9d1a3f0] - [log.bisect bisect.good|bisect: good] - [log.user|user: A. N. Other <other@place>] - [log.date|date: Tue Jan 13 17:33:20 1970 +0000] - [log.summary|summary: other 1] - - [log.changeset changeset.public|changeset: 2:97054abb4ab8] - [log.bisect bisect.untested|bisect: untested] - [log.user|user: other@place] - [log.date|date: Wed Jan 14 21:20:00 1970 +0000] - [log.summary|summary: no person] - - [log.changeset changeset.public|changeset: 3:10e46f2dcbf4] - [log.bisect bisect.bad|bisect: bad] - [log.user|user: person] - [log.date|date: Fri Jan 16 01:06:40 1970 +0000] - [log.summary|summary: no user, no domain] - - [log.changeset changeset.draft|changeset: 4:bbe44766e73d] - [log.bisect bisect.bad|bisect: bad (implicit)] - [log.branch|branch: foo] - [log.user|user: person] - [log.date|date: Sat Jan 17 04:53:20 1970 +0000] - [log.summary|summary: new branch] - - $ hg --color=debug log --debug -T bisect -r 0:4 - [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f] - [log.bisect bisect.good|bisect: good (implicit)] - [log.phase|phase: public] - [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] - [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] - [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0] - [log.user|user: User Name <user@hostname>] - [log.date|date: Mon Jan 12 13:46:40 1970 +0000] - [ui.debug log.files|files+: a] - [ui.debug log.extra|extra: branch=default] - [ui.note log.description|description:] - [ui.note log.description|line 1 - line 2] - - - [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965] - [log.bisect bisect.good|bisect: good] - [log.phase|phase: public] - [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f] - [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] - [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55] - [log.user|user: A. N. Other <other@place>] - [log.date|date: Tue Jan 13 17:33:20 1970 +0000] - [ui.debug log.files|files+: b] - [ui.debug log.extra|extra: branch=default] - [ui.note log.description|description:] - [ui.note log.description|other 1 - other 2 - - other 3] - - - [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465] - [log.bisect bisect.untested|bisect: untested] - [log.phase|phase: public] - [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965] - [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] - [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1] - [log.user|user: other@place] - [log.date|date: Wed Jan 14 21:20:00 1970 +0000] - [ui.debug log.files|files+: c] - [ui.debug log.extra|extra: branch=default] - [ui.note log.description|description:] - [ui.note log.description|no person] - - - [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47] - [log.bisect bisect.bad|bisect: bad] - [log.phase|phase: public] - [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465] - [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] - [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc] - [log.user|user: person] - [log.date|date: Fri Jan 16 01:06:40 1970 +0000] - [ui.debug log.files|files: c] - [ui.debug log.extra|extra: branch=default] - [ui.note log.description|description:] - [ui.note log.description|no user, no domain] - - - [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74] - [log.bisect bisect.bad|bisect: bad (implicit)] - [log.branch|branch: foo] - [log.phase|phase: draft] - [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47] - [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] - [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc] - [log.user|user: person] - [log.date|date: Sat Jan 17 04:53:20 1970 +0000] - [ui.debug log.extra|extra: branch=foo] - [ui.note log.description|description:] - [ui.note log.description|new branch] - - - $ hg --color=debug log -v -T bisect -r 0:4 - [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0] - [log.bisect bisect.good|bisect: good (implicit)] - [log.user|user: User Name <user@hostname>] - [log.date|date: Mon Jan 12 13:46:40 1970 +0000] - [ui.note log.files|files: a] - [ui.note log.description|description:] - [ui.note log.description|line 1 - line 2] - - - [log.changeset changeset.public|changeset: 1:b608e9d1a3f0] - [log.bisect bisect.good|bisect: good] - [log.user|user: A. N. Other <other@place>] - [log.date|date: Tue Jan 13 17:33:20 1970 +0000] - [ui.note log.files|files: b] - [ui.note log.description|description:] - [ui.note log.description|other 1 - other 2 - - other 3] - - - [log.changeset changeset.public|changeset: 2:97054abb4ab8] - [log.bisect bisect.untested|bisect: untested] - [log.user|user: other@place] - [log.date|date: Wed Jan 14 21:20:00 1970 +0000] - [ui.note log.files|files: c] - [ui.note log.description|description:] - [ui.note log.description|no person] - - - [log.changeset changeset.public|changeset: 3:10e46f2dcbf4] - [log.bisect bisect.bad|bisect: bad] - [log.user|user: person] - [log.date|date: Fri Jan 16 01:06:40 1970 +0000] - [ui.note log.files|files: c] - [ui.note log.description|description:] - [ui.note log.description|no user, no domain] - - - [log.changeset changeset.draft|changeset: 4:bbe44766e73d] - [log.bisect bisect.bad|bisect: bad (implicit)] - [log.branch|branch: foo] - [log.user|user: person] - [log.date|date: Sat Jan 17 04:53:20 1970 +0000] - [ui.note log.description|description:] - [ui.note log.description|new branch] - - - $ hg bisect --reset - -Error on syntax: - - $ echo 'x = "f' >> t - $ hg log - hg: parse error at t:3: unmatched quotes - [255] - - $ hg log -T '{date' - hg: parse error at 1: unterminated template expansion - ({date - ^ here) - [255] - $ hg log -T '{date(}' - hg: parse error at 6: not a prefix: end - ({date(} - ^ here) - [255] - $ hg log -T '{date)}' - hg: parse error at 5: invalid token - ({date)} - ^ here) - [255] - $ hg log -T '{date date}' - hg: parse error at 6: invalid token - ({date date} - ^ here) - [255] - - $ hg log -T '{}' - hg: parse error at 1: not a prefix: end - ({} - ^ here) - [255] - $ hg debugtemplate -v '{()}' - (template - (group - None)) - hg: parse error: missing argument - [255] - -Behind the scenes, this would throw TypeError without intype=bytes - - $ hg log -l 3 --template '{date|obfuscate}\n' - 0.00 - 0.00 - 1577872860.00 - -Behind the scenes, this will throw a ValueError - - $ hg log -l 3 --template 'line: {desc|shortdate}\n' - hg: parse error: invalid date: 'Modify, add, remove, rename' - (template filter 'shortdate' is not compatible with keyword 'desc') - [255] - -Behind the scenes, this would throw AttributeError without intype=bytes - - $ hg log -l 3 --template 'line: {date|escape}\n' - line: 0.00 - line: 0.00 - line: 1577872860.00 - - $ hg log -l 3 --template 'line: {extras|localdate}\n' - hg: parse error: localdate expects a date information - [255] - -Behind the scenes, this will throw ValueError - - $ hg tip --template '{author|email|date}\n' - hg: parse error: date expects a date information - [255] - - $ hg tip -T '{author|email|shortdate}\n' - hg: parse error: invalid date: 'test' - (template filter 'shortdate' is not compatible with keyword 'author') - [255] - - $ hg tip -T '{get(extras, "branch")|shortdate}\n' - hg: parse error: invalid date: 'default' - (incompatible use of template filter 'shortdate') - [255] - -Error in nested template: - - $ hg log -T '{"date' - hg: parse error at 2: unterminated string - ({"date - ^ here) - [255] - - $ hg log -T '{"foo{date|?}"}' - hg: parse error at 11: syntax error - ({"foo{date|?}"} - ^ here) - [255] - -Thrown an error if a template function doesn't exist - - $ hg tip --template '{foo()}\n' - hg: parse error: unknown function 'foo' - [255] - -Pass generator object created by template function to filter - - $ hg log -l 1 --template '{if(author, author)|user}\n' - test - -Test index keyword: - - $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n' - 10 0:a 1:b 2:fifth 3:fourth 4:third - 11 0:a - - $ hg branches -T '{index} {branch}\n' - 0 default - 1 foo - -Test diff function: - - $ hg diff -c 8 - diff -r 29114dbae42b -r 95c24699272e fourth - --- /dev/null Thu Jan 01 00:00:00 1970 +0000 - +++ b/fourth Wed Jan 01 10:01:00 2020 +0000 - @@ -0,0 +1,1 @@ - +second - diff -r 29114dbae42b -r 95c24699272e second - --- a/second Mon Jan 12 13:46:40 1970 +0000 - +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 - @@ -1,1 +0,0 @@ - -second - diff -r 29114dbae42b -r 95c24699272e third - --- /dev/null Thu Jan 01 00:00:00 1970 +0000 - +++ b/third Wed Jan 01 10:01:00 2020 +0000 - @@ -0,0 +1,1 @@ - +third - - $ hg log -r 8 -T "{diff()}" - diff -r 29114dbae42b -r 95c24699272e fourth - --- /dev/null Thu Jan 01 00:00:00 1970 +0000 - +++ b/fourth Wed Jan 01 10:01:00 2020 +0000 - @@ -0,0 +1,1 @@ - +second - diff -r 29114dbae42b -r 95c24699272e second - --- a/second Mon Jan 12 13:46:40 1970 +0000 - +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 - @@ -1,1 +0,0 @@ - -second - diff -r 29114dbae42b -r 95c24699272e third - --- /dev/null Thu Jan 01 00:00:00 1970 +0000 - +++ b/third Wed Jan 01 10:01:00 2020 +0000 - @@ -0,0 +1,1 @@ - +third - - $ hg log -r 8 -T "{diff('glob:f*')}" - diff -r 29114dbae42b -r 95c24699272e fourth - --- /dev/null Thu Jan 01 00:00:00 1970 +0000 - +++ b/fourth Wed Jan 01 10:01:00 2020 +0000 - @@ -0,0 +1,1 @@ - +second - - $ hg log -r 8 -T "{diff('', 'glob:f*')}" - diff -r 29114dbae42b -r 95c24699272e second - --- a/second Mon Jan 12 13:46:40 1970 +0000 - +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 - @@ -1,1 +0,0 @@ - -second - diff -r 29114dbae42b -r 95c24699272e third - --- /dev/null Thu Jan 01 00:00:00 1970 +0000 - +++ b/third Wed Jan 01 10:01:00 2020 +0000 - @@ -0,0 +1,1 @@ - +third - - $ hg log -r 8 -T "{diff('FOURTH'|lower)}" - diff -r 29114dbae42b -r 95c24699272e fourth - --- /dev/null Thu Jan 01 00:00:00 1970 +0000 - +++ b/fourth Wed Jan 01 10:01:00 2020 +0000 - @@ -0,0 +1,1 @@ - +second - -ui verbosity: - - $ hg log -l1 -T '{verbosity}\n' - - $ hg log -l1 -T '{verbosity}\n' --debug - debug - $ hg log -l1 -T '{verbosity}\n' --quiet - quiet - $ hg log -l1 -T '{verbosity}\n' --verbose - verbose - - $ cd .. - - -latesttag: - - $ hg init latesttag - $ cd latesttag - - $ echo a > file - $ hg ci -Am a -d '0 0' - adding file - - $ echo b >> file - $ hg ci -m b -d '1 0' - - $ echo c >> head1 - $ hg ci -Am h1c -d '2 0' - adding head1 - - $ hg update -q 1 - $ echo d >> head2 - $ hg ci -Am h2d -d '3 0' - adding head2 - created new head - - $ echo e >> head2 - $ hg ci -m h2e -d '4 0' - - $ hg merge -q - $ hg ci -m merge -d '5 -3600' - -No tag set: - - $ 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 -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 -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 -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 -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 .. - - -Style path expansion: issue1948 - ui.style option doesn't work on OSX -if it is a relative path - - $ mkdir -p home/styles - - $ cat > home/styles/teststyle <<EOF - > changeset = 'test {rev}:{node|short}\n' - > EOF - - $ HOME=`pwd`/home; export HOME - - $ cat > latesttag/.hg/hgrc <<EOF - > [ui] - > style = ~/styles/teststyle - > EOF - - $ hg -R latesttag tip - test 11:97e5943b523a - -Test recursive showlist template (issue1989): - - $ cat > style1989 <<EOF - > changeset = '{file_mods}{manifest}{extras}' - > file_mod = 'M|{author|person}\n' - > manifest = '{rev},{author}\n' - > extra = '{key}: {author}\n' - > EOF - - $ hg -R latesttag log -r tip --style=style1989 - M|test - 11,test - branch: test - -Test new-style inline templating: - - $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n' - modified files: .hgtags - - - $ hg log -R latesttag -r tip -T '{rev % "a"}\n' - hg: parse error: keyword 'rev' is not iterable of mappings - [255] - $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n' - hg: parse error: None is not iterable of mappings - [255] - $ hg log -R latesttag -r tip -T '{extras % "{key}\n" % "{key}\n"}' - hg: parse error: list of strings is not mappable - [255] - -Test new-style inline templating of non-list/dict type: - - $ hg log -R latesttag -r tip -T '{manifest}\n' - 11:2bc6e9006ce2 - $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n' - string length: 15 - $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n' - 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc - - $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}' - branch: default - $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}' - hg: parse error: None is not iterable of mappings - [255] - $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}' - branch: default - $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}' - 0:ce3cec86e6c2 - $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}' - 9:fbc7cd862e9c - -Test manifest/get() can be join()-ed as before, though it's silly: - - $ hg log -R latesttag -r tip -T '{join(manifest, "")}\n' - 11:2bc6e9006ce2 - $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), "")}\n' - default - -Test min/max of integers - - $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n' - 9 - $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n' - 10 - -Test min/max over map operation: - - $ hg log -R latesttag -r3 -T '{min(tags % "{tag}")}\n' - at3 - $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n' - t3 - -Test min/max of if() result - - $ cd latesttag - $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n' - 9 - $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n' - 10 - $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n' - 9 - $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n' - 10 - $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n' - 9 - $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n' - 10 - $ cd .. - -Test laziness of if() then/else clause - - $ hg debugtemplate '{count(0)}' - hg: parse error: not countable - (incompatible use of template filter 'count') - [255] - $ hg debugtemplate '{if(true, "", count(0))}' - $ hg debugtemplate '{if(false, count(0), "")}' - $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}' - $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}' - $ hg debugtemplate '{ifeq(0, 0, "", count(0))}' - $ hg debugtemplate '{ifeq(0, 1, count(0), "")}' - -Test dot operator precedence: - - $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n' - (template - (| - (. - (symbol 'manifest') - (symbol 'node')) - (symbol 'short')) - (string '\n')) - 89f4071fec70 - - (the following examples are invalid, but seem natural in parsing POV) - - $ hg debugtemplate -R latesttag -r0 -v '{foo|bar.baz}\n' 2> /dev/null - (template - (| - (symbol 'foo') - (. - (symbol 'bar') - (symbol 'baz'))) - (string '\n')) - [255] - $ hg debugtemplate -R latesttag -r0 -v '{foo.bar()}\n' 2> /dev/null - (template - (. - (symbol 'foo') - (func - (symbol 'bar') - None)) - (string '\n')) - [255] - -Test evaluation of dot operator: - - $ hg log -R latesttag -l1 -T '{min(revset("0:9")).node}\n' - ce3cec86e6c26bd9bdfc590a6b92abc9680f1796 - $ hg log -R latesttag -r0 -T '{extras.branch}\n' - default - - $ hg log -R latesttag -l1 -T '{author.invalid}\n' - hg: parse error: keyword 'author' has no member - [255] - $ hg log -R latesttag -l1 -T '{min("abc").invalid}\n' - hg: parse error: 'a' has no member - [255] - -Test the sub function of templating for expansion: - - $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n' - xx - - $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n' - hg: parse error: sub got an invalid pattern: [ - [255] - $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n' - hg: parse error: sub got an invalid replacement: \1 - [255] - -Test the strip function with chars specified: - - $ hg log -R latesttag --template '{desc}\n' - at3 - t5 - t4 - t3 - t2 - t1 - merge - h2e - h2d - h1c - b - a - - $ hg log -R latesttag --template '{strip(desc, "te")}\n' - at3 - 5 - 4 - 3 - 2 - 1 - merg - h2 - h2d - h1c - b - a - -Test date format: - - $ 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 - date: 70 01 01 05 +0100 - date: 70 01 01 04 +0000 - date: 70 01 01 03 +0000 - date: 70 01 01 02 +0000 - date: 70 01 01 01 +0000 - date: 70 01 01 00 +0000 - -Test invalid date: - - $ hg log -R latesttag -T '{date(rev)}\n' - hg: parse error: date expects a date information - [255] - -Test integer literal: - - $ hg debugtemplate -v '{(0)}\n' - (template - (group - (integer '0')) - (string '\n')) - 0 - $ hg debugtemplate -v '{(123)}\n' - (template - (group - (integer '123')) - (string '\n')) - 123 - $ hg debugtemplate -v '{(-4)}\n' - (template - (group - (negate - (integer '4'))) - (string '\n')) - -4 - $ hg debugtemplate '{(-)}\n' - hg: parse error at 3: not a prefix: ) - ({(-)}\n - ^ here) - [255] - $ hg debugtemplate '{(-a)}\n' - hg: parse error: negation needs an integer argument - [255] - -top-level integer literal is interpreted as symbol (i.e. variable name): - - $ hg debugtemplate -D 1=one -v '{1}\n' - (template - (integer '1') - (string '\n')) - one - $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n' - (template - (func - (symbol 'if') - (list - (string 't') - (template - (integer '1')))) - (string '\n')) - one - $ hg debugtemplate -D 1=one -v '{1|stringify}\n' - (template - (| - (integer '1') - (symbol 'stringify')) - (string '\n')) - one - -unless explicit symbol is expected: - - $ hg log -Ra -r0 -T '{desc|1}\n' - hg: parse error: expected a symbol, got 'integer' - [255] - $ hg log -Ra -r0 -T '{1()}\n' - hg: parse error: expected a symbol, got 'integer' - [255] - -Test string literal: - - $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n' - (template - (string 'string with no template fragment') - (string '\n')) - string with no template fragment - $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n' - (template - (template - (string 'template: ') - (symbol 'rev')) - (string '\n')) - template: 0 - $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n' - (template - (string 'rawstring: {rev}') - (string '\n')) - rawstring: {rev} - $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n' - (template - (% - (symbol 'files') - (string 'rawstring: {file}')) - (string '\n')) - rawstring: {file} - -Test string escaping: - - $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n' - > - <>\n<[> - <>\n<]> - <>\n< - - $ hg log -R latesttag -r 0 \ - > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n' - > - <>\n<[> - <>\n<]> - <>\n< - - $ hg log -R latesttag -r 0 -T esc \ - > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n' - > - <>\n<[> - <>\n<]> - <>\n< - - $ cat <<'EOF' > esctmpl - > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n' - > EOF - $ hg log -R latesttag -r 0 --style ./esctmpl - > - <>\n<[> - <>\n<]> - <>\n< - -Test string escaping of quotes: - - $ hg log -Ra -r0 -T '{"\""}\n' - " - $ hg log -Ra -r0 -T '{"\\\""}\n' - \" - $ hg log -Ra -r0 -T '{r"\""}\n' - \" - $ hg log -Ra -r0 -T '{r"\\\""}\n' - \\\" - - - $ hg log -Ra -r0 -T '{"\""}\n' - " - $ hg log -Ra -r0 -T '{"\\\""}\n' - \" - $ hg log -Ra -r0 -T '{r"\""}\n' - \" - $ hg log -Ra -r0 -T '{r"\\\""}\n' - \\\" - -Test exception in quoted template. single backslash before quotation mark is -stripped before parsing: - - $ cat <<'EOF' > escquotetmpl - > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n" - > EOF - $ cd latesttag - $ hg log -r 2 --style ../escquotetmpl - " \" \" \\" head1 - - $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"' - valid - $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'" - valid - -Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested -_evalifliteral() templates (issue4733): - - $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n' - "2 - $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n' - "2 - $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n' - "2 - - $ hg log -r 2 -T '{if(rev, "\\\"")}\n' - \" - $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n' - \" - $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n' - \" - - $ hg log -r 2 -T '{if(rev, r"\\\"")}\n' - \\\" - $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n' - \\\" - $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n' - \\\" - -escaped single quotes and errors: - - $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n' - foo - $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n' - foo - $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n' - hg: parse error at 21: unterminated string - ({if(rev, "{if(rev, \")}")}\n - ^ here) - [255] - $ hg log -r 2 -T '{if(rev, \"\\"")}\n' - hg: parse error: trailing \ in string - [255] - $ hg log -r 2 -T '{if(rev, r\"\\"")}\n' - hg: parse error: trailing \ in string - [255] - - $ cd .. - -Test leading backslashes: - - $ cd latesttag - $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n' - {rev} {file} - $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n' - \2 \head1 - $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n' - \{rev} \{file} - $ cd .. - -Test leading backslashes in "if" expression (issue4714): - - $ cd latesttag - $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n' - {rev} \{rev} - $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n' - \2 \\{rev} - $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n' - \{rev} \\\{rev} - $ cd .. - -"string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice) - - $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n' - \x6e - $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n' - \x5c\x786e - $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n' - \x6e - $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n' - \x5c\x786e - - $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n' - \x6e - $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n' - \x5c\x786e - $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n' - \x6e - $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n' - \x5c\x786e - - $ hg log -R a -r 8 --template '{join(files, "\n")}\n' - fourth - second - third - $ hg log -R a -r 8 --template '{join(files, r"\n")}\n' - fourth\nsecond\nthird - - $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}' - <p> - 1st - </p> - <p> - 2nd - </p> - $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}' - <p> - 1st\n\n2nd - </p> - $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}' - 1st - - 2nd - - $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n' - o perso - $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n' - no person - $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n' - o perso - $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n' - no perso - - $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n' - -o perso- - $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n' - no person - $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n' - \x2do perso\x2d - $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n' - -o perso- - $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n' - \x2do perso\x6e - - $ hg log -R a -r 8 --template '{files % "{file}\n"}' - fourth - second - third - -Test string escaping in nested expression: - - $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n' - fourth\x6esecond\x6ethird - $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n' - fourth\x6esecond\x6ethird - - $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n' - fourth\x6esecond\x6ethird - $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n' - fourth\x5c\x786esecond\x5c\x786ethird - - $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n' - 3:\x6eo user, \x6eo domai\x6e - 4:\x5c\x786eew bra\x5c\x786ech - -Test quotes in nested expression are evaluated just like a $(command) -substitution in POSIX shells: - - $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n' - 8:95c24699272e - $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n' - {8} "95c24699272e" - -Test recursive evaluation: - - $ hg init r - $ cd r - $ echo a > a - $ hg ci -Am '{rev}' - adding a - $ hg log -r 0 --template '{if(rev, desc)}\n' - {rev} - $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n' - test 0 - - $ hg branch -q 'text.{rev}' - $ echo aa >> aa - $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped' - - $ hg log -l1 --template '{fill(desc, "20", author, branch)}' - {node|short}desc to - text.{rev}be wrapped - text.{rev}desc to be - text.{rev}wrapped (no-eol) - $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}' - bcc7ff960b8e:desc to - text.1:be wrapped - text.1:desc to be - text.1:wrapped (no-eol) - $ hg log -l1 -T '{fill(desc, date, "", "")}\n' - hg: parse error: fill expects an integer width - [255] - - $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}' - bcc7ff960b8e:desc to be - termwidth.1:wrapped desc - termwidth.1:to be wrapped (no-eol) - - $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}' - {node|short} (no-eol) - $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}' - bcc-ff---b-e (no-eol) - - $ cat >> .hg/hgrc <<EOF - > [extensions] - > color= - > [color] - > mode=ansi - > text.{rev} = red - > text.1 = green - > EOF - $ hg log --color=always -l 1 --template '{label(branch, "text\n")}' - \x1b[0;31mtext\x1b[0m (esc) - $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}' - \x1b[0;32mtext\x1b[0m (esc) - -color effect can be specified without quoting: - - $ hg log --color=always -l 1 --template '{label(red, "text\n")}' - \x1b[0;31mtext\x1b[0m (esc) - -color effects can be nested (issue5413) - - $ hg debugtemplate --color=always \ - > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n' - \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc) - -pad() should interact well with color codes (issue5416) - - $ hg debugtemplate --color=always \ - > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n' - \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc) - -label should be no-op if color is disabled: - - $ hg log --color=never -l 1 --template '{label(red, "text\n")}' - text - $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}' - text - -Test branches inside if statement: - - $ hg log -r 0 --template '{if(branches, "yes", "no")}\n' - no - -Test dict constructor: - - $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n' - y=f7769ec2ab97 x=0 - $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}' - x=0 - y=f7769ec2ab97 - $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n' - {"x": 0, "y": "f7769ec2ab97"} - $ hg log -r 0 -T '{dict()|json}\n' - {} - - $ hg log -r 0 -T '{dict(rev, node=node|short)}\n' - rev=0 node=f7769ec2ab97 - $ hg log -r 0 -T '{dict(rev, node|short)}\n' - rev=0 node=f7769ec2ab97 - - $ hg log -r 0 -T '{dict(rev, rev=rev)}\n' - hg: parse error: duplicated dict key 'rev' inferred - [255] - $ hg log -r 0 -T '{dict(node, node|short)}\n' - hg: parse error: duplicated dict key 'node' inferred - [255] - $ hg log -r 0 -T '{dict(1 + 2)}' - hg: parse error: dict key cannot be inferred - [255] - - $ hg log -r 0 -T '{dict(x=rev, x=node)}' - hg: parse error: dict got multiple values for keyword argument 'x' - [255] - -Test get function: - - $ hg log -r 0 --template '{get(extras, "branch")}\n' - default - $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n' - default - $ hg log -r 0 --template '{get(files, "should_fail")}\n' - hg: parse error: get() expects a dict as first argument - [255] - -Test json filter applied to hybrid object: - - $ hg log -r0 -T '{files|json}\n' - ["a"] - $ hg log -r0 -T '{extras|json}\n' - {"branch": "default"} - -Test json filter applied to map result: - - $ hg log -r0 -T '{json(extras % "{key}")}\n' - ["branch"] - -Test localdate(date, tz) function: - - $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n' - 1970-01-01 09:00 +0900 - $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n' - 1970-01-01 00:00 +0000 - $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n' - hg: parse error: localdate expects a timezone - [255] - $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n' - 1970-01-01 02:00 +0200 - $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n' - 1970-01-01 00:00 +0000 - $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n' - 1970-01-01 00:00 +0000 - $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n' - hg: parse error: localdate expects a timezone - [255] - $ hg log -r0 -T '{localdate(date, date)|isodate}\n' - hg: parse error: localdate expects a timezone - [255] - -Test shortest(node) function: - - $ echo b > b - $ hg ci -qAm b - $ hg log --template '{shortest(node)}\n' - e777 - bcc7 - f776 - $ hg log --template '{shortest(node, 10)}\n' - e777603221 - bcc7ff960b - f7769ec2ab - $ hg log --template '{node|shortest}\n' -l1 - e777 - - $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n' - f7769ec2ab - $ hg log -r 0 -T '{shortest(node, "not an int")}\n' - hg: parse error: shortest() expects an integer minlength - [255] - - $ hg log -r 'wdir()' -T '{node|shortest}\n' - ffff - - $ hg log --template '{shortest("f")}\n' -l1 - f - - $ hg log --template '{shortest("0123456789012345678901234567890123456789")}\n' -l1 - 0123456789012345678901234567890123456789 - - $ hg log --template '{shortest("01234567890123456789012345678901234567890123456789")}\n' -l1 - 01234567890123456789012345678901234567890123456789 - - $ hg log --template '{shortest("not a hex string")}\n' -l1 - not a hex string - - $ hg log --template '{shortest("not a hex string, but it'\''s 40 bytes long")}\n' -l1 - not a hex string, but it's 40 bytes long - - $ cd .. - -Test shortest(node) with the repo having short hash collision: - - $ hg init hashcollision - $ cd hashcollision - $ cat <<EOF >> .hg/hgrc - > [experimental] - > evolution.createmarkers=True - > EOF - $ echo 0 > a - $ hg ci -qAm 0 - $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do - > hg up -q 0 - > echo $i > a - > hg ci -qm $i - > done - $ hg up -q null - $ hg log -r0: -T '{rev}:{node}\n' - 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a - 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc - 2:11407b3f1b9c3e76a79c1ec5373924df096f0499 - 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4 - 4:10776689e627b465361ad5c296a20a487e153ca4 - 5:a00be79088084cb3aff086ab799f8790e01a976b - 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6 - 7:a0457b3450b8e1b778f1163b31a435802987fe5d - 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a - 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48 - 10:c562ddd9c94164376c20b86b0b4991636a3bf84f - $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b - obsoleted 1 changesets - $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48 - obsoleted 1 changesets - $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f - obsoleted 1 changesets - - nodes starting with '11' (we don't have the revision number '11' though) - - $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n' - 1:1142 - 2:1140 - 3:11d - - '5:a00' is hidden, but still we have two nodes starting with 'a0' - - $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n' - 6:a0b - 7:a04 - - node '10' conflicts with the revision number '10' even if it is hidden - (we could exclude hidden revision numbers, but currently we don't) - - $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' - 4:107 - $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden - 4:107 - - node 'c562' should be unique if the other 'c562' nodes are hidden - (but we don't try the slow path to filter out hidden nodes for now) - - $ hg log -r 8 -T '{rev}:{node|shortest}\n' - 8:c5625 - $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden - 8:c5625 - 9:c5623 - 10:c562d - - $ cd .. - -Test pad function - - $ cd r - - $ hg log --template '{pad(rev, 20)} {author|user}\n' - 2 test - 1 {node|short} - 0 test - - $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n' - 2 test - 1 {node|short} - 0 test - - $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n' - 2------------------- test - 1------------------- {node|short} - 0------------------- test - -Test template string in pad function - - $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n' - {0} test - - $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n' - \{rev} test - -Test width argument passed to pad function - - $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n' - 0 test - $ hg log -r 0 -T '{pad(rev, "not an int")}\n' - hg: parse error: pad() expects an integer width - [255] - -Test invalid fillchar passed to pad function - - $ hg log -r 0 -T '{pad(rev, 10, "")}\n' - hg: parse error: pad() expects a single fill character - [255] - $ hg log -r 0 -T '{pad(rev, 10, "--")}\n' - hg: parse error: pad() expects a single fill character - [255] - -Test boolean argument passed to pad function - - no crash - - $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n' - ---------0 - - string/literal - - $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n' - ---------0 - $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n' - 0--------- - $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n' - 0--------- - - unknown keyword is evaluated to '' - - $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n' - 0--------- - -Test separate function - - $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n' - a-b-c - $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n' - 0:f7769ec2ab97 test default - $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n' - a \x1b[0;31mb\x1b[0m c d (esc) - -Test boolean expression/literal passed to if function - - $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n' - rev 0 is True - $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n' - literal 0 is True as well - $ hg log -r 0 -T '{if("", "", "empty string is False")}\n' - empty string is False - $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n' - empty list is False - $ hg log -r 0 -T '{if(true, "true is True")}\n' - true is True - $ hg log -r 0 -T '{if(false, "", "false is False")}\n' - false is False - $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n' - non-empty string is True - -Test ifcontains function - - $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n' - 2 is in the string - 1 is not - 0 is in the string - - $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n' - 2 is in the string - 1 is not - 0 is in the string - - $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n' - 2 did not add a - 1 did not add a - 0 added a - - $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n' - 2 is parent of 1 - 1 - 0 - -Test revset function - - $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n' - 2 current rev - 1 not current rev - 0 not current rev - - $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n' - 2 match rev - 1 match rev - 0 not match rev - - $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1 - type not match - - $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n' - 2 Parents: 1 - 1 Parents: 0 - 0 Parents: - - $ cat >> .hg/hgrc <<EOF - > [revsetalias] - > myparents(\$1) = parents(\$1) - > EOF - $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n' - 2 Parents: 1 - 1 Parents: 0 - 0 Parents: - - $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n' - Rev: 2 - Ancestor: 0 - Ancestor: 1 - Ancestor: 2 - - Rev: 1 - Ancestor: 0 - Ancestor: 1 - - Rev: 0 - Ancestor: 0 - - $ hg log --template '{revset("TIP"|lower)}\n' -l1 - 2 - - $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1 - 2 - - a list template is evaluated for each item of revset/parents - - $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n' - 2 p: 1:bcc7ff960b8e - 1 p: 0:f7769ec2ab97 - 0 p: - - $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n' - 2 p: 1:bcc7ff960b8e -1:000000000000 - 1 p: 0:f7769ec2ab97 -1:000000000000 - 0 p: -1:000000000000 -1:000000000000 - - therefore, 'revcache' should be recreated for each rev - - $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n' - 2 aa b - p - 1 - p a - 0 a - p - - $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n' - 2 aa b - p - 1 - p a - 0 a - p - -a revset item must be evaluated as an integer revision, not an offset from tip - - $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n' - -1:000000000000 - $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n' - -1:000000000000 - -join() should pick '{rev}' from revset items: - - $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6 - 4, 5 - -on the other hand, parents are formatted as '{rev}:{node|formatnode}' by -default. join() should agree with the default formatting: - - $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6 - 5:13207e5a10d9, 4:bbe44766e73d - - $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug - 5:13207e5a10d9fd28ec424934298e176197f2c67f, - 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74 - -Invalid arguments passed to revset() - - $ hg log -T '{revset("%whatever", 0)}\n' - hg: parse error: unexpected revspec format character w - [255] - $ hg log -T '{revset("%lwhatever", files)}\n' - hg: parse error: unexpected revspec format character w - [255] - $ hg log -T '{revset("%s %s", 0)}\n' - hg: parse error: missing argument for revspec - [255] - $ hg log -T '{revset("", 0)}\n' - hg: parse error: too many revspec arguments specified - [255] - $ hg log -T '{revset("%s", 0, 1)}\n' - hg: parse error: too many revspec arguments specified - [255] - $ hg log -T '{revset("%", 0)}\n' - hg: parse error: incomplete revspec format character - [255] - $ hg log -T '{revset("%l", 0)}\n' - hg: parse error: incomplete revspec format character - [255] - $ hg log -T '{revset("%d", 'foo')}\n' - hg: parse error: invalid argument for revspec - [255] - $ hg log -T '{revset("%ld", files)}\n' - hg: parse error: invalid argument for revspec - [255] - $ hg log -T '{revset("%ls", 0)}\n' - hg: parse error: invalid argument for revspec - [255] - $ hg log -T '{revset("%b", 'foo')}\n' - hg: parse error: invalid argument for revspec - [255] - $ hg log -T '{revset("%lb", files)}\n' - hg: parse error: invalid argument for revspec - [255] - $ hg log -T '{revset("%r", 0)}\n' - hg: parse error: invalid argument for revspec - [255] - -Test 'originalnode' - - $ hg log -r 1 -T '{revset("null") % "{node|short} {originalnode|short}"}\n' - 000000000000 bcc7ff960b8e - $ hg log -r 0 -T '{manifest % "{node} {originalnode}"}\n' - a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 f7769ec2ab975ad19684098ad1ffd9b81ecc71a1 - -Test files function - - $ hg log -T "{rev}\n{join(files('*'), '\n')}\n" - 2 - a - aa - b - 1 - a - 0 - a - - $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n" - 2 - aa - 1 - - 0 - - -Test relpath function - - $ hg log -r0 -T '{files % "{file|relpath}\n"}' - a - $ cd .. - $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}' - r/a - $ cd r - -Test active bookmark templating - - $ hg book foo - $ hg book bar - $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n" - 2 bar* foo - 1 - 0 - $ hg log --template "{rev} {activebookmark}\n" - 2 bar - 1 - 0 - $ hg bookmarks --inactive bar - $ hg log --template "{rev} {activebookmark}\n" - 2 - 1 - 0 - $ hg book -r1 baz - $ hg log --template "{rev} {join(bookmarks, ' ')}\n" - 2 bar foo - 1 baz - 0 - $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n" - 2 t - 1 f - 0 f - -Test namespaces dict - - $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n' - 2 - bookmarks color=bookmark builtin=True - bar,foo - tags color=tag builtin=True - tip - branches color=branch builtin=True - text.{rev} - revnames color=revname builtin=False - r2 - - 1 - bookmarks color=bookmark builtin=True - baz - tags color=tag builtin=True - - branches color=branch builtin=True - text.{rev} - revnames color=revname builtin=False - r1 - - 0 - bookmarks color=bookmark builtin=True - - tags color=tag builtin=True - - branches color=branch builtin=True - default - revnames color=revname builtin=False - r0 - - $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}' - bookmarks: bar foo - tags: tip - branches: text.{rev} - $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}' - bookmarks: - bar - foo - tags: - tip - branches: - text.{rev} - $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}' - bar - foo - $ hg log -r2 -T '{namespaces.bookmarks % "{bookmark}\n"}' - bar - foo - -Test stringify on sub expressions - - $ cd .. - $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n' - fourth, second, third - $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n' - abc - -Test splitlines - - $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}" - @ foo Modify, add, remove, rename - | - o foo future - | - o foo third - | - o foo second - - o foo merge - |\ - | o foo new head - | | - o | foo new branch - |/ - o foo no user, no domain - | - o foo no person - | - o foo other 1 - | foo other 2 - | foo - | foo other 3 - o foo line 1 - foo line 2 - - $ hg log -R a -r0 -T '{desc|splitlines}\n' - line 1 line 2 - $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n' - line 1|line 2 - -Test startswith - $ hg log -Gv -R a --template "{startswith(desc)}" - hg: parse error: startswith expects two arguments - [255] - - $ hg log -Gv -R a --template "{startswith('line', desc)}" - @ - | - o - | - o - | - o - - o - |\ - | o - | | - o | - |/ - o - | - o - | - o - | - o line 1 - line 2 - -Test bad template with better error message - - $ hg log -Gv -R a --template '{desc|user()}' - hg: parse error: expected a symbol, got 'func' - [255] - -Test word function (including index out of bounds graceful failure) - - $ hg log -Gv -R a --template "{word('1', desc)}" - @ add, - | - o - | - o - | - o - - o - |\ - | o head - | | - o | branch - |/ - o user, - | - o person - | - o 1 - | - o 1 - - -Test word third parameter used as splitter - - $ hg log -Gv -R a --template "{word('0', desc, 'o')}" - @ M - | - o future - | - o third - | - o sec - - o merge - |\ - | o new head - | | - o | new branch - |/ - o n - | - o n - | - o - | - o line 1 - line 2 - -Test word error messages for not enough and too many arguments - - $ hg log -Gv -R a --template "{word('0')}" - hg: parse error: word expects two or three arguments, got 1 - [255] - - $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}" - hg: parse error: word expects two or three arguments, got 7 - [255] - -Test word for integer literal - - $ hg log -R a --template "{word(2, desc)}\n" -r0 - line - -Test word for invalid numbers - - $ hg log -Gv -R a --template "{word('a', desc)}" - hg: parse error: word expects an integer index - [255] - -Test word for out of range - - $ hg log -R a --template "{word(10000, desc)}" - $ hg log -R a --template "{word(-10000, desc)}" - -Test indent and not adding to empty lines - - $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a - ----- - > line 1 - >> line 2 - ----- - > other 1 - >> other 2 - - >> other 3 - -Test with non-strings like dates - - $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a - 1200000.00 - 1300000.00 - -Test broken string escapes: - - $ hg log -T "bogus\\" -R a - hg: parse error: trailing \ in string - [255] - $ hg log -T "\\xy" -R a - hg: parse error: invalid \x escape* (glob) - [255] - -json filter should escape HTML tags so that the output can be embedded in hgweb: - - $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1 - "\u003cfoo@example.org\u003e" - -Templater supports aliases of symbol and func() styles: - - $ hg clone -q a aliases - $ cd aliases - $ cat <<EOF >> .hg/hgrc - > [templatealias] - > r = rev - > rn = "{r}:{node|short}" - > status(c, files) = files % "{c} {file}\n" - > utcdate(d) = localdate(d, "UTC") - > EOF - - $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n' - (template - (symbol 'rn') - (string ' ') - (| - (func - (symbol 'utcdate') - (symbol 'date')) - (symbol 'isodate')) - (string '\n')) - * expanded: - (template - (template - (symbol 'rev') - (string ':') - (| - (symbol 'node') - (symbol 'short'))) - (string ' ') - (| - (func - (symbol 'localdate') - (list - (symbol 'date') - (string 'UTC'))) - (symbol 'isodate')) - (string '\n')) - 0:1e4e1b8f71e0 1970-01-12 13:46 +0000 - - $ hg debugtemplate -vr0 '{status("A", file_adds)}' - (template - (func - (symbol 'status') - (list - (string 'A') - (symbol 'file_adds')))) - * expanded: - (template - (% - (symbol 'file_adds') - (template - (string 'A') - (string ' ') - (symbol 'file') - (string '\n')))) - A a - -A unary function alias can be called as a filter: - - $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n' - (template - (| - (| - (symbol 'date') - (symbol 'utcdate')) - (symbol 'isodate')) - (string '\n')) - * expanded: - (template - (| - (func - (symbol 'localdate') - (list - (symbol 'date') - (string 'UTC'))) - (symbol 'isodate')) - (string '\n')) - 1970-01-12 13:46 +0000 - -Aliases should be applied only to command arguments and templates in hgrc. -Otherwise, our stock styles and web templates could be corrupted: - - $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n' - 0:1e4e1b8f71e0 1970-01-12 13:46 +0000 - - $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"' - 0:1e4e1b8f71e0 1970-01-12 13:46 +0000 - - $ cat <<EOF > tmpl - > changeset = 'nothing expanded:{rn}\n' - > EOF - $ hg log -r0 --style ./tmpl - nothing expanded: - -Aliases in formatter: - - $ hg branches -T '{pad(branch, 7)} {rn}\n' - default 6:d41e714fe50d - foo 4:bbe44766e73d - -Aliases should honor HGPLAIN: - - $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n' - nothing expanded: - $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n' - 0:1e4e1b8f71e0 - -Unparsable alias: - - $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}' - (template - (symbol 'bad')) - abort: bad definition of template alias "bad": at 2: not a prefix: end - [255] - $ hg log --config templatealias.bad='x(' -T '{bad}' - abort: bad definition of template alias "bad": at 2: not a prefix: end - [255] - - $ cd .. - -Set up repository for non-ascii encoding tests: - - $ hg init nonascii - $ cd nonascii - $ $PYTHON <<EOF - > open('latin1', 'wb').write(b'\xe9') - > open('utf-8', 'wb').write(b'\xc3\xa9') - > EOF - $ HGENCODING=utf-8 hg branch -q `cat utf-8` - $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8 - -json filter should try round-trip conversion to utf-8: - - $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0 - "\u00e9" - $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0 - "non-ascii branch: \u00e9" - -json filter takes input as utf-8b: - - $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1 - "\u00e9" - $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1 - "\udce9" - -utf8 filter: - - $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0 - round-trip: c3a9 - $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1 - decoded: c3a9 - $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1 - abort: decoding near * (glob) - [255] - $ hg log -T "coerced to string: {rev|utf8}\n" -r0 - coerced to string: 0 - -pad width: - - $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n" - \xc3\xa9- (esc) - - $ cd .. - -Test that template function in extension is registered as expected - - $ cd a - - $ cat <<EOF > $TESTTMP/customfunc.py - > from mercurial import registrar - > - > templatefunc = registrar.templatefunc() - > - > @templatefunc(b'custom()') - > def custom(context, mapping, args): - > return b'custom' - > EOF - $ cat <<EOF > .hg/hgrc - > [extensions] - > customfunc = $TESTTMP/customfunc.py - > EOF - - $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true - custom - - $ cd .. - -Test 'graphwidth' in 'hg log' on various topologies. The key here is that the -printed graphwidths 3, 5, 7, etc. should all line up in their respective -columns. We don't care about other aspects of the graph rendering here. - - $ hg init graphwidth - $ cd graphwidth - - $ wrappabletext="a a a a a a a a a a a a" - - $ printf "first\n" > file - $ hg add file - $ hg commit -m "$wrappabletext" - - $ printf "first\nsecond\n" > file - $ hg commit -m "$wrappabletext" - - $ hg checkout 0 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ printf "third\nfirst\n" > file - $ hg commit -m "$wrappabletext" - created new head - - $ hg merge - merging file - 0 files updated, 1 files merged, 0 files removed, 0 files unresolved - (branch merge, don't forget to commit) - - $ hg log --graph -T "{graphwidth}" - @ 3 - | - | @ 5 - |/ - o 3 - - $ hg commit -m "$wrappabletext" - - $ hg log --graph -T "{graphwidth}" - @ 5 - |\ - | o 5 - | | - o | 5 - |/ - o 3 - - - $ hg checkout 0 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ printf "third\nfirst\nsecond\n" > file - $ hg commit -m "$wrappabletext" - created new head - - $ hg log --graph -T "{graphwidth}" - @ 3 - | - | o 7 - | |\ - +---o 7 - | | - | o 5 - |/ - o 3 - - - $ hg log --graph -T "{graphwidth}" -r 3 - o 5 - |\ - ~ ~ - - $ hg log --graph -T "{graphwidth}" -r 1 - o 3 - | - ~ - - $ hg merge - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - (branch merge, don't forget to commit) - $ hg commit -m "$wrappabletext" - - $ printf "seventh\n" >> file - $ hg commit -m "$wrappabletext" - - $ hg log --graph -T "{graphwidth}" - @ 3 - | - o 5 - |\ - | o 5 - | | - o | 7 - |\ \ - | o | 7 - | |/ - o / 5 - |/ - o 3 - - -The point of graphwidth is to allow wrapping that accounts for the space taken -by the graph. - - $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}" - @ a a a a - | a a a a - | a a a a - o a a a - |\ a a a - | | a a a - | | a a a - | o a a a - | | a a a - | | a a a - | | a a a - o | a a - |\ \ a a - | | | a a - | | | a a - | | | a a - | | | a a - | o | a a - | |/ a a - | | a a - | | a a - | | a a - | | a a - o | a a a - |/ a a a - | a a a - | a a a - o a a a a - a a a a - a a a a - -Something tricky happens when there are elided nodes; the next drawn row of -edges can be more than one column wider, but the graph width only increases by -one column. The remaining columns are added in between the nodes. - - $ hg log --graph -T "{graphwidth}" -r "0|2|4|5" - o 5 - |\ - | \ - | :\ - o : : 7 - :/ / - : o 5 - :/ - o 3 - - - $ cd .. -
--- a/tests/test-commit-interactive-curses.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-commit-interactive-curses.t Thu Jul 19 13:55:54 2018 -0400 @@ -68,7 +68,7 @@ Committing only one file $ echo "a" >> a - >>> open('b', 'wb').write("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n") + >>> open('b', 'wb').write(b"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n") and None $ hg add b $ cat <<EOF >testModeCommands > TOGGLE @@ -368,6 +368,16 @@ $ chunkselectorinterface curses +If TERM=dumb, we use text, even if the config says curses + $ chunkselectorinterface + curses + $ TERM=dumb chunkselectorinterface + text +(Something is keeping TERM=dumb in the environment unless I do this, it's not +scoped to just that previous command like in many shells) + $ TERM=xterm chunkselectorinterface + curses + It is possible to override the default interface with a feature specific interface $ cp $HGRCPATH.pretest $HGRCPATH
--- a/tests/test-completion.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-completion.t Thu Jul 19 13:55:54 2018 -0400 @@ -191,6 +191,7 @@ --pid-file --port --prefix + --print-url --profile --quiet --repository @@ -231,7 +232,7 @@ clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos - export: output, switch-parent, rev, text, git, binary, nodates, template + export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template forget: interactive, include, exclude, dry-run init: ssh, remotecmd, insecure log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude @@ -239,7 +240,7 @@ pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure push: force, rev, bookmark, branch, new-branch, pushvars, ssh, remotecmd, insecure remove: after, force, subrepos, include, exclude, dry-run - serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, subrepos + serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template summary: remote update: clean, check, merge, date, rev, tool @@ -272,7 +273,7 @@ debugdiscovery: old, nonheads, rev, ssh, remotecmd, insecure debugdownload: output debugextensions: template - debugfileset: rev + debugfileset: rev, all-files debugformat: template debugfsinfo: debuggetbundle: head, common, type @@ -311,8 +312,8 @@ debugwireargs: three, four, five, ssh, remotecmd, insecure debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure files: rev, print0, include, exclude, template, subrepos - graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run - grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, template, include, exclude + graft: rev, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run + grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude heads: rev, topo, active, closed, style, template help: extension, command, keyword, system identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
--- a/tests/test-conflict.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-conflict.t Thu Jul 19 13:55:54 2018 -0400 @@ -57,8 +57,8 @@ # # To mark files as resolved: hg resolve --mark FILE - # To continue: hg commit - # To abort: hg update --clean . (warning: this will discard uncommitted changes) + # To continue: hg commit + # To abort: hg update --clean . (warning: this will discard uncommitted changes) $ cat a
--- a/tests/test-context-metadata.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-context-metadata.t Thu Jul 19 13:55:54 2018 -0400 @@ -23,7 +23,7 @@ > old = repo[b'.'] > kwargs = dict(s.split(b'=', 1) for s in arg.split(b';')) > if 'parents' in kwargs: - > kwargs[b'parents'] = kwargs[b'parents'].split(b',') + > kwargs[b'parents'] = map(int, kwargs[b'parents'].split(b',')) > new = context.metadataonlyctx(repo, old, > **pycompat.strkwargs(kwargs)) > new.commit()
--- a/tests/test-context.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-context.py Thu Jul 19 13:55:54 2018 -0400 @@ -1,18 +1,35 @@ from __future__ import absolute_import, print_function import os import stat +import sys from mercurial.node import hex from mercurial import ( context, + diffutil, encoding, hg, scmutil, ui as uimod, ) +print_ = print +def print(*args, **kwargs): + """print() wrapper that flushes stdout buffers to avoid py3 buffer issues + + We could also just write directly to sys.stdout.buffer the way the + ui object will, but this was easier for porting the test. + """ + print_(*args, **kwargs) + sys.stdout.flush() + +def printb(data, end=b'\n'): + out = getattr(sys.stdout, 'buffer', sys.stdout) + out.write(data + end) + out.flush() + u = uimod.ui.load() -repo = hg.repository(u, 'test1', create=1) +repo = hg.repository(u, b'test1', create=1) os.chdir('test1') # create 'foo' with fixed time stamp @@ -22,10 +39,10 @@ os.utime('foo', (1000, 1000)) # add+commit 'foo' -repo[None].add(['foo']) -repo.commit(text='commit1', date="0 0") +repo[None].add([b'foo']) +repo.commit(text=b'commit1', date=b"0 0") -d = repo[None]['foo'].date() +d = repo[None][b'foo'].date() if os.name == 'nt': d = d[:2] print("workingfilectx.date = (%d, %d)" % d) @@ -33,54 +50,55 @@ # test memctx with non-ASCII commit message def filectxfn(repo, memctx, path): - return context.memfilectx(repo, memctx, "foo", "") + return context.memfilectx(repo, memctx, b"foo", b"") -ctx = context.memctx(repo, ['tip', None], - encoding.tolocal("Gr\xc3\xbcezi!"), - ["foo"], filectxfn) +ctx = context.memctx(repo, [b'tip', None], + encoding.tolocal(b"Gr\xc3\xbcezi!"), + [b"foo"], filectxfn) ctx.commit() for enc in "ASCII", "Latin-1", "UTF-8": encoding.encoding = enc - print("%-8s: %s" % (enc, repo["tip"].description())) + printb(b"%-8s: %s" % (enc.encode('ascii'), repo[b"tip"].description())) # test performing a status def getfilectx(repo, memctx, f): fctx = memctx.parents()[0][f] data, flags = fctx.data(), fctx.flags() - if f == 'foo': - data += 'bar\n' - return context.memfilectx(repo, memctx, f, data, 'l' in flags, 'x' in flags) + if f == b'foo': + data += b'bar\n' + return context.memfilectx( + repo, memctx, f, data, b'l' in flags, b'x' in flags) ctxa = repo[0] -ctxb = context.memctx(repo, [ctxa.node(), None], "test diff", ["foo"], +ctxb = context.memctx(repo, [ctxa.node(), None], b"test diff", [b"foo"], getfilectx, ctxa.user(), ctxa.date()) print(ctxb.status(ctxa)) # test performing a diff on a memctx - -for d in ctxb.diff(ctxa, git=True): - print(d, end='') +diffopts = diffutil.diffallopts(repo.ui, {b'git': True}) +for d in ctxb.diff(ctxa, opts=diffopts): + printb(d, end=b'') # test safeness and correctness of "ctx.status()" print('= checking context.status():') # ancestor "wcctx ~ 2" -actx2 = repo['.'] +actx2 = repo[b'.'] -repo.wwrite('bar-m', 'bar-m\n', '') -repo.wwrite('bar-r', 'bar-r\n', '') -repo[None].add(['bar-m', 'bar-r']) -repo.commit(text='add bar-m, bar-r', date="0 0") +repo.wwrite(b'bar-m', b'bar-m\n', b'') +repo.wwrite(b'bar-r', b'bar-r\n', b'') +repo[None].add([b'bar-m', b'bar-r']) +repo.commit(text=b'add bar-m, bar-r', date=b"0 0") # ancestor "wcctx ~ 1" -actx1 = repo['.'] +actx1 = repo[b'.'] -repo.wwrite('bar-m', 'bar-m bar-m\n', '') -repo.wwrite('bar-a', 'bar-a\n', '') -repo[None].add(['bar-a']) -repo[None].forget(['bar-r']) +repo.wwrite(b'bar-m', b'bar-m bar-m\n', b'') +repo.wwrite(b'bar-a', b'bar-a\n', b'') +repo[None].add([b'bar-a']) +repo[None].forget([b'bar-r']) # status at this point: # M bar-m @@ -97,10 +115,10 @@ print('=== with "pattern match":') print(actx1.status(other=wctx, - match=scmutil.matchfiles(repo, ['bar-m', 'foo']))) + match=scmutil.matchfiles(repo, [b'bar-m', b'foo']))) print('wctx._status=%s' % (str(wctx._status))) print(actx2.status(other=wctx, - match=scmutil.matchfiles(repo, ['bar-m', 'foo']))) + match=scmutil.matchfiles(repo, [b'bar-m', b'foo']))) print('wctx._status=%s' % (str(wctx._status))) print('=== with "always match" and "listclean=True":') @@ -112,11 +130,11 @@ print("== checking workingcommitctx.status:") wcctx = context.workingcommitctx(repo, - scmutil.status(['bar-m'], - ['bar-a'], + scmutil.status([b'bar-m'], + [b'bar-a'], [], [], [], [], []), - text='', date='0 0') + text=b'', date=b'0 0') print('wcctx._status=%s' % (str(wcctx._status))) print('=== with "always match":') @@ -133,19 +151,19 @@ print('=== with "pattern match":') print(actx1.status(other=wcctx, - match=scmutil.matchfiles(repo, ['bar-m', 'foo']))) + match=scmutil.matchfiles(repo, [b'bar-m', b'foo']))) print('wcctx._status=%s' % (str(wcctx._status))) print(actx2.status(other=wcctx, - match=scmutil.matchfiles(repo, ['bar-m', 'foo']))) + match=scmutil.matchfiles(repo, [b'bar-m', b'foo']))) print('wcctx._status=%s' % (str(wcctx._status))) print('=== with "pattern match" and "listclean=True":') print(actx1.status(other=wcctx, - match=scmutil.matchfiles(repo, ['bar-r', 'foo']), + match=scmutil.matchfiles(repo, [b'bar-r', b'foo']), listclean=True)) print('wcctx._status=%s' % (str(wcctx._status))) print(actx2.status(other=wcctx, - match=scmutil.matchfiles(repo, ['bar-r', 'foo']), + match=scmutil.matchfiles(repo, [b'bar-r', b'foo']), listclean=True)) print('wcctx._status=%s' % (str(wcctx._status))) @@ -154,7 +172,7 @@ # test manifestlog being changed print('== commit with manifestlog invalidated') -repo = hg.repository(u, 'test2', create=1) +repo = hg.repository(u, b'test2', create=1) os.chdir('test2') # make some commits @@ -166,12 +184,12 @@ date=(0, 0)) ctx.p1().manifest() # side effect: cache manifestctx n = repo.commitctx(ctx) - print('commit %s: %s' % (i, hex(n))) + printb(b'commit %s: %s' % (i, hex(n))) # touch 00manifest.i mtime so storecache could expire. # repo.__dict__['manifestlog'] is deleted by transaction releasefn. - st = repo.svfs.stat('00manifest.i') - repo.svfs.utime('00manifest.i', + st = repo.svfs.stat(b'00manifest.i') + repo.svfs.utime(b'00manifest.i', (st[stat.ST_MTIME] + 1, st[stat.ST_MTIME] + 1)) # read the file just committed @@ -181,11 +199,11 @@ except Exception as ex: print('cannot read data: %r' % ex) -with repo.wlock(), repo.lock(), repo.transaction('test'): +with repo.wlock(), repo.lock(), repo.transaction(b'test'): with open(b'4', 'wb') as f: f.write(b'4') - repo.dirstate.normal('4') - repo.commit('4') + repo.dirstate.normal(b'4') + repo.commit(b'4') revsbefore = len(repo.changelog) repo.invalidate(clearfilecache=True) revsafter = len(repo.changelog)
--- a/tests/test-contrib-perf.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-contrib-perf.t Thu Jul 19 13:55:54 2018 -0400 @@ -88,7 +88,8 @@ perflookup (no help text available) perflrucachedict (no help text available) - perfmanifest (no help text available) + perfmanifest benchmark the time to read a manifest from disk and return a + usable perfmergecalculate (no help text available) perfmoonwalk benchmark walking the changelog backwards @@ -113,7 +114,7 @@ perfstatus (no help text available) perftags (no help text available) perftemplating - (no help text available) + test the rendering time of a given template perfunidiff benchmark a unified diff between revisions perfvolatilesets benchmark the computation of various volatile set @@ -145,7 +146,11 @@ #if repofncache $ hg perffncacheencode $ hg perffncacheload + $ hg debugrebuildfncache + fncache already up to date $ hg perffncachewrite + $ hg debugrebuildfncache + fncache already up to date #endif $ hg perfheads $ hg perfindex @@ -175,7 +180,24 @@ $ hg perfwalk $ hg perfparents +test actual output +------------------ + +normal output: + + $ hg perfheads --config perf.stub=no + ! wall * comb * user * sys * (best of *) (glob) + +detailed output: + + $ hg perfheads --config perf.all-timing=yes --config perf.stub=no + ! wall * comb * user * sys * (best of *) (glob) + ! wall * comb * user * sys * (max of *) (glob) + ! wall * comb * user * sys * (avg of *) (glob) + ! wall * comb * user * sys * (median of *) (glob) + Check perf.py for historical portability +---------------------------------------- $ cd "$TESTDIR/.."
--- a/tests/test-convert-bzr-merges.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-convert-bzr-merges.t Thu Jul 19 13:55:54 2018 -0400 @@ -14,32 +14,32 @@ $ cd source $ echo content > file $ bzr add -q file - $ bzr commit -q -m 'Initial add' + $ bzr commit -q -m 'Initial add' '--commit-time=2009-10-10 08:00:00 +0100' $ cd .. $ bzr branch -q source source-branch1 $ cd source-branch1 $ echo morecontent >> file $ echo evenmorecontent > file-branch1 $ bzr add -q file-branch1 - $ bzr commit -q -m 'Added branch1 file' + $ bzr commit -q -m 'Added branch1 file' '--commit-time=2009-10-10 08:00:01 +0100' $ cd ../source $ sleep 1 $ echo content > file-parent $ bzr add -q file-parent - $ bzr commit -q -m 'Added parent file' + $ bzr commit -q -m 'Added parent file' '--commit-time=2009-10-10 08:00:02 +0100' $ cd .. $ bzr branch -q source source-branch2 $ cd source-branch2 $ echo somecontent > file-branch2 $ bzr add -q file-branch2 - $ bzr commit -q -m 'Added brach2 file' + $ bzr commit -q -m 'Added brach2 file' '--commit-time=2009-10-10 08:00:03 +0100' $ sleep 1 $ cd ../source $ bzr merge -q ../source-branch1 $ bzr merge -q --force ../source-branch2 - $ bzr commit -q -m 'Merged branches' + $ bzr commit -q -m 'Merged branches' '--commit-time=2009-10-10 08:00:04 +0100' $ cd .. - $ hg convert --datesort source source-hg + $ hg convert --datesort --config convert.bzr.saverev=False source source-hg initializing destination source-hg repository scanning source... sorting... @@ -69,4 +69,76 @@ 644 file-branch2 644 file-parent + $ hg convert source-hg hg2hg + initializing destination hg2hg repository + scanning source... + sorting... + converting... + 5 Initial add + 4 Added branch1 file + 3 Added parent file + 2 Added brach2 file + 1 Merged branches + 0 (octopus merge fixup) + $ hg -R hg2hg out source-hg -T compact + comparing with source-hg + searching for changes + 5[tip]:4,3 6bd55e826939 2009-10-10 08:00 +0100 foo + (octopus merge fixup) + +XXX: The manifest lines should probably agree, to avoid changing the hash when +converting hg -> hg + + $ hg -R source-hg log --debug -r tip + changeset: 5:b209510f11b2c987f920749cd8e352aa4b3230f2 + branch: source + tag: tip + phase: draft + parent: 4:1dc38c377bb35eeea4fa955056fbe4440d54a743 + parent: 3:4aaba1bfb426b8941bbf63f9dd52301152695164 + manifest: 5:1109e42bdcbd1f51baa69bc91079011d77057dbb + user: Foo Bar <foo.bar@example.com> + date: Sat Oct 10 08:00:04 2009 +0100 + extra: branch=source + description: + (octopus merge fixup) + + + $ hg -R hg2hg log --debug -r tip + changeset: 5:6bd55e8269392769783345686faf7ff7b3b0215d + branch: source + tag: tip + phase: draft + parent: 4:1dc38c377bb35eeea4fa955056fbe4440d54a743 + parent: 3:4aaba1bfb426b8941bbf63f9dd52301152695164 + manifest: 4:daa315d56a98ba20811fdd0d9d575861f65cfa8c + user: Foo Bar <foo.bar@example.com> + date: Sat Oct 10 08:00:04 2009 +0100 + extra: branch=source + description: + (octopus merge fixup) + + + $ hg -R source-hg manifest --debug -r tip + cdf31ed9242b209cd94697112160e2c5b37a667d 644 file + 5108144f585149b29779d7c7e51d61dd22303ffe 644 file-branch1 + 80753c4a9ac3806858405b96b24a907b309e3616 644 file-branch2 + 7108421418404a937c684d2479a34a24d2ce4757 644 file-parent + $ hg -R source-hg manifest --debug -r 'tip^' + cdf31ed9242b209cd94697112160e2c5b37a667d 644 file + 5108144f585149b29779d7c7e51d61dd22303ffe 644 file-branch1 + 80753c4a9ac3806858405b96b24a907b309e3616 644 file-branch2 + 7108421418404a937c684d2479a34a24d2ce4757 644 file-parent + + $ hg -R hg2hg manifest --debug -r tip + cdf31ed9242b209cd94697112160e2c5b37a667d 644 file + 5108144f585149b29779d7c7e51d61dd22303ffe 644 file-branch1 + 80753c4a9ac3806858405b96b24a907b309e3616 644 file-branch2 + 7108421418404a937c684d2479a34a24d2ce4757 644 file-parent + $ hg -R hg2hg manifest --debug -r 'tip^' + cdf31ed9242b209cd94697112160e2c5b37a667d 644 file + 5108144f585149b29779d7c7e51d61dd22303ffe 644 file-branch1 + 80753c4a9ac3806858405b96b24a907b309e3616 644 file-branch2 + 7108421418404a937c684d2479a34a24d2ce4757 644 file-parent + $ cd ..
--- a/tests/test-convert-cvs.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-convert-cvs.t Thu Jul 19 13:55:54 2018 -0400 @@ -12,10 +12,10 @@ $ echo "convert = " >> $HGRCPATH $ cat > cvshooks.py <<EOF > def cvslog(ui,repo,hooktype,log): - > ui.write('%s hook: %d entries\n' % (hooktype,len(log))) + > ui.write(b'%s hook: %d entries\n' % (hooktype,len(log))) > > def cvschangesets(ui,repo,hooktype,changesets): - > ui.write('%s hook: %d changesets\n' % (hooktype,len(changesets))) + > ui.write(b'%s hook: %d changesets\n' % (hooktype,len(changesets))) > EOF $ hookpath=`pwd` $ cat <<EOF >> $HGRCPATH @@ -522,8 +522,8 @@ $ mkdir -p cvsrepo/transcoding $ python <<EOF - > fp = open('cvsrepo/transcoding/file,v', 'w') - > fp.write((''' + > fp = open('cvsrepo/transcoding/file,v', 'wb') + > fp.write((b''' > head 1.4; > access; > symbols @@ -570,7 +570,7 @@ > > 1.4 > log - > @''' + u'\u3042'.encode('cp932') + ''' (cp932) + > @''' + u'\u3042'.encode('cp932') + b''' (cp932) > @ > text > @1 @@ -582,7 +582,7 @@ > > 1.3 > log - > @''' + u'\u3042'.encode('euc-jp') + ''' (euc-jp) + > @''' + u'\u3042'.encode('euc-jp') + b''' (euc-jp) > @ > text > @d4 1 @@ -591,7 +591,7 @@ > > 1.2 > log - > @''' + u'\u3042'.encode('utf-8') + ''' (utf-8) + > @''' + u'\u3042'.encode('utf-8') + b''' (utf-8) > @ > text > @d3 1
--- a/tests/test-convert.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-convert.t Thu Jul 19 13:55:54 2018 -0400 @@ -148,6 +148,15 @@ convert.hg.revs revset specifying the source revisions to convert. + Bazaar Source + ############# + + The following options can be used with "--config": + + convert.bzr.saverev + whether to store the original Bazaar commit ID in the + metadata of the destination commit. The default is True. + CVS Source ########## @@ -419,6 +428,7 @@ pulling from ../a searching for changes no changes found + 5 local changesets published conversion to existing file should fail
--- a/tests/test-copytrace-heuristics.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-copytrace-heuristics.t Thu Jul 19 13:55:54 2018 -0400 @@ -583,7 +583,6 @@ $ hg unshelve unshelving change 'default' rebasing shelved changes - rebasing 2:45f63161acea "changes to: initial" (tip) merging b and a to b $ ls b
--- a/tests/test-custom-filters.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-custom-filters.t Thu Jul 19 13:55:54 2018 -0400 @@ -12,15 +12,15 @@ $ cat > prefix.py <<EOF > from mercurial import error > def stripprefix(s, cmd, filename, **kwargs): - > header = '%s\n' % cmd + > header = b'%s\n' % cmd > if s[:len(header)] != header: - > raise error.Abort('missing header "%s" in %s' % (cmd, filename)) + > raise error.Abort(b'missing header "%s" in %s' % (cmd, filename)) > return s[len(header):] > def insertprefix(s, cmd): - > return '%s\n%s' % (cmd, s) + > return b'%s\n%s' % (cmd, s) > def reposetup(ui, repo): - > repo.adddatafilter('stripprefix:', stripprefix) - > repo.adddatafilter('insertprefix:', insertprefix) + > repo.adddatafilter(b'stripprefix:', stripprefix) + > repo.adddatafilter(b'insertprefix:', insertprefix) > EOF $ cat > .hgignore <<EOF
--- a/tests/test-debugcommands.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-debugcommands.t Thu Jul 19 13:55:54 2018 -0400 @@ -8,33 +8,41 @@ $ echo a > a $ hg ci -Am adda adding a + $ hg rm . + removing a + $ hg ci -Am make-it-empty + $ hg revert --all -r 0 + adding a + $ hg ci -Am make-it-full #if reporevlogstore $ hg debugrevlog -m format : 1 flags : inline, generaldelta - revisions : 1 + revisions : 3 merges : 0 ( 0.00%) - normal : 1 (100.00%) - revisions : 1 - full : 1 (100.00%) + normal : 3 (100.00%) + revisions : 3 + full : 3 (100.00%) deltas : 0 ( 0.00%) - revision size : 44 - full : 44 (100.00%) + revision size : 88 + full : 88 (100.00%) deltas : 0 ( 0.00%) - chunks : 1 - 0x75 (u) : 1 (100.00%) - chunks size : 44 - 0x75 (u) : 44 (100.00%) + chunks : 3 + empty : 1 (33.33%) + 0x75 (u) : 2 (66.67%) + chunks size : 88 + empty : 0 ( 0.00%) + 0x75 (u) : 88 (100.00%) avg chain length : 0 max chain length : 0 max chain reach : 44 compression ratio : 0 - uncompressed data size (min/max/avg) : 43 / 43 / 43 - full revision size (min/max/avg) : 44 / 44 / 44 + uncompressed data size (min/max/avg) : 0 / 43 / 28 + full revision size (min/max/avg) : 0 / 44 / 29 delta size (min/max/avg) : 0 / 0 / 0 #endif @@ -73,9 +81,13 @@ $ hg debugdeltachain -m rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio 0 1 1 -1 base 44 43 44 1.02326 44 0 0.00000 + 1 2 1 -1 base 0 0 0 0.00000 0 0 0.00000 + 2 3 1 -1 base 44 43 44 1.02326 44 0 0.00000 $ hg debugdeltachain -m -T '{rev} {chainid} {chainlen}\n' 0 1 1 + 1 2 1 + 2 3 1 $ hg debugdeltachain -m -Tjson [ @@ -92,6 +104,34 @@ "prevrev": -1, "rev": 0, "uncompsize": 43 + }, + { + "chainid": 2, + "chainlen": 1, + "chainratio": 0, + "chainsize": 0, + "compsize": 0, + "deltatype": "base", + "extradist": 0, + "extraratio": 0, + "lindist": 0, + "prevrev": -1, + "rev": 1, + "uncompsize": 0 + }, + { + "chainid": 3, + "chainlen": 1, + "chainratio": 1.02325581395, + "chainsize": 44, + "compsize": 44, + "deltatype": "base", + "extradist": 0, + "extraratio": 0.0, + "lindist": 44, + "prevrev": -1, + "rev": 2, + "uncompsize": 43 } ] @@ -104,9 +144,13 @@ $ hg debugdeltachain -m rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio readsize largestblk rddensity srchunks 0 1 1 -1 base 44 43 44 1.02326 44 0 0.00000 44 44 1.00000 1 + 1 2 1 -1 base 0 0 0 0.00000 0 0 0.00000 0 0 1.00000 1 + 2 3 1 -1 base 44 43 44 1.02326 44 0 0.00000 44 44 1.00000 1 $ hg debugdeltachain -m -T '{rev} {chainid} {chainlen} {readsize} {largestblock} {readdensity}\n' 0 1 1 44 44 1.0 + 1 2 1 0 0 1 + 2 3 1 44 44 1.0 $ hg debugdeltachain -m -Tjson [ @@ -127,6 +171,42 @@ "rev": 0, "srchunks": 1, "uncompsize": 43 + }, + { + "chainid": 2, + "chainlen": 1, + "chainratio": 0, + "chainsize": 0, + "compsize": 0, + "deltatype": "base", + "extradist": 0, + "extraratio": 0, + "largestblock": 0, + "lindist": 0, + "prevrev": -1, + "readdensity": 1, + "readsize": 0, + "rev": 1, + "srchunks": 1, + "uncompsize": 0 + }, + { + "chainid": 3, + "chainlen": 1, + "chainratio": 1.02325581395, + "chainsize": 44, + "compsize": 44, + "deltatype": "base", + "extradist": 0, + "extraratio": 0.0, + "largestblock": 44, + "lindist": 44, + "prevrev": -1, + "readdensity": 1.0, + "readsize": 44, + "rev": 2, + "srchunks": 1, + "uncompsize": 43 } ] @@ -337,7 +417,7 @@ #if no-windows $ hg debugcolor --style --color always | egrep 'mode|style|log\.' - color mode: ansi + color mode: 'ansi' available style: \x1b[0;33mlog.changeset\x1b[0m: \x1b[0;33myellow\x1b[0m (esc) #endif
--- a/tests/test-demandimport.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-demandimport.py Thu Jul 19 13:55:54 2018 -0400 @@ -58,12 +58,12 @@ print("os.system =", f(os.system)) print("os =", f(os)) -from mercurial import util +from mercurial.utils import procutil -print("util =", f(util)) -print("util.system =", f(util.system)) -print("util =", f(util)) -print("util.system =", f(util.system)) +print("procutil =", f(procutil)) +print("procutil.system =", f(procutil.system)) +print("procutil =", f(procutil)) +print("procutil.system =", f(procutil.system)) from mercurial import hgweb print("hgweb =", f(hgweb)) @@ -100,6 +100,8 @@ print('contextlib.unknownattr = ImportError: %s' % rsub(r"'", '', str(inst))) +from mercurial import util + # Unlike the import statement, __import__() function should not raise # ImportError even if fromlist has an unknown item # (see Python/import.c:import_module_level() and ensure_fromlist())
--- a/tests/test-demandimport.py.out Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-demandimport.py.out Thu Jul 19 13:55:54 2018 -0400 @@ -7,10 +7,10 @@ os = <unloaded module 'os'> os.system = <built-in function system> os = <module 'os' from '?'> -util = <unloaded module 'util'> -util.system = <function system at 0x?> -util = <module 'mercurial.util' from '?'> -util.system = <function system at 0x?> +procutil = <unloaded module 'procutil'> +procutil.system = <function system at 0x?> +procutil = <module 'mercurial.utils.procutil' from '?'> +procutil.system = <function system at 0x?> hgweb = <unloaded module 'hgweb'> hgweb_mod = <unloaded module 'hgweb_mod'> hgweb = <module 'mercurial.hgweb' from '?'>
--- a/tests/test-diff-antipatience.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-diff-antipatience.t Thu Jul 19 13:55:54 2018 -0400 @@ -11,9 +11,9 @@ Test case that makes use of the weakness of patience diff algorithm $ hg init - >>> open('a', 'wb').write(b'\n'.join(list(b'a' + b'x' * 10 + b'u' + b'x' * 30 + b'a\n'))) + >>> open('a', 'wb').write(('\n'.join(list('a' + 'x' * 10 + 'u' + 'x' * 30 + 'a\n'))).encode('ascii')) and None $ hg commit -m 1 -A a - >>> open('a', 'wb').write(b'\n'.join(list(b'b' + b'x' * 30 + b'u' + b'x' * 10 + b'b\n'))) + >>> open('a', 'wb').write(('\n'.join(list('b' + 'x' * 30 + 'u' + 'x' * 10 + 'b\n'))).encode('ascii')) and None #if xdiff $ hg diff diff -r f0aeecb49805 a
--- a/tests/test-diff-color.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-diff-color.t Thu Jul 19 13:55:54 2018 -0400 @@ -51,6 +51,27 @@ a c +trailing whitespace + + $ cp a a.orig + $ sed 's/^dd$/dd \r/' a >a.new + $ mv a.new a + $ hg diff --nodates + \x1b[0;1mdiff -r cf9f4ba66af2 a\x1b[0m (esc) + \x1b[0;31;1m--- a/a\x1b[0m (esc) + \x1b[0;32;1m+++ b/a\x1b[0m (esc) + \x1b[0;35m@@ -2,7 +2,7 @@\x1b[0m (esc) + c + a + a + \x1b[0;31m-b\x1b[0m (esc) + \x1b[0;32m+dd\x1b[0m\x1b[0;1;41m \x1b[0m\r (esc) + a + a + c + + $ mv a.orig a + (check that 'ui.color=yes' match '--color=auto') $ hg diff --nodates --config ui.formatted=no @@ -304,7 +325,7 @@ > three of those lines have > collapsed onto one > EOF - $ hg diff --config experimental.worddiff=False --color=debug + $ hg diff --config diff.word-diff=False --color=debug [diff.diffline|diff --git a/file1 b/file1] [diff.file_a|--- a/file1] [diff.file_b|+++ b/file1] @@ -337,7 +358,7 @@ [diff.deleted|-(to see if it works)] [diff.inserted|+three of those lines have] [diff.inserted|+collapsed onto one] - $ hg diff --config experimental.worddiff=True --color=debug + $ hg diff --config diff.word-diff=True --color=debug [diff.diffline|diff --git a/file1 b/file1] [diff.file_a|--- a/file1] [diff.file_b|+++ b/file1] @@ -384,7 +405,7 @@ > EOF $ hg ci -m 'slightly change utf8 char' utf8 - $ hg diff --config experimental.worddiff=True --color=debug -c. + $ hg diff --config diff.word-diff=True --color=debug -c. [diff.diffline|diff --git a/utf8 b/utf8] [diff.file_a|--- a/utf8] [diff.file_b|+++ b/utf8]
--- a/tests/test-directaccess.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-directaccess.t Thu Jul 19 13:55:54 2018 -0400 @@ -156,9 +156,9 @@ `hg update` $ hg up 28ad74 - updating to a hidden changeset 28ad74487de9 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 28ad74487de9 (hidden revision '28ad74487de9' was rewritten as: 2443a0e66469) - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg up 3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-dispatch.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-dispatch.py Thu Jul 19 13:55:54 2018 -0400 @@ -1,18 +1,24 @@ from __future__ import absolute_import, print_function import os +import sys from mercurial import ( dispatch, ) +def printb(data, end=b'\n'): + out = getattr(sys.stdout, 'buffer', sys.stdout) + out.write(data + end) + out.flush() + def testdispatch(cmd): """Simple wrapper around dispatch.dispatch() Prints command and result value, but does not handle quoting. """ - print(b"running: %s" % (cmd,)) + printb(b"running: %s" % (cmd,)) req = dispatch.request(cmd.split()) result = dispatch.dispatch(req) - print(b"result: %r" % (result,)) + printb(b"result: %r" % (result,)) testdispatch(b"init test1") os.chdir('test1')
--- a/tests/test-dispatch.py.out Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-dispatch.py.out Thu Jul 19 13:55:54 2018 -0400 @@ -1,18 +1,18 @@ running: init test1 -result: None +result: 0 running: add foo result: 0 running: commit -m commit1 -d 2000-01-01 foo -result: None +result: 0 running: commit -m commit2 -d 2000-01-02 foo -result: None +result: 0 running: log -r 0 changeset: 0:0e4634943879 user: test date: Sat Jan 01 00:00:00 2000 +0000 summary: commit1 -result: None +result: 0 running: log -r tip changeset: 1:45589e459b2e tag: tip @@ -20,4 +20,4 @@ date: Sun Jan 02 00:00:00 2000 +0000 summary: commit2 -result: None +result: 0
--- a/tests/test-doctest.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-doctest.py Thu Jul 19 13:55:54 2018 -0400 @@ -60,6 +60,7 @@ testmod('mercurial.pathutil') testmod('mercurial.parser') testmod('mercurial.pycompat') +testmod('mercurial.revlog') testmod('mercurial.revsetlang') testmod('mercurial.smartset') testmod('mercurial.store')
--- a/tests/test-duplicateoptions.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-duplicateoptions.py Thu Jul 19 13:55:54 2018 -0400 @@ -6,7 +6,7 @@ ui as uimod, ) -ignore = {b'highlight', b'win32text', b'factotum'} +ignore = {b'highlight', b'win32text', b'factotum', b'beautifygraph'} if os.name != 'nt': ignore.add(b'win32mbcs')
--- a/tests/test-encoding-func.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-encoding-func.py Thu Jul 19 13:55:54 2018 -0400 @@ -35,11 +35,46 @@ self.assertTrue(s is encoding.fromlocal(s)) class Utf8bEncodingTest(unittest.TestCase): + def setUp(self): + self.origencoding = encoding.encoding + + def tearDown(self): + encoding.encoding = self.origencoding + def testasciifastpath(self): s = b'\0' * 100 self.assertTrue(s is encoding.toutf8b(s)) self.assertTrue(s is encoding.fromutf8b(s)) + def testlossylatin(self): + encoding.encoding = b'ascii' + s = u'\xc0'.encode('utf-8') + l = encoding.tolocal(s) + self.assertEqual(l, b'?') # lossy + self.assertEqual(s, encoding.toutf8b(l)) # utf8 sequence preserved + + def testlosslesslatin(self): + encoding.encoding = b'latin-1' + s = u'\xc0'.encode('utf-8') + l = encoding.tolocal(s) + self.assertEqual(l, b'\xc0') # lossless + self.assertEqual(s, encoding.toutf8b(l)) # convert back to utf-8 + + def testlossy0xed(self): + encoding.encoding = b'euc-kr' # U+Dxxx Hangul + s = u'\ud1bc\xc0'.encode('utf-8') + l = encoding.tolocal(s) + self.assertIn(b'\xed', l) + self.assertTrue(l.endswith(b'?')) # lossy + self.assertEqual(s, encoding.toutf8b(l)) # utf8 sequence preserved + + def testlossless0xed(self): + encoding.encoding = b'euc-kr' # U+Dxxx Hangul + s = u'\ud1bc'.encode('utf-8') + l = encoding.tolocal(s) + self.assertEqual(l, b'\xc5\xed') # lossless + self.assertEqual(s, encoding.toutf8b(l)) # convert back to utf-8 + if __name__ == '__main__': import silenttestrunner silenttestrunner.main(__name__)
--- a/tests/test-eol.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-eol.t Thu Jul 19 13:55:54 2018 -0400 @@ -7,7 +7,7 @@ Set up helpers - $ cat > switch-eol.py <<EOF + $ cat > switch-eol.py <<'EOF' > from __future__ import absolute_import > import os > import sys @@ -17,8 +17,10 @@ > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) > except ImportError: > pass + > eolmap = {b'\n': '\\n', b'\r\n': '\\r\\n'} > (old, new) = sys.argv[1] == 'LF' and (b'\n', b'\r\n') or (b'\r\n', b'\n') - > print("%% switching encoding from %r to %r" % (old, new)) + > print("%% switching encoding from '%s' to '%s'" + > % (eolmap[old], eolmap[new])) > for path in sys.argv[2:]: > data = open(path, 'rb').read() > data = data.replace(old, new)
--- a/tests/test-eolfilename.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-eolfilename.t Thu Jul 19 13:55:54 2018 -0400 @@ -32,8 +32,9 @@ abort: '\n' and '\r' disallowed in filenames: 'hell\no' [255] $ echo foo > "$A" - $ hg debugwalk - matcher: <alwaysmatcher> + $ hg debugwalk -v + * matcher: + <alwaysmatcher> f he\r (no-eol) (esc) llo he\r (no-eol) (esc) llo
--- a/tests/test-export.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-export.t Thu Jul 19 13:55:54 2018 -0400 @@ -101,6 +101,44 @@ $ grep HG foo-foo_3.patch | wc -l \s*1 (re) +Using bookmarks: + + $ hg book -f -r 9 @ + $ hg book -f -r 11 test + $ hg export -B test + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 5f17a83f5fbd9414006a5e563eab4c8a00729efd + # Parent 747d3c68f8ec44bb35816bfcd59aeb50b9654c2f + foo-10 + + diff -r 747d3c68f8ec -r 5f17a83f5fbd foo + --- a/foo Thu Jan 01 00:00:00 1970 +0000 + +++ b/foo Thu Jan 01 00:00:00 1970 +0000 + @@ -8,3 +8,4 @@ + foo-7 + foo-8 + foo-9 + +foo-10 + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID f3acbafac161ec68f1598af38f794f28847ca5d3 + # Parent 5f17a83f5fbd9414006a5e563eab4c8a00729efd + foo-11 + + diff -r 5f17a83f5fbd -r f3acbafac161 foo + --- a/foo Thu Jan 01 00:00:00 1970 +0000 + +++ b/foo Thu Jan 01 00:00:00 1970 +0000 + @@ -9,3 +9,4 @@ + foo-8 + foo-9 + foo-10 + +foo-11 + Exporting 4 changesets to a file: $ hg export -o export_internal 1 2 3 4
--- a/tests/test-extdata.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-extdata.t Thu Jul 19 13:55:54 2018 -0400 @@ -82,6 +82,13 @@ $ hg log -T "{extdata('unknown')}\n" abort: unknown extdata source 'unknown' [255] + $ hg log -T "{extdata(unknown)}\n" + hg: parse error: empty data source specified + (did you mean extdata('unknown')?) + [255] + $ hg log -T "{extdata('{unknown}')}\n" + hg: parse error: empty data source specified + [255] we don't fix up relative file URLs, but we do run shell commands in repo root
--- a/tests/test-extension.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-extension.t Thu Jul 19 13:55:54 2018 -0400 @@ -9,7 +9,9 @@ > configitem = registrar.configitem(configtable) > configitem(b'tests', b'foo', default=b"Foo") > def uisetup(ui): + > ui.debug(b"uisetup called [debug]\\n") > ui.write(b"uisetup called\\n") + > ui.status(b"uisetup called [status]\\n") > ui.flush() > def reposetup(ui, repo): > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root)) @@ -40,15 +42,29 @@ $ echo "foobar = $abspath" >> $HGRCPATH $ hg foo uisetup called + uisetup called [status] reposetup called for a ui == repo.ui reposetup called for a (chg !) ui == repo.ui (chg !) Foo + $ hg foo --quiet + uisetup called (no-chg !) + reposetup called for a (chg !) + ui == repo.ui + Foo + $ hg foo --debug + uisetup called [debug] (no-chg !) + uisetup called (no-chg !) + uisetup called [status] (no-chg !) + reposetup called for a (chg !) + ui == repo.ui + Foo $ cd .. $ hg clone a b uisetup called (no-chg !) + uisetup called [status] (no-chg !) reposetup called for a ui == repo.ui reposetup called for b @@ -58,6 +74,7 @@ $ hg bar uisetup called (no-chg !) + uisetup called [status] (no-chg !) Bar $ echo 'foobar = !' >> $HGRCPATH @@ -67,6 +84,7 @@ $ cd a $ hg foo uisetup called + uisetup called [status] reposetup called for a ui == repo.ui reposetup called for a (chg !) @@ -460,7 +478,7 @@ > result.append(absdetail) > result.append(legacydetail) > result.append(proxied.detail) - > ui.write('LIB: %s\n' % '\nLIB: '.join(result)) + > ui.write(b'LIB: %s\n' % '\nLIB: '.join(result)) > EOF Examine module importing. @@ -1229,9 +1247,14 @@ $ cat > hgext/forest.py <<EOF > cmdtable = None + > @command() + > def f(): + > pass + > @command(123) + > def g(): + > pass > EOF $ hg --config extensions.path=./path.py help foo > /dev/null - warning: error finding commands in $TESTTMP/hgext/forest.py abort: no such help topic: foo (try 'hg help --keyword foo') [255] @@ -1283,7 +1306,7 @@ If the extensions declare outdated versions, accuse the older extension first: $ echo "from mercurial import util" >> older.py - $ echo "util.version = lambda:'2.2'" >> older.py + $ echo "util.version = lambda:b'2.2'" >> older.py $ echo "testedwith = b'1.9.3'" >> older.py $ echo "testedwith = b'2.1.1'" >> throw.py $ rm -f throw.pyc throw.pyo @@ -1388,7 +1411,7 @@ Enabled extensions: throw external 1.2.3 - $ echo 'getversion = lambda: "1.twentythree"' >> throw.py + $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py $ rm -f throw.pyc throw.pyo $ rm -Rf __pycache__ $ hg version -v --config extensions.throw=throw.py --config extensions.strip= @@ -1517,6 +1540,14 @@ reposetup() for $TESTTMP/reposetup-test/src reposetup() for $TESTTMP/reposetup-test/src (chg !) + $ hg --cwd src debugextensions + reposetup() for $TESTTMP/reposetup-test/src + dodo (untested!) + dudu (untested!) + mq + reposetuptest (untested!) + strip + $ hg clone -U src clone-dst1 reposetup() for $TESTTMP/reposetup-test/src $ hg init push-dst1 @@ -1670,7 +1701,7 @@ > def exbookmarks(orig, *args, **opts): > return orig(*args, **opts) > def uisetup(ui): - > synopsis = ' GREPME [--foo] [-x]' + > synopsis = b' GREPME [--foo] [-x]' > docstring = ''' > GREPME make sure that this is in the help! > ''' @@ -1697,10 +1728,6 @@ > pass > EOF - $ hg --config extensions.nonregistrar=`pwd`/nonregistrar.py version > /dev/null - devel-warn: cmdutil.command is deprecated, use registrar.command to register 'foo' - (compatibility will be dropped after Mercurial-4.6, update your code.) * (glob) - Prohibit the use of unicode strings as the default value of options $ hg init $TESTTMP/opt-unicode-default @@ -1709,9 +1736,9 @@ > from mercurial import registrar > cmdtable = {} > command = registrar.command(cmdtable) - > @command(b'dummy', [('', 'opt', u'value', u'help')], 'ext [OPTIONS]') + > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]') > def ext(*args, **opts): - > print(opts['opt']) + > print(opts[b'opt']) > EOF $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF > [extensions]
--- a/tests/test-extensions-afterloaded.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-extensions-afterloaded.t Thu Jul 19 13:55:54 2018 -0400 @@ -3,16 +3,16 @@ $ cat > foo.py <<EOF > from mercurial import extensions > def uisetup(ui): - > ui.write("foo.uisetup\\n") + > ui.write(b"foo.uisetup\\n") > ui.flush() > def bar_loaded(loaded): - > ui.write("foo: bar loaded: %r\\n" % (loaded,)) + > ui.write(b"foo: bar loaded: %r\\n" % (loaded,)) > ui.flush() - > extensions.afterloaded('bar', bar_loaded) + > extensions.afterloaded(b'bar', bar_loaded) > EOF $ cat > bar.py <<EOF > def uisetup(ui): - > ui.write("bar.uisetup\\n") + > ui.write(b"bar.uisetup\\n") > ui.flush() > EOF $ basepath=`pwd` @@ -72,9 +72,9 @@ $ cd .. $ cat > minvers.py <<EOF - > minimumhgversion = '9999.9999' + > minimumhgversion = b'9999.9999' > def uisetup(ui): - > ui.write("minvers.uisetup\\n") + > ui.write(b"minvers.uisetup\\n") > ui.flush() > EOF $ hg init minversion
--- a/tests/test-extensions-wrapfunction.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-extensions-wrapfunction.py Thu Jul 19 13:55:54 2018 -0400 @@ -35,7 +35,7 @@ print('unwrap %s: %s: %s' % (getid(w), getid(result), msg)) batchwrap(wrappers + [wrappers[0]]) -batchunwrap([(wrappers[i] if i >= 0 else None) +batchunwrap([(wrappers[i] if i is not None and i >= 0 else None) for i in [3, None, 0, 4, 0, 2, 1, None]]) wrap0 = extensions.wrappedfunction(dummy, 'getstack', wrappers[0])
--- a/tests/test-filecache.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-filecache.py Thu Jul 19 13:55:54 2018 -0400 @@ -8,6 +8,16 @@ 'cacheable']): sys.exit(80) +print_ = print +def print(*args, **kwargs): + """print() wrapper that flushes stdout buffers to avoid py3 buffer issues + + We could also just write directly to sys.stdout.buffer the way the + ui object will, but this was easier for porting the test. + """ + print_(*args, **kwargs) + sys.stdout.flush() + from mercurial import ( extensions, hg, @@ -46,7 +56,7 @@ def invalidate(self): for k in self._filecache: try: - delattr(self, k) + delattr(self, pycompat.sysstr(k)) except AttributeError: pass @@ -84,8 +94,8 @@ # atomic replace file, size doesn't change # hopefully st_mtime doesn't change as well so this doesn't use the cache # because of inode change - f = vfsmod.vfs('.')('x', 'w', atomictemp=True) - f.write('b') + f = vfsmod.vfs(b'.')(b'x', b'w', atomictemp=True) + f.write(b'b') f.close() repo.invalidate() @@ -108,19 +118,19 @@ # should recreate the object repo.cached - f = vfsmod.vfs('.')('y', 'w', atomictemp=True) - f.write('B') + f = vfsmod.vfs(b'.')(b'y', b'w', atomictemp=True) + f.write(b'B') f.close() repo.invalidate() print("* file y changed inode") repo.cached - f = vfsmod.vfs('.')('x', 'w', atomictemp=True) - f.write('c') + f = vfsmod.vfs(b'.')(b'x', b'w', atomictemp=True) + f.write(b'c') f.close() - f = vfsmod.vfs('.')('y', 'w', atomictemp=True) - f.write('C') + f = vfsmod.vfs(b'.')(b'y', b'w', atomictemp=True) + f.write(b'C') f.close() repo.invalidate() @@ -155,14 +165,14 @@ repo = hg.repository(uimod.ui.load()) # first rollback clears the filecache, but changelog to stays in __dict__ repo.rollback() - repo.commit('.') + repo.commit(b'.') # second rollback comes along and touches the changelog externally # (file is moved) repo.rollback() # but since changelog isn't under the filecache control anymore, we don't # see that it changed, and return the old changelog without reconstructing # it - repo.commit('.') + repo.commit(b'.') def setbeforeget(repo): os.remove('x')
--- a/tests/test-filelog.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-filelog.py Thu Jul 19 13:55:54 2018 -0400 @@ -14,21 +14,21 @@ ) myui = uimod.ui.load() -repo = hg.repository(myui, path='.', create=True) +repo = hg.repository(myui, path=b'.', create=True) -fl = repo.file('foobar') +fl = repo.file(b'foobar') def addrev(text, renamed=False): if renamed: # data doesn't matter. Just make sure filelog.renamed() returns True - meta = {'copyrev': hex(nullid), 'copy': 'bar'} + meta = {b'copyrev': hex(nullid), b'copy': b'bar'} else: meta = {} lock = t = None try: lock = repo.lock() - t = repo.transaction('commit') + t = repo.transaction(b'commit') node = fl.add(text, meta, t, 0, nullid, nullid) return node finally: @@ -40,8 +40,8 @@ def error(text): print('ERROR: ' + text) -textwith = '\1\nfoo' -without = 'foo' +textwith = b'\1\nfoo' +without = b'foo' node = addrev(textwith) if not textwith == fl.read(node):
--- a/tests/test-fileset.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-fileset.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,5 @@ $ fileset() { - > hg debugfileset "$@" + > hg debugfileset --all-files "$@" > } $ hg init repo @@ -68,7 +68,7 @@ a2 $ fileset 'a_b' $ fileset '"\xy"' - hg: parse error: invalid \x escape + hg: parse error: invalid \x escape* (glob) [255] Test invalid syntax @@ -142,8 +142,10 @@ .hgignore c2 $ fileset 'hgignore()' + .hgignore a2 b2 + c2 $ fileset 'clean()' b1 $ fileset 'copied()' @@ -169,8 +171,8 @@ R a2 ? c3 $ fileset -r0 'added() and revs("wdir()", modified() or removed() or unknown())' + a2 b2 - a2 $ fileset -r0 'added() or revs("wdir()", added())' a1 a2 @@ -180,8 +182,9 @@ Test files properties - >>> open('bin', 'wb').write(b'\0a') + >>> open('bin', 'wb').write(b'\0a') and None $ fileset 'binary()' + bin $ fileset 'binary() and unknown()' bin $ echo '^bin$' >> .hgignore @@ -192,11 +195,12 @@ bin $ fileset 'grep("b{1}")' + .hgignore + b1 b2 c1 - b1 $ fileset 'grep("missingparens(")' - hg: parse error: invalid match pattern: unbalanced parenthesis + hg: parse error: invalid match pattern: (unbalanced parenthesis|missing \)).* (re) [255] #if execbit @@ -219,8 +223,8 @@ $ hg --config ui.portablefilenames=ignore add con.xml #endif - >>> open('1k', 'wb').write(b' '*1024) - >>> open('2k', 'wb').write(b' '*2048) + >>> open('1k', 'wb').write(b' '*1024) and None + >>> open('2k', 'wb').write(b' '*2048) and None $ hg add 1k 2k $ fileset 'size("bar")' hg: parse error: couldn't parse size: bar @@ -354,8 +358,12 @@ $ fileset -r1 'unknown()' $ fileset -r1 'ignored()' $ fileset -r1 'hgignore()' + .hgignore + a2 b2 bin + c2 + sub2 $ fileset -r1 'binary()' bin $ fileset -r1 'size(1k)' @@ -391,9 +399,9 @@ b2 c1 - >>> open('dos', 'wb').write("dos\r\n") - >>> open('mixed', 'wb').write("dos\r\nunix\n") - >>> open('mac', 'wb').write("mac\r") + >>> open('dos', 'wb').write(b"dos\r\n") and None + >>> open('mixed', 'wb').write(b"dos\r\nunix\n") and None + >>> open('mac', 'wb').write(b"mac\r") and None $ hg add dos mixed mac (remove a1, to examine safety of 'eol' on removed files) @@ -403,50 +411,42 @@ dos mixed $ fileset 'eol(unix)' - mixed + .hgignore .hgsub .hgsubstate b1 b2 + b2.orig c1 + c2 + c3 + con.xml + mixed + unknown $ fileset 'eol(mac)' mac Test safety of 'encoding' on removed files $ fileset 'encoding("ascii")' - dos - mac - mixed + .hgignore .hgsub .hgsubstate 1k 2k b1 b2 + b2.orig b2link (symlink !) bin c1 - -Test detection of unintentional 'matchctx.existing()' invocation - - $ cat > $TESTTMP/existingcaller.py <<EOF - > from mercurial import registrar - > - > filesetpredicate = registrar.filesetpredicate() - > @filesetpredicate('existingcaller()', callexisting=False) - > def existingcaller(mctx, x): - > # this 'mctx.existing()' invocation is unintentional - > return [f for f in mctx.existing()] - > EOF - - $ cat >> .hg/hgrc <<EOF - > [extensions] - > existingcaller = $TESTTMP/existingcaller.py - > EOF - - $ fileset 'existingcaller()' 2>&1 | tail -1 - AssertionError: unexpected existing() invocation + c2 + c3 + con.xml + dos + mac + mixed + unknown Test 'revs(...)' ================ @@ -524,7 +524,7 @@ Test files at -r0 should be filtered by files at wdir ----------------------------------------------------- - $ fileset -r0 '* and revs("wdir()", *)' + $ fileset -r0 'tracked() and revs("wdir()", tracked())' a1 b1 b2 @@ -561,12 +561,12 @@ --------------------------------------- $ fileset "revs('0+4', added())" + .hgsub + .hgsubstate a1 a2 b1 b2 - .hgsub - .hgsubstate overlapping set @@ -590,12 +590,12 @@ R a2 $ fileset "status(0, 1, removed())" a2 - $ fileset "* and status(0, 1, removed())" + $ fileset "tracked() and status(0, 1, removed())" $ fileset -r 4 "status(0, 1, removed())" a2 - $ fileset -r 4 "* and status(0, 1, removed())" - $ fileset "revs('4', * and status(0, 1, removed()))" - $ fileset "revs('0', * and status(0, 1, removed()))" + $ fileset -r 4 "tracked() and status(0, 1, removed())" + $ fileset "revs('4', tracked() and status(0, 1, removed()))" + $ fileset "revs('0', tracked() and status(0, 1, removed()))" a2 check wdir()
--- a/tests/test-fix-topology.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-fix-topology.t Thu Jul 19 13:55:54 2018 -0400 @@ -322,7 +322,6 @@ $ hg fix --all - 1 new orphan changesets $ hg log --graph --template '{rev} {desc}\n' -r 'sort(all(), topo)' --hidden o 11 fifth
--- a/tests/test-fix.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-fix.t Thu Jul 19 13:55:54 2018 -0400 @@ -1027,3 +1027,29 @@ $ cd .. +The --base flag affects the set of files being fixed. So while the --whole flag +makes the base irrelevant for changed line ranges, it still changes the +meaning and effect of the command. In this example, no files or lines are fixed +until we specify the base, but then we do fix unchanged lines. + + $ hg init basewhole + $ cd basewhole + $ printf "foo1\n" > foo.changed + $ hg commit -Aqm "first" + $ printf "foo2\n" >> foo.changed + $ printf "bar\n" > bar.changed + $ hg commit -Aqm "second" + + $ hg fix --working-dir --whole + $ cat *.changed + bar + foo1 + foo2 + + $ hg fix --working-dir --base 0 --whole + $ cat *.changed + BAR + FOO1 + FOO2 + + $ cd ..
--- a/tests/test-flags.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-flags.t Thu Jul 19 13:55:54 2018 -0400 @@ -46,6 +46,7 @@ adding file changes added 1 changesets with 0 changes to 0 files (+1 heads) new changesets 7f4313b42a34 + 1 local changesets published (run 'hg heads' to see heads, 'hg merge' to merge) $ hg heads changeset: 2:7f4313b42a34
--- a/tests/test-fncache.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-fncache.t Thu Jul 19 13:55:54 2018 -0400 @@ -436,3 +436,73 @@ $ cat .hg/store/fncache | sort data/.bar.i data/foo.i + + $ cd .. + +In repositories that have accumulated a large number of files over time, the +fncache file is going to be large. If we possibly can avoid loading it, so much the better. +The cache should not loaded when committing changes to existing files, or when unbundling +changesets that only contain changes to existing files: + + $ cat > fncacheloadwarn.py << EOF + > from __future__ import absolute_import + > from mercurial import extensions, store + > + > def extsetup(ui): + > def wrapstore(orig, requirements, *args): + > store = orig(requirements, *args) + > if 'store' in requirements and 'fncache' in requirements: + > instrumentfncachestore(store, ui) + > return store + > extensions.wrapfunction(store, 'store', wrapstore) + > + > def instrumentfncachestore(fncachestore, ui): + > class instrumentedfncache(type(fncachestore.fncache)): + > def _load(self): + > ui.warn('fncache load triggered!\n') + > super(instrumentedfncache, self)._load() + > fncachestore.fncache.__class__ = instrumentedfncache + > EOF + + $ fncachextpath=`pwd`/fncacheloadwarn.py + $ hg init nofncacheload + $ cd nofncacheload + $ printf "[extensions]\nfncacheloadwarn=$fncachextpath\n" >> .hg/hgrc + +A new file should trigger a load, as we'd want to update the fncache set in that case: + + $ touch foo + $ hg ci -qAm foo + fncache load triggered! + +But modifying that file should not: + + $ echo bar >> foo + $ hg ci -qm foo + +If a transaction has been aborted, the zero-size truncated index file will +not prevent the fncache from being loaded; rather than actually abort +a transaction, we simulate the situation by creating a zero-size index file: + + $ touch .hg/store/data/bar.i + $ touch bar + $ hg ci -qAm bar + fncache load triggered! + +Unbundling should follow the same rules; existing files should not cause a load: + + $ hg clone -q . tobundle + $ echo 'new line' > tobundle/bar + $ hg -R tobundle ci -qm bar + $ hg -R tobundle bundle -q barupdated.hg + $ hg unbundle -q barupdated.hg + +but adding new files should: + + $ touch tobundle/newfile + $ hg -R tobundle ci -qAm newfile + $ hg -R tobundle bundle -q newfile.hg + $ hg unbundle -q newfile.hg + fncache load triggered! + + $ cd ..
--- a/tests/test-fuzz-targets.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-fuzz-targets.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,18 @@ -#require clang-libfuzzer test-repo +#require test-repo + $ cd $TESTDIR/../contrib/fuzz - $ make -Just run the fuzzer for five seconds to verify it works at all. + +#if clang-libfuzzer + $ make -s clean all +#endif +#if no-clang-libfuzzer clang-6.0 + $ make -s clean all CC=clang-6.0 CXX=clang++-6.0 +#endif +#if no-clang-libfuzzer no-clang-6.0 + $ exit 80 +#endif + +Just run the fuzzers for five seconds each to verify it works at all. $ ./bdiff -max_total_time 5 + $ ./mpatch -max_total_time 5 + $ ./xdiff -max_total_time 5
--- a/tests/test-generaldelta.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-generaldelta.t Thu Jul 19 13:55:54 2018 -0400 @@ -118,7 +118,7 @@ 2 1 2 0 p1 57 135 161 1.19259 218 57 0.35404 3 1 2 0 p1 57 135 161 1.19259 275 114 0.70807 -Test format.aggressivemergedeltas +Test revlog.optimize-delta-parent-choice $ hg init --config format.generaldelta=1 aggressive $ cd aggressive @@ -139,14 +139,14 @@ rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio 0 1 1 -1 base 59 215 59 0.27442 59 0 0.00000 1 1 2 0 prev 61 86 120 1.39535 120 0 0.00000 - 2 1 3 1 p1 65 301 185 0.61462 185 0 0.00000 + 2 1 2 0 p2 62 301 121 0.40199 182 61 0.50413 $ hg strip -q -r . --config extensions.strip= - Verify aggressive merge uses p2 (commit 0) as delta parent $ hg up -q -C 1 $ hg merge -q 0 - $ hg commit -q -m merge --config format.aggressivemergedeltas=True + $ hg commit -q -m merge --config revlog.optimize-delta-parent-choice=yes $ hg debugdeltachain -m rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio 0 1 1 -1 base 59 215 59 0.27442 59 0 0.00000 @@ -172,6 +172,44 @@ $ hg init source-repo $ cd source-repo $ hg debugbuilddag --new-file '.+5:brancha$.+11:branchb$.+30:branchc<brancha+2<branchb+2' +# add an empty revision somewhere + $ hg up tip + 14 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg rm . + removing nf10 + removing nf11 + removing nf12 + removing nf13 + removing nf14 + removing nf15 + removing nf16 + removing nf17 + removing nf51 + removing nf52 + removing nf6 + removing nf7 + removing nf8 + removing nf9 + $ hg commit -m 'empty all' + $ hg revert --all --rev 'p1(.)' + adding nf10 + adding nf11 + adding nf12 + adding nf13 + adding nf14 + adding nf15 + adding nf16 + adding nf17 + adding nf51 + adding nf52 + adding nf6 + adding nf7 + adding nf8 + adding nf9 + $ hg commit -m 'restore all' + $ hg up null + 0 files updated, 0 files merged, 14 files removed, 0 files unresolved + $ $ cd .. $ hg -R source-repo debugdeltachain -m rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio @@ -228,13 +266,15 @@ 50 4 2 49 p1 58 362 255 0.70442 255 0 0.00000 51 4 3 50 prev 356 594 611 1.02862 611 0 0.00000 52 4 4 51 p1 58 640 669 1.04531 669 0 0.00000 + 53 5 1 -1 base 0 0 0 0.00000 0 0 0.00000 + 54 5 2 53 p1 376 640 376 0.58750 376 0 0.00000 $ hg clone --pull source-repo --config experimental.maxdeltachainspan=2800 relax-chain --config format.generaldelta=yes requesting all changes adding changesets adding manifests adding file changes - added 53 changesets with 53 changes to 53 files (+2 heads) - new changesets 61246295ee1e:99cae3713489 + added 55 changesets with 53 changes to 53 files (+2 heads) + new changesets 61246295ee1e:c930ac4a5b32 updating to branch default 14 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R relax-chain debugdeltachain -m @@ -292,13 +332,15 @@ 50 4 2 49 p1 58 362 255 0.70442 255 0 0.00000 51 2 13 17 p1 58 594 739 1.24411 2781 2042 2.76319 52 5 1 -1 base 369 640 369 0.57656 369 0 0.00000 + 53 6 1 -1 base 0 0 0 0.00000 0 0 0.00000 + 54 6 2 53 p1 376 640 376 0.58750 376 0 0.00000 $ hg clone --pull source-repo --config experimental.maxdeltachainspan=0 noconst-chain --config format.generaldelta=yes requesting all changes adding changesets adding manifests adding file changes - added 53 changesets with 53 changes to 53 files (+2 heads) - new changesets 61246295ee1e:99cae3713489 + added 55 changesets with 53 changes to 53 files (+2 heads) + new changesets 61246295ee1e:c930ac4a5b32 updating to branch default 14 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R noconst-chain debugdeltachain -m @@ -356,3 +398,5 @@ 50 1 8 49 p1 58 362 447 1.23481 2915 2468 5.52125 51 2 13 17 p1 58 594 739 1.24411 2642 1903 2.57510 52 2 14 51 p1 58 640 797 1.24531 2700 1903 2.38770 + 53 4 1 -1 base 0 0 0 0.00000 0 0 0.00000 + 54 4 2 53 p1 376 640 376 0.58750 376 0 0.00000
--- a/tests/test-githelp.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-githelp.t Thu Jul 19 13:55:54 2018 -0400 @@ -43,8 +43,8 @@ githelp on a command with unrecognized option packed with other options should fail with error $ hg githelp -- commit -pv - abort: unknown option v packed with other options - Please try passing the option as it's own flag: -v + abort: unknown option 'v' packed with other options + (please try passing the option as its own flag: -v) [255] githelp for git rebase --skip @@ -165,11 +165,11 @@ hg update .~3 $ hg githelp -- reset --mixed HEAD - NOTE: --mixed has no meaning since Mercurial has no staging area + note: --mixed has no meaning since Mercurial has no staging area hg update . $ hg githelp -- reset --soft HEAD - NOTE: --soft has no meaning since Mercurial has no staging area + note: --soft has no meaning since Mercurial has no staging area hg update . $ hg githelp -- reset --hard HEAD @@ -221,7 +221,7 @@ githelp for whatchanged should show deprecated message $ hg githelp -- whatchanged -p - This command has been deprecated in the git project, thus isn't supported by this tool. + this command has been deprecated in the git project, thus isn't supported by this tool githelp for git branch -m renaming @@ -259,8 +259,8 @@ git merge-base $ hg githelp -- git merge-base --is-ancestor ignoring unknown option --is-ancestor - NOTE: ancestors() is part of the revset language. - Learn more about revsets with 'hg help revsets' + note: ancestors() is part of the revset language + (learn more about revsets with 'hg help revsets') hg log -T '{node}\n' -r 'ancestor(A,B)' @@ -279,7 +279,7 @@ hg commit --interactive $ hg githelp -- git add --all - note: use hg addremove to remove files that have been deleted. + note: use hg addremove to remove files that have been deleted hg add @@ -288,9 +288,9 @@ $ hg githelp -- git reflog hg journal - note: in hg commits can be deleted from repo but we always have backups. + note: in hg commits can be deleted from repo but we always have backups $ hg githelp -- git reflog --all hg journal --all - note: in hg commits can be deleted from repo but we always have backups. + note: in hg commits can be deleted from repo but we always have backups
--- a/tests/test-globalopts.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-globalopts.t Thu Jul 19 13:55:54 2018 -0400 @@ -353,6 +353,7 @@ color Colorizing Outputs config Configuration Files dates Date Formats + deprecated Deprecated Features diffs Diff Formats environment Environment Variables extensions Using Additional Features @@ -436,6 +437,7 @@ color Colorizing Outputs config Configuration Files dates Date Formats + deprecated Deprecated Features diffs Diff Formats environment Environment Variables extensions Using Additional Features
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-glog-beautifygraph.t Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,3158 @@ +@ (34) head +| +| o (33) head +| | +o | (32) expand +|\ \ +| o \ (31) expand +| |\ \ +| | o \ (30) expand +| | |\ \ +| | | o | (29) regular commit +| | | | | +| | o | | (28) merge zero known +| | |\ \ \ +o | | | | | (27) collapse +|/ / / / / +| | o---+ (26) merge one known; far right +| | | | | ++---o | | (25) merge one known; far left +| | | | | +| | o | | (24) merge one known; immediate right +| | |\| | +| | o | | (23) merge one known; immediate left +| |/| | | ++---o---+ (22) merge two known; one far left, one far right +| | / / +o | | | (21) expand +|\ \ \ \ +| o---+-+ (20) merge two known; two far right +| / / / +o | | | (19) expand +|\ \ \ \ ++---+---o (18) merge two known; two far left +| | | | +| o | | (17) expand +| |\ \ \ +| | o---+ (16) merge two known; one immediate right, one near right +| | |/ / +o | | | (15) expand +|\ \ \ \ +| o-----+ (14) merge two known; one immediate right, one far right +| |/ / / +o | | | (13) expand +|\ \ \ \ ++---o | | (12) merge two known; one immediate right, one far left +| | |/ / +| o | | (11) expand +| |\ \ \ +| | o---+ (10) merge two known; one immediate left, one near right +| |/ / / +o | | | (9) expand +|\ \ \ \ +| o-----+ (8) merge two known; one immediate left, one far right +|/ / / / +o | | | (7) expand +|\ \ \ \ ++---o | | (6) merge two known; one immediate left, one far left +| |/ / / +| o | | (5) expand +| |\ \ \ +| | o | | (4) merge two known; one immediate left, one immediate right +| |/|/ / +| o / / (3) collapse +|/ / / +o / / (2) collapse +|/ / +o / (1) collapse +|/ +o (0) root + + $ commit() + > { + > rev=$1 + > msg=$2 + > shift 2 + > if [ "$#" -gt 0 ]; then + > hg debugsetparents "$@" + > fi + > echo $rev > a + > hg commit -Aqd "$rev 0" -m "($rev) $msg" + > } + + $ cat > printrevset.py <<EOF + > from __future__ import absolute_import + > from mercurial import ( + > cmdutil, + > commands, + > extensions, + > logcmdutil, + > revsetlang, + > smartset, + > ) + > + > from mercurial.utils import ( + > stringutil, + > ) + > + > def logrevset(repo, pats, opts): + > revs = logcmdutil._initialrevs(repo, opts) + > if not revs: + > return None + > match, pats, slowpath = logcmdutil._makematcher(repo, revs, pats, opts) + > return logcmdutil._makerevset(repo, match, pats, slowpath, opts) + > + > def uisetup(ui): + > def printrevset(orig, repo, pats, opts): + > revs, filematcher = orig(repo, pats, opts) + > if opts.get(b'print_revset'): + > expr = logrevset(repo, pats, opts) + > if expr: + > tree = revsetlang.parse(expr) + > tree = revsetlang.analyze(tree) + > else: + > tree = [] + > ui = repo.ui + > ui.write(b'%r\n' % (opts.get(b'rev', []),)) + > ui.write(revsetlang.prettyformat(tree) + b'\n') + > ui.write(stringutil.prettyrepr(revs) + b'\n') + > revs = smartset.baseset() # display no revisions + > return revs, filematcher + > extensions.wrapfunction(logcmdutil, 'getrevs', printrevset) + > aliases, entry = cmdutil.findcmd(b'log', commands.table) + > entry[1].append((b'', b'print-revset', False, + > b'print generated revset and exit (DEPRECATED)')) + > EOF + + $ echo "[extensions]" >> $HGRCPATH + $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH + $ echo "beautifygraph=" >> $HGRCPATH + +Set a default of narrow-text UTF-8. + + $ HGENCODING=UTF-8; export HGENCODING + $ HGENCODINGAMBIGUOUS=narrow; export HGENCODINGAMBIGUOUS + +Empty repo: + + $ hg init repo + $ cd repo + $ hg log -G + +Building DAG: + + $ commit 0 "root" + $ commit 1 "collapse" 0 + $ commit 2 "collapse" 1 + $ commit 3 "collapse" 2 + $ commit 4 "merge two known; one immediate left, one immediate right" 1 3 + $ commit 5 "expand" 3 4 + $ commit 6 "merge two known; one immediate left, one far left" 2 5 + $ commit 7 "expand" 2 5 + $ commit 8 "merge two known; one immediate left, one far right" 0 7 + $ commit 9 "expand" 7 8 + $ commit 10 "merge two known; one immediate left, one near right" 0 6 + $ commit 11 "expand" 6 10 + $ commit 12 "merge two known; one immediate right, one far left" 1 9 + $ commit 13 "expand" 9 11 + $ commit 14 "merge two known; one immediate right, one far right" 0 12 + $ commit 15 "expand" 13 14 + $ commit 16 "merge two known; one immediate right, one near right" 0 1 + $ commit 17 "expand" 12 16 + $ commit 18 "merge two known; two far left" 1 15 + $ commit 19 "expand" 15 17 + $ commit 20 "merge two known; two far right" 0 18 + $ commit 21 "expand" 19 20 + $ commit 22 "merge two known; one far left, one far right" 18 21 + $ commit 23 "merge one known; immediate left" 1 22 + $ commit 24 "merge one known; immediate right" 0 23 + $ commit 25 "merge one known; far left" 21 24 + $ commit 26 "merge one known; far right" 18 25 + $ commit 27 "collapse" 21 + $ commit 28 "merge zero known" 1 26 + $ commit 29 "regular commit" 0 + $ commit 30 "expand" 28 29 + $ commit 31 "expand" 21 30 + $ commit 32 "expand" 27 31 + $ commit 33 "head" 18 + $ commit 34 "head" 32 + +The extension should not turn on unless we're in UTF-8. + + $ HGENCODING=latin1 hg log -G -q + beautifygraph: unsupported encoding, UTF-8 required + @ 34:fea3ac5810e0 + | + | o 33:68608f5145f9 + | | + o | 32:d06dffa21a31 + |\ \ + | o \ 31:621d83e11f67 + | |\ \ + | | o \ 30:6e11cd4b648f + | | |\ \ + | | | o | 29:cd9bb2be7593 + | | | | | + | | o | | 28:44ecd0b9ae99 + | | |\ \ \ + o | | | | | 27:886ed638191b + |/ / / / / + | | o---+ 26:7f25b6c2f0b9 + | | | | | + +---o | | 25:91da8ed57247 + | | | | | + | | o | | 24:a9c19a3d96b7 + | | |\| | + | | o | | 23:a01cddf0766d + | |/| | | + +---o---+ 22:e0d9cccacb5d + | | / / + o | | | 21:d42a756af44d + |\ \ \ \ + | o---+-+ 20:d30ed6450e32 + | / / / + o | | | 19:31ddc2c1573b + |\ \ \ \ + +---+---o 18:1aa84d96232a + | | | | + | o | | 17:44765d7c06e0 + | |\ \ \ + | | o---+ 16:3677d192927d + | | |/ / + o | | | 15:1dda3f72782d + |\ \ \ \ + | o-----+ 14:8eac370358ef + | |/ / / + o | | | 13:22d8966a97e3 + |\ \ \ \ + +---o | | 12:86b91144a6e9 + | | |/ / + | o | | 11:832d76e6bdf2 + | |\ \ \ + | | o---+ 10:74c64d036d72 + | |/ / / + o | | | 9:7010c0af0a35 + |\ \ \ \ + | o-----+ 8:7a0b11f71937 + |/ / / / + o | | | 7:b632bb1b1224 + |\ \ \ \ + +---o | | 6:b105a072e251 + | |/ / / + | o | | 5:4409d547b708 + | |\ \ \ + | | o | | 4:26a8bac39d9f + | |/|/ / + | o / / 3:27eef8ed80b4 + |/ / / + o / / 2:3d9a33b8d1e1 + |/ / + o / 1:6db2ef61d156 + |/ + o 0:e6eb3150255d + + +The extension should not turn on if we're using wide text. + + $ HGENCODINGAMBIGUOUS=wide hg log -G -q + beautifygraph: unsupported terminal settings, monospace narrow text required + @ 34:fea3ac5810e0 + | + | o 33:68608f5145f9 + | | + o | 32:d06dffa21a31 + |\ \ + | o \ 31:621d83e11f67 + | |\ \ + | | o \ 30:6e11cd4b648f + | | |\ \ + | | | o | 29:cd9bb2be7593 + | | | | | + | | o | | 28:44ecd0b9ae99 + | | |\ \ \ + o | | | | | 27:886ed638191b + |/ / / / / + | | o---+ 26:7f25b6c2f0b9 + | | | | | + +---o | | 25:91da8ed57247 + | | | | | + | | o | | 24:a9c19a3d96b7 + | | |\| | + | | o | | 23:a01cddf0766d + | |/| | | + +---o---+ 22:e0d9cccacb5d + | | / / + o | | | 21:d42a756af44d + |\ \ \ \ + | o---+-+ 20:d30ed6450e32 + | / / / + o | | | 19:31ddc2c1573b + |\ \ \ \ + +---+---o 18:1aa84d96232a + | | | | + | o | | 17:44765d7c06e0 + | |\ \ \ + | | o---+ 16:3677d192927d + | | |/ / + o | | | 15:1dda3f72782d + |\ \ \ \ + | o-----+ 14:8eac370358ef + | |/ / / + o | | | 13:22d8966a97e3 + |\ \ \ \ + +---o | | 12:86b91144a6e9 + | | |/ / + | o | | 11:832d76e6bdf2 + | |\ \ \ + | | o---+ 10:74c64d036d72 + | |/ / / + o | | | 9:7010c0af0a35 + |\ \ \ \ + | o-----+ 8:7a0b11f71937 + |/ / / / + o | | | 7:b632bb1b1224 + |\ \ \ \ + +---o | | 6:b105a072e251 + | |/ / / + | o | | 5:4409d547b708 + | |\ \ \ + | | o | | 4:26a8bac39d9f + | |/|/ / + | o / / 3:27eef8ed80b4 + |/ / / + o / / 2:3d9a33b8d1e1 + |/ / + o / 1:6db2ef61d156 + |/ + o 0:e6eb3150255d + + +The rest of our tests will use the default narrow text UTF-8. + + $ hg log -G -q + \xe2\x97\x8d 34:fea3ac5810e0 (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b 33:68608f5145f9 (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 32:d06dffa21a31 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb2 31:621d83e11f67 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb2 30:6e11cd4b648f (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 29:cd9bb2be7593 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 28:44ecd0b9ae99 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 27:886ed638191b (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 26:7f25b6c2f0b9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 25:91da8ed57247 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 24:a9c19a3d96b7 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 23:a01cddf0766d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 22:e0d9cccacb5d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 21:d42a756af44d (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 20:d30ed6450e32 (esc) + \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 19:31ddc2c1573b (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 17:44765d7c06e0 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 16:3677d192927d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 15:1dda3f72782d (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 14:8eac370358ef (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 13:22d8966a97e3 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 11:832d76e6bdf2 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 10:74c64d036d72 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 9:7010c0af0a35 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 8:7a0b11f71937 (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 7:b632bb1b1224 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 4:26a8bac39d9f (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb1 \xe2\x95\xb1 3:27eef8ed80b4 (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x95\xb1 \xe2\x95\xb1 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x95\xb1 1:6db2ef61d156 (esc) + \xe2\x94\x82\xe2\x95\xb1 (esc) + \xe2\x97\x8b 0:e6eb3150255d (esc) + + + $ hg log -G + \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 tag: tip (esc) + \xe2\x94\x82 parent: 32:d06dffa21a31 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc) + \xe2\x94\x82 summary: (34) head (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc) + \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc) + \xe2\x94\x82\xe2\x95\xb1 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc) + \xe2\x94\x82 summary: (1) collapse (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 0:e6eb3150255d (esc) + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: (0) root + +File glog: + $ hg log -G a + \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 tag: tip (esc) + \xe2\x94\x82 parent: 32:d06dffa21a31 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc) + \xe2\x94\x82 summary: (34) head (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc) + \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc) + \xe2\x94\x82\xe2\x95\xb1 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc) + \xe2\x94\x82 summary: (1) collapse (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 0:e6eb3150255d (esc) + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: (0) root + +File glog per revset: + + $ hg log -G -r 'file("a")' + \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 tag: tip (esc) + \xe2\x94\x82 parent: 32:d06dffa21a31 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc) + \xe2\x94\x82 summary: (34) head (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc) + \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc) + \xe2\x94\x82\xe2\x95\xb1 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc) + \xe2\x94\x82 summary: (1) collapse (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 0:e6eb3150255d (esc) + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: (0) root + + +File glog per revset (only merges): + + $ hg log -G -r 'file("a")' -m + \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc) + \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc) + \xe2\x94\x82 \xe2\x94\x86 parent: 31:621d83e11f67 (esc) + \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:32 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x86 summary: (32) expand (esc) + \xe2\x94\x82 \xe2\x94\x86 (esc) + \xe2\x97\x8b \xe2\x94\x86 changeset: 31:621d83e11f67 (esc) + \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x86 parent: 30:6e11cd4b648f (esc) + \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:31 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x86 summary: (31) expand (esc) + \xe2\x94\x82 \xe2\x94\x86 (esc) + \xe2\x97\x8b \xe2\x94\x86 changeset: 30:6e11cd4b648f (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc) + \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 29:cd9bb2be7593 (esc) + \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:30 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x86 summary: (30) expand (esc) + \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x86 changeset: 28:44ecd0b9ae99 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 26:7f25b6c2f0b9 (esc) + \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:28 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x86 summary: (28) merge zero known (esc) + \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x86 changeset: 26:7f25b6c2f0b9 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 25:91da8ed57247 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:26 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (26) merge one known; far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 25:91da8ed57247 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 24:a9c19a3d96b7 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:25 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (25) merge one known; far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 24:a9c19a3d96b7 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 23:a01cddf0766d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:24 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (24) merge one known; immediate right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 23:a01cddf0766d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 22:e0d9cccacb5d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:23 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (23) merge one known; immediate left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 22:e0d9cccacb5d (esc) + \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x86\xe2\x95\xb1 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x86 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:22 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x86 summary: (22) merge two known; one far left, one far right (esc) + \xe2\x94\x82 \xe2\x94\x86 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 19:31ddc2c1573b (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 20:d30ed6450e32 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 19:31ddc2c1573b (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 15:1dda3f72782d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 18:1aa84d96232a (esc) + \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc) + \xe2\x95\xa7 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc) + \xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 17:44765d7c06e0 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 16:3677d192927d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x95\xa7 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 15:1dda3f72782d (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 14:8eac370358ef (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc) + \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 9:7010c0af0a35 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 11:832d76e6bdf2 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 10:74c64d036d72 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 8:7a0b11f71937 (esc) + \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 7:b632bb1b1224 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc) + \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc) + \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 6:b105a072e251 (esc) + \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82 \xe2\x95\xa7 parent: 5:4409d547b708 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc) + \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 5:4409d547b708 (esc) + \xe2\x94\x82\xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc) + \xe2\x94\x82 \xe2\x95\xa7 parent: 4:26a8bac39d9f (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc) + \xe2\x94\x82 summary: (5) expand (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 4:26a8bac39d9f (esc) + \xe2\x94\x82\xe2\x95\xb2 parent: 1:6db2ef61d156 (esc) + \xe2\x95\xa7 \xe2\x95\xa7 parent: 3:27eef8ed80b4 (esc) + user: test + date: Thu Jan 01 00:00:04 1970 +0000 + summary: (4) merge two known; one immediate left, one immediate right + + +Empty revision range - display nothing: + $ hg log -G -r 1..0 + + $ cd .. + +#if no-outer-repo + +From outer space: + $ hg log -G -l1 repo + \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 tag: tip (esc) + \xe2\x95\xa7 parent: 32:d06dffa21a31 (esc) + user: test + date: Thu Jan 01 00:00:34 1970 +0000 + summary: (34) head + + $ hg log -G -l1 repo/a + \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 tag: tip (esc) + \xe2\x95\xa7 parent: 32:d06dffa21a31 (esc) + user: test + date: Thu Jan 01 00:00:34 1970 +0000 + summary: (34) head + + $ hg log -G -l1 repo/missing + +#endif + +File log with revs != cset revs: + $ hg init flog + $ cd flog + $ echo one >one + $ hg add one + $ hg commit -mone + $ echo two >two + $ hg add two + $ hg commit -mtwo + $ echo more >two + $ hg commit -mmore + $ hg log -G two + \xe2\x97\x8d changeset: 2:12c28321755b (esc) + \xe2\x94\x82 tag: tip (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + \xe2\x94\x82 summary: more (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 1:5ac72c0599bf (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + summary: two + + +Issue1896: File log with explicit style + $ hg log -G --style=default one + \xe2\x97\x8b changeset: 0:3d578b4a1f53 (esc) + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: one + +Issue2395: glog --style header and footer + $ hg log -G --style=xml one + <?xml version="1.0"?> + <log> + \xe2\x97\x8b <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1"> (esc) + <author email="test">test</author> + <date>1970-01-01T00:00:00+00:00</date> + <msg xml:space="preserve">one</msg> + </logentry> + </log> + + $ cd .. + +Incoming and outgoing: + + $ hg clone -U -r31 repo repo2 + adding changesets + adding manifests + adding file changes + added 31 changesets with 31 changes to 1 files + new changesets e6eb3150255d:621d83e11f67 + $ cd repo2 + + $ hg incoming --graph ../repo + comparing with ../repo + searching for changes + \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 tag: tip (esc) + \xe2\x94\x82 parent: 32:d06dffa21a31 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc) + \xe2\x94\x82 summary: (34) head (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc) + \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc) + \xe2\x94\x82 summary: (33) head (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc) + \xe2\x94\x82 parent: 27:886ed638191b (esc) + \xe2\x94\x82 parent: 31:621d83e11f67 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc) + \xe2\x94\x82 summary: (32) expand (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 27:886ed638191b (esc) + parent: 21:d42a756af44d + user: test + date: Thu Jan 01 00:00:27 1970 +0000 + summary: (27) collapse + + $ cd .. + + $ hg -R repo outgoing --graph repo2 + comparing with repo2 + searching for changes + \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 tag: tip (esc) + \xe2\x94\x82 parent: 32:d06dffa21a31 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc) + \xe2\x94\x82 summary: (34) head (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc) + \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc) + \xe2\x94\x82 summary: (33) head (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc) + \xe2\x94\x82 parent: 27:886ed638191b (esc) + \xe2\x94\x82 parent: 31:621d83e11f67 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc) + \xe2\x94\x82 summary: (32) expand (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 27:886ed638191b (esc) + parent: 21:d42a756af44d + user: test + date: Thu Jan 01 00:00:27 1970 +0000 + summary: (27) collapse + + +File + limit with revs != cset revs: + $ cd repo + $ touch b + $ hg ci -Aqm0 + $ hg log -G -l2 a + \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 parent: 32:d06dffa21a31 (esc) + \xe2\x95\xa7 user: test (esc) + date: Thu Jan 01 00:00:34 1970 +0000 + summary: (34) head + + \xe2\x97\x8b changeset: 33:68608f5145f9 (esc) + \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x95\xa7 user: test (esc) + date: Thu Jan 01 00:00:33 1970 +0000 + summary: (33) head + + +File + limit + -ra:b, (b - a) < limit: + $ hg log -G -l3000 -r32:tip a + \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 parent: 32:d06dffa21a31 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc) + \xe2\x94\x82 summary: (34) head (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc) + \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x95\xa7 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc) + \xe2\x94\x82 summary: (33) head (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc) + \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc) + \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc) + user: test + date: Thu Jan 01 00:00:32 1970 +0000 + summary: (32) expand + + +Point out a common and an uncommon unshown parent + + $ hg log -G -r 'rev(8) or rev(9)' + \xe2\x97\x8b changeset: 9:7010c0af0a35 (esc) + \xe2\x94\x82\xe2\x95\xb2 parent: 7:b632bb1b1224 (esc) + \xe2\x94\x82 \xe2\x95\xa7 parent: 8:7a0b11f71937 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc) + \xe2\x94\x82 summary: (9) expand (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 8:7a0b11f71937 (esc) + \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc) + \xe2\x95\xa7 \xe2\x95\xa7 parent: 7:b632bb1b1224 (esc) + user: test + date: Thu Jan 01 00:00:08 1970 +0000 + summary: (8) merge two known; one immediate left, one far right + + +File + limit + -ra:b, b < tip: + + $ hg log -G -l1 -r32:34 a + \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 parent: 32:d06dffa21a31 (esc) + \xe2\x95\xa7 user: test (esc) + date: Thu Jan 01 00:00:34 1970 +0000 + summary: (34) head + + +file(File) + limit + -ra:b, b < tip: + + $ hg log -G -l1 -r32:34 -r 'file("a")' + \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 parent: 32:d06dffa21a31 (esc) + \xe2\x95\xa7 user: test (esc) + date: Thu Jan 01 00:00:34 1970 +0000 + summary: (34) head + + +limit(file(File) and a::b), b < tip: + + $ hg log -G -r 'limit(file("a") and 32::34, 1)' + \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc) + \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc) + \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc) + user: test + date: Thu Jan 01 00:00:32 1970 +0000 + summary: (32) expand + + +File + limit + -ra:b, b < tip: + + $ hg log -G -r 'limit(file("a") and 34::32, 1)' + +File + limit + -ra:b, b < tip, (b - a) < limit: + + $ hg log -G -l10 -r33:34 a + \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 parent: 32:d06dffa21a31 (esc) + \xe2\x95\xa7 user: test (esc) + date: Thu Jan 01 00:00:34 1970 +0000 + summary: (34) head + + \xe2\x97\x8b changeset: 33:68608f5145f9 (esc) + \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x95\xa7 user: test (esc) + date: Thu Jan 01 00:00:33 1970 +0000 + summary: (33) head + + +Do not crash or produce strange graphs if history is buggy + + $ hg branch branch + marked working directory as branch branch + (branches are permanent and global, did you want a bookmark?) + $ commit 36 "buggy merge: identical parents" 35 35 + $ hg log -G -l5 + \xe2\x97\x8d changeset: 36:08a19a744424 (esc) + \xe2\x94\x82 branch: branch (esc) + \xe2\x94\x82 tag: tip (esc) + \xe2\x94\x82 parent: 35:9159c3644c5e (esc) + \xe2\x94\x82 parent: 35:9159c3644c5e (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:36 1970 +0000 (esc) + \xe2\x94\x82 summary: (36) buggy merge: identical parents (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 35:9159c3644c5e (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + \xe2\x94\x82 summary: 0 (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc) + \xe2\x94\x82 parent: 32:d06dffa21a31 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc) + \xe2\x94\x82 summary: (34) head (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc) + \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x95\xa7 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc) + \xe2\x94\x82 summary: (33) head (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc) + \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc) + \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc) + user: test + date: Thu Jan 01 00:00:32 1970 +0000 + summary: (32) expand + + +Test log -G options + + $ testlog() { + > hg log -G --print-revset "$@" + > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \ + > | sed 's/.*nodetag/nodetag/' > log.nodes + > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \ + > | sed 's/.*nodetag/nodetag/' > glog.nodes + > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \ + > | grep '^[-+@ ]' || : + > } + +glog always reorders nodes which explains the difference with log + + $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31 + ['27', '25', '21', '34', '32', '31'] + [] + <baseset- [21, 25, 27, 31, 32, 34]> + --- log.nodes * (glob) + +++ glog.nodes * (glob) + @@ -1,6 +1,6 @@ + -nodetag 27 + -nodetag 25 + -nodetag 21 + nodetag 34 + nodetag 32 + nodetag 31 + +nodetag 27 + +nodetag 25 + +nodetag 21 + $ testlog -u test -u not-a-user + [] + (or + (list + (func + (symbol 'user') + (string 'test')) + (func + (symbol 'user') + (string 'not-a-user')))) + <filteredset + <spanset- 0:37>, + <addset + <filteredset + <fullreposet+ 0:37>, + <user 'test'>>, + <filteredset + <fullreposet+ 0:37>, + <user 'not-a-user'>>>> + $ testlog -b not-a-branch + abort: unknown revision 'not-a-branch'! + abort: unknown revision 'not-a-branch'! + abort: unknown revision 'not-a-branch'! + $ testlog -b 35 -b 36 --only-branch branch + [] + (or + (list + (func + (symbol 'branch') + (string 'default')) + (or + (list + (func + (symbol 'branch') + (string 'branch')) + (func + (symbol 'branch') + (string 'branch')))))) + <filteredset + <spanset- 0:37>, + <addset + <filteredset + <fullreposet+ 0:37>, + <branch 'default'>>, + <addset + <filteredset + <fullreposet+ 0:37>, + <branch 'branch'>>, + <filteredset + <fullreposet+ 0:37>, + <branch 'branch'>>>>> + $ testlog -k expand -k merge + [] + (or + (list + (func + (symbol 'keyword') + (string 'expand')) + (func + (symbol 'keyword') + (string 'merge')))) + <filteredset + <spanset- 0:37>, + <addset + <filteredset + <fullreposet+ 0:37>, + <keyword 'expand'>>, + <filteredset + <fullreposet+ 0:37>, + <keyword 'merge'>>>> + $ testlog --only-merges + [] + (func + (symbol 'merge') + None) + <filteredset + <spanset- 0:37>, + <merge>> + $ testlog --no-merges + [] + (not + (func + (symbol 'merge') + None)) + <filteredset + <spanset- 0:37>, + <not + <filteredset + <spanset- 0:37>, + <merge>>>> + $ testlog --date '2 0 to 4 0' + [] + (func + (symbol 'date') + (string '2 0 to 4 0')) + <filteredset + <spanset- 0:37>, + <date '2 0 to 4 0'>> + $ hg log -G -d 'brace ) in a date' + hg: parse error: invalid date: 'brace ) in a date' + [255] + $ testlog --prune 31 --prune 32 + [] + (not + (or + (list + (func + (symbol 'ancestors') + (string '31')) + (func + (symbol 'ancestors') + (string '32'))))) + <filteredset + <spanset- 0:37>, + <not + <addset + <filteredset + <spanset- 0:37>, + <generatorsetdesc+>>, + <filteredset + <spanset- 0:37>, + <generatorsetdesc+>>>>> + +Dedicated repo for --follow and paths filtering. The g is crafted to +have 2 filelog topological heads in a linear changeset graph. + + $ cd .. + $ hg init follow + $ cd follow + $ testlog --follow + [] + [] + <baseset []> + $ testlog -rnull + ['null'] + [] + <baseset [-1]> + $ echo a > a + $ echo aa > aa + $ echo f > f + $ hg ci -Am "add a" a aa f + $ hg cp a b + $ hg cp f g + $ hg ci -m "copy a b" + $ mkdir dir + $ hg mv b dir + $ echo g >> g + $ echo f >> f + $ hg ci -m "mv b dir/b" + $ hg mv a b + $ hg cp -f f g + $ echo a > d + $ hg add d + $ hg ci -m "mv a b; add d" + $ hg mv dir/b e + $ hg ci -m "mv dir/b e" + $ hg log -G --template '({rev}) {desc|firstline}\n' + \xe2\x97\x8d (4) mv dir/b e (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b (3) mv a b; add d (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b (2) mv b dir/b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b (1) copy a b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b (0) add a (esc) + + + $ testlog a + [] + (func + (symbol 'filelog') + (string 'a')) + <filteredset + <spanset- 0:5>, set([0])> + $ testlog a b + [] + (or + (list + (func + (symbol 'filelog') + (string 'a')) + (func + (symbol 'filelog') + (string 'b')))) + <filteredset + <spanset- 0:5>, + <addset + <baseset+ [0]>, + <baseset+ [1]>>> + +Test falling back to slow path for non-existing files + + $ testlog a c + [] + (func + (symbol '_matchfiles') + (list + (string 'r:') + (string 'd:relpath') + (string 'p:a') + (string 'p:c'))) + <filteredset + <spanset- 0:5>, + <matchfiles patterns=['a', 'c'], include=[] exclude=[], default='relpath', rev=2147483647>> + +Test multiple --include/--exclude/paths + + $ testlog --include a --include e --exclude b --exclude e a e + [] + (func + (symbol '_matchfiles') + (list + (string 'r:') + (string 'd:relpath') + (string 'p:a') + (string 'p:e') + (string 'i:a') + (string 'i:e') + (string 'x:b') + (string 'x:e'))) + <filteredset + <spanset- 0:5>, + <matchfiles patterns=['a', 'e'], include=['a', 'e'] exclude=['b', 'e'], default='relpath', rev=2147483647>> + +Test glob expansion of pats + + $ expandglobs=`$PYTHON -c "import mercurial.util; \ + > print(mercurial.util.expandglobs and 'true' or 'false')"` + $ if [ $expandglobs = "true" ]; then + > testlog 'a*'; + > else + > testlog a*; + > fi; + [] + (func + (symbol 'filelog') + (string 'aa')) + <filteredset + <spanset- 0:5>, set([0])> + +Test --follow on a non-existent directory + + $ testlog -f dir + abort: cannot follow file not in parent revision: "dir" + abort: cannot follow file not in parent revision: "dir" + abort: cannot follow file not in parent revision: "dir" + +Test --follow on a directory + + $ hg up -q '.^' + $ testlog -f dir + [] + (func + (symbol '_matchfiles') + (list + (string 'r:') + (string 'd:relpath') + (string 'p:dir'))) + <filteredset + <generatorsetdesc->, + <matchfiles patterns=['dir'], include=[] exclude=[], default='relpath', rev=2147483647>> + $ hg up -q tip + +Test --follow on file not in parent revision + + $ testlog -f a + abort: cannot follow file not in parent revision: "a" + abort: cannot follow file not in parent revision: "a" + abort: cannot follow file not in parent revision: "a" + +Test --follow and patterns + + $ testlog -f 'glob:*' + [] + (func + (symbol '_matchfiles') + (list + (string 'r:') + (string 'd:relpath') + (string 'p:glob:*'))) + <filteredset + <generatorsetdesc->, + <matchfiles patterns=['glob:*'], include=[] exclude=[], default='relpath', rev=2147483647>> + +Test --follow on a single rename + + $ hg up -q 2 + $ testlog -f a + [] + [] + <generatorsetdesc-> + +Test --follow and multiple renames + + $ hg up -q tip + $ testlog -f e + [] + [] + <generatorsetdesc-> + +Test --follow and multiple filelog heads + + $ hg up -q 2 + $ testlog -f g + [] + [] + <generatorsetdesc-> + $ cat log.nodes + nodetag 2 + nodetag 1 + nodetag 0 + $ hg up -q tip + $ testlog -f g + [] + [] + <generatorsetdesc-> + $ cat log.nodes + nodetag 3 + nodetag 2 + nodetag 0 + +Test --follow and multiple files + + $ testlog -f g e + [] + [] + <generatorsetdesc-> + $ cat log.nodes + nodetag 4 + nodetag 3 + nodetag 2 + nodetag 1 + nodetag 0 + +Test --follow null parent + + $ hg up -q null + $ testlog -f + [] + [] + <baseset []> + +Test --follow-first + + $ hg up -q 3 + $ echo ee > e + $ hg ci -Am "add another e" e + created new head + $ hg merge --tool internal:other 4 + 0 files updated, 1 files merged, 1 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ echo merge > e + $ hg ci -m "merge 5 and 4" + $ testlog --follow-first + [] + [] + <generatorsetdesc-> + +Cannot compare with log --follow-first FILE as it never worked + + $ hg log -G --print-revset --follow-first e + [] + [] + <generatorsetdesc-> + $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n' + \xe2\x97\x8d 6 merge 5 and 4 (esc) + \xe2\x94\x82\xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x95\xa7 (esc) + \xe2\x97\x8b 5 add another e (esc) + \xe2\x94\x82 (esc) + \xe2\x95\xa7 (esc) + +Test --copies + + $ hg log -G --copies --template "{rev} {desc|firstline} \ + > copies: {file_copies_switch}\n" + \xe2\x97\x8d 6 merge 5 and 4 copies: (esc) + \xe2\x94\x82\xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x97\x8b 5 add another e copies: (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e copies: e (dir/b) (esc) + \xe2\x94\x82\xe2\x95\xb1 (esc) + \xe2\x97\x8b 3 mv a b; add d copies: b (a)g (f) (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 2 mv b dir/b copies: dir/b (b) (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 1 copy a b copies: b (a)g (f) (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 0 add a copies: (esc) + +Test "set:..." and parent revision + + $ hg up -q 4 + $ testlog "set:copied()" + [] + (func + (symbol '_matchfiles') + (list + (string 'r:') + (string 'd:relpath') + (string 'p:set:copied()'))) + <filteredset + <spanset- 0:7>, + <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='relpath', rev=2147483647>> + $ testlog --include "set:copied()" + [] + (func + (symbol '_matchfiles') + (list + (string 'r:') + (string 'd:relpath') + (string 'i:set:copied()'))) + <filteredset + <spanset- 0:7>, + <matchfiles patterns=[], include=['set:copied()'] exclude=[], default='relpath', rev=2147483647>> + $ testlog -r "sort(file('set:copied()'), -rev)" + ["sort(file('set:copied()'), -rev)"] + [] + <filteredset + <fullreposet- 0:7>, + <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='glob', rev=None>> + +Test --removed + + $ testlog --removed + [] + [] + <spanset- 0:7> + $ testlog --removed a + [] + (func + (symbol '_matchfiles') + (list + (string 'r:') + (string 'd:relpath') + (string 'p:a'))) + <filteredset + <spanset- 0:7>, + <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=2147483647>> + $ testlog --removed --follow a + [] + (func + (symbol '_matchfiles') + (list + (string 'r:') + (string 'd:relpath') + (string 'p:a'))) + <filteredset + <generatorsetdesc->, + <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=2147483647>> + +Test --patch and --stat with --follow and --follow-first + + $ hg up -q 3 + $ hg log -G --git --patch b + \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + summary: copy a b + + diff --git a/a b/b + copy from a + copy to b + + + $ hg log -G --git --stat b + \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + summary: copy a b + + b | 0 + 1 files changed, 0 insertions(+), 0 deletions(-) + + + $ hg log -G --git --patch --follow b + \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + \xe2\x94\x82 summary: copy a b (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 diff --git a/a b/b (esc) + \xe2\x94\x82 copy from a (esc) + \xe2\x94\x82 copy to b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 0:f8035bb17114 (esc) + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: add a + + diff --git a/a b/a + new file mode 100644 + --- /dev/null + +++ b/a + @@ -0,0 +1,1 @@ + +a + + + $ hg log -G --git --stat --follow b + \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + \xe2\x94\x82 summary: copy a b (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 b | 0 (esc) + \xe2\x94\x82 1 files changed, 0 insertions(+), 0 deletions(-) (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 0:f8035bb17114 (esc) + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: add a + + a | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + + + $ hg up -q 6 + $ hg log -G --git --patch --follow-first e + \xe2\x97\x8d changeset: 6:fc281d8ff18d (esc) + \xe2\x94\x82\xe2\x95\xb2 tag: tip (esc) + \xe2\x94\x82 \xe2\x95\xa7 parent: 5:99b31f1c2782 (esc) + \xe2\x94\x82 parent: 4:17d952250a9d (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + \xe2\x94\x82 summary: merge 5 and 4 (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 diff --git a/e b/e (esc) + \xe2\x94\x82 --- a/e (esc) + \xe2\x94\x82 +++ b/e (esc) + \xe2\x94\x82 @@ -1,1 +1,1 @@ (esc) + \xe2\x94\x82 -ee (esc) + \xe2\x94\x82 +merge (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 5:99b31f1c2782 (esc) + \xe2\x94\x82 parent: 3:5918b8d165d1 (esc) + \xe2\x95\xa7 user: test (esc) + date: Thu Jan 01 00:00:00 1970 +0000 + summary: add another e + + diff --git a/e b/e + new file mode 100644 + --- /dev/null + +++ b/e + @@ -0,0 +1,1 @@ + +ee + + +Test old-style --rev + + $ hg tag 'foo-bar' + $ testlog -r 'foo-bar' + ['foo-bar'] + [] + <baseset [6]> + +Test --follow and forward --rev + + $ hg up -q 6 + $ echo g > g + $ hg ci -Am 'add g' g + created new head + $ hg up -q 2 + $ hg log -G --template "{rev} {desc|firstline}\n" + \xe2\x97\x8b 8 add g (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b 7 Added tag foo-bar for changeset fc281d8ff18d (esc) + \xe2\x94\x82\xe2\x95\xb1 (esc) + \xe2\x97\x8b 6 merge 5 and 4 (esc) + \xe2\x94\x82\xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x97\x8b 5 add another e (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e (esc) + \xe2\x94\x82\xe2\x95\xb1 (esc) + \xe2\x97\x8b 3 mv a b; add d (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8d 2 mv b dir/b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 1 copy a b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 0 add a (esc) + + $ hg archive -r 7 archive + $ grep changessincelatesttag archive/.hg_archival.txt + changessincelatesttag: 1 + $ rm -r archive + +changessincelatesttag with no prior tag + $ hg archive -r 4 archive + $ grep changessincelatesttag archive/.hg_archival.txt + changessincelatesttag: 5 + + $ hg export 'all()' + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID f8035bb17114da16215af3436ec5222428ace8ee + # Parent 0000000000000000000000000000000000000000 + add a + + diff -r 000000000000 -r f8035bb17114 a + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +a + diff -r 000000000000 -r f8035bb17114 aa + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/aa Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +aa + diff -r 000000000000 -r f8035bb17114 f + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/f Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +f + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92 + # Parent f8035bb17114da16215af3436ec5222428ace8ee + copy a b + + diff -r f8035bb17114 -r 216d4c92cf98 b + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/b Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +a + diff -r f8035bb17114 -r 216d4c92cf98 g + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/g Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +f + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3 + # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92 + mv b dir/b + + diff -r 216d4c92cf98 -r bb573313a9e8 b + --- a/b Thu Jan 01 00:00:00 1970 +0000 + +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +0,0 @@ + -a + diff -r 216d4c92cf98 -r bb573313a9e8 dir/b + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +a + diff -r 216d4c92cf98 -r bb573313a9e8 f + --- a/f Thu Jan 01 00:00:00 1970 +0000 + +++ b/f Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,2 @@ + f + +f + diff -r 216d4c92cf98 -r bb573313a9e8 g + --- a/g Thu Jan 01 00:00:00 1970 +0000 + +++ b/g Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,2 @@ + f + +g + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed + # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3 + mv a b; add d + + diff -r bb573313a9e8 -r 5918b8d165d1 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +0,0 @@ + -a + diff -r bb573313a9e8 -r 5918b8d165d1 b + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/b Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +a + diff -r bb573313a9e8 -r 5918b8d165d1 d + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/d Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +a + diff -r bb573313a9e8 -r 5918b8d165d1 g + --- a/g Thu Jan 01 00:00:00 1970 +0000 + +++ b/g Thu Jan 01 00:00:00 1970 +0000 + @@ -1,2 +1,2 @@ + f + -g + +f + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260 + # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed + mv dir/b e + + diff -r 5918b8d165d1 -r 17d952250a9d dir/b + --- a/dir/b Thu Jan 01 00:00:00 1970 +0000 + +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +0,0 @@ + -a + diff -r 5918b8d165d1 -r 17d952250a9d e + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/e Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +a + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b + # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed + add another e + + diff -r 5918b8d165d1 -r 99b31f1c2782 e + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/e Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +ee + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73 + # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b + # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260 + merge 5 and 4 + + diff -r 99b31f1c2782 -r fc281d8ff18d dir/b + --- a/dir/b Thu Jan 01 00:00:00 1970 +0000 + +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +0,0 @@ + -a + diff -r 99b31f1c2782 -r fc281d8ff18d e + --- a/e Thu Jan 01 00:00:00 1970 +0000 + +++ b/e Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,1 @@ + -ee + +merge + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd + # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73 + Added tag foo-bar for changeset fc281d8ff18d + + diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449 + # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73 + add g + + diff -r fc281d8ff18d -r 24c2e826ddeb g + --- a/g Thu Jan 01 00:00:00 1970 +0000 + +++ b/g Thu Jan 01 00:00:00 1970 +0000 + @@ -1,2 +1,1 @@ + -f + -f + +g + $ testlog --follow -r6 -r8 -r5 -r7 -r4 + ['6', '8', '5', '7', '4'] + [] + <generatorsetdesc-> + +Test --follow-first and forward --rev + + $ testlog --follow-first -r6 -r8 -r5 -r7 -r4 + ['6', '8', '5', '7', '4'] + [] + <generatorsetdesc-> + +Test --follow and backward --rev + + $ testlog --follow -r6 -r5 -r7 -r8 -r4 + ['6', '5', '7', '8', '4'] + [] + <generatorsetdesc-> + +Test --follow-first and backward --rev + + $ testlog --follow-first -r6 -r5 -r7 -r8 -r4 + ['6', '5', '7', '8', '4'] + [] + <generatorsetdesc-> + +Test --follow with --rev of graphlog extension + + $ hg --config extensions.graphlog= glog -qfr1 + \xe2\x97\x8b 1:216d4c92cf98 (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 0:f8035bb17114 (esc) + + +Test subdir + + $ hg up -q 3 + $ cd dir + $ testlog . + [] + (func + (symbol '_matchfiles') + (list + (string 'r:') + (string 'd:relpath') + (string 'p:.'))) + <filteredset + <spanset- 0:9>, + <matchfiles patterns=['.'], include=[] exclude=[], default='relpath', rev=2147483647>> + $ testlog ../b + [] + (func + (symbol 'filelog') + (string '../b')) + <filteredset + <spanset- 0:9>, set([1])> + $ testlog -f ../b + [] + [] + <generatorsetdesc-> + $ cd .. + +Test --hidden + (enable obsolete) + + $ cat >> $HGRCPATH << EOF + > [experimental] + > evolution.createmarkers=True + > EOF + + $ hg debugobsolete `hg id --debug -i -r 8` + obsoleted 1 changesets + $ testlog + [] + [] + <spanset- 0:9> + $ testlog --hidden + [] + [] + <spanset- 0:9> + $ hg log -G --template '{rev} {desc}\n' + \xe2\x97\x8b 7 Added tag foo-bar for changeset fc281d8ff18d (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 6 merge 5 and 4 (esc) + \xe2\x94\x82\xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x97\x8b 5 add another e (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e (esc) + \xe2\x94\x82\xe2\x95\xb1 (esc) + \xe2\x97\x8d 3 mv a b; add d (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 2 mv b dir/b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 1 copy a b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 0 add a (esc) + + +A template without trailing newline should do something sane + + $ hg log -G -r ::2 --template '{rev} {desc}' + \xe2\x97\x8b 2 mv b dir/b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 1 copy a b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 0 add a (esc) + + +Extra newlines must be preserved + + $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n' + \xe2\x97\x8b (esc) + \xe2\x94\x82 2 mv b dir/b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b (esc) + \xe2\x94\x82 1 copy a b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b (esc) + 0 add a + + +The almost-empty template should do something sane too ... + + $ hg log -G -r ::2 --template '\n' + \xe2\x97\x8b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b (esc) + + +issue3772 + + $ hg log -G -r :null + \xe2\x97\x8b changeset: 0:f8035bb17114 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + \xe2\x94\x82 summary: add a (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: -1:000000000000 (esc) + user: + date: Thu Jan 01 00:00:00 1970 +0000 + + $ hg log -G -r null:null + \xe2\x97\x8b changeset: -1:000000000000 (esc) + user: + date: Thu Jan 01 00:00:00 1970 +0000 + + +should not draw line down to null due to the magic of fullreposet + + $ hg log -G -r 'all()' | tail -6 + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 0:f8035bb17114 (esc) + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: add a + + + $ hg log -G -r 'branch(default)' | tail -6 + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 0:f8035bb17114 (esc) + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: add a + + +working-directory revision + + $ hg log -G -qr '. + wdir()' + \xe2\x97\x8b 2147483647:ffffffffffff (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8d 3:5918b8d165d1 (esc) + \xe2\x94\x82 (esc) + \xe2\x95\xa7 (esc) + +node template with changesetprinter: + + $ hg log -Gqr 5:7 --config ui.graphnodetemplate='"{rev}"' + 7 7:02dbb8e276b8 + \xe2\x94\x82 (esc) + 6 6:fc281d8ff18d + \xe2\x94\x82\xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x95\xa7 (esc) + 5 5:99b31f1c2782 + \xe2\x94\x82 (esc) + \xe2\x95\xa7 (esc) + +node template with changesettemplater (shared cache variable): + + $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \ + > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}' + \xe2\x97\x8b 7 foo-bar+1 (esc) + \xe2\x94\x82 (esc) + # 6 foo-bar+0 + \xe2\x94\x82\xe2\x95\xb2 (esc) + \xe2\x94\x82 \xe2\x95\xa7 (esc) + \xe2\x97\x8b 5 null+5 (esc) + \xe2\x94\x82 (esc) + \xe2\x95\xa7 (esc) + +label() should just work in node template: + + $ hg log -Gqr 7 --config extensions.color= --color=debug \ + > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}' + [branch.default\xe2\x94\x827] [log.node|7:02dbb8e276b8] (esc) + \xe2\x94\x82 (esc) + \xe2\x95\xa7 (esc) + + $ cd .. + +change graph edge styling + + $ cd repo + +Setting HGPLAIN ignores graphmod styling: + + $ HGPLAIN=1 hg log -G -r 'file("a")' -m + @ changeset: 36:08a19a744424 + | branch: branch + | tag: tip + | parent: 35:9159c3644c5e + | parent: 35:9159c3644c5e + | user: test + | date: Thu Jan 01 00:00:36 1970 +0000 + | summary: (36) buggy merge: identical parents + | + o changeset: 32:d06dffa21a31 + |\ parent: 27:886ed638191b + | | parent: 31:621d83e11f67 + | | user: test + | | date: Thu Jan 01 00:00:32 1970 +0000 + | | summary: (32) expand + | | + o | changeset: 31:621d83e11f67 + |\| parent: 21:d42a756af44d + | | parent: 30:6e11cd4b648f + | | user: test + | | date: Thu Jan 01 00:00:31 1970 +0000 + | | summary: (31) expand + | | + o | changeset: 30:6e11cd4b648f + |\ \ parent: 28:44ecd0b9ae99 + | | | parent: 29:cd9bb2be7593 + | | | user: test + | | | date: Thu Jan 01 00:00:30 1970 +0000 + | | | summary: (30) expand + | | | + o | | changeset: 28:44ecd0b9ae99 + |\ \ \ parent: 1:6db2ef61d156 + | | | | parent: 26:7f25b6c2f0b9 + | | | | user: test + | | | | date: Thu Jan 01 00:00:28 1970 +0000 + | | | | summary: (28) merge zero known + | | | | + o | | | changeset: 26:7f25b6c2f0b9 + |\ \ \ \ parent: 18:1aa84d96232a + | | | | | parent: 25:91da8ed57247 + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:26 1970 +0000 + | | | | | summary: (26) merge one known; far right + | | | | | + | o-----+ changeset: 25:91da8ed57247 + | | | | | parent: 21:d42a756af44d + | | | | | parent: 24:a9c19a3d96b7 + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:25 1970 +0000 + | | | | | summary: (25) merge one known; far left + | | | | | + | o | | | changeset: 24:a9c19a3d96b7 + | |\ \ \ \ parent: 0:e6eb3150255d + | | | | | | parent: 23:a01cddf0766d + | | | | | | user: test + | | | | | | date: Thu Jan 01 00:00:24 1970 +0000 + | | | | | | summary: (24) merge one known; immediate right + | | | | | | + | o---+ | | changeset: 23:a01cddf0766d + | | | | | | parent: 1:6db2ef61d156 + | | | | | | parent: 22:e0d9cccacb5d + | | | | | | user: test + | | | | | | date: Thu Jan 01 00:00:23 1970 +0000 + | | | | | | summary: (23) merge one known; immediate left + | | | | | | + | o-------+ changeset: 22:e0d9cccacb5d + | | | | | | parent: 18:1aa84d96232a + |/ / / / / parent: 21:d42a756af44d + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:22 1970 +0000 + | | | | | summary: (22) merge two known; one far left, one far right + | | | | | + | | | | o changeset: 21:d42a756af44d + | | | | |\ parent: 19:31ddc2c1573b + | | | | | | parent: 20:d30ed6450e32 + | | | | | | user: test + | | | | | | date: Thu Jan 01 00:00:21 1970 +0000 + | | | | | | summary: (21) expand + | | | | | | + +-+-------o changeset: 20:d30ed6450e32 + | | | | | parent: 0:e6eb3150255d + | | | | | parent: 18:1aa84d96232a + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:20 1970 +0000 + | | | | | summary: (20) merge two known; two far right + | | | | | + | | | | o changeset: 19:31ddc2c1573b + | | | | |\ parent: 15:1dda3f72782d + | | | | | | parent: 17:44765d7c06e0 + | | | | | | user: test + | | | | | | date: Thu Jan 01 00:00:19 1970 +0000 + | | | | | | summary: (19) expand + | | | | | | + o---+---+ | changeset: 18:1aa84d96232a + | | | | | parent: 1:6db2ef61d156 + / / / / / parent: 15:1dda3f72782d + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:18 1970 +0000 + | | | | | summary: (18) merge two known; two far left + | | | | | + | | | | o changeset: 17:44765d7c06e0 + | | | | |\ parent: 12:86b91144a6e9 + | | | | | | parent: 16:3677d192927d + | | | | | | user: test + | | | | | | date: Thu Jan 01 00:00:17 1970 +0000 + | | | | | | summary: (17) expand + | | | | | | + +-+-------o changeset: 16:3677d192927d + | | | | | parent: 0:e6eb3150255d + | | | | | parent: 1:6db2ef61d156 + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:16 1970 +0000 + | | | | | summary: (16) merge two known; one immediate right, one near right + | | | | | + | | | o | changeset: 15:1dda3f72782d + | | | |\ \ parent: 13:22d8966a97e3 + | | | | | | parent: 14:8eac370358ef + | | | | | | user: test + | | | | | | date: Thu Jan 01 00:00:15 1970 +0000 + | | | | | | summary: (15) expand + | | | | | | + +-------o | changeset: 14:8eac370358ef + | | | | |/ parent: 0:e6eb3150255d + | | | | | parent: 12:86b91144a6e9 + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:14 1970 +0000 + | | | | | summary: (14) merge two known; one immediate right, one far right + | | | | | + | | | o | changeset: 13:22d8966a97e3 + | | | |\ \ parent: 9:7010c0af0a35 + | | | | | | parent: 11:832d76e6bdf2 + | | | | | | user: test + | | | | | | date: Thu Jan 01 00:00:13 1970 +0000 + | | | | | | summary: (13) expand + | | | | | | + | +---+---o changeset: 12:86b91144a6e9 + | | | | | parent: 1:6db2ef61d156 + | | | | | parent: 9:7010c0af0a35 + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:12 1970 +0000 + | | | | | summary: (12) merge two known; one immediate right, one far left + | | | | | + | | | | o changeset: 11:832d76e6bdf2 + | | | | |\ parent: 6:b105a072e251 + | | | | | | parent: 10:74c64d036d72 + | | | | | | user: test + | | | | | | date: Thu Jan 01 00:00:11 1970 +0000 + | | | | | | summary: (11) expand + | | | | | | + +---------o changeset: 10:74c64d036d72 + | | | | |/ parent: 0:e6eb3150255d + | | | | | parent: 6:b105a072e251 + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:10 1970 +0000 + | | | | | summary: (10) merge two known; one immediate left, one near right + | | | | | + | | | o | changeset: 9:7010c0af0a35 + | | | |\ \ parent: 7:b632bb1b1224 + | | | | | | parent: 8:7a0b11f71937 + | | | | | | user: test + | | | | | | date: Thu Jan 01 00:00:09 1970 +0000 + | | | | | | summary: (9) expand + | | | | | | + +-------o | changeset: 8:7a0b11f71937 + | | | |/ / parent: 0:e6eb3150255d + | | | | | parent: 7:b632bb1b1224 + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:08 1970 +0000 + | | | | | summary: (8) merge two known; one immediate left, one far right + | | | | | + | | | o | changeset: 7:b632bb1b1224 + | | | |\ \ parent: 2:3d9a33b8d1e1 + | | | | | | parent: 5:4409d547b708 + | | | | | | user: test + | | | | | | date: Thu Jan 01 00:00:07 1970 +0000 + | | | | | | summary: (7) expand + | | | | | | + | | | +---o changeset: 6:b105a072e251 + | | | | |/ parent: 2:3d9a33b8d1e1 + | | | | | parent: 5:4409d547b708 + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:06 1970 +0000 + | | | | | summary: (6) merge two known; one immediate left, one far left + | | | | | + | | | o | changeset: 5:4409d547b708 + | | | |\ \ parent: 3:27eef8ed80b4 + | | | | | | parent: 4:26a8bac39d9f + | | | | | | user: test + | | | | | | date: Thu Jan 01 00:00:05 1970 +0000 + | | | | | | summary: (5) expand + | | | | | | + | +---o | | changeset: 4:26a8bac39d9f + | | | |/ / parent: 1:6db2ef61d156 + | | | | | parent: 3:27eef8ed80b4 + | | | | | user: test + | | | | | date: Thu Jan 01 00:00:04 1970 +0000 + | | | | | summary: (4) merge two known; one immediate left, one immediate right + | | | | | + +.. unless HGPLAINEXCEPT=graph is set: + + $ HGPLAIN=1 HGPLAINEXCEPT=graph hg log -G -r 'file("a")' -m + \xe2\x97\x8d changeset: 36:08a19a744424 (esc) + \xe2\x94\x86 branch: branch (esc) + \xe2\x94\x86 tag: tip (esc) + \xe2\x94\x86 parent: 35:9159c3644c5e (esc) + \xe2\x94\x86 parent: 35:9159c3644c5e (esc) + \xe2\x94\x86 user: test (esc) + \xe2\x94\x86 date: Thu Jan 01 00:00:36 1970 +0000 (esc) + \xe2\x94\x86 summary: (36) buggy merge: identical parents (esc) + \xe2\x94\x86 (esc) + \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc) + \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc) + \xe2\x94\x82 \xe2\x94\x86 parent: 31:621d83e11f67 (esc) + \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:32 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x86 summary: (32) expand (esc) + \xe2\x94\x82 \xe2\x94\x86 (esc) + \xe2\x97\x8b \xe2\x94\x86 changeset: 31:621d83e11f67 (esc) + \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x86 parent: 30:6e11cd4b648f (esc) + \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:31 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x86 summary: (31) expand (esc) + \xe2\x94\x82 \xe2\x94\x86 (esc) + \xe2\x97\x8b \xe2\x94\x86 changeset: 30:6e11cd4b648f (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc) + \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 29:cd9bb2be7593 (esc) + \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:30 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x86 summary: (30) expand (esc) + \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x86 changeset: 28:44ecd0b9ae99 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 26:7f25b6c2f0b9 (esc) + \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:28 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x86 summary: (28) merge zero known (esc) + \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x86 changeset: 26:7f25b6c2f0b9 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 25:91da8ed57247 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:26 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (26) merge one known; far right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 25:91da8ed57247 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 24:a9c19a3d96b7 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:25 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (25) merge one known; far left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 24:a9c19a3d96b7 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 23:a01cddf0766d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:24 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (24) merge one known; immediate right (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 23:a01cddf0766d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 22:e0d9cccacb5d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:23 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (23) merge one known; immediate left (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 22:e0d9cccacb5d (esc) + \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x86\xe2\x95\xb1 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x86 parent: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x86 user: test (esc) + \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:22 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x86 summary: (22) merge two known; one far left, one far right (esc) + \xe2\x94\x82 \xe2\x94\x86 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 21:d42a756af44d (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 19:31ddc2c1573b (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 20:d30ed6450e32 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 18:1aa84d96232a (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 19:31ddc2c1573b (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 15:1dda3f72782d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 18:1aa84d96232a (esc) + \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc) + \xe2\x95\xa7 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc) + \xe2\x95\xb1 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 17:44765d7c06e0 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 16:3677d192927d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x95\xa7 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 15:1dda3f72782d (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 14:8eac370358ef (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc) + \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 12:86b91144a6e9 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 9:7010c0af0a35 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 11:832d76e6bdf2 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 10:74c64d036d72 (esc) + \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 6:b105a072e251 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc) + \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc) + \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 8:7a0b11f71937 (esc) + \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc) + \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 7:b632bb1b1224 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc) + \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x97\x8b \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc) + \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 5:4409d547b708 (esc) + \xe2\x94\x82 \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc) + \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc) + \xe2\x94\x82 \xe2\x95\xb1 (esc) + \xe2\x94\x82 \xe2\x97\x8b changeset: 6:b105a072e251 (esc) + \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 2:3d9a33b8d1e1 (esc) + \xe2\x94\x82 \xe2\x95\xa7 parent: 5:4409d547b708 (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc) + \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 5:4409d547b708 (esc) + \xe2\x94\x82\xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc) + \xe2\x94\x82 \xe2\x95\xa7 parent: 4:26a8bac39d9f (esc) + \xe2\x94\x82 user: test (esc) + \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc) + \xe2\x94\x82 summary: (5) expand (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b changeset: 4:26a8bac39d9f (esc) + \xe2\x94\x82\xe2\x95\xb2 parent: 1:6db2ef61d156 (esc) + \xe2\x95\xa7 \xe2\x95\xa7 parent: 3:27eef8ed80b4 (esc) + user: test + date: Thu Jan 01 00:00:04 1970 +0000 + summary: (4) merge two known; one immediate left, one immediate right + + $ cd .. + $ cd repo + +behavior with newlines + + $ hg log -G -r ::2 -T '{rev} {desc}' + \xe2\x97\x8b 2 (2) collapse (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 1 (1) collapse (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 0 (0) root (esc) + + + $ hg log -G -r ::2 -T '{rev} {desc}\n' + \xe2\x97\x8b 2 (2) collapse (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 1 (1) collapse (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 0 (0) root (esc) + + + $ hg log -G -r ::2 -T '{rev} {desc}\n\n' + \xe2\x97\x8b 2 (2) collapse (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 1 (1) collapse (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 0 (0) root (esc) + + + $ hg log -G -r ::2 -T '\n{rev} {desc}' + \xe2\x97\x8b (esc) + \xe2\x94\x82 2 (2) collapse (esc) + \xe2\x97\x8b (esc) + \xe2\x94\x82 1 (1) collapse (esc) + \xe2\x97\x8b (esc) + 0 (0) root + + $ hg log -G -r ::2 -T '{rev} {desc}\n\n\n' + \xe2\x97\x8b 2 (2) collapse (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 1 (1) collapse (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b 0 (0) root (esc) + + + $ cd .. + +When inserting extra line nodes to handle more than 2 parents, ensure that +the right node styles are used (issue5174): + + $ hg init repo-issue5174 + $ cd repo-issue5174 + $ echo a > f0 + $ hg ci -Aqm 0 + $ echo a > f1 + $ hg ci -Aqm 1 + $ echo a > f2 + $ hg ci -Aqm 2 + $ hg co ".^" + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo a > f3 + $ hg ci -Aqm 3 + $ hg co ".^^" + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ echo a > f4 + $ hg ci -Aqm 4 + $ hg merge -r 2 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg ci -qm 5 + $ hg merge -r 3 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg ci -qm 6 + $ hg log -G -r '0 | 1 | 2 | 6' + \xe2\x97\x8d changeset: 6:851fe89689ad (esc) + \xe2\x94\x86\xe2\x95\xb2 tag: tip (esc) + \xe2\x94\x86 \xe2\x94\x86 parent: 5:4f1e3cf15f5d (esc) + \xe2\x94\x86 \xe2\x94\x86 parent: 3:b74ba7084d2d (esc) + \xe2\x94\x86 \xe2\x94\x86 user: test (esc) + \xe2\x94\x86 \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + \xe2\x94\x86 \xe2\x94\x86 summary: 6 (esc) + \xe2\x94\x86 \xe2\x94\x86 (esc) + \xe2\x94\x86 \xe2\x95\xb2 (esc) + \xe2\x94\x86 \xe2\x94\x86\xe2\x95\xb2 (esc) + \xe2\x94\x86 \xe2\x97\x8b \xe2\x94\x86 changeset: 2:3e6599df4cce (esc) + \xe2\x94\x86 \xe2\x94\x86\xe2\x95\xb1 user: test (esc) + \xe2\x94\x86 \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + \xe2\x94\x86 \xe2\x94\x86 summary: 2 (esc) + \xe2\x94\x86 \xe2\x94\x86 (esc) + \xe2\x94\x86 \xe2\x97\x8b changeset: 1:bd9a55143933 (esc) + \xe2\x94\x86\xe2\x95\xb1 user: test (esc) + \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc) + \xe2\x94\x86 summary: 1 (esc) + \xe2\x94\x86 (esc) + \xe2\x97\x8b changeset: 0:870a5edc339c (esc) + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 0 + + + $ cd .. + +Multiple roots (issue5440): + + $ hg init multiroots + $ cd multiroots + $ cat <<EOF > .hg/hgrc + > [ui] + > logtemplate = '{rev} {desc}\n\n' + > EOF + + $ touch foo + $ hg ci -Aqm foo + $ hg co -q null + $ touch bar + $ hg ci -Aqm bar + + $ hg log -Gr null: + \xe2\x97\x8d 1 bar (esc) + \xe2\x94\x82 (esc) + \xe2\x94\x82 \xe2\x97\x8b 0 foo (esc) + \xe2\x94\x82\xe2\x95\xb1 (esc) + \xe2\x97\x8b -1 (esc) + + $ hg log -Gr null+0 + \xe2\x97\x8b 0 foo (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b -1 (esc) + + $ hg log -Gr null+1 + \xe2\x97\x8d 1 bar (esc) + \xe2\x94\x82 (esc) + \xe2\x97\x8b -1 (esc) + + + $ cd ..
--- a/tests/test-glog.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-glog.t Thu Jul 19 13:55:54 2018 -0400 @@ -91,6 +91,7 @@ > revsetlang, > smartset, > ) + > from mercurial.utils import stringutil > > def logrevset(repo, pats, opts): > revs = logcmdutil._initialrevs(repo, opts) @@ -112,7 +113,7 @@ > ui = repo.ui > ui.write(b'%r\n' % (opts.get(b'rev', []),)) > ui.write(revsetlang.prettyformat(tree) + b'\n') - > ui.write(smartset.prettyformat(revs) + b'\n') + > ui.write(stringutil.prettyrepr(revs) + b'\n') > revs = smartset.baseset() # display no revisions > return revs, filematcher > extensions.wrapfunction(logcmdutil, 'getrevs', printrevset)
--- a/tests/test-graft.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-graft.t Thu Jul 19 13:55:54 2018 -0400 @@ -213,7 +213,7 @@ my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304 warning: conflicts while merging e! (edit, then use 'hg resolve --mark') abort: unresolved conflicts, can't continue - (use 'hg resolve' and 'hg graft --continue --log') + (use 'hg resolve' and 'hg graft --continue') [255] Summary should mention graft: @@ -236,15 +236,15 @@ # # To mark files as resolved: hg resolve --mark FILE - # To continue: hg graft --continue - # To abort: hg update --clean . (warning: this will discard uncommitted changes) + # To continue: hg graft --continue + # To abort: hg update --clean . (warning: this will discard uncommitted changes) Commit while interrupted should fail: $ hg ci -m 'commit interrupted graft' abort: graft in progress - (use 'hg graft --continue' or 'hg update' to abort) + (use 'hg graft --continue' or 'hg graft --stop' to stop) [255] Abort the graft and try committing: @@ -1373,3 +1373,758 @@ note: graft of 7:d3c3f2b38ecc created no changes to commit $ cd .. + +Testing the reading of old format graftstate file with newer mercurial + + $ hg init oldgraft + $ cd oldgraft + $ for ch in a b c; do echo foo > $ch; hg add $ch; hg ci -Aqm "added "$ch; done; + $ hg log -GT "{rev}:{node|short} {desc}\n" + @ 2:8be98ac1a569 added c + | + o 1:80e6d2c47cfe added b + | + o 0:f7ad41964313 added a + + $ hg up 0 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ echo bar > b + $ hg add b + $ hg ci -m "bar to b" + created new head + $ hg graft -r 1 -r 2 + grafting 1:80e6d2c47cfe "added b" + merging b + warning: conflicts while merging b! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + +Writing the nodes in old format to graftstate + + $ hg log -r 1 -r 2 -T '{node}\n' > .hg/graftstate + $ echo foo > b + $ hg resolve -m + (no more unresolved files) + continue: hg graft --continue + $ hg graft --continue + grafting 1:80e6d2c47cfe "added b" + grafting 2:8be98ac1a569 "added c" + +Testing that --user is preserved during conflicts and value is reused while +running `hg graft --continue` + + $ hg log -G + @ changeset: 5:711e9fa999f1 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: added c + | + o changeset: 4:e5ad7353b408 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: added b + | + o changeset: 3:9e887f7a939c + | parent: 0:f7ad41964313 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: bar to b + | + | o changeset: 2:8be98ac1a569 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: added c + | | + | o changeset: 1:80e6d2c47cfe + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: added b + | + o changeset: 0:f7ad41964313 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: added a + + + $ hg up '.^^' + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + + $ hg graft -r 1 -r 2 --user batman + grafting 1:80e6d2c47cfe "added b" + merging b + warning: conflicts while merging b! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ echo wat > b + $ hg resolve -m + (no more unresolved files) + continue: hg graft --continue + + $ hg graft --continue + grafting 1:80e6d2c47cfe "added b" + grafting 2:8be98ac1a569 "added c" + + $ hg log -Gr 3:: + @ changeset: 7:11a36ffaacf2 + | tag: tip + | user: batman + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: added c + | + o changeset: 6:76803afc6511 + | parent: 3:9e887f7a939c + | user: batman + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: added b + | + | o changeset: 5:711e9fa999f1 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: added c + | | + | o changeset: 4:e5ad7353b408 + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: added b + | + o changeset: 3:9e887f7a939c + | parent: 0:f7ad41964313 + ~ user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: bar to b + +Test that --date is preserved and reused in `hg graft --continue` + + $ hg up '.^^' + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg graft -r 1 -r 2 --date '1234560000 120' + grafting 1:80e6d2c47cfe "added b" + merging b + warning: conflicts while merging b! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ echo foobar > b + $ hg resolve -m + (no more unresolved files) + continue: hg graft --continue + $ hg graft --continue + grafting 1:80e6d2c47cfe "added b" + grafting 2:8be98ac1a569 "added c" + + $ hg log -Gr '.^^::.' + @ changeset: 9:1896b76e007a + | tag: tip + | user: test + | date: Fri Feb 13 21:18:00 2009 -0002 + | summary: added c + | + o changeset: 8:ce2b4f1632af + | parent: 3:9e887f7a939c + | user: test + | date: Fri Feb 13 21:18:00 2009 -0002 + | summary: added b + | + o changeset: 3:9e887f7a939c + | parent: 0:f7ad41964313 + ~ user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: bar to b + +Test that --log is preserved and reused in `hg graft --continue` + + $ hg up '.^^' + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg graft -r 1 -r 2 --log + grafting 1:80e6d2c47cfe "added b" + merging b + warning: conflicts while merging b! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ echo foobar > b + $ hg resolve -m + (no more unresolved files) + continue: hg graft --continue + + $ hg graft --continue + grafting 1:80e6d2c47cfe "added b" + grafting 2:8be98ac1a569 "added c" + + $ hg log -GT "{rev}:{node|short} {desc}" -r '.^^::.' + @ 11:30c1050a58b2 added c + | (grafted from 8be98ac1a56990c2d9ca6861041b8390af7bd6f3) + o 10:ec7eda2313e2 added b + | (grafted from 80e6d2c47cfe5b3185519568327a17a061c7efb6) + o 3:9e887f7a939c bar to b + | + ~ + + $ cd .. + +Testing the --stop flag of `hg graft` which stops the interrupted graft + + $ hg init stopgraft + $ cd stopgraft + $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done; + + $ hg log -G + @ changeset: 3:9150fe93bec6 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: added d + | + o changeset: 2:155349b645be + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: added c + | + o changeset: 1:5f6d8a4bf34a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: added b + | + o changeset: 0:9092f1db7931 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: added a + + $ hg up '.^^' + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + + $ echo foo > d + $ hg ci -Aqm "added foo to d" + + $ hg graft --stop + abort: no interrupted graft found + [255] + + $ hg graft -r 3 + grafting 3:9150fe93bec6 "added d" + merging d + warning: conflicts while merging d! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ hg graft --stop --continue + abort: cannot use '--continue' and '--stop' together + [255] + + $ hg graft --stop -U + abort: cannot specify any other flag with '--stop' + [255] + $ hg graft --stop --rev 4 + abort: cannot specify any other flag with '--stop' + [255] + $ hg graft --stop --log + abort: cannot specify any other flag with '--stop' + [255] + + $ hg graft --stop + stopped the interrupted graft + working directory is now at a0deacecd59d + + $ hg diff + + $ hg log -Gr '.' + @ changeset: 4:a0deacecd59d + | tag: tip + ~ parent: 1:5f6d8a4bf34a + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: added foo to d + + $ hg graft -r 2 -r 3 + grafting 2:155349b645be "added c" + grafting 3:9150fe93bec6 "added d" + merging d + warning: conflicts while merging d! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ hg graft --stop + stopped the interrupted graft + working directory is now at 75b447541a9e + + $ hg diff + + $ hg log -G -T "{rev}:{node|short} {desc}" + @ 5:75b447541a9e added c + | + o 4:a0deacecd59d added foo to d + | + | o 3:9150fe93bec6 added d + | | + | o 2:155349b645be added c + |/ + o 1:5f6d8a4bf34a added b + | + o 0:9092f1db7931 added a + + $ cd .. + +Testing the --abort flag for `hg graft` which aborts and rollback to state +before the graft + + $ hg init abortgraft + $ cd abortgraft + $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done; + + $ hg up '.^^' + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + + $ echo x > x + $ hg ci -Aqm "added x" + $ hg up '.^' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo foo > c + $ hg ci -Aqm "added foo to c" + + $ hg log -GT "{rev}:{node|short} {desc}" + @ 5:36b793615f78 added foo to c + | + | o 4:863a25e1a9ea added x + |/ + | o 3:9150fe93bec6 added d + | | + | o 2:155349b645be added c + |/ + o 1:5f6d8a4bf34a added b + | + o 0:9092f1db7931 added a + + $ hg up 9150fe93bec6 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ hg graft --abort + abort: no interrupted graft to abort + [255] + +when stripping is required + $ hg graft -r 4 -r 5 + grafting 4:863a25e1a9ea "added x" + grafting 5:36b793615f78 "added foo to c" (tip) + merging c + warning: conflicts while merging c! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ hg graft --continue --abort + abort: cannot use '--continue' and '--abort' together + [255] + + $ hg graft --abort --stop + abort: cannot use '--abort' and '--stop' together + [255] + + $ hg graft --abort --currentuser + abort: cannot specify any other flag with '--abort' + [255] + + $ hg graft --abort --edit + abort: cannot specify any other flag with '--abort' + [255] + + $ hg graft --abort + graft aborted + working directory is now at 9150fe93bec6 + $ hg log -GT "{rev}:{node|short} {desc}" + o 5:36b793615f78 added foo to c + | + | o 4:863a25e1a9ea added x + |/ + | @ 3:9150fe93bec6 added d + | | + | o 2:155349b645be added c + |/ + o 1:5f6d8a4bf34a added b + | + o 0:9092f1db7931 added a + +when stripping is not required + $ hg graft -r 5 + grafting 5:36b793615f78 "added foo to c" (tip) + merging c + warning: conflicts while merging c! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ hg graft --abort + graft aborted + working directory is now at 9150fe93bec6 + $ hg log -GT "{rev}:{node|short} {desc}" + o 5:36b793615f78 added foo to c + | + | o 4:863a25e1a9ea added x + |/ + | @ 3:9150fe93bec6 added d + | | + | o 2:155349b645be added c + |/ + o 1:5f6d8a4bf34a added b + | + o 0:9092f1db7931 added a + +when some of the changesets became public + + $ hg graft -r 4 -r 5 + grafting 4:863a25e1a9ea "added x" + grafting 5:36b793615f78 "added foo to c" (tip) + merging c + warning: conflicts while merging c! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ hg log -GT "{rev}:{node|short} {desc}" + @ 6:6ec71c037d94 added x + | + | o 5:36b793615f78 added foo to c + | | + | | o 4:863a25e1a9ea added x + | |/ + o | 3:9150fe93bec6 added d + | | + o | 2:155349b645be added c + |/ + o 1:5f6d8a4bf34a added b + | + o 0:9092f1db7931 added a + + $ hg phase -r 6 --public + + $ hg graft --abort + cannot clean up public changesets 6ec71c037d94 + graft aborted + working directory is now at 6ec71c037d94 + +when we created new changesets on top of existing one + + $ hg up '.^^' + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ echo y > y + $ hg ci -Aqm "added y" + $ echo z > z + $ hg ci -Aqm "added z" + + $ hg up 3 + 1 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ hg log -GT "{rev}:{node|short} {desc}" + o 8:637f9e9bbfd4 added z + | + o 7:123221671fd4 added y + | + | o 6:6ec71c037d94 added x + | | + | | o 5:36b793615f78 added foo to c + | | | + | | | o 4:863a25e1a9ea added x + | | |/ + | @ | 3:9150fe93bec6 added d + |/ / + o / 2:155349b645be added c + |/ + o 1:5f6d8a4bf34a added b + | + o 0:9092f1db7931 added a + + $ hg graft -r 8 -r 7 -r 5 + grafting 8:637f9e9bbfd4 "added z" (tip) + grafting 7:123221671fd4 "added y" + grafting 5:36b793615f78 "added foo to c" + merging c + warning: conflicts while merging c! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ cd .. + $ hg init pullrepo + $ cd pullrepo + $ cat >> .hg/hgrc <<EOF + > [phases] + > publish=False + > EOF + $ hg pull ../abortgraft --config phases.publish=False + pulling from ../abortgraft + requesting all changes + adding changesets + adding manifests + adding file changes + added 11 changesets with 9 changes to 8 files (+4 heads) + new changesets 9092f1db7931:6b98ff0062dd + (run 'hg heads' to see heads, 'hg merge' to merge) + $ hg up 9 + 5 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ echo w > w + $ hg ci -Aqm "added w" --config phases.publish=False + + $ cd ../abortgraft + $ hg pull ../pullrepo + pulling from ../pullrepo + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + new changesets 311dfc6cf3bf + (run 'hg heads .' to see heads, 'hg merge' to merge) + + $ hg graft --abort + new changesets detected on destination branch, can't strip + graft aborted + working directory is now at 6b98ff0062dd + + $ cd .. + +============================ +Testing --no-commit option:| +============================ + + $ hg init nocommit + $ cd nocommit + $ echo a > a + $ hg ci -qAma + $ echo b > b + $ hg ci -qAmb + $ hg up -q 0 + $ echo c > c + $ hg ci -qAmc + $ hg log -GT "{rev}:{node|short} {desc}\n" + @ 2:d36c0562f908 c + | + | o 1:d2ae7f538514 b + |/ + o 0:cb9a9f314b8b a + + +Check reporting when --no-commit used with non-applicable options: + + $ hg graft 1 --no-commit -e + abort: cannot specify --no-commit and --edit together + [255] + + $ hg graft 1 --no-commit --log + abort: cannot specify --no-commit and --log together + [255] + + $ hg graft 1 --no-commit -D + abort: cannot specify --no-commit and --currentdate together + [255] + +Test --no-commit is working: + $ hg graft 1 --no-commit + grafting 1:d2ae7f538514 "b" + + $ hg log -GT "{rev}:{node|short} {desc}\n" + @ 2:d36c0562f908 c + | + | o 1:d2ae7f538514 b + |/ + o 0:cb9a9f314b8b a + + + $ hg diff + diff -r d36c0562f908 b + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/b Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +b + +Prepare wrdir to check --no-commit is resepected after --continue: + + $ hg up -qC + $ echo A>a + $ hg ci -qm "A in file a" + $ hg up -q 1 + $ echo B>a + $ hg ci -qm "B in file a" + $ hg log -GT "{rev}:{node|short} {desc}\n" + @ 4:2aa9ad1006ff B in file a + | + | o 3:09e253b87e17 A in file a + | | + | o 2:d36c0562f908 c + | | + o | 1:d2ae7f538514 b + |/ + o 0:cb9a9f314b8b a + + + $ hg graft 3 --no-commit + grafting 3:09e253b87e17 "A in file a" + merging a + warning: conflicts while merging a! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + +Resolve conflict: + $ echo A>a + $ hg resolve --mark + (no more unresolved files) + continue: hg graft --continue + + $ hg graft --continue + grafting 3:09e253b87e17 "A in file a" + $ hg log -GT "{rev}:{node|short} {desc}\n" + @ 4:2aa9ad1006ff B in file a + | + | o 3:09e253b87e17 A in file a + | | + | o 2:d36c0562f908 c + | | + o | 1:d2ae7f538514 b + |/ + o 0:cb9a9f314b8b a + + $ hg diff + diff -r 2aa9ad1006ff a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,1 @@ + -B + +A + + $ hg up -qC + +Check --no-commit is resepected when passed with --continue: + + $ hg graft 3 + grafting 3:09e253b87e17 "A in file a" + merging a + warning: conflicts while merging a! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + +Resolve conflict: + $ echo A>a + $ hg resolve --mark + (no more unresolved files) + continue: hg graft --continue + + $ hg graft --continue --no-commit + grafting 3:09e253b87e17 "A in file a" + $ hg diff + diff -r 2aa9ad1006ff a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,1 @@ + -B + +A + + $ hg log -GT "{rev}:{node|short} {desc}\n" + @ 4:2aa9ad1006ff B in file a + | + | o 3:09e253b87e17 A in file a + | | + | o 2:d36c0562f908 c + | | + o | 1:d2ae7f538514 b + |/ + o 0:cb9a9f314b8b a + + $ hg up -qC + +Test --no-commit when graft multiple revisions: +When there is conflict: + $ hg graft -r "2::3" --no-commit + grafting 2:d36c0562f908 "c" + grafting 3:09e253b87e17 "A in file a" + merging a + warning: conflicts while merging a! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ echo A>a + $ hg resolve --mark + (no more unresolved files) + continue: hg graft --continue + $ hg graft --continue + grafting 3:09e253b87e17 "A in file a" + $ hg diff + diff -r 2aa9ad1006ff a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,1 @@ + -B + +A + diff -r 2aa9ad1006ff c + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/c Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +c + + $ hg log -GT "{rev}:{node|short} {desc}\n" + @ 4:2aa9ad1006ff B in file a + | + | o 3:09e253b87e17 A in file a + | | + | o 2:d36c0562f908 c + | | + o | 1:d2ae7f538514 b + |/ + o 0:cb9a9f314b8b a + + $ hg up -qC + +When there is no conflict: + $ echo d>d + $ hg add d -q + $ hg ci -qmd + $ hg up 3 -q + $ hg log -GT "{rev}:{node|short} {desc}\n" + o 5:baefa8927fc0 d + | + o 4:2aa9ad1006ff B in file a + | + | @ 3:09e253b87e17 A in file a + | | + | o 2:d36c0562f908 c + | | + o | 1:d2ae7f538514 b + |/ + o 0:cb9a9f314b8b a + + + $ hg graft -r 1 -r 5 --no-commit + grafting 1:d2ae7f538514 "b" + grafting 5:baefa8927fc0 "d" (tip) + $ hg diff + diff -r 09e253b87e17 b + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/b Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +b + diff -r 09e253b87e17 d + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/d Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +d + $ hg log -GT "{rev}:{node|short} {desc}\n" + o 5:baefa8927fc0 d + | + o 4:2aa9ad1006ff B in file a + | + | @ 3:09e253b87e17 A in file a + | | + | o 2:d36c0562f908 c + | | + o | 1:d2ae7f538514 b + |/ + o 0:cb9a9f314b8b a + + $ cd ..
--- a/tests/test-grep.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-grep.t Thu Jul 19 13:55:54 2018 -0400 @@ -23,11 +23,11 @@ simple - $ hg grep '.*' + $ hg grep -r tip:0 '.*' port:4:export port:4:vaportight port:4:import/export - $ hg grep port port + $ hg grep -r tip:0 port port port:4:export port:4:vaportight port:4:import/export @@ -35,27 +35,32 @@ simple with color $ hg --config extensions.color= grep --config color.mode=ansi \ - > --color=always port port + > --color=always port port -r tip:0 \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mex\x1b[0;31;1mport\x1b[0m (esc) \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mva\x1b[0;31;1mport\x1b[0might (esc) \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mim\x1b[0;31;1mport\x1b[0m/ex\x1b[0;31;1mport\x1b[0m (esc) simple templated - $ hg grep port \ + $ hg grep port -r tip:0 \ > -T '{file}:{rev}:{node|short}:{texts % "{if(matched, text|upper, text)}"}\n' port:4:914fa752cdea:exPORT port:4:914fa752cdea:vaPORTight port:4:914fa752cdea:imPORT/exPORT - $ hg grep port -T '{file}:{rev}:{texts}\n' + $ hg grep port -r tip:0 -T '{file}:{rev}:{texts}\n' port:4:export port:4:vaportight port:4:import/export + $ hg grep port -r tip:0 -T '{file}:{tags}:{texts}\n' + port:tip:export + port:tip:vaportight + port:tip:import/export + simple JSON (no "change" field) - $ hg grep -Tjson port + $ hg grep -r tip:0 -Tjson port [ { "date": [4, 0], @@ -88,7 +93,7 @@ simple JSON without matching lines - $ hg grep -Tjson -l port + $ hg grep -r tip:0 -Tjson -l port [ { "date": [4, 0], @@ -211,9 +216,9 @@ other - $ hg grep -l port port + $ hg grep -r tip:0 -l port port port:4 - $ hg grep import port + $ hg grep -r tip:0 import port port:4:import/export $ hg cp port port2 @@ -221,7 +226,7 @@ follow - $ hg grep --traceback -f 'import\n\Z' port2 + $ hg grep -r tip:0 --traceback -f 'import\n\Z' port2 port:0:import $ echo deport >> port2 @@ -239,8 +244,8 @@ port:0:1:+:spam:import $ hg up -q null - $ hg grep -f port - [1] + $ hg grep -r 'reverse(:.)' -f port + port:0:import Test wdir (at least, this shouldn't crash) @@ -250,15 +255,18 @@ $ hg stat M port2 $ hg grep -r 'wdir()' port - abort: working directory revision cannot be specified - [255] + port2:2147483647:export + port2:2147483647:vaportight + port2:2147483647:import/export + port2:2147483647:deport + port2:2147483647:wport $ cd .. $ hg init t2 $ cd t2 - $ hg grep foobar foo + $ hg grep -r tip:0 foobar foo [1] - $ hg grep foobar + $ hg grep -r tip:0 foobar [1] $ echo blue >> color $ echo black >> color @@ -271,16 +279,21 @@ $ echo orange >> color $ echo blue >> color $ hg ci -m 3 - $ hg grep orange + $ hg grep -r tip:0 orange color:3:orange $ hg grep --all orange color:3:+:orange color:2:-:orange color:1:+:orange + $ hg grep --diff orange + color:3:+:orange + color:2:-:orange + color:1:+:orange + test substring match: '^' should only match at the beginning - $ hg grep '^.' --config extensions.color= --color debug + $ hg grep -r tip:0 '^.' --config extensions.color= --color debug [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|b]lack [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|o]range [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|b]lue @@ -290,7 +303,7 @@ $ $PYTHON -c 'fp = open("noeol", "wb"); fp.write(b"no infinite loop"); fp.close();' $ hg ci -Amnoeol adding noeol - $ hg grep loop + $ hg grep -r tip:0 loop noeol:4:no infinite loop $ cd .. @@ -307,7 +320,7 @@ adding color $ hg rename color colour $ hg ci -Am rename - $ hg grep octarine + $ hg grep -r tip:0 octarine colour:1:octarine color:0:octarine @@ -346,6 +359,10 @@ color:3:-:red color:1:+:red + $ hg grep --diff red + color:3:-:red + color:1:+:red + Issue3885: test that changing revision order does not alter the revisions printed, just their order. @@ -357,6 +374,14 @@ color:3:-:red color:1:+:red + $ hg grep --diff red -r "all()" + color:1:+:red + color:3:-:red + + $ hg grep --diff red -r "reverse(all())" + color:3:-:red + color:1:+:red + $ cd .. $ hg init a @@ -367,4 +392,102 @@ $ hg grep "MaCam" --all binfile.bin:0:+: Binary file matches + $ hg grep "MaCam" --diff + binfile.bin:0:+: Binary file matches + $ cd .. + +Test for showing working of allfiles flag + + $ hg init sng + $ cd sng + $ echo "unmod" >> um + $ hg ci -A -m "adds unmod to um" + adding um + $ echo "something else" >> new + $ hg ci -A -m "second commit" + adding new + $ hg grep -r "." "unmod" + [1] + $ hg grep -r "." "unmod" --all-files + um:1:unmod + +With --all-files, the working directory is searched by default + + $ echo modified >> new + $ hg grep --all-files mod + new:modified + um:unmod + + which can be overridden by -rREV + + $ hg grep --all-files -r. mod + um:1:unmod + +commands.all-files can be negated by --no-all-files + + $ hg grep --config commands.grep.all-files=True mod + new:modified + um:unmod + $ hg grep --config commands.grep.all-files=True --no-all-files mod + um:0:unmod + +--diff --all-files makes no sense since --diff is the option to grep history + + $ hg grep --diff --all-files um + abort: --diff and --all-files are mutually exclusive + [255] + +but --diff should precede the commands.grep.all-files option + + $ hg grep --config commands.grep.all-files=True --diff mod + um:0:+:unmod + + $ cd .. + +Fix_Wdir(): test that passing wdir() t -r flag does greps on the +files modified in the working directory + + $ cd a + $ echo "abracadara" >> a + $ hg add a + $ hg grep -r "wdir()" "abra" + a:2147483647:abracadara + + $ cd .. + +Change Default of grep by ui.tweakdefaults, that is, the files not in current +working directory should not be grepp-ed on + + $ hg init ab + $ cd ab + $ cat <<'EOF' >> .hg/hgrc + > [ui] + > tweakdefaults = True + > EOF + $ echo "some text">>file1 + $ hg add file1 + $ hg commit -m "adds file1" + $ hg mv file1 file2 + +wdir revision is hidden by default: + + $ hg grep "some" + file2:some text + +but it should be available in template dict: + + $ hg grep "some" -Tjson + [ + { + "date": [0, 0], + "file": "file2", + "line_number": 1, + "node": "ffffffffffffffffffffffffffffffffffffffff", + "rev": 2147483647, + "texts": [{"matched": true, "text": "some"}, {"matched": false, "text": " text"}], + "user": "test" + } + ] + + $ cd ..
--- a/tests/test-hardlinks.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-hardlinks.t Thu Jul 19 13:55:54 2018 -0400 @@ -122,7 +122,7 @@ $ cd r3/d1 >>> f = open('data1', 'wb') >>> for x in range(10000): - ... f.write("%s\n" % str(x)) + ... f.write(b"%d\n" % x) and None >>> f.close() $ for j in 0 1 2 3 4 5 6 7 8 9; do > cat data1 >> f2
--- a/tests/test-help.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-help.t Thu Jul 19 13:55:54 2018 -0400 @@ -110,6 +110,7 @@ color Colorizing Outputs config Configuration Files dates Date Formats + deprecated Deprecated Features diffs Diff Formats environment Environment Variables extensions Using Additional Features @@ -189,6 +190,7 @@ color Colorizing Outputs config Configuration Files dates Date Formats + deprecated Deprecated Features diffs Diff Formats environment Environment Variables extensions Using Additional Features @@ -889,6 +891,7 @@ color Colorizing Outputs config Configuration Files dates Date Formats + deprecated Deprecated Features diffs Diff Formats environment Environment Variables extensions Using Additional Features @@ -2032,6 +2035,13 @@ Date Formats </td></tr> <tr><td> + <a href="/help/deprecated"> + deprecated + </a> + </td><td> + Deprecated Features + </td></tr> + <tr><td> <a href="/help/diffs"> diffs </a>
--- a/tests/test-hgrc.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-hgrc.t Thu Jul 19 13:55:54 2018 -0400 @@ -199,10 +199,10 @@ $ cat > plain.py <<EOF > from mercurial import commands, extensions > def _config(orig, ui, repo, *values, **opts): - > ui.write('plain: %r\n' % ui.plain()) + > ui.write(b'plain: %r\n' % ui.plain()) > return orig(ui, repo, *values, **opts) > def uisetup(ui): - > extensions.wrapcommand(commands.table, 'config', _config) + > extensions.wrapcommand(commands.table, b'config', _config) > EOF $ echo "[extensions]" >> $HGRC $ echo "plain=./plain.py" >> $HGRC
--- a/tests/test-hgweb-auth.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-hgweb-auth.py Thu Jul 19 13:55:54 2018 -0400 @@ -3,10 +3,14 @@ from mercurial import demandimport; demandimport.enable() from mercurial import ( error, + pycompat, ui as uimod, url, util, ) +from mercurial.utils import ( + stringutil, +) urlerr = util.urlerr urlreq = util.urlreq @@ -23,12 +27,8 @@ ui.setconfig('auth', name, value) return ui -def dumpdict(dict): - return '{' + ', '.join(['%s: %s' % (k, dict[k]) - for k in sorted(dict)]) + '}' - def test(auth, urls=None): - print('CFG:', dumpdict(auth)) + print('CFG:', pycompat.sysstr(stringutil.pprint(auth, bprefix=True))) prefixes = set() for k in auth: prefixes.add(k.split('.', 1)[0])
--- a/tests/test-hgweb-auth.py.out Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-hgweb-auth.py.out Thu Jul 19 13:55:54 2018 -0400 @@ -1,7 +1,7 @@ *** Test in-uri schemes -CFG: {x.prefix: http://example.org} +CFG: {b'x.prefix': b'http://example.org'} URI: http://example.org/foo ('x', 'x') URI: http://example.org/foo/bar @@ -18,7 +18,7 @@ abort URI: https://y@example.org/bar abort -CFG: {x.prefix: https://example.org} +CFG: {b'x.prefix': b'https://example.org'} URI: http://example.org/foo abort URI: http://example.org/foo/bar @@ -35,7 +35,7 @@ ('x', 'x') URI: https://y@example.org/bar abort -CFG: {x.prefix: http://example.org, x.schemes: https} +CFG: {b'x.prefix': b'http://example.org', b'x.schemes': b'https'} URI: http://example.org/foo ('x', 'x') URI: http://example.org/foo/bar @@ -52,7 +52,7 @@ abort URI: https://y@example.org/bar abort -CFG: {x.prefix: https://example.org, x.schemes: http} +CFG: {b'x.prefix': b'https://example.org', b'x.schemes': b'http'} URI: http://example.org/foo abort URI: http://example.org/foo/bar @@ -72,7 +72,7 @@ *** Test separately configured schemes -CFG: {x.prefix: example.org, x.schemes: http} +CFG: {b'x.prefix': b'example.org', b'x.schemes': b'http'} URI: http://example.org/foo ('x', 'x') URI: http://example.org/foo/bar @@ -89,7 +89,7 @@ abort URI: https://y@example.org/bar abort -CFG: {x.prefix: example.org, x.schemes: https} +CFG: {b'x.prefix': b'example.org', b'x.schemes': b'https'} URI: http://example.org/foo abort URI: http://example.org/foo/bar @@ -106,7 +106,7 @@ ('x', 'x') URI: https://y@example.org/bar abort -CFG: {x.prefix: example.org, x.schemes: http https} +CFG: {b'x.prefix': b'example.org', b'x.schemes': b'http https'} URI: http://example.org/foo ('x', 'x') URI: http://example.org/foo/bar @@ -126,7 +126,7 @@ *** Test prefix matching -CFG: {x.prefix: http://example.org/foo, y.prefix: http://example.org/bar} +CFG: {b'x.prefix': b'http://example.org/foo', b'y.prefix': b'http://example.org/bar'} URI: http://example.org/foo ('x', 'x') URI: http://example.org/foo/bar @@ -143,7 +143,7 @@ abort URI: https://y@example.org/bar abort -CFG: {x.prefix: http://example.org/foo, y.prefix: http://example.org/foo/bar} +CFG: {b'x.prefix': b'http://example.org/foo', b'y.prefix': b'http://example.org/foo/bar'} URI: http://example.org/foo ('x', 'x') URI: http://example.org/foo/bar @@ -160,7 +160,7 @@ abort URI: https://y@example.org/bar abort -CFG: {x.prefix: *, y.prefix: https://example.org/bar} +CFG: {b'x.prefix': b'*', b'y.prefix': b'https://example.org/bar'} URI: http://example.org/foo abort URI: http://example.org/foo/bar @@ -180,13 +180,13 @@ *** Test user matching -CFG: {x.password: xpassword, x.prefix: http://example.org/foo, x.username: None} +CFG: {b'x.password': b'xpassword', b'x.prefix': b'http://example.org/foo', b'x.username': None} URI: http://y@example.org/foo ('y', 'xpassword') -CFG: {x.password: xpassword, x.prefix: http://example.org/foo, x.username: None, y.password: ypassword, y.prefix: http://example.org/foo, y.username: y} +CFG: {b'x.password': b'xpassword', b'x.prefix': b'http://example.org/foo', b'x.username': None, b'y.password': b'ypassword', b'y.prefix': b'http://example.org/foo', b'y.username': b'y'} URI: http://y@example.org/foo ('y', 'ypassword') -CFG: {x.password: xpassword, x.prefix: http://example.org/foo/bar, x.username: None, y.password: ypassword, y.prefix: http://example.org/foo, y.username: y} +CFG: {b'x.password': b'xpassword', b'x.prefix': b'http://example.org/foo/bar', b'x.username': None, b'y.password': b'ypassword', b'y.prefix': b'http://example.org/foo', b'y.username': b'y'} URI: http://y@example.org/foo/bar ('y', 'xpassword')
--- a/tests/test-hgweb-descend-empties.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-hgweb-descend-empties.t Thu Jul 19 13:55:54 2018 -0400 @@ -93,11 +93,7 @@ </tr> </thead> <tbody class="stripes2"> - <tr class="fileline"> - <td class="name"><a href="/file/tip/">[up]</a></td> - <td class="size"></td> - <td class="permissions">drwxr-xr-x</td> - </tr> + <tr class="fileline"> <td class="name"> @@ -213,11 +209,7 @@ </tr> </thead> <tbody class="stripes2"> - <tr class="fileline"> - <td class="name"><a href="/file/tip/?style=coal">[up]</a></td> - <td class="size"></td> - <td class="permissions">drwxr-xr-x</td> - </tr> + <tr class="fileline"> <td class="name"> @@ -320,13 +312,7 @@ <p class="files">/ <span class="logtags"><span class="phasetag" title="draft">draft</span> <span class="branchtag" title="default">default</span> <span class="tagtag" title="tip">tip</span> </span></p> <table> - <tr class="parity0"> - <td>drwxr-xr-x</td> - <td></td> - <td></td> - <td><a href="/file/tip/?style=monoblue">[up]</a></td> - <td class="link"> </td> - </tr> + <tr class="parity1"> <td>drwxr-xr-x</td> @@ -433,13 +419,7 @@ <div class="title">/ <span class="logtags"><span class="phasetag" title="draft">draft</span> <span class="branchtag" title="default">default</span> <span class="tagtag" title="tip">tip</span> </span></div> <table cellspacing="0"> - <tr class="parity0"> - <td style="font-family:monospace">drwxr-xr-x</td> - <td style="font-family:monospace"></td> - <td style="font-family:monospace"></td> - <td><a href="/file/tip/?style=gitweb">[up]</a></td> - <td class="link"> </td> - </tr> + <tr class="parity1"> <td style="font-family:monospace">drwxr-xr-x</td> @@ -524,40 +504,41 @@ <h2><a href="/">Mercurial</a> / files for changeset <a href="/rev/c9f45f7a1659">c9f45f7a1659</a>: /</h2> <table cellpadding="0" cellspacing="0"> - <tr class="parity0"> - <td><tt>drwxr-xr-x</tt> - <td> - <td> - <td><a href="/file/tip/?style=spartan">[up]</a> - </tr> + <tr class="parity1"> - <td><tt>drwxr-xr-x</tt> - <td> - <td> + <td><tt>drwxr-xr-x</tt> </td> + <td> </td> + <td> </td> <td> <a href="/file/tip/a1?style=spartan">a1/</a> <a href="/file/tip/a1/a2/a3/a4?style=spartan"> a2/a3/a4 </a> + </td> + </tr> <tr class="parity0"> - <td><tt>drwxr-xr-x</tt> - <td> - <td> + <td><tt>drwxr-xr-x</tt> </td> + <td> </td> + <td> </td> <td> <a href="/file/tip/b1?style=spartan">b1/</a> <a href="/file/tip/b1/b2/b3?style=spartan"> b2/b3 </a> + </td> + </tr> <tr class="parity1"> - <td><tt>drwxr-xr-x</tt> - <td> - <td> + <td><tt>drwxr-xr-x</tt> </td> + <td> </td> + <td> </td> <td> <a href="/file/tip/d1?style=spartan">d1/</a> <a href="/file/tip/d1/d2?style=spartan"> d2 </a> + </td> + </tr> </table>
--- a/tests/test-hgweb-empty.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-hgweb-empty.t Thu Jul 19 13:55:54 2018 -0400 @@ -263,6 +263,9 @@ <li><a href="/file/tip">browse</a></li> </ul> <ul> + + </ul> + <ul> <li><a href="/help">help</a></li> </ul> <div class="atom-logo"> @@ -396,11 +399,7 @@ </tr> </thead> <tbody class="stripes2"> - <tr class="fileline"> - <td class="name"><a href="/file/tip/">[up]</a></td> - <td class="size"></td> - <td class="permissions">drwxr-xr-x</td> - </tr> + </tbody>
--- a/tests/test-hgweb-json.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-hgweb-json.t Thu Jul 19 13:55:54 2018 -0400 @@ -101,7 +101,7 @@ $ echo '[web]' >> .hg/hgrc - $ echo 'allow_archive = bz2' >> .hg/hgrc + $ echo 'allow-archive = bz2' >> .hg/hgrc $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E error.log $ cat hg.pid >> $DAEMON_PIDS @@ -1914,6 +1914,10 @@ "topic": "dates" }, { + "summary": "Deprecated Features", + "topic": "deprecated" + }, + { "summary": "Diff Formats", "topic": "diffs" },
--- a/tests/test-hgweb-symrev.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-hgweb-symrev.t Thu Jul 19 13:55:54 2018 -0400 @@ -30,7 +30,7 @@ | o 0:43c799df6e75 - $ hg serve --config web.allow_archive=zip -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log + $ hg serve --config web.allow-archive=zip -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log $ cat hg.pid >> $DAEMON_PIDS $ REVLINKS='href=[^>]+(rev=|/)(43c799df6e75|0|a7c1559b7bba|1|xyzzy|9d8c40cba617|2|tip|default)' @@ -56,6 +56,7 @@ <li><a href="/shortlog/tip?style=paper">log</a></li> <li><a href="/rev/tip?style=paper">changeset</a></li> <li><a href="/file/tip?style=paper">browse</a></li> + <a href="/archive/tip.zip">zip</a> <a href="/graph/tip?revcount=30&style=paper">less</a> <a href="/graph/tip?revcount=120&style=paper">more</a> | rev 2: <a href="/graph/43c799df6e75?style=paper">(0)</a> <a href="/graph/tip?style=paper">tip</a> @@ -72,7 +73,6 @@ <li><a href="/rev/tip?style=paper">changeset</a></li> <a href="/archive/tip.zip">zip</a> directory / @ 2:<a href="/rev/9d8c40cba617?style=paper">9d8c40cba617</a> - <td class="name"><a href="/file/tip/?style=paper">[up]</a></td> <a href="/file/tip/dir?style=paper"> <a href="/file/tip/dir/?style=paper"> <a href="/file/tip/foo?style=paper"> @@ -123,6 +123,7 @@ <li><a href="/shortlog/xyzzy?style=paper">log</a></li> <li><a href="/rev/xyzzy?style=paper">changeset</a></li> <li><a href="/file/xyzzy?style=paper">browse</a></li> + <a href="/archive/xyzzy.zip">zip</a> <a href="/graph/xyzzy?revcount=30&style=paper">less</a> <a href="/graph/xyzzy?revcount=120&style=paper">more</a> | rev 1: <a href="/graph/43c799df6e75?style=paper">(0)</a> <a href="/graph/tip?style=paper">tip</a> @@ -138,7 +139,6 @@ <li><a href="/rev/xyzzy?style=paper">changeset</a></li> <a href="/archive/xyzzy.zip">zip</a> directory / @ 1:<a href="/rev/a7c1559b7bba?style=paper">a7c1559b7bba</a> - <td class="name"><a href="/file/xyzzy/?style=paper">[up]</a></td> <a href="/file/xyzzy/dir?style=paper"> <a href="/file/xyzzy/dir/?style=paper"> <a href="/file/xyzzy/foo?style=paper"> @@ -256,6 +256,7 @@ <li><a href="/shortlog/tip?style=coal">log</a></li> <li><a href="/rev/tip?style=coal">changeset</a></li> <li><a href="/file/tip?style=coal">browse</a></li> + <a href="/archive/tip.zip">zip</a> <a href="/graph/tip?revcount=30&style=coal">less</a> <a href="/graph/tip?revcount=120&style=coal">more</a> | rev 2: <a href="/graph/43c799df6e75?style=coal">(0)</a> <a href="/graph/tip?style=coal">tip</a> @@ -272,7 +273,6 @@ <li><a href="/rev/tip?style=coal">changeset</a></li> <a href="/archive/tip.zip">zip</a> directory / @ 2:<a href="/rev/9d8c40cba617?style=coal">9d8c40cba617</a> - <td class="name"><a href="/file/tip/?style=coal">[up]</a></td> <a href="/file/tip/dir?style=coal"> <a href="/file/tip/dir/?style=coal"> <a href="/file/tip/foo?style=coal"> @@ -323,6 +323,7 @@ <li><a href="/shortlog/xyzzy?style=coal">log</a></li> <li><a href="/rev/xyzzy?style=coal">changeset</a></li> <li><a href="/file/xyzzy?style=coal">browse</a></li> + <a href="/archive/xyzzy.zip">zip</a> <a href="/graph/xyzzy?revcount=30&style=coal">less</a> <a href="/graph/xyzzy?revcount=120&style=coal">more</a> | rev 1: <a href="/graph/43c799df6e75?style=coal">(0)</a> <a href="/graph/tip?style=coal">tip</a> @@ -338,7 +339,6 @@ <li><a href="/rev/xyzzy?style=coal">changeset</a></li> <a href="/archive/xyzzy.zip">zip</a> directory / @ 1:<a href="/rev/a7c1559b7bba?style=coal">a7c1559b7bba</a> - <td class="name"><a href="/file/xyzzy/?style=coal">[up]</a></td> <a href="/file/xyzzy/dir?style=coal"> <a href="/file/xyzzy/dir/?style=coal"> <a href="/file/xyzzy/foo?style=coal"> @@ -489,7 +489,7 @@ $ "$TESTDIR/get-with-headers.py" $LOCALIP:$HGPORT 'graph?style=gitweb' | egrep $REVLINKS <a href="/shortlog/tip?style=gitweb">shortlog</a> | <a href="/log/tip?style=gitweb">changelog</a> | - <a href="/file/tip?style=gitweb">files</a> | + <a href="/file/tip?style=gitweb">files</a> | <a href="/archive/tip.zip">zip</a> | <a href="/graph/tip?revcount=30&style=gitweb">less</a> <a href="/graph/tip?revcount=120&style=gitweb">more</a> | <a href="/graph/43c799df6e75?style=gitweb">(0)</a> <a href="/graph/tip?style=gitweb">tip</a> @@ -520,7 +520,6 @@ $ "$TESTDIR/get-with-headers.py" $LOCALIP:$HGPORT 'file?style=gitweb' | egrep $REVLINKS <a href="/rev/tip?style=gitweb">changeset</a> | <a href="/archive/tip.zip">zip</a> | - <td><a href="/file/tip/?style=gitweb">[up]</a></td> <a href="/file/tip/dir?style=gitweb">dir</a> <a href="/file/tip/dir/?style=gitweb"></a> <a href="/file/tip/dir?style=gitweb">files</a> @@ -582,7 +581,7 @@ $ "$TESTDIR/get-with-headers.py" $LOCALIP:$HGPORT 'graph/xyzzy?style=gitweb' | egrep $REVLINKS <a href="/shortlog/xyzzy?style=gitweb">shortlog</a> | <a href="/log/xyzzy?style=gitweb">changelog</a> | - <a href="/file/xyzzy?style=gitweb">files</a> | + <a href="/file/xyzzy?style=gitweb">files</a> | <a href="/archive/xyzzy.zip">zip</a> | <a href="/graph/xyzzy?revcount=30&style=gitweb">less</a> <a href="/graph/xyzzy?revcount=120&style=gitweb">more</a> | <a href="/graph/43c799df6e75?style=gitweb">(0)</a> <a href="/graph/tip?style=gitweb">tip</a> @@ -594,7 +593,6 @@ $ "$TESTDIR/get-with-headers.py" $LOCALIP:$HGPORT 'file/xyzzy?style=gitweb' | egrep $REVLINKS <a href="/rev/xyzzy?style=gitweb">changeset</a> | <a href="/archive/xyzzy.zip">zip</a> | - <td><a href="/file/xyzzy/?style=gitweb">[up]</a></td> <a href="/file/xyzzy/dir?style=gitweb">dir</a> <a href="/file/xyzzy/dir/?style=gitweb"></a> <a href="/file/xyzzy/dir?style=gitweb">files</a> @@ -731,6 +729,7 @@ $ "$TESTDIR/get-with-headers.py" $LOCALIP:$HGPORT 'graph?style=monoblue' | egrep $REVLINKS <li><a href="/file/tip?style=monoblue">files</a></li> + <li><a href="/archive/tip.zip">zip</a></li> <a href="/rev/9d8c40cba617?style=monoblue">third</a> <a href="/rev/a7c1559b7bba?style=monoblue">second</a> <a href="/rev/43c799df6e75?style=monoblue">first</a> @@ -760,7 +759,6 @@ <li><a href="/graph/tip?style=monoblue">graph</a></li> <li><a href="/rev/tip?style=monoblue">changeset</a></li> <li><a href="/archive/tip.zip">zip</a></li> - <td><a href="/file/tip/?style=monoblue">[up]</a></td> <a href="/file/tip/dir?style=monoblue">dir</a> <a href="/file/tip/dir/?style=monoblue"></a> <td><a href="/file/tip/dir?style=monoblue">files</a></td> @@ -813,6 +811,7 @@ $ "$TESTDIR/get-with-headers.py" $LOCALIP:$HGPORT 'graph/xyzzy?style=monoblue' | egrep $REVLINKS <li><a href="/file/xyzzy?style=monoblue">files</a></li> + <li><a href="/archive/xyzzy.zip">zip</a></li> <a href="/rev/a7c1559b7bba?style=monoblue">second</a> <a href="/rev/43c799df6e75?style=monoblue">first</a> <a href="/graph/xyzzy?revcount=30&style=monoblue">less</a> @@ -823,7 +822,6 @@ <li><a href="/graph/xyzzy?style=monoblue">graph</a></li> <li><a href="/rev/xyzzy?style=monoblue">changeset</a></li> <li><a href="/archive/xyzzy.zip">zip</a></li> - <td><a href="/file/xyzzy/?style=monoblue">[up]</a></td> <a href="/file/xyzzy/dir?style=monoblue">dir</a> <a href="/file/xyzzy/dir/?style=monoblue"></a> <td><a href="/file/xyzzy/dir?style=monoblue">files</a></td> @@ -964,10 +962,9 @@ <a href="/rev/tip?style=spartan">changeset</a> <a href="/archive/tip.zip">zip</a> <h2><a href="/">Mercurial</a> / files for changeset <a href="/rev/9d8c40cba617">9d8c40cba617</a>: /</h2> - <td><a href="/file/tip/?style=spartan">[up]</a> <a href="/file/tip/dir?style=spartan">dir/</a> <a href="/file/tip/dir/?style=spartan"> - <td><a href="/file/tip/foo?style=spartan">foo</a> + <td><a href="/file/tip/foo?style=spartan">foo</a></td> $ "$TESTDIR/get-with-headers.py" $LOCALIP:$HGPORT 'shortlog?style=spartan&rev=all()' | egrep $REVLINKS <a href="/archive/tip.zip">zip</a> @@ -1037,10 +1034,9 @@ <a href="/rev/xyzzy?style=spartan">changeset</a> <a href="/archive/xyzzy.zip">zip</a> <h2><a href="/">Mercurial</a> / files for changeset <a href="/rev/a7c1559b7bba">a7c1559b7bba</a>: /</h2> - <td><a href="/file/xyzzy/?style=spartan">[up]</a> <a href="/file/xyzzy/dir?style=spartan">dir/</a> <a href="/file/xyzzy/dir/?style=spartan"> - <td><a href="/file/xyzzy/foo?style=spartan">foo</a> + <td><a href="/file/xyzzy/foo?style=spartan">foo</a></td> $ "$TESTDIR/get-with-headers.py" $LOCALIP:$HGPORT 'file/xyzzy/foo?style=spartan' | egrep $REVLINKS <a href="/log/xyzzy?style=spartan">changelog</a>
--- a/tests/test-hgweb.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-hgweb.t Thu Jul 19 13:55:54 2018 -0400 @@ -287,11 +287,7 @@ </tr> </thead> <tbody class="stripes2"> - <tr class="fileline"> - <td class="name"><a href="/file/tip/">[up]</a></td> - <td class="size"></td> - <td class="permissions">drwxr-xr-x</td> - </tr> + <tr class="fileline"> <td class="name"> @@ -340,7 +336,7 @@ $ get-with-headers.py --twice localhost:$HGPORT 'static/style-gitweb.css' - date etag server 200 Script output follows - content-length: 9059 + content-length: 9074 content-type: text/css body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; } @@ -419,6 +415,7 @@ background: #ffc; border: 1px solid yellow; border-radius: 5px; + z-index: 15; } #searchform:hover div#hint { display: block; } @@ -802,6 +799,29 @@ 200 Script output follows changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a> +no '[up]' entry in file view when in root directory + + $ get-with-headers.py localhost:$HGPORT 'file/tip?style=paper' | grep -F '[up]' + [1] + $ get-with-headers.py localhost:$HGPORT 'file/tip/da?style=paper' | grep -F '[up]' + <a href="/file/tip/?style=paper">[up]</a> + $ get-with-headers.py localhost:$HGPORT 'file/tip?style=coal' | grep -F '[up]' + [1] + $ get-with-headers.py localhost:$HGPORT 'file/tip/da?style=coal' | grep -F '[up]' + <a href="/file/tip/?style=coal">[up]</a> + $ get-with-headers.py localhost:$HGPORT 'file/tip?style=gitweb' | grep -F '[up]' + [1] + $ get-with-headers.py localhost:$HGPORT 'file/tip/da?style=gitweb' | grep -F '[up]' + <a href="/file/tip/?style=gitweb">[up]</a> + $ get-with-headers.py localhost:$HGPORT 'file/tip?style=monoblue' | grep -F '[up]' + [1] + $ get-with-headers.py localhost:$HGPORT 'file/tip/da?style=monoblue' | grep -F '[up]' + <a href="/file/tip/?style=monoblue">[up]</a> + $ get-with-headers.py localhost:$HGPORT 'file/tip?style=spartan' | grep -F '[up]' + [1] + $ get-with-headers.py localhost:$HGPORT 'file/tip/da?style=spartan' | grep -F '[up]' + <a href="/file/tip/?style=spartan">[up]</a> + no style can be loaded from directories other than the specified paths $ mkdir -p x/templates/fallback
--- a/tests/test-hgwebdir-paths.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-hgwebdir-paths.py Thu Jul 19 13:55:54 2018 -0400 @@ -10,30 +10,30 @@ ) hgwebdir = hgwebdir_mod.hgwebdir -os.mkdir('webdir') -os.chdir('webdir') +os.mkdir(b'webdir') +os.chdir(b'webdir') -webdir = os.path.realpath('.') +webdir = os.path.realpath(b'.') u = uimod.ui.load() -hg.repository(u, 'a', create=1) -hg.repository(u, 'b', create=1) -os.chdir('b') -hg.repository(u, 'd', create=1) -os.chdir('..') -hg.repository(u, 'c', create=1) -os.chdir('..') +hg.repository(u, b'a', create=1) +hg.repository(u, b'b', create=1) +os.chdir(b'b') +hg.repository(u, b'd', create=1) +os.chdir(b'..') +hg.repository(u, b'c', create=1) +os.chdir(b'..') -paths = {'t/a/': '%s/a' % webdir, - 'b': '%s/b' % webdir, - 'coll': '%s/*' % webdir, - 'rcoll': '%s/**' % webdir} +paths = {b't/a/': b'%s/a' % webdir, + b'b': b'%s/b' % webdir, + b'coll': b'%s/*' % webdir, + b'rcoll': b'%s/**' % webdir} -config = os.path.join(webdir, 'hgwebdir.conf') -configfile = open(config, 'w') -configfile.write('[paths]\n') +config = os.path.join(webdir, b'hgwebdir.conf') +configfile = open(config, 'wb') +configfile.write(b'[paths]\n') for k, v in paths.items(): - configfile.write('%s = %s\n' % (k, v)) + configfile.write(b'%s = %s\n' % (k, v)) configfile.close() confwd = hgwebdir(config)
--- a/tests/test-histedit-fold.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-histedit-fold.t Thu Jul 19 13:55:54 2018 -0400 @@ -306,8 +306,8 @@ # # To mark files as resolved: hg resolve --mark FILE - # To continue: hg histedit --continue - # To abort: hg histedit --abort + # To continue: hg histedit --continue + # To abort: hg histedit --abort $ hg resolve -l U file @@ -478,14 +478,8 @@ 1:199b6bb90248 b 0:6c795aa153cb a -Setup the proper environment variable symbol for the platform, to be subbed -into the hook command. -#if windows - $ NODE="%HG_NODE%" -#else - $ NODE="\$HG_NODE" -#endif - $ hg histedit 6c795aa153cb --config hooks.commit="echo commit $NODE" --commands - 2>&1 << EOF | fixbundle + $ hg histedit 6c795aa153cb --config hooks.commit='echo commit $HG_NODE' --config hooks.tonative.commit=True \ + > --commands - 2>&1 << EOF | fixbundle > pick 199b6bb90248 b > fold a1a953ffb4b0 c > pick 6c795aa153cb a @@ -496,8 +490,25 @@ 1:9599899f62c0 a 0:79b99e9c8e49 b +Test unix -> windows style variable substitution in external hooks. + + $ cat > $TESTTMP/tmp.hgrc <<'EOF' + > [hooks] + > pre-add = echo no variables + > post-add = echo ran $HG_ARGS, literal \$non-var, 'also $non-var', $HG_RESULT + > tonative.post-add = True + > EOF + $ echo "foo" > amended.txt - $ hg add amended.txt + $ HGRCPATH=$TESTTMP/tmp.hgrc hg add -v amended.txt + running hook pre-add: echo no variables + no variables + adding amended.txt + converting hook "post-add" to native (windows !) + running hook post-add: echo ran %HG_ARGS%, literal $non-var, "also $non-var", %HG_RESULT% (windows !) + running hook post-add: echo ran $HG_ARGS, literal \$non-var, 'also $non-var', $HG_RESULT (no-windows !) + ran add -v amended.txt, literal $non-var, "also $non-var", 0 (windows !) + ran add -v amended.txt, literal $non-var, also $non-var, 0 (no-windows !) $ hg ci -q --config extensions.largefiles= --amend -I amended.txt The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-no-backup.t Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,70 @@ + $ . "$TESTDIR/histedit-helpers.sh" + +Enable extension used by this test + $ cat >>$HGRCPATH <<EOF + > [extensions] + > histedit= + > EOF + +========================================== +Test history-editing-backup config option| +========================================== +Repo setup: + $ hg init foo + $ cd foo + $ echo first>file + $ hg ci -qAm one + $ echo second>>file + $ hg ci -m two + $ echo third>>file + $ hg ci -m three + $ echo forth>>file + $ hg ci -m four + $ hg log -G --style compact + @ 3[tip] 7d5187087c79 1970-01-01 00:00 +0000 test + | four + | + o 2 80d23dfa866d 1970-01-01 00:00 +0000 test + | three + | + o 1 6153eb23e623 1970-01-01 00:00 +0000 test + | two + | + o 0 36b4bdd91f5b 1970-01-01 00:00 +0000 test + one + +Test when `history-editing-backup` config option is enabled: + $ hg histedit -r '36b4bdd91f5b' --commands - << EOF + > pick 36b4bdd91f5b 0 one + > pick 6153eb23e623 1 two + > roll 80d23dfa866d 2 three + > edit 7d5187087c79 3 four + > EOF + merging file + Editing (7d5187087c79), you may commit or record as needed now. + (hg histedit --continue to resume) + [1] + $ hg histedit --abort + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/foo/.hg/strip-backup/1d8f701c7b35-cf7be322-backup.hg + saved backup bundle to $TESTTMP/foo/.hg/strip-backup/5c0056670bce-b54b65d0-backup.hg + +Test when `history-editing-backup` config option is not enabled +Enable config option: + $ cat >>$HGRCPATH <<EOF + > [ui] + > history-editing-backup=False + > EOF + + $ hg histedit -r '36b4bdd91f5b' --commands - << EOF + > pick 36b4bdd91f5b 0 one + > pick 6153eb23e623 1 two + > roll 80d23dfa866d 2 three + > edit 7d5187087c79 3 four + > EOF + merging file + Editing (7d5187087c79), you may commit or record as needed now. + (hg histedit --continue to resume) + [1] + $ hg histedit --abort + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-http-api-httpv2.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-http-api-httpv2.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,3 +1,5 @@ +#require no-chg + $ . $TESTDIR/wireprotohelpers.sh $ enabledummycommands
--- a/tests/test-http-api.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-http-api.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,3 +1,5 @@ +#require no-chg + $ send() { > hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT/ > }
--- a/tests/test-http-bad-server.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-http-bad-server.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,4 +1,4 @@ -#require killdaemons serve zstd +#require serve zstd Client version is embedded in HTTP request and is effectively dynamic. Pin the version so behavior is deterministic. @@ -120,7 +120,7 @@ write(41) -> Content-Type: application/mercurial-0.1\r\n write(21) -> Content-Length: 436\r\n write(2) -> \r\n - write(436) -> batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + write(436) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash readline(4? from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n (glob) readline(1? from -1) -> (1?) Accept-Encoding* (glob) read limit reached; closing socket @@ -161,7 +161,7 @@ write(41) -> Content-Type: application/mercurial-0.1\r\n write(21) -> Content-Length: 436\r\n write(2) -> \r\n - write(436) -> batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + write(436) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash readline(13? from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n (glob) readline(1?? from -1) -> (27) Accept-Encoding: identity\r\n (glob) readline(8? from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob) @@ -218,7 +218,7 @@ write(41) -> Content-Type: application/mercurial-0.1\r\n write(21) -> Content-Length: 449\r\n write(2) -> \r\n - write(449) -> batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx httppostargs known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + write(449) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx httppostargs known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash readline(1?? from 65537) -> (27) POST /?cmd=batch HTTP/1.1\r\n (glob) readline(1?? from -1) -> (27) Accept-Encoding: identity\r\n (glob) readline(1?? from -1) -> (41) content-type: application/mercurial-0.1\r\n (glob) @@ -329,7 +329,7 @@ write(41 from 41) -> (577) Content-Type: application/mercurial-0.1\r\n write(21 from 21) -> (556) Content-Length: 436\r\n write(2 from 2) -> (554) \r\n - write(436 from 436) -> (118) batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + write(436 from 436) -> (118) batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n readline(-1) -> (27) Accept-Encoding: identity\r\n readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n @@ -377,7 +377,7 @@ write(41 from 41) -> (642) Content-Type: application/mercurial-0.1\r\n write(21 from 21) -> (621) Content-Length: 436\r\n write(2 from 2) -> (619) \r\n - write(436 from 436) -> (183) batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + write(436 from 436) -> (183) batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n readline(-1) -> (27) Accept-Encoding: identity\r\n readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n @@ -429,7 +429,7 @@ write(41 from 41) -> (789) Content-Type: application/mercurial-0.1\r\n write(21 from 21) -> (768) Content-Length: 436\r\n write(2 from 2) -> (766) \r\n - write(436 from 436) -> (330) batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + write(436 from 436) -> (330) batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n readline(-1) -> (27) Accept-Encoding: identity\r\n readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n @@ -490,7 +490,7 @@ write(41 from 41) -> (827) Content-Type: application/mercurial-0.1\r\n write(21 from 21) -> (806) Content-Length: 436\r\n write(2 from 2) -> (804) \r\n - write(436 from 436) -> (368) batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + write(436 from 436) -> (368) batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n readline(-1) -> (27) Accept-Encoding: identity\r\n readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n @@ -553,7 +553,7 @@ write(41 from 41) -> (851) Content-Type: application/mercurial-0.1\r\n write(21 from 21) -> (830) Content-Length: 436\r\n write(2 from 2) -> (828) \r\n - write(436 from 436) -> (392) batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + write(436 from 436) -> (392) batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n readline(-1) -> (27) Accept-Encoding: identity\r\n readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
--- a/tests/test-http-branchmap.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-http-branchmap.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - $ hgserve() { > hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid \ > -E errors.log -v $@ > startup.log @@ -60,8 +58,8 @@ verify 7e7d56fe4833 (encoding fallback in branchmap to maintain compatibility with 1.3.x) $ cat <<EOF > oldhg - > import sys - > from mercurial import ui, hg, commands + > import threading + > from mercurial import dispatch, hg, ui, wireprotoserver > > class StdoutWrapper(object): > def __init__(self, stdout): @@ -79,12 +77,12 @@ > def __getattr__(self, name): > return getattr(self._file, name) > - > sys.stdout = StdoutWrapper(getattr(sys.stdout, 'buffer', sys.stdout)) - > sys.stderr = StdoutWrapper(getattr(sys.stderr, 'buffer', sys.stderr)) - > + > dispatch.initstdio() > myui = ui.ui.load() + > fout = StdoutWrapper(myui.fout) + > myui.fout = myui.ferr > repo = hg.repository(myui, b'a') - > commands.serve(myui, repo, stdio=True, cmdserver=False) + > wireprotoserver._runsshserver(myui, repo, myui.fin, fout, threading.Event()) > EOF $ echo baz >> b/foo $ hg -R b ci -m baz
--- a/tests/test-http-permissions.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-http-permissions.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - $ cat > fakeremoteuser.py << EOF > import os > from mercurial.hgweb import hgweb_mod
--- a/tests/test-http-protocol.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-http-protocol.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,3 +1,5 @@ +#require no-chg + $ . $TESTDIR/wireprotohelpers.sh $ cat >> $HGRCPATH << EOF
--- a/tests/test-http.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-http.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,4 +1,4 @@ -#require killdaemons serve +#require serve $ hg init test $ cd test
--- a/tests/test-hybridencode.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-hybridencode.py Thu Jul 19 13:55:54 2018 -0400 @@ -1,471 +1,877 @@ from __future__ import absolute_import, print_function + +import unittest + from mercurial import ( store, ) -def show(s): - # show test input - print("A = '%s'" % s.encode("string_escape")) - - # show the result of the C implementation, if available - h = store._pathencode(s) - print("B = '%s'" % h.encode("string_escape")) +class hybridencodetests(unittest.TestCase): + def hybridencode(self, input, want): - # compare it with reference implementation in Python - r = store._hybridencode(s, True) - if h != r: - print("R = '%s'" % r.encode("string_escape")) - print() - -show("data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&'()+,-.;=[]^`{}") + # Check the C implementation if it's in use + got = store._pathencode(input) + self.assertEqual(want, got) + # Check the reference implementation in Python + refgot = store._hybridencode(input, True) + self.assertEqual(want, refgot) -print("uppercase char X is encoded as _x") -show("data/ABCDEFGHIJKLMNOPQRSTUVWXYZ") - -print("underbar is doubled") -show("data/_") - -print("tilde is character-encoded") -show("data/~") + def testnoencodingrequired(self): + self.hybridencode( + b'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}', + b'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}') -print("characters in ASCII code range 1..31") -show('data/\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f' - '\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f') + def testuppercasechars(self): # uppercase char X is encoded as _x + self.hybridencode( + b'data/ABCDEFGHIJKLMNOPQRSTUVWXYZ', + b'data/_a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z') + + def testunderbar(self): # underbar is doubled + self.hybridencode(b'data/_', b'data/__') -print("characters in ASCII code range 126..255") -show('data/\x7e\x7f' - '\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f' - '\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f') -show('data/\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf' - '\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf') -show('data/\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf' - '\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf') -show('data/\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef' - '\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff') + def testtilde(self): # tilde is character-encoded + self.hybridencode(b'data/~', b'data/~7e') -print("Windows reserved characters") -show('data/less <, greater >, colon :, double-quote ", backslash \\' - ', pipe |, question-mark ?, asterisk *') - -print("encoding directories ending in .hg, .i or .d with '.hg' suffix") -show('data/x.h.i/x.hg/x.i/x.d/foo') -show('data/a.hg/a.i/a.d/foo') -show('data/au.hg/au.i/au.d/foo') -show('data/aux.hg/aux.i/aux.d/foo') -show('data/auxy.hg/auxy.i/auxy.d/foo') + def testcontrolchars(self): # characters in ASCII code range 1..31 + self.hybridencode( + (b'data/\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f' + b'\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e' + b'\x1f'), + (b'data/~01~02~03~04~05~06~07~08~09~0a~0b~0c~0d~0e~0f~10~11~12~13' + b'~14~15~16~17~18~19~1a~1b~1c~1d~1e~1f')) -print("but these are not encoded on *filenames*") -show('data/foo/x.hg') -show('data/foo/x.i') -show('data/foo/x.d') -show('data/foo/a.hg') -show('data/foo/a.i') -show('data/foo/a.d') -show('data/foo/au.hg') -show('data/foo/au.i') -show('data/foo/au.d') -show('data/foo/aux.hg') -show('data/foo/aux.i') -show('data/foo/aux.d') -show('data/foo/auxy.hg') -show('data/foo/auxy.i') -show('data/foo/auxy.d') + def testhighascii(self):# characters in ASCII code range 126..255 + self.hybridencode( + (b'data/~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c' + b'\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b' + b'\x9c\x9d\x9e\x9f'), + (b'data/~7e~7f~80~81~82~83~84~85~86~87~88~89~8a~8b~8c~8d~8e~8f~90' + b'~91~92~93~94~95~96~97~98~99~9a~9b~9c~9d~9e~9f')) + self.hybridencode( + (b'data/\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad' + b'\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc' + b'\xbd\xbe\xbf'), + (b'data/~a0~a1~a2~a3~a4~a5~a6~a7~a8~a9~aa~ab~ac~ad~ae~af~b0~b1~b2' + b'~b3~b4~b5~b6~b7~b8~b9~ba~bb~bc~bd~be~bf')) + self.hybridencode( + (b'data/\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca' + b'\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6' + b'\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf'), + (b'data/~c0~c1~c2~c3~c4~c5~c6~c7~c8~c9~ca~cb~cc~cd~ce~cf~d0~d1~d2' + b'~d3~d4~d5~d6~d7~d8~d9~da~db~dc~dd~de~df')) + self.hybridencode( + (b'data/\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed' + b'\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd' + b'\xfe\xff'), + (b'data/~e0~e1~e2~e3~e4~e5~e6~e7~e8~e9~ea~eb~ec~ed~ee~ef~f0~f1~f2' + b'~f3~f4~f5~f6~f7~f8~f9~fa~fb~fc~fd~fe~ff')) -print("plain .hg, .i and .d directories have the leading dot encoded") -show('data/.hg/.i/.d/foo') - -show('data/aux.bla/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c.i') + def testwinreserved(self): # Windows reserved characters + self.hybridencode( + (b'data/less <, greater >, colon :, double-quote ", backslash \\, ' + b'pipe |, question-mark ?, asterisk *'), + (b'data/less ~3c, greater ~3e, colon ~3a, double-quote ~22, ' + b'backslash ~5c, pipe ~7c, question-mark ~3f, asterisk ~2a')) -show('data/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/' - 'TENTH/ELEVENTH/LOREMIPSUM.TXT.i') -show('data/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/' - 'wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules' - '.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider.i') -show('data/AUX.THE-QUICK-BROWN-FOX-JU:MPS-OVER-THE-LAZY-DOG-THE-QUICK-' - 'BROWN-FOX-JUMPS-OVER-THE-LAZY-DOG.TXT.i') -show('data/Project Planning/Resources/AnotherLongDirectoryName/' - 'Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt') -show('data/Project.Planning/Resources/AnotherLongDirectoryName/' - 'Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt') -show('data/foo.../foo / /a./_. /__/.x../ bla/.FOO/something.i') + def testhgreserved(self): + # encoding directories ending in .hg, .i or .d with '.hg' suffix + self.hybridencode(b'data/x.h.i/x.hg/x.i/x.d/foo', + b'data/x.h.i.hg/x.hg.hg/x.i.hg/x.d.hg/foo') + self.hybridencode(b'data/a.hg/a.i/a.d/foo', + b'data/a.hg.hg/a.i.hg/a.d.hg/foo') + self.hybridencode(b'data/au.hg/au.i/au.d/foo', + b'data/au.hg.hg/au.i.hg/au.d.hg/foo') + self.hybridencode(b'data/aux.hg/aux.i/aux.d/foo', + b'data/au~78.hg.hg/au~78.i.hg/au~78.d.hg/foo') + self.hybridencode(b'data/auxy.hg/auxy.i/auxy.d/foo', + b'data/auxy.hg.hg/auxy.i.hg/auxy.d.hg/foo') + # but these are not encoded on *filenames* + self.hybridencode(b'data/foo/x.hg', b'data/foo/x.hg') + self.hybridencode(b'data/foo/x.i', b'data/foo/x.i') + self.hybridencode(b'data/foo/x.d', b'data/foo/x.d') + self.hybridencode(b'data/foo/a.hg', b'data/foo/a.hg') + self.hybridencode(b'data/foo/a.i', b'data/foo/a.i') + self.hybridencode(b'data/foo/a.d', b'data/foo/a.d') + self.hybridencode(b'data/foo/au.hg', b'data/foo/au.hg') + self.hybridencode(b'data/foo/au.i', b'data/foo/au.i') + self.hybridencode(b'data/foo/au.d', b'data/foo/au.d') + self.hybridencode(b'data/foo/aux.hg', b'data/foo/au~78.hg') + self.hybridencode(b'data/foo/aux.i', b'data/foo/au~78.i') + self.hybridencode(b'data/foo/aux.d', b'data/foo/au~78.d') + self.hybridencode(b'data/foo/auxy.hg', b'data/foo/auxy.hg') + self.hybridencode(b'data/foo/auxy.i', b'data/foo/auxy.i') + self.hybridencode(b'data/foo/auxy.d', b'data/foo/auxy.d') -show('data/c/co/com/com0/com1/com2/com3/com4/com5/com6/com7/com8/com9') -show('data/C/CO/COM/COM0/COM1/COM2/COM3/COM4/COM5/COM6/COM7/COM8/COM9') -show('data/c.x/co.x/com.x/com0.x/com1.x/com2.x/com3.x/com4.x/com5.x' - '/com6.x/com7.x/com8.x/com9.x') -show('data/x.c/x.co/x.com0/x.com1/x.com2/x.com3/x.com4/x.com5' - '/x.com6/x.com7/x.com8/x.com9') -show('data/cx/cox/comx/com0x/com1x/com2x/com3x/com4x/com5x' - '/com6x/com7x/com8x/com9x') -show('data/xc/xco/xcom0/xcom1/xcom2/xcom3/xcom4/xcom5' - '/xcom6/xcom7/xcom8/xcom9') - -show('data/l/lp/lpt/lpt0/lpt1/lpt2/lpt3/lpt4/lpt5/lpt6/lpt7/lpt8/lpt9') -show('data/L/LP/LPT/LPT0/LPT1/LPT2/LPT3/LPT4/LPT5/LPT6/LPT7/LPT8/LPT9') -show('data/l.x/lp.x/lpt.x/lpt0.x/lpt1.x/lpt2.x/lpt3.x/lpt4.x/lpt5.x' - '/lpt6.x/lpt7.x/lpt8.x/lpt9.x') -show('data/x.l/x.lp/x.lpt/x.lpt0/x.lpt1/x.lpt2/x.lpt3/x.lpt4/x.lpt5' - '/x.lpt6/x.lpt7/x.lpt8/x.lpt9') -show('data/lx/lpx/lptx/lpt0x/lpt1x/lpt2x/lpt3x/lpt4x/lpt5x' - '/lpt6x/lpt7x/lpt8x/lpt9x') -show('data/xl/xlp/xlpt/xlpt0/xlpt1/xlpt2/xlpt3/xlpt4/xlpt5' - '/xlpt6/xlpt7/xlpt8/xlpt9') - -show('data/con/p/pr/prn/a/au/aux/n/nu/nul') -show('data/CON/P/PR/PRN/A/AU/AUX/N/NU/NUL') -show('data/con.x/p.x/pr.x/prn.x/a.x/au.x/aux.x/n.x/nu.x/nul.x') -show('data/x.con/x.p/x.pr/x.prn/x.a/x.au/x.aux/x.n/x.nu/x.nul') -show('data/conx/px/prx/prnx/ax/aux/auxx/nx/nux/nulx') -show('data/xcon/xp/xpr/xprn/xa/xau/xaux/xn/xnu/xnul') + # plain .hg, .i and .d directories have the leading dot encoded + self.hybridencode(b'data/.hg/.i/.d/foo', + b'data/~2ehg.hg/~2ei.hg/~2ed.hg/foo') -show('data/a./au./aux./auxy./aux.') -show('data/c./co./con./cony./con.') -show('data/p./pr./prn./prny./prn.') -show('data/n./nu./nul./nuly./nul.') -show('data/l./lp./lpt./lpt1./lpt1y./lpt1.') -show('data/lpt9./lpt9y./lpt9.') -show('data/com./com1./com1y./com1.') -show('data/com9./com9y./com9.') - -show('data/a /au /aux /auxy /aux ') - -print("largest unhashed path") -show('data/123456789-123456789-123456789-123456789-123456789-' - 'unhashed--xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') - -print("shortest hashed path") -show('data/123456789-123456789-123456789-123456789-123456789-' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') - -print("changing one char in part that's hashed away produces a different hash") -show('data/123456789-123456789-123456789-123456789-123456789-' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxy-' - '123456789-123456') + def testmisclongcases(self): + self.hybridencode( + (b'data/aux.bla/bla.aux/prn/PRN/lpt/com3/nul/' + b'coma/foo.NUL/normal.c.i'), + (b'data/au~78.bla/bla.aux/pr~6e/_p_r_n/lpt/co~6d3' + b'/nu~6c/coma/foo._n_u_l/normal.c.i')) + self.hybridencode( + (b'data/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH' + b'/TENTH/ELEVENTH/LOREMIPSUM.TXT.i'), + (b'dh/au~78/second/x.prn/fourth/fi~3afth/sixth/seventh/eighth/' + b'nineth/tenth/loremia20419e358ddff1bf8751e38288aff1d7c32ec05.i')) + self.hybridencode( + (b'data/enterprise/openesbaddons/contrib-imola/corba-bc/' + b'netbeansplugin/wsdlExtension/src/main/java/META-INF/services' + b'/org.netbeans.modules.xml.wsdl.bindingsupport.spi.' + b'ExtensibilityElementTemplateProvider.i'), + (b'dh/enterpri/openesba/contrib-/corba-bc/netbeans/wsdlexte/src/' + b'main/java/org.net7018f27961fdf338a598a40c4683429e7ffb9743.i')) + self.hybridencode( + (b'data/AUX.THE-QUICK-BROWN-FOX-JU:MPS-OVER-THE-LAZY-DOG-THE-' + b'QUICK-BROWN-FOX-JUMPS-OVER-THE-LAZY-DOG.TXT.i'), + (b'dh/au~78.the-quick-brown-fox-ju~3amps-over-the-lazy-dog-the-' + b'quick-brown-fox-jud4dcadd033000ab2b26eb66bae1906bcb15d4a70.i')) + self.hybridencode( + (b'data/Project Planning/Resources/AnotherLongDirectoryName/Follow' + b'edbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt'), + (b'dh/project_/resource/anotherl/followed/andanoth/andthenanextrem' + b'elylongfilenaf93030515d9849cfdca52937c2204d19f83913e5.txt')) + self.hybridencode( + (b'data/Project.Planning/Resources/AnotherLongDirectoryName/Follo' + b'wedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt'), + (b'dh/project_/resource/anotherl/followed/andanoth/andthenanextre' + b'melylongfilena0fd7c506f5c9d58204444fc67e9499006bd2d445.txt')) + self.hybridencode( + b'data/foo.../foo / /a./_. /__/.x../ bla/.FOO/something.i', + (b'data/foo..~2e/foo ~20/~20/a~2e/__.~20/____/~2ex.~2e/~20 bla/' + b'~2e_f_o_o/something.i')) + self.hybridencode( + b'data/c/co/com/com0/com1/com2/com3/com4/com5/com6/com7/com8/com9', + (b'data/c/co/com/com0/co~6d1/co~6d2/co~6d3/co~6d4/co~6d5/co~6d6/' + b'co~6d7/co~6d8/co~6d9')) + self.hybridencode( + b'data/C/CO/COM/COM0/COM1/COM2/COM3/COM4/COM5/COM6/COM7/COM8/COM9', + (b'data/_c/_c_o/_c_o_m/_c_o_m0/_c_o_m1/_c_o_m2/_c_o_m3/_c_o_m4/' + b'_c_o_m5/_c_o_m6/_c_o_m7/_c_o_m8/_c_o_m9')) + self.hybridencode( + (b'data/c.x/co.x/com.x/com0.x/com1.x/com2.x/com3.x/com4.x/com5.x/' + b'com6.x/com7.x/com8.x/com9.x'), + (b'data/c.x/co.x/com.x/com0.x/co~6d1.x/co~6d2.x/co~6d3.x/co~6d4.x' + b'/co~6d5.x/co~6d6.x/co~6d7.x/co~6d8.x/co~6d9.x')) + self.hybridencode( + (b'data/x.c/x.co/x.com0/x.com1/x.com2/x.com3/x.com4/x.com5/x.com6' + b'/x.com7/x.com8/x.com9'), + (b'data/x.c/x.co/x.com0/x.com1/x.com2/x.com3/x.com4/x.com5/x.com6' + b'/x.com7/x.com8/x.com9')) + self.hybridencode( + (b'data/cx/cox/comx/com0x/com1x/com2x/com3x/com4x/com5x/com6x/' + b'com7x/com8x/com9x'), + (b'data/cx/cox/comx/com0x/com1x/com2x/com3x/com4x/com5x/com6x/' + b'com7x/com8x/com9x')) + self.hybridencode( + (b'data/xc/xco/xcom0/xcom1/xcom2/xcom3/xcom4/xcom5/xcom6/xcom7/' + b'xcom8/xcom9'), + (b'data/xc/xco/xcom0/xcom1/xcom2/xcom3/xcom4/xcom5/xcom6/xcom7/' + b'xcom8/xcom9')) + self.hybridencode( + b'data/l/lp/lpt/lpt0/lpt1/lpt2/lpt3/lpt4/lpt5/lpt6/lpt7/lpt8/lpt9', + (b'data/l/lp/lpt/lpt0/lp~741/lp~742/lp~743/lp~744/lp~745/lp~746/' + b'lp~747/lp~748/lp~749')) + self.hybridencode( + b'data/L/LP/LPT/LPT0/LPT1/LPT2/LPT3/LPT4/LPT5/LPT6/LPT7/LPT8/LPT9', + (b'data/_l/_l_p/_l_p_t/_l_p_t0/_l_p_t1/_l_p_t2/_l_p_t3/_l_p_t4/' + b'_l_p_t5/_l_p_t6/_l_p_t7/_l_p_t8/_l_p_t9')) + self.hybridencode( + (b'data/l.x/lp.x/lpt.x/lpt0.x/lpt1.x/lpt2.x/lpt3.x/lpt4.x/lpt5.x/' + b'lpt6.x/lpt7.x/lpt8.x/lpt9.x'), + (b'data/l.x/lp.x/lpt.x/lpt0.x/lp~741.x/lp~742.x/lp~743.x/lp~744.x/' + b'lp~745.x/lp~746.x/lp~747.x/lp~748.x/lp~749.x')) + self.hybridencode( + (b'data/x.l/x.lp/x.lpt/x.lpt0/x.lpt1/x.lpt2/x.lpt3/x.lpt4/x.lpt5/' + b'x.lpt6/x.lpt7/x.lpt8/x.lpt9'), + (b'data/x.l/x.lp/x.lpt/x.lpt0/x.lpt1/x.lpt2/x.lpt3/x.lpt4/x.lpt5' + b'/x.lpt6/x.lpt7/x.lpt8/x.lpt9')) + self.hybridencode( + (b'data/lx/lpx/lptx/lpt0x/lpt1x/lpt2x/lpt3x/lpt4x/lpt5x/lpt6x/' + b'lpt7x/lpt8x/lpt9x'), + (b'data/lx/lpx/lptx/lpt0x/lpt1x/lpt2x/lpt3x/lpt4x/lpt5x/lpt6x/' + b'lpt7x/lpt8x/lpt9x')) + self.hybridencode( + (b'data/xl/xlp/xlpt/xlpt0/xlpt1/xlpt2/xlpt3/xlpt4/xlpt5/xlpt6/' + b'xlpt7/xlpt8/xlpt9'), + (b'data/xl/xlp/xlpt/xlpt0/xlpt1/xlpt2/xlpt3/xlpt4/xlpt5/xlpt6/' + b'xlpt7/xlpt8/xlpt9')) + self.hybridencode(b'data/con/p/pr/prn/a/au/aux/n/nu/nul', + b'data/co~6e/p/pr/pr~6e/a/au/au~78/n/nu/nu~6c') + self.hybridencode( + b'data/CON/P/PR/PRN/A/AU/AUX/N/NU/NUL', + b'data/_c_o_n/_p/_p_r/_p_r_n/_a/_a_u/_a_u_x/_n/_n_u/_n_u_l') + self.hybridencode( + b'data/con.x/p.x/pr.x/prn.x/a.x/au.x/aux.x/n.x/nu.x/nul.x', + b'data/co~6e.x/p.x/pr.x/pr~6e.x/a.x/au.x/au~78.x/n.x/nu.x/nu~6c.x') + self.hybridencode( + b'data/x.con/x.p/x.pr/x.prn/x.a/x.au/x.aux/x.n/x.nu/x.nul', + b'data/x.con/x.p/x.pr/x.prn/x.a/x.au/x.aux/x.n/x.nu/x.nul') + self.hybridencode(b'data/conx/px/prx/prnx/ax/aux/auxx/nx/nux/nulx', + b'data/conx/px/prx/prnx/ax/au~78/auxx/nx/nux/nulx') + self.hybridencode(b'data/xcon/xp/xpr/xprn/xa/xau/xaux/xn/xnu/xnul', + b'data/xcon/xp/xpr/xprn/xa/xau/xaux/xn/xnu/xnul') + self.hybridencode(b'data/a./au./aux./auxy./aux.', + b'data/a~2e/au~2e/au~78~2e/auxy~2e/au~78~2e') + self.hybridencode(b'data/c./co./con./cony./con.', + b'data/c~2e/co~2e/co~6e~2e/cony~2e/co~6e~2e') + self.hybridencode(b'data/p./pr./prn./prny./prn.', + b'data/p~2e/pr~2e/pr~6e~2e/prny~2e/pr~6e~2e') + self.hybridencode(b'data/n./nu./nul./nuly./nul.', + b'data/n~2e/nu~2e/nu~6c~2e/nuly~2e/nu~6c~2e') + self.hybridencode( + b'data/l./lp./lpt./lpt1./lpt1y./lpt1.', + b'data/l~2e/lp~2e/lpt~2e/lp~741~2e/lpt1y~2e/lp~741~2e') + self.hybridencode(b'data/lpt9./lpt9y./lpt9.', + b'data/lp~749~2e/lpt9y~2e/lp~749~2e') + self.hybridencode(b'data/com./com1./com1y./com1.', + b'data/com~2e/co~6d1~2e/com1y~2e/co~6d1~2e') + self.hybridencode(b'data/com9./com9y./com9.', + b'data/co~6d9~2e/com9y~2e/co~6d9~2e') + self.hybridencode(b'data/a /au /aux /auxy /aux ', + b'data/a~20/au~20/aux~20/auxy~20/aux~20') -print("uppercase hitting length limit due to encoding") -show('data/A23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/Z23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') - -print("compare with lowercase not hitting limit") -show('data/a23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/z23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') - -print("not hitting limit with any of these") -show("data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&'()+,-.;=" - "[]^`{}xxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-" - "123456789-12345") - -print("underbar hitting length limit due to encoding") -show('data/_23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') - -print("tilde hitting length limit due to encoding") -show('data/~23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') + def testhashingboundarycases(self): + # largest unhashed path + self.hybridencode( + (b'data/123456789-123456789-123456789-123456789-123456789-unhashed' + b'--xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'), + (b'data/123456789-123456789-123456789-123456789-123456789-unhashed' + b'--xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345')) + # shortest hashed path + self.hybridencode( + (b'data/123456789-123456789-123456789-123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/123456789-123456789-123456789-123456789-123456789-hashed---' + b'-xxxxxxxxx-xxxxxxxe9c55002b50bf5181e7a6fc1f60b126e2a6fcf71')) -print("Windows reserved characters hitting length limit") -show('data/<23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/>23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/:23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/"23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/\\23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/|23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/?23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/*23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') + def testhashing(self): + # changing one char in part that's hashed away produces a different hash + self.hybridencode( + (b'data/123456789-123456789-123456789-123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxy-123456789-123456'), + (b'dh/123456789-123456789-123456789-123456789-123456789-hashed---' + b'-xxxxxxxxx-xxxxxxxd24fa4455faf8a94350c18e5eace7c2bb17af706')) + # uppercase hitting length limit due to encoding + self.hybridencode( + (b'data/A23456789-123456789-123456789-123456789-123456789-' + b'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-12345'), + (b'dh/a23456789-123456789-123456789-123456789-123456789-' + b'xxxxxxxxx-xxxxxxxxx-xxxxxxx' + b'cbbc657029b41b94ed510d05feb6716a5c03bc6b')) + self.hybridencode( + (b'data/Z23456789-123456789-123456789-123456789-123456789-' + b'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-12345'), + (b'dh/z23456789-123456789-123456789-123456789-123456789-xxxxxxxxx' + b'-xxxxxxxxx-xxxxxxx938f32a725c89512833fb96b6602dd9ebff51ddd')) + # compare with lowercase not hitting limit + self.hybridencode( + (b'data/a23456789-123456789-123456789-123456789-123456789-' + b'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-' + b'12345'), + (b'data/a23456789-123456789-123456789-123456789-123456789-' + b'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-' + b'12345')) + self.hybridencode( + (b'data/z23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789' + b'-12345'), + (b'data/z23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-' + b'12345')) + # not hitting limit with any of these + self.hybridencode( + (b'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}' + b'xxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'), + (b'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}' + b'xxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345')) + # underbar hitting length limit due to encoding + self.hybridencode( + (b'data/_23456789-123456789-123456789-123456789-123456789-' + b'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-' + b'12345'), + (b'dh/_23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-' + b'xxxxxxxxx-xxxxxxx9921a01af50feeabc060ce00eee4cba6efc31d2b')) + + # tilde hitting length limit due to encoding + self.hybridencode( + (b'data/~23456789-123456789-123456789-123456789-123456789-' + b'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-' + b'12345'), + (b'dh/~7e23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'9cec6f97d569c10995f785720044ea2e4227481b')) -print("initial space hitting length limit") -show('data/ 23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') - -print("initial dot hitting length limit") -show('data/.23456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') - -print("trailing space in filename hitting length limit") -show('data/123456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-1234 ') + def testwinreservedoverlimit(self): + # Windows reserved characters hitting length limit + self.hybridencode( + (b'data/<23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/~3c23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxee' + b'67d8f275876ca1ef2500fc542e63c885c4e62d')) + self.hybridencode( + (b'data/>23456789-123456789-123456789-123456789-123456789-' + b'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-12345'), + (b'dh/~3e23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'387a85a5b1547cc9136310c974df716818458ddb')) + self.hybridencode( + (b'data/:23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-12345'), + (b'dh/~3a23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'2e4154fb571d13d22399c58cc4ef4858e4b75999')) + self.hybridencode( + (b'data/"23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/~2223456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'fc7e3ec7b0687ee06ed8c32fef0eb0c1980259f5')) + self.hybridencode( + (b'data/\\23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-12345'), + (b'dh/~5c23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'944e1f2b7110687e116e0d151328ac648b06ab4a')) + self.hybridencode( + (b'data/|23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/~7c23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'28b23dd3fd0242946334126ab62bcd772aac32f4')) + self.hybridencode( + (b'data/?23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/~3f23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'a263022d3994d2143d98f94f431eef8b5e7e0f8a')) + self.hybridencode( + (b'data/*23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-12345'), + (b'dh/~2a23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'0e7e6020e3c00ba7bb7893d84ca2966fbf53e140')) -print("trailing dot in filename hitting length limit") -show('data/123456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-1234.') + def testinitialspacelenlimit(self): + # initial space hitting length limit + self.hybridencode( + (b'data/ 23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-12345'), + (b'dh/~2023456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'92acbc78ef8c0b796111629a02601f07d8aec4ea')) -print("initial space in directory hitting length limit") -show('data/ x/456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') + def testinitialdotlenlimit(self): + # initial dot hitting length limit + self.hybridencode( + (b'data/.23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/~2e23456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'dbe19cc6505b3515ab9228cebf877ad07075168f')) + + def testtrailingspacelenlimit(self): + # trailing space in filename hitting length limit + self.hybridencode( + (b'data/123456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-1234 '), + (b'dh/123456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxx' + b'0025dc73e04f97426db4893e3bf67d581dc6d066')) -print("initial dot in directory hitting length limit") -show('data/.x/456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') + def testtrailingdotlenlimit(self): + # trailing dot in filename hitting length limit + self.hybridencode( + (b'data/123456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-' + b'1234.'), + (b'dh/123456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxx' + b'85a16cf03ee7feba8a5abc626f1ba9886d01e89d')) + + def testinitialspacedirlenlimit(self): + # initial space in directory hitting length limit + self.hybridencode( + (b'data/ x/456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/~20x/456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'1b3a3b712b2ac00d6af14ae8b4c14fdbf904f516')) -print("trailing space in directory hitting length limit") -show('data/x /456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') + def testinitialdotdirlenlimit(self): + # initial dot in directory hitting length limit + self.hybridencode( + (b'data/.x/456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/~2ex/456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'39dbc4c193a5643a8936fc69c3363cd7ac91ab14')) + + def testtrailspacedirlenlimit(self): + # trailing space in directory hitting length limit + self.hybridencode( + (b'data/x /456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/x~20/456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'2253c341df0b5290790ad312cd8499850f2273e5')) -print("trailing dot in directory hitting length limit") -show('data/x./456789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') + def testtrailingdotdirlenlimit(self): + # trailing dot in directory hitting length limit + self.hybridencode( + (b'data/x./456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-12345'), + (b'dh/x~2e/456789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'cc0324d696d34562b44b5138db08ee1594ccc583')) -print("with directories that need direncoding, hitting length limit") -show('data/x.i/56789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/x.d/56789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/x.hg/5789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') + def testdirencodinglenlimit(self): + # with directories that need direncoding, hitting length limit + self.hybridencode( + (b'data/x.i/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-' + b'12345'), + (b'dh/x.i.hg/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxx' + b'a4c4399bdf81c67dbbbb7060aa0124d8dea94f74')) + self.hybridencode( + (b'data/x.d/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/x.d.hg/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxx' + b'1303fa90473b230615f5b3ea7b660e881ae5270a')) + self.hybridencode( + (b'data/x.hg/5789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/x.hg.hg/5789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxx' + b'26d724a8af68e7a4e4455e6602ea9adbd0eb801f')) -print("Windows reserved filenames, hitting length limit") -show('data/con/56789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/prn/56789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/aux/56789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/nul/56789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/com1/6789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/com9/6789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/lpt1/6789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') -show('data/lpt9/6789-123456789-123456789-123456789-123456789-' - 'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') + def testwinreservedfilenameslimit(self): + # Windows reserved filenames, hitting length limit + self.hybridencode( + (b'data/con/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-12345'), + (b'dh/co~6e/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'c0794d4f4c605a2617900eb2563d7113cf6ea7d3')) + self.hybridencode( + (b'data/prn/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/pr~6e/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'64db876e1a9730e27236cb9b167aff942240e932')) + self.hybridencode( + (b'data/aux/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/au~78/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'8a178558405ca6fb4bbd75446dfa186f06751a0d')) + self.hybridencode( + (b'data/nul/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/nu~6c/56789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'c5e51b6fec1bd07bd243b053a0c3f7209855b886')) + self.hybridencode( + (b'data/com1/6789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/co~6d1/6789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'32f5f44ece3bb62b9327369ca84cc19c86259fcd')) + self.hybridencode( + (b'data/com9/6789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/co~6d9/6789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'734360b28c66a3230f55849fe8926206d229f990')) + self.hybridencode( + (b'data/lpt1/6789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/lp~741/6789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'e6f16ab4b6b0637676b2842b3345c9836df46ef7')) + self.hybridencode( + (b'data/lpt9/6789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-12345'), + (b'dh/lp~749/6789-123456789-123456789-123456789-123456789' + b'-xxxxxxxxx-xxxxxxxxx-xxxxx' + b'a475814c51acead3e44f2ff801f0c4903f986157')) -print("non-reserved names, just not hitting limit") -show('data/123456789-123456789-123456789-123456789-123456789-' - '/com/com0/lpt/lpt0/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12345') - -print("hashed path with largest untruncated 1st dir") -show('data/12345678/-123456789-123456789-123456789-123456789-' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') - -print("hashed path with smallest truncated 1st dir") -show('data/123456789/123456789-123456789-123456789-123456789-' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testnonreservednolimit(self): + # non-reserved names, just not hitting limit + self.hybridencode( + (b'data/123456789-123456789-123456789-123456789-123456789-' + b'/com/com0/lpt/lpt0/' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'), + (b'data/123456789-123456789-123456789-123456789-123456789-' + b'/com/com0/lpt/lpt0/' + b'-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345')) -print("hashed path with largest untruncated two dirs") -show('data/12345678/12345678/9-123456789-123456789-123456789-' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashedpathuntrucfirst(self): + # hashed path with largest untruncated 1st dir + self.hybridencode( + (b'data/12345678/-123456789-123456789-123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/-123456789-123456789-123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxx4e9e9e384d00929a93b6835fbf976eb32321ff3c')) -print("hashed path with smallest truncated two dirs") -show('data/123456789/123456789/123456789-123456789-123456789-' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashedpathsmallesttrucdir(self): + # hashed path with smallest truncated 1st dir + self.hybridencode( + (b'data/123456789/123456789-123456789-123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/123456789-123456789-123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxx1f4e4ec5f2be76e109bfaa8e31c062fe426d5490')) -print("hashed path with largest untruncated three dirs") -show('data/12345678/12345678/12345678/89-123456789-123456789-' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashedlargesttwountruc(self): + # hashed path with largest untruncated two dirs + self.hybridencode( + (b'data/12345678/12345678/9-123456789-123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/9-123456789-123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxx3332d8329d969cf835542a9f2cbcfb385b6cf39d')) -print("hashed path with smallest truncated three dirs") -show('data/123456789/123456789/123456789/123456789-123456789-' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashedpathsmallesttrunctwodirs(self): + # hashed path with smallest truncated two dirs + self.hybridencode( + (b'data/123456789/123456789/123456789-123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/123456789-123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx' + b'9699559798247dffa18717138859be5f8874840e')) -print("hashed path with largest untruncated four dirs") -show('data/12345678/12345678/12345678/12345678/789-123456789-' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashuntruncthree(self): + # hashed path with largest untruncated three dirs + self.hybridencode( + (b'data/12345678/12345678/12345678/89-123456789-123456789-' + b'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-123456'), + (b'dh/12345678/12345678/12345678/89-123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxf0a2b053bb1369cce02f78c217d6a7aaea18c439')) -print("hashed path with smallest truncated four dirs") -show('data/123456789/123456789/123456789/123456789/123456789-' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashtruncthree(self): + # hashed path with smallest truncated three dirs + self.hybridencode( + (b'data/123456789/123456789/123456789/123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/123456789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-' + b'1c6f8284967384ec13985a046d3553179d9d03cd')) -print("hashed path with largest untruncated five dirs") -show('data/12345678/12345678/12345678/12345678/12345678/6789-' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashuntrucfour(self): + # hashed path with largest untruncated four dirs + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/789-123456789-hashed' + b'----xxxxxxxxx-xxxxxxx0d30c99049d8f0ff97b94d4ef302027e8d54c6fd')) -print("hashed path with smallest truncated five dirs") -show('data/123456789/123456789/123456789/123456789/123456789/' - 'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashtruncfour(self): + # hashed path with smallest truncated four dirs + self.hybridencode( + (b'data/123456789/123456789/123456789/123456789/123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/123456789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-x' + b'46162779e1a771810b37a737f82ae7ed33771402')) -print("hashed path with largest untruncated six dirs") -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/ed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashuntruncfive(self): + # hashed path with largest untruncated five dirs + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/6789-hashed' + b'----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/6789-hashed' + b'----xxxxxxxxx-xxxxxxxbfe752ddc8b003c2790c66a9f2eb1ea75c114390')) -print("hashed path with smallest truncated six dirs") -show('data/123456789/123456789/123456789/123456789/123456789/' - '123456789/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashtruncfive(self): + # hashed path with smallest truncated five dirs + self.hybridencode( + (b'data/123456789/123456789/123456789/123456789/123456789/hashed' + b'----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/hashed' + b'----xxxxxxxxx-xxxxxxxxx-xx' + b'b94c27b3532fa880cdd572b1c514785cab7b6ff2')) + + def testhashuntruncsix(self): + # hashed path with largest untruncated six dirs + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'ed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/' + b'ed----xxxxxxxxx-xxxxxxx' + b'cd8cc5483a0f3be409e0e5d4bf9e36e113c59235')) -print("hashed path with largest untruncated seven dirs") -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/xxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashtruncsix(self): + # hashed path with smallest truncated six dirs + self.hybridencode( + (b'data/123456789/123456789/123456789/123456789/123456789/' + b'123456789/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/' + b'xxxxxxxxx-xxxxxxxxx-xxx' + b'47dd6f616f833a142da00701b334cebbf640da06')) -print("hashed path with smallest truncated seven dirs") -show('data/123456789/123456789/123456789/123456789/123456789/' - '123456789/123456789/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashuntrunc7(self): + # hashed path with largest untruncated seven dirs + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/xxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/xxxxxx-xxxxxxx' + b'1c8ed635229fc22efe51035feeadeb4c8a0ecb82')) -print("hashed path with largest untruncated eight dirs") -print("(directory 8 is dropped because it hits _maxshortdirslen)") -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345678/xxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashtrunc7(self): + # hashed path with smallest truncated seven dirs + self.hybridencode( + (b'data/123456789/123456789/123456789/123456789/123456789/' + b'123456789/123456789/' + b'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/123' + b'45678/xxxxxxxxx-xxxx298ff7d33f8ce6db57930837ffea2fb2f48bb926')) -print("hashed path with smallest truncated eight dirs") -print("(directory 8 is dropped because it hits _maxshortdirslen)") -show('data/123456789/123456789/123456789/123456789/123456789/' - '123456789/123456789/123456789/xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashuntrunc8(self): + # hashed path with largest untruncated eight dirs + # (directory 8 is dropped because it hits _maxshortdirslen) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345678/xxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/1' + b'2345678/xxxxxxx-xxxxxxc8996ccd41b471f768057181a4d59d2febe7277d')) -print("hashed path with largest non-dropped directory 8") -print("(just not hitting the _maxshortdirslen boundary)") -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashtrunc8(self): + # hashed path with smallest truncated eight dirs + # (directory 8 is dropped because it hits _maxshortdirslen) + self.hybridencode( + (b'data/123456789/123456789/123456789/123456789/123456789/' + b'123456789/123456789/123456789/xxxxxxxxx-xxxxxxxxx-' + b'123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/xxxxxxxxx-xxxx' + b'4fa04a839a6bda93e1c21c713f2edcbd16e8890d')) -print("...adding one truncated char to dir 1..7 won't drop dir 8") -show('data/12345678x/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') -show('data/12345678/12345678x/12345678/12345678/12345678/12345' - '678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') -show('data/12345678/12345678/12345678x/12345678/12345678/12345' - '678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') -show('data/12345678/12345678/12345678/12345678x/12345678/12345' - '678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') -show('data/12345678/12345678/12345678/12345678/12345678x/12345' - '678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678x/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678x/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashnondropped8(self): + # hashed path with largest non-dropped directory 8 + # (just not hitting the _maxshortdirslen boundary) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789' + b'-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/12345/-xxxxxxx' + b'4d43d1ccaa20efbfe99ec779dc063611536ff2c5')) + # ...adding one truncated char to dir 1..7 won't drop dir 8 + self.hybridencode( + (b'data/12345678x/12345678/12345678/12345678/12345678/12345678' + b'/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/1234' + b'5678/12345/xxxxxxxx0f9efce65189cc60fd90fe4ffd49d7b58bbe0f2e')) + self.hybridencode( + (b'data/12345678/12345678x/12345678/12345678/12345678/12345678' + b'/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/1234' + b'5678/12345/xxxxxxxx945ca395708cafdd54a94501859beabd3e243921')) + self.hybridencode( + (b'data/12345678/12345678/12345678x/12345678/12345678/12345678/12' + b'345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/1234' + b'5678/12345/xxxxxxxxac62bf6898c4fd0502146074547c11caa751a327')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678x/12345678/12345678/12' + b'345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/1234' + b'5678/12345/xxxxxxxx2ae5a2baed7983fae8974d0ca06c6bf08b9aee92')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678x/12345678/' + b'12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/1234' + b'5678/12345/xxxxxxxx214aba07b6687532a43d1e9eaf6e88cfca96b68c')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678x' + b'/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/1234' + b'5678/12345/xxxxxxxxe7a022ae82f0f55cf4e0498e55ba59ea4ebb55bf')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678x/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345' + b'678/12345/xxxxxxxxb51ce61164996a80f36ce3cfe64b62d519aedae3')) -print("hashed path with shortest dropped directory 8") -print("(just hitting the _maxshortdirslen boundary)") -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/123456/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') - -print("hashed path that drops dir 8 due to dot or space at end is") -print("encoded, and thus causing to hit _maxshortdirslen") -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/1234./-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/1234 /-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashedpathshortestdropped8(self): + # hashed path with shortest dropped directory 8 + # (just hitting the _maxshortdirslen boundary) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/123456/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/xxxxxxxxx-xxxx' + b'11fa9873cc6c3215eae864528b5530a04efc6cfe')) -print("... with dir 8 short enough for encoding") -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12./xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12 /xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-123456') + def testhashedpathdropsdir8fortrailingdotspace(self): + # hashed path that drops dir 8 due to dot or space at end is + # encoded, and thus causing to hit _maxshortdirslen + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/1234./-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/-xxxxxxxxx-xxx' + b'602df9b45bec564e2e1f0645d5140dddcc76ed58')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/1234 /-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' + b'123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/-xxxxxxxxx-xxx' + b'd99ff212bc84b4d1f70cd6b0071e3ef69d4e12ce')) + # ... with dir 8 short enough for encoding + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/12./xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx' + b'-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/12~2e/' + b'xx-xxxxx7baeb5ed7f14a586ee1cacecdbcbff70032d1b3c')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/12 ' + b'/xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/12~20/' + b'xx-xxxxxcf79ca9795f77d7f75745da36807e5d772bd5182')) -print('''Extensions are replicated on hashed paths. Note that -we only get to encode files that end in .i or .d inside the -store. Encoded filenames are thus bound in length.''') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.345.i') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.345.d') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.3456.i') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.34567.i') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.345678.i') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.3456789.i') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.3456789-.i') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.3456789-1.i') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.3456789-12.i') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.3456789-123.i') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.3456789-1234.i') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.3456789-12345.i') -show('data/12345678/12345678/12345678/12345678/12345678/12345' - '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-' - '123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWX' - 'YZ-abcdefghjiklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPRSTU' - 'VWXYZ-1234567890-xxxxxxxxx-xxxxxxxxx-xxxxxxxx-xxxx' - 'xxxxx-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwww' - 'wwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww.i') + def testextensionsreplicatedonhashedpaths(self): + # Extensions are replicated on hashed paths. Note that + # we only get to encode files that end in .i or .d inside the + # store. Encoded filenames are thus bound in length. + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'45.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxxc10ad03b5755ed524f5286aab1815dfe07729438.i')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'45.d'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxx9eec83381f2b39ef5ac8b4ecdf2c94f7983f57c8.d')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'456.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxxb7796dc7d175cfb0bb8a7728f58f6ebec9042568.i')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'4567.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxxb515857a6bfeef017c4894d8df42458ac65d55b8.i')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'45678.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxxb05a0f247bc0a776211cd6a32ab714fd9cc09f2b.i')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'456789.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxxf192b48bff08d9e0e12035fb52bc58c70de72c94.i')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'456789-.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxx435551e0ed4c7b083b9ba83cee916670e02e80ad.i')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'456789-1.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxxa7f74eb98d8d58b716356dfd26e2f9aaa65d6a9a.i')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'456789-12.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxxed68d9bd43b931f0b100267fee488d65a0c66f62.i')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'456789-123.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxx5cea44de2b642d2ba2b4a30693ffb1049644d698.i')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'456789-1234.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxx68462f62a7f230b39c1b5400d73ec35920990b7e.i')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'456789-12345.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxx4cb852a314c6da240a83eec94761cdd71c6ec22e.i')) + self.hybridencode( + (b'data/12345678/12345678/12345678/12345678/12345678/12345678/' + b'12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3' + b'456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-' + b'abcdefghjiklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPRSTUVWXYZ' + b'-1234567890-xxxxxxxxx-xxxxxxxxx-xxxxxxxx' + b'-xxxxxxxxx-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww' + b'-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww.i'), + (b'dh/12345678/12345678/12345678/12345678/12345678/12345678/12' + b'345678/12345/-xxxxx93352aa50377751d9e5ebdf52da1e6e69a6887a6.i')) -print("paths outside data/ can be encoded") -show('metadata/dir/00manifest.i') -show('metadata/12345678/12345678/12345678/12345678/12345678/' - '12345678/12345678/12345678/12345678/12345678/12345678/' - '12345678/12345678/00manifest.i') + def testpathsoutsidedata(self): + # paths outside data/ can be encoded + self.hybridencode(b'metadata/dir/00manifest.i', + b'metadata/dir/00manifest.i') + self.hybridencode( + (b'metadata/12345678/12345678/12345678/12345678/12345678' + b'/12345678/12345678/12345678/12345678/12345678/12345678' + b'/12345678/12345678/00manifest.i'), + (b'dh/ata/12345678/12345678/12345678/12345678/12345678' + b'/12345678/12345678/00manife' + b'0a4da1f89aa2aa9eb0896eb451288419049781b4.i')) + +if __name__ == '__main__': + import silenttestrunner + silenttestrunner.main(__name__)
--- a/tests/test-hybridencode.py.out Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,500 +0,0 @@ -A = 'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}' -B = 'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}' - -uppercase char X is encoded as _x -A = 'data/ABCDEFGHIJKLMNOPQRSTUVWXYZ' -B = 'data/_a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z' - -underbar is doubled -A = 'data/_' -B = 'data/__' - -tilde is character-encoded -A = 'data/~' -B = 'data/~7e' - -characters in ASCII code range 1..31 -A = 'data/\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f' -B = 'data/~01~02~03~04~05~06~07~08~09~0a~0b~0c~0d~0e~0f~10~11~12~13~14~15~16~17~18~19~1a~1b~1c~1d~1e~1f' - -characters in ASCII code range 126..255 -A = 'data/~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f' -B = 'data/~7e~7f~80~81~82~83~84~85~86~87~88~89~8a~8b~8c~8d~8e~8f~90~91~92~93~94~95~96~97~98~99~9a~9b~9c~9d~9e~9f' - -A = 'data/\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf' -B = 'data/~a0~a1~a2~a3~a4~a5~a6~a7~a8~a9~aa~ab~ac~ad~ae~af~b0~b1~b2~b3~b4~b5~b6~b7~b8~b9~ba~bb~bc~bd~be~bf' - -A = 'data/\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf' -B = 'data/~c0~c1~c2~c3~c4~c5~c6~c7~c8~c9~ca~cb~cc~cd~ce~cf~d0~d1~d2~d3~d4~d5~d6~d7~d8~d9~da~db~dc~dd~de~df' - -A = 'data/\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff' -B = 'data/~e0~e1~e2~e3~e4~e5~e6~e7~e8~e9~ea~eb~ec~ed~ee~ef~f0~f1~f2~f3~f4~f5~f6~f7~f8~f9~fa~fb~fc~fd~fe~ff' - -Windows reserved characters -A = 'data/less <, greater >, colon :, double-quote ", backslash \\, pipe |, question-mark ?, asterisk *' -B = 'data/less ~3c, greater ~3e, colon ~3a, double-quote ~22, backslash ~5c, pipe ~7c, question-mark ~3f, asterisk ~2a' - -encoding directories ending in .hg, .i or .d with '.hg' suffix -A = 'data/x.h.i/x.hg/x.i/x.d/foo' -B = 'data/x.h.i.hg/x.hg.hg/x.i.hg/x.d.hg/foo' - -A = 'data/a.hg/a.i/a.d/foo' -B = 'data/a.hg.hg/a.i.hg/a.d.hg/foo' - -A = 'data/au.hg/au.i/au.d/foo' -B = 'data/au.hg.hg/au.i.hg/au.d.hg/foo' - -A = 'data/aux.hg/aux.i/aux.d/foo' -B = 'data/au~78.hg.hg/au~78.i.hg/au~78.d.hg/foo' - -A = 'data/auxy.hg/auxy.i/auxy.d/foo' -B = 'data/auxy.hg.hg/auxy.i.hg/auxy.d.hg/foo' - -but these are not encoded on *filenames* -A = 'data/foo/x.hg' -B = 'data/foo/x.hg' - -A = 'data/foo/x.i' -B = 'data/foo/x.i' - -A = 'data/foo/x.d' -B = 'data/foo/x.d' - -A = 'data/foo/a.hg' -B = 'data/foo/a.hg' - -A = 'data/foo/a.i' -B = 'data/foo/a.i' - -A = 'data/foo/a.d' -B = 'data/foo/a.d' - -A = 'data/foo/au.hg' -B = 'data/foo/au.hg' - -A = 'data/foo/au.i' -B = 'data/foo/au.i' - -A = 'data/foo/au.d' -B = 'data/foo/au.d' - -A = 'data/foo/aux.hg' -B = 'data/foo/au~78.hg' - -A = 'data/foo/aux.i' -B = 'data/foo/au~78.i' - -A = 'data/foo/aux.d' -B = 'data/foo/au~78.d' - -A = 'data/foo/auxy.hg' -B = 'data/foo/auxy.hg' - -A = 'data/foo/auxy.i' -B = 'data/foo/auxy.i' - -A = 'data/foo/auxy.d' -B = 'data/foo/auxy.d' - -plain .hg, .i and .d directories have the leading dot encoded -A = 'data/.hg/.i/.d/foo' -B = 'data/~2ehg.hg/~2ei.hg/~2ed.hg/foo' - -A = 'data/aux.bla/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c.i' -B = 'data/au~78.bla/bla.aux/pr~6e/_p_r_n/lpt/co~6d3/nu~6c/coma/foo._n_u_l/normal.c.i' - -A = 'data/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT.i' -B = 'dh/au~78/second/x.prn/fourth/fi~3afth/sixth/seventh/eighth/nineth/tenth/loremia20419e358ddff1bf8751e38288aff1d7c32ec05.i' - -A = 'data/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider.i' -B = 'dh/enterpri/openesba/contrib-/corba-bc/netbeans/wsdlexte/src/main/java/org.net7018f27961fdf338a598a40c4683429e7ffb9743.i' - -A = 'data/AUX.THE-QUICK-BROWN-FOX-JU:MPS-OVER-THE-LAZY-DOG-THE-QUICK-BROWN-FOX-JUMPS-OVER-THE-LAZY-DOG.TXT.i' -B = 'dh/au~78.the-quick-brown-fox-ju~3amps-over-the-lazy-dog-the-quick-brown-fox-jud4dcadd033000ab2b26eb66bae1906bcb15d4a70.i' - -A = 'data/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt' -B = 'dh/project_/resource/anotherl/followed/andanoth/andthenanextremelylongfilenaf93030515d9849cfdca52937c2204d19f83913e5.txt' - -A = 'data/Project.Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt' -B = 'dh/project_/resource/anotherl/followed/andanoth/andthenanextremelylongfilena0fd7c506f5c9d58204444fc67e9499006bd2d445.txt' - -A = 'data/foo.../foo / /a./_. /__/.x../ bla/.FOO/something.i' -B = 'data/foo..~2e/foo ~20/~20/a~2e/__.~20/____/~2ex.~2e/~20 bla/~2e_f_o_o/something.i' - -A = 'data/c/co/com/com0/com1/com2/com3/com4/com5/com6/com7/com8/com9' -B = 'data/c/co/com/com0/co~6d1/co~6d2/co~6d3/co~6d4/co~6d5/co~6d6/co~6d7/co~6d8/co~6d9' - -A = 'data/C/CO/COM/COM0/COM1/COM2/COM3/COM4/COM5/COM6/COM7/COM8/COM9' -B = 'data/_c/_c_o/_c_o_m/_c_o_m0/_c_o_m1/_c_o_m2/_c_o_m3/_c_o_m4/_c_o_m5/_c_o_m6/_c_o_m7/_c_o_m8/_c_o_m9' - -A = 'data/c.x/co.x/com.x/com0.x/com1.x/com2.x/com3.x/com4.x/com5.x/com6.x/com7.x/com8.x/com9.x' -B = 'data/c.x/co.x/com.x/com0.x/co~6d1.x/co~6d2.x/co~6d3.x/co~6d4.x/co~6d5.x/co~6d6.x/co~6d7.x/co~6d8.x/co~6d9.x' - -A = 'data/x.c/x.co/x.com0/x.com1/x.com2/x.com3/x.com4/x.com5/x.com6/x.com7/x.com8/x.com9' -B = 'data/x.c/x.co/x.com0/x.com1/x.com2/x.com3/x.com4/x.com5/x.com6/x.com7/x.com8/x.com9' - -A = 'data/cx/cox/comx/com0x/com1x/com2x/com3x/com4x/com5x/com6x/com7x/com8x/com9x' -B = 'data/cx/cox/comx/com0x/com1x/com2x/com3x/com4x/com5x/com6x/com7x/com8x/com9x' - -A = 'data/xc/xco/xcom0/xcom1/xcom2/xcom3/xcom4/xcom5/xcom6/xcom7/xcom8/xcom9' -B = 'data/xc/xco/xcom0/xcom1/xcom2/xcom3/xcom4/xcom5/xcom6/xcom7/xcom8/xcom9' - -A = 'data/l/lp/lpt/lpt0/lpt1/lpt2/lpt3/lpt4/lpt5/lpt6/lpt7/lpt8/lpt9' -B = 'data/l/lp/lpt/lpt0/lp~741/lp~742/lp~743/lp~744/lp~745/lp~746/lp~747/lp~748/lp~749' - -A = 'data/L/LP/LPT/LPT0/LPT1/LPT2/LPT3/LPT4/LPT5/LPT6/LPT7/LPT8/LPT9' -B = 'data/_l/_l_p/_l_p_t/_l_p_t0/_l_p_t1/_l_p_t2/_l_p_t3/_l_p_t4/_l_p_t5/_l_p_t6/_l_p_t7/_l_p_t8/_l_p_t9' - -A = 'data/l.x/lp.x/lpt.x/lpt0.x/lpt1.x/lpt2.x/lpt3.x/lpt4.x/lpt5.x/lpt6.x/lpt7.x/lpt8.x/lpt9.x' -B = 'data/l.x/lp.x/lpt.x/lpt0.x/lp~741.x/lp~742.x/lp~743.x/lp~744.x/lp~745.x/lp~746.x/lp~747.x/lp~748.x/lp~749.x' - -A = 'data/x.l/x.lp/x.lpt/x.lpt0/x.lpt1/x.lpt2/x.lpt3/x.lpt4/x.lpt5/x.lpt6/x.lpt7/x.lpt8/x.lpt9' -B = 'data/x.l/x.lp/x.lpt/x.lpt0/x.lpt1/x.lpt2/x.lpt3/x.lpt4/x.lpt5/x.lpt6/x.lpt7/x.lpt8/x.lpt9' - -A = 'data/lx/lpx/lptx/lpt0x/lpt1x/lpt2x/lpt3x/lpt4x/lpt5x/lpt6x/lpt7x/lpt8x/lpt9x' -B = 'data/lx/lpx/lptx/lpt0x/lpt1x/lpt2x/lpt3x/lpt4x/lpt5x/lpt6x/lpt7x/lpt8x/lpt9x' - -A = 'data/xl/xlp/xlpt/xlpt0/xlpt1/xlpt2/xlpt3/xlpt4/xlpt5/xlpt6/xlpt7/xlpt8/xlpt9' -B = 'data/xl/xlp/xlpt/xlpt0/xlpt1/xlpt2/xlpt3/xlpt4/xlpt5/xlpt6/xlpt7/xlpt8/xlpt9' - -A = 'data/con/p/pr/prn/a/au/aux/n/nu/nul' -B = 'data/co~6e/p/pr/pr~6e/a/au/au~78/n/nu/nu~6c' - -A = 'data/CON/P/PR/PRN/A/AU/AUX/N/NU/NUL' -B = 'data/_c_o_n/_p/_p_r/_p_r_n/_a/_a_u/_a_u_x/_n/_n_u/_n_u_l' - -A = 'data/con.x/p.x/pr.x/prn.x/a.x/au.x/aux.x/n.x/nu.x/nul.x' -B = 'data/co~6e.x/p.x/pr.x/pr~6e.x/a.x/au.x/au~78.x/n.x/nu.x/nu~6c.x' - -A = 'data/x.con/x.p/x.pr/x.prn/x.a/x.au/x.aux/x.n/x.nu/x.nul' -B = 'data/x.con/x.p/x.pr/x.prn/x.a/x.au/x.aux/x.n/x.nu/x.nul' - -A = 'data/conx/px/prx/prnx/ax/aux/auxx/nx/nux/nulx' -B = 'data/conx/px/prx/prnx/ax/au~78/auxx/nx/nux/nulx' - -A = 'data/xcon/xp/xpr/xprn/xa/xau/xaux/xn/xnu/xnul' -B = 'data/xcon/xp/xpr/xprn/xa/xau/xaux/xn/xnu/xnul' - -A = 'data/a./au./aux./auxy./aux.' -B = 'data/a~2e/au~2e/au~78~2e/auxy~2e/au~78~2e' - -A = 'data/c./co./con./cony./con.' -B = 'data/c~2e/co~2e/co~6e~2e/cony~2e/co~6e~2e' - -A = 'data/p./pr./prn./prny./prn.' -B = 'data/p~2e/pr~2e/pr~6e~2e/prny~2e/pr~6e~2e' - -A = 'data/n./nu./nul./nuly./nul.' -B = 'data/n~2e/nu~2e/nu~6c~2e/nuly~2e/nu~6c~2e' - -A = 'data/l./lp./lpt./lpt1./lpt1y./lpt1.' -B = 'data/l~2e/lp~2e/lpt~2e/lp~741~2e/lpt1y~2e/lp~741~2e' - -A = 'data/lpt9./lpt9y./lpt9.' -B = 'data/lp~749~2e/lpt9y~2e/lp~749~2e' - -A = 'data/com./com1./com1y./com1.' -B = 'data/com~2e/co~6d1~2e/com1y~2e/co~6d1~2e' - -A = 'data/com9./com9y./com9.' -B = 'data/co~6d9~2e/com9y~2e/co~6d9~2e' - -A = 'data/a /au /aux /auxy /aux ' -B = 'data/a~20/au~20/aux~20/auxy~20/aux~20' - -largest unhashed path -A = 'data/123456789-123456789-123456789-123456789-123456789-unhashed--xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'data/123456789-123456789-123456789-123456789-123456789-unhashed--xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' - -shortest hashed path -A = 'data/123456789-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/123456789-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxe9c55002b50bf5181e7a6fc1f60b126e2a6fcf71' - -changing one char in part that's hashed away produces a different hash -A = 'data/123456789-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxy-123456789-123456' -B = 'dh/123456789-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxd24fa4455faf8a94350c18e5eace7c2bb17af706' - -uppercase hitting length limit due to encoding -A = 'data/A23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/a23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxcbbc657029b41b94ed510d05feb6716a5c03bc6b' - -A = 'data/Z23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/z23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxx938f32a725c89512833fb96b6602dd9ebff51ddd' - -compare with lowercase not hitting limit -A = 'data/a23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'data/a23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' - -A = 'data/z23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'data/z23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' - -not hitting limit with any of these -A = 'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}xxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}xxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' - -underbar hitting length limit due to encoding -A = 'data/_23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/_23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxx9921a01af50feeabc060ce00eee4cba6efc31d2b' - -tilde hitting length limit due to encoding -A = 'data/~23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~7e23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx9cec6f97d569c10995f785720044ea2e4227481b' - -Windows reserved characters hitting length limit -A = 'data/<23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~3c23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxee67d8f275876ca1ef2500fc542e63c885c4e62d' - -A = 'data/>23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~3e23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx387a85a5b1547cc9136310c974df716818458ddb' - -A = 'data/:23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~3a23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx2e4154fb571d13d22399c58cc4ef4858e4b75999' - -A = 'data/"23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~2223456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxfc7e3ec7b0687ee06ed8c32fef0eb0c1980259f5' - -A = 'data/\\23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~5c23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx944e1f2b7110687e116e0d151328ac648b06ab4a' - -A = 'data/|23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~7c23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx28b23dd3fd0242946334126ab62bcd772aac32f4' - -A = 'data/?23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~3f23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxa263022d3994d2143d98f94f431eef8b5e7e0f8a' - -A = 'data/*23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~2a23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx0e7e6020e3c00ba7bb7893d84ca2966fbf53e140' - -initial space hitting length limit -A = 'data/ 23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~2023456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx92acbc78ef8c0b796111629a02601f07d8aec4ea' - -initial dot hitting length limit -A = 'data/.23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~2e23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxdbe19cc6505b3515ab9228cebf877ad07075168f' - -trailing space in filename hitting length limit -A = 'data/123456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-1234 ' -B = 'dh/123456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxx0025dc73e04f97426db4893e3bf67d581dc6d066' - -trailing dot in filename hitting length limit -A = 'data/123456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-1234.' -B = 'dh/123456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxx85a16cf03ee7feba8a5abc626f1ba9886d01e89d' - -initial space in directory hitting length limit -A = 'data/ x/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~20x/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx1b3a3b712b2ac00d6af14ae8b4c14fdbf904f516' - -initial dot in directory hitting length limit -A = 'data/.x/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/~2ex/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx39dbc4c193a5643a8936fc69c3363cd7ac91ab14' - -trailing space in directory hitting length limit -A = 'data/x /456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/x~20/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx2253c341df0b5290790ad312cd8499850f2273e5' - -trailing dot in directory hitting length limit -A = 'data/x./456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/x~2e/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxcc0324d696d34562b44b5138db08ee1594ccc583' - -with directories that need direncoding, hitting length limit -A = 'data/x.i/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/x.i.hg/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxa4c4399bdf81c67dbbbb7060aa0124d8dea94f74' - -A = 'data/x.d/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/x.d.hg/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxx1303fa90473b230615f5b3ea7b660e881ae5270a' - -A = 'data/x.hg/5789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/x.hg.hg/5789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxx26d724a8af68e7a4e4455e6602ea9adbd0eb801f' - -Windows reserved filenames, hitting length limit -A = 'data/con/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/co~6e/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxc0794d4f4c605a2617900eb2563d7113cf6ea7d3' - -A = 'data/prn/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/pr~6e/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx64db876e1a9730e27236cb9b167aff942240e932' - -A = 'data/aux/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/au~78/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx8a178558405ca6fb4bbd75446dfa186f06751a0d' - -A = 'data/nul/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/nu~6c/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxc5e51b6fec1bd07bd243b053a0c3f7209855b886' - -A = 'data/com1/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/co~6d1/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx32f5f44ece3bb62b9327369ca84cc19c86259fcd' - -A = 'data/com9/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/co~6d9/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx734360b28c66a3230f55849fe8926206d229f990' - -A = 'data/lpt1/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/lp~741/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxe6f16ab4b6b0637676b2842b3345c9836df46ef7' - -A = 'data/lpt9/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'dh/lp~749/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxa475814c51acead3e44f2ff801f0c4903f986157' - -non-reserved names, just not hitting limit -A = 'data/123456789-123456789-123456789-123456789-123456789-/com/com0/lpt/lpt0/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' -B = 'data/123456789-123456789-123456789-123456789-123456789-/com/com0/lpt/lpt0/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345' - -hashed path with largest untruncated 1st dir -A = 'data/12345678/-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxx4e9e9e384d00929a93b6835fbf976eb32321ff3c' - -hashed path with smallest truncated 1st dir -A = 'data/123456789/123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxx1f4e4ec5f2be76e109bfaa8e31c062fe426d5490' - -hashed path with largest untruncated two dirs -A = 'data/12345678/12345678/9-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/9-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxx3332d8329d969cf835542a9f2cbcfb385b6cf39d' - -hashed path with smallest truncated two dirs -A = 'data/123456789/123456789/123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx9699559798247dffa18717138859be5f8874840e' - -hashed path with largest untruncated three dirs -A = 'data/12345678/12345678/12345678/89-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/89-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxf0a2b053bb1369cce02f78c217d6a7aaea18c439' - -hashed path with smallest truncated three dirs -A = 'data/123456789/123456789/123456789/123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-1c6f8284967384ec13985a046d3553179d9d03cd' - -hashed path with largest untruncated four dirs -A = 'data/12345678/12345678/12345678/12345678/789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/789-123456789-hashed----xxxxxxxxx-xxxxxxx0d30c99049d8f0ff97b94d4ef302027e8d54c6fd' - -hashed path with smallest truncated four dirs -A = 'data/123456789/123456789/123456789/123456789/123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/123456789-hashed----xxxxxxxxx-xxxxxxxxx-x46162779e1a771810b37a737f82ae7ed33771402' - -hashed path with largest untruncated five dirs -A = 'data/12345678/12345678/12345678/12345678/12345678/6789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/6789-hashed----xxxxxxxxx-xxxxxxxbfe752ddc8b003c2790c66a9f2eb1ea75c114390' - -hashed path with smallest truncated five dirs -A = 'data/123456789/123456789/123456789/123456789/123456789/hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/hashed----xxxxxxxxx-xxxxxxxxx-xxb94c27b3532fa880cdd572b1c514785cab7b6ff2' - -hashed path with largest untruncated six dirs -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/ed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/ed----xxxxxxxxx-xxxxxxxcd8cc5483a0f3be409e0e5d4bf9e36e113c59235' - -hashed path with smallest truncated six dirs -A = 'data/123456789/123456789/123456789/123456789/123456789/123456789/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxxxx-xxxxxxxxx-xxx47dd6f616f833a142da00701b334cebbf640da06' - -hashed path with largest untruncated seven dirs -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxx-xxxxxxx1c8ed635229fc22efe51035feeadeb4c8a0ecb82' - -hashed path with smallest truncated seven dirs -A = 'data/123456789/123456789/123456789/123456789/123456789/123456789/123456789/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxxxx-xxxx298ff7d33f8ce6db57930837ffea2fb2f48bb926' - -hashed path with largest untruncated eight dirs -(directory 8 is dropped because it hits _maxshortdirslen) -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxx-xxxxxxc8996ccd41b471f768057181a4d59d2febe7277d' - -hashed path with smallest truncated eight dirs -(directory 8 is dropped because it hits _maxshortdirslen) -A = 'data/123456789/123456789/123456789/123456789/123456789/123456789/123456789/123456789/xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxxxx-xxxx4fa04a839a6bda93e1c21c713f2edcbd16e8890d' - -hashed path with largest non-dropped directory 8 -(just not hitting the _maxshortdirslen boundary) -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxx4d43d1ccaa20efbfe99ec779dc063611536ff2c5' - -...adding one truncated char to dir 1..7 won't drop dir 8 -A = 'data/12345678x/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxx0f9efce65189cc60fd90fe4ffd49d7b58bbe0f2e' - -A = 'data/12345678/12345678x/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxx945ca395708cafdd54a94501859beabd3e243921' - -A = 'data/12345678/12345678/12345678x/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxac62bf6898c4fd0502146074547c11caa751a327' - -A = 'data/12345678/12345678/12345678/12345678x/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxx2ae5a2baed7983fae8974d0ca06c6bf08b9aee92' - -A = 'data/12345678/12345678/12345678/12345678/12345678x/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxx214aba07b6687532a43d1e9eaf6e88cfca96b68c' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678x/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxe7a022ae82f0f55cf4e0498e55ba59ea4ebb55bf' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678x/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxb51ce61164996a80f36ce3cfe64b62d519aedae3' - -hashed path with shortest dropped directory 8 -(just hitting the _maxshortdirslen boundary) -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/123456/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxxxx-xxxx11fa9873cc6c3215eae864528b5530a04efc6cfe' - -hashed path that drops dir 8 due to dot or space at end is -encoded, and thus causing to hit _maxshortdirslen -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/1234./-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/-xxxxxxxxx-xxx602df9b45bec564e2e1f0645d5140dddcc76ed58' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/1234 /-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/-xxxxxxxxx-xxxd99ff212bc84b4d1f70cd6b0071e3ef69d4e12ce' - -... with dir 8 short enough for encoding -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12./xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12~2e/xx-xxxxx7baeb5ed7f14a586ee1cacecdbcbff70032d1b3c' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12 /xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12~20/xx-xxxxxcf79ca9795f77d7f75745da36807e5d772bd5182' - -Extensions are replicated on hashed paths. Note that -we only get to encode files that end in .i or .d inside the -store. Encoded filenames are thus bound in length. -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.345.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxc10ad03b5755ed524f5286aab1815dfe07729438.i' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.345.d' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx9eec83381f2b39ef5ac8b4ecdf2c94f7983f57c8.d' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxb7796dc7d175cfb0bb8a7728f58f6ebec9042568.i' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.34567.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxb515857a6bfeef017c4894d8df42458ac65d55b8.i' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.345678.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxb05a0f247bc0a776211cd6a32ab714fd9cc09f2b.i' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxf192b48bff08d9e0e12035fb52bc58c70de72c94.i' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx435551e0ed4c7b083b9ba83cee916670e02e80ad.i' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-1.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxa7f74eb98d8d58b716356dfd26e2f9aaa65d6a9a.i' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxed68d9bd43b931f0b100267fee488d65a0c66f62.i' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-123.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx5cea44de2b642d2ba2b4a30693ffb1049644d698.i' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-1234.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx68462f62a7f230b39c1b5400d73ec35920990b7e.i' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx4cb852a314c6da240a83eec94761cdd71c6ec22e.i' - -A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPRSTUVWXYZ-1234567890-xxxxxxxxx-xxxxxxxxx-xxxxxxxx-xxxxxxxxx-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww.i' -B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx93352aa50377751d9e5ebdf52da1e6e69a6887a6.i' - -paths outside data/ can be encoded -A = 'metadata/dir/00manifest.i' -B = 'metadata/dir/00manifest.i' - -A = 'metadata/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/00manifest.i' -B = 'dh/ata/12345678/12345678/12345678/12345678/12345678/12345678/12345678/00manife0a4da1f89aa2aa9eb0896eb451288419049781b4.i' -
--- a/tests/test-impexp-branch.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-impexp-branch.t Thu Jul 19 13:55:54 2018 -0400 @@ -75,8 +75,8 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved >>> import re >>> p = open('../r1.patch', 'rb').read() - >>> p = re.sub(r'Parent\s+', 'Parent ', p) - >>> open('../r1-ws.patch', 'wb').write(p) + >>> p = re.sub(br'Parent\s+', b'Parent ', p) + >>> open('../r1-ws.patch', 'wb').write(p) and None $ hg import --exact ../r1-ws.patch applying ../r1-ws.patch
--- a/tests/test-import-context.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-import-context.t Thu Jul 19 13:55:54 2018 -0400 @@ -10,7 +10,7 @@ > fp = open(path, 'wb') > for i, pattern in enumerate(patterns): > count = int(pattern[0:-1]) - > char = pattern[-1] + '\n' + > char = pattern[-1].encode('utf8') + b'\n' > if not lasteol and i == len(patterns) - 1: > fp.write((char*count)[:-1]) > else:
--- a/tests/test-import-git.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-import-git.t Thu Jul 19 13:55:54 2018 -0400 @@ -566,7 +566,7 @@ >>> fp = open('binary.diff', 'rb') >>> data = fp.read() >>> fp.close() - >>> open('binary.diff', 'wb').write(data.replace(b'\n', b'\r\n')) + >>> open('binary.diff', 'wb').write(data.replace(b'\n', b'\r\n')) and None $ rm binary2 $ hg import --no-commit binary.diff applying binary.diff
--- a/tests/test-import-merge.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-import-merge.t Thu Jul 19 13:55:54 2018 -0400 @@ -143,7 +143,7 @@ $ hg export 2 | head -7 > ../a.patch $ hg export tip > out >>> apatch = open("../a.patch", "ab") - >>> apatch.write("".join(open("out").readlines()[7:])) + >>> apatch.write(b"".join(open("out", 'rb').readlines()[7:])) and None $ cd .. $ hg clone -qr0 repo3 repo3-clone
--- a/tests/test-known.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-known.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - = Test the known() protocol function = Create a test repository:
--- a/tests/test-largefiles-wireproto.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-largefiles-wireproto.t Thu Jul 19 13:55:54 2018 -0400 @@ -26,7 +26,7 @@ > patterns=glob:**.dat > usercache=${USERCACHE} > [web] - > allow_archive = zip + > allow-archive = zip > [hooks] > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status" > EOF
--- a/tests/test-largefiles.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-largefiles.t Thu Jul 19 13:55:54 2018 -0400 @@ -1860,6 +1860,8 @@ $ hg add --normal new-largefile new-largefile: up to 69 MB of RAM may be required to manage this file (use 'hg revert new-largefile' to cancel the pending addition) + $ hg revert new-largefile + $ hg --config ui.large-file-limit=22M add --normal new-largefile Test explicit commit of switch between normal and largefile - make sure both the add and the remove is committed.
--- a/tests/test-lfs-largefiles.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-lfs-largefiles.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,4 +1,4 @@ -#require no-reposimplestore +#require no-reposimplestore no-chg This tests the interaction between the largefiles and lfs extensions, and conversion from largefiles -> lfs.
--- a/tests/test-lfs-pointer.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-lfs-pointer.py Thu Jul 19 13:55:54 2018 -0400 @@ -6,6 +6,10 @@ # make it runnable using python directly without run-tests.py sys.path[0:0] = [os.path.join(os.path.dirname(__file__), '..')] +# Import something from Mercurial, so the module loader gets initialized. +from mercurial import pycompat +del pycompat # unused for now + from hgext.lfs import pointer def tryparse(text): @@ -14,28 +18,28 @@ r = pointer.deserialize(text) print('ok') except Exception as ex: - print(ex) + print((b'%s' % ex).decode('ascii')) if r: text2 = r.serialize() if text2 != text: print('reconstructed text differs') return r -t = ('version https://git-lfs.github.com/spec/v1\n' - 'oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1' - '258daaa5e2ca24d17e2393\n' - 'size 12345\n' - 'x-foo extra-information\n') +t = (b'version https://git-lfs.github.com/spec/v1\n' + b'oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1' + b'258daaa5e2ca24d17e2393\n' + b'size 12345\n' + b'x-foo extra-information\n') -tryparse('') +tryparse(b'') tryparse(t) -tryparse(t.replace('git-lfs', 'unknown')) -tryparse(t.replace('v1\n', 'v1\n\n')) -tryparse(t.replace('sha256', 'ahs256')) -tryparse(t.replace('sha256:', '')) -tryparse(t.replace('12345', '0x12345')) -tryparse(t.replace('extra-information', 'extra\0information')) -tryparse(t.replace('extra-information', 'extra\ninformation')) -tryparse(t.replace('x-foo', 'x_foo')) -tryparse(t.replace('oid', 'blobid')) -tryparse(t.replace('size', 'size-bytes').replace('oid', 'object-id')) +tryparse(t.replace(b'git-lfs', b'unknown')) +tryparse(t.replace(b'v1\n', b'v1\n\n')) +tryparse(t.replace(b'sha256', b'ahs256')) +tryparse(t.replace(b'sha256:', b'')) +tryparse(t.replace(b'12345', b'0x12345')) +tryparse(t.replace(b'extra-information', b'extra\0information')) +tryparse(t.replace(b'extra-information', b'extra\ninformation')) +tryparse(t.replace(b'x-foo', b'x_foo')) +tryparse(t.replace(b'oid', b'blobid')) +tryparse(t.replace(b'size', b'size-bytes').replace(b'oid', b'object-id'))
--- a/tests/test-lfs-pointer.py.out Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-lfs-pointer.py.out Thu Jul 19 13:55:54 2018 -0400 @@ -1,12 +1,12 @@ -missed keys: oid, size +missing lfs pointer keys: oid, size ok -unexpected value: version='https://unknown.github.com/spec/v1' +unexpected lfs pointer value: version='https://unknown.github.com/spec/v1' cannot parse git-lfs text: 'version https://git-lfs.github.com/spec/v1\n\noid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393\nsize 12345\nx-foo extra-information\n' -unexpected value: oid='ahs256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393' -unexpected value: oid='4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393' -unexpected value: size='0x12345' +unexpected lfs pointer value: oid='ahs256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393' +unexpected lfs pointer value: oid='4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393' +unexpected lfs pointer value: size='0x12345' ok cannot parse git-lfs text: 'version https://git-lfs.github.com/spec/v1\noid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393\nsize 12345\nx-foo extra\ninformation\n' -unexpected key: x_foo -missed keys: oid -missed keys: oid, size +unexpected lfs pointer key: x_foo +missing lfs pointer keys: oid +missing lfs pointer keys: oid, size
--- a/tests/test-lfs-serve-access.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-lfs-serve-access.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,4 +1,4 @@ -#require serve no-reposimplestore +#require serve no-reposimplestore no-chg $ cat >> $HGRCPATH <<EOF > [extensions]
--- a/tests/test-lfs-serve.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-lfs-serve.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,5 @@ #testcases lfsremote-on lfsremote-off -#require serve no-reposimplestore +#require serve no-reposimplestore no-chg This test splits `hg serve` with and without using the extension into separate tests cases. The tests are broken down as follows, where "LFS"/"No-LFS"
--- a/tests/test-lfs-test-server.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-lfs-test-server.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,4 +1,4 @@ -#require no-reposimplestore +#require no-reposimplestore no-chg #testcases git-server hg-server #if git-server @@ -880,7 +880,6 @@ $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob) #endif - $ rm $DAEMON_PIDS $ mkdir $TESTTMP/lfs-server2 $ cd $TESTTMP/lfs-server2 #if no-windows git-server
--- a/tests/test-lfs.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-lfs.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,4 +1,4 @@ -#require no-reposimplestore +#require no-reposimplestore no-chg # Initial setup @@ -515,6 +515,21 @@ d: binary=False b55353847f02 tip +Binary blobs don't need to be present to be skipped in filesets. (And their +absence doesn't cause an abort.) + + $ rm .hg/store/lfs/objects/96/a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7 + $ rm .hg/store/lfs/objects/92/f76135a4baf4faccb8586a60faf830c2bdfce147cefa188aaf4b790bd01b7e + + $ hg files --debug -r . 'set:eol("unix")' --config 'experimental.lfs.disableusercache=True' + lfs: found c04b5bb1a5b2eb3e9cd4805420dba5a9d133da5b7adeeafb5474c4adae9faa80 in the local lfs store + 2 b + lfs: found 5dde896887f6754c9b15bfe3a441ae4806df2fde94001311e08bf110622e0bbe in the local lfs store + + $ hg files --debug -r . 'set:binary()' --config 'experimental.lfs.disableusercache=True' + 2 a + 3 c + $ cd .. # Test fctx.cmp fastpath - diff without LFS blobs
--- a/tests/test-log.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-log.t Thu Jul 19 13:55:54 2018 -0400 @@ -1943,7 +1943,7 @@ test that parent prevent a changeset to be hidden $ hg up 1 -q --hidden - updating to a hidden changeset a765632148dc + updated to hidden changeset a765632148dc (hidden revision 'a765632148dc' is pruned) $ hg log --template='{rev}:{node}\n' 1:a765632148dc55d38c35c4f247c618701886cb2f
--- a/tests/test-logexchange.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-logexchange.t Thu Jul 19 13:55:54 2018 -0400 @@ -2,10 +2,10 @@ ============================================= $ cat >> $HGRCPATH << EOF + > [ui] + > ssh = $PYTHON "$TESTDIR/dummyssh" > [alias] > glog = log -G -T '{rev}:{node|short} {desc}' - > [experimental] - > remotenames = True > [extensions] > remotenames = > show = @@ -52,7 +52,13 @@ $ cd .. - $ hg clone server client + $ hg clone ssh://user@dummy/server client + requesting all changes + adding changesets + adding manifests + adding file changes + added 9 changesets with 9 changes to 9 files (+1 heads) + new changesets 18d04c59bb5d:3e1487808078 updating to branch default 8 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -333,3 +339,59 @@ default/bar 6:87d6d6676308 default/foo 3:62615734edd5 * foo 8:3e1487808078 + +Testing the remotenames sychronization during `hg push` +------------------------------------------------------- + + $ cd ../server/ + $ hg bookmark foo + moving bookmark 'foo' forward from 62615734edd5 + +After the push, default/foo should move to rev 8 + $ cd ../client/ + $ hg push + pushing to ssh://user@dummy/server + searching for changes + no changes found + [1] + $ hg log -Gr 'remotenames()' + @ changeset: 8:3e1487808078 + : branch: wat + : bookmark: foo + : tag: tip + : remote bookmark: default/foo + : hoisted name: foo + : remote branch: $TESTTMP/server2/wat + : remote branch: default/wat + : parent: 4:aa98ab95a928 + : user: test + : date: Thu Jan 01 00:00:00 1970 +0000 + : summary: added bar + : + : o changeset: 7:ec2426147f0e + : | remote branch: $TESTTMP/server2/default + : | remote branch: default/default + : | user: test + : | date: Thu Jan 01 00:00:00 1970 +0000 + : | summary: Added h + : | + : o changeset: 6:87d6d6676308 + :/ remote bookmark: $TESTTMP/server2/bar + : remote bookmark: default/bar + : hoisted name: bar + : user: test + : date: Thu Jan 01 00:00:00 1970 +0000 + : summary: Added g + : + o changeset: 3:62615734edd5 + | remote bookmark: $TESTTMP/server2/foo + ~ user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: Added d + + $ hg bookmarks + $TESTTMP/server2/bar 6:87d6d6676308 + $TESTTMP/server2/foo 3:62615734edd5 + default/bar 6:87d6d6676308 + default/foo 8:3e1487808078 + * foo 8:3e1487808078
--- a/tests/test-manifest.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-manifest.t Thu Jul 19 13:55:54 2018 -0400 @@ -37,11 +37,23 @@ $ hg files -r . -X b a l + $ hg files -T '{path} {size} {flags}\n' + a 2 + b/a 2 x + l 1 l + $ hg files -T '{path} {node|shortest}\n' -r. + a 5bdc + b/a 5bdc + l 5bdc $ hg manifest -v 644 a 755 * b/a 644 @ l + $ hg manifest -T '{path} {rev}\n' + a 1 + b/a 1 + l 1 $ hg manifest --debug b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 644 a
--- a/tests/test-merge-criss-cross.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-merge-criss-cross.t Thu Jul 19 13:55:54 2018 -0400 @@ -410,6 +410,21 @@ 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) +Test the greatest common ancestor returning multiple changesets + + $ hg log -r 'heads(commonancestors(head()))' + changeset: 1:0f6b37dbe527 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 1 first change f1 + + changeset: 2:d1d156401c1b + parent: 0:40494bf2444c + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 first change f2 + + $ cd .. http://stackoverflow.com/questions/9350005/how-do-i-specify-a-merge-base-to-use-in-a-hg-merge/9430810
--- a/tests/test-merge-tools.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-merge-tools.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,7 +1,8 @@ test merge-tools configuration - mostly exercising filemerge.py $ unset HGMERGE # make sure HGMERGE doesn't interfere with the test - $ hg init + $ hg init repo + $ cd repo revision 0 @@ -328,6 +329,183 @@ # hg resolve --list R f +executable set to python script that succeeds: + + $ cat > "$TESTTMP/myworkingmerge.py" <<EOF + > def myworkingmergefn(ui, repo, args, **kwargs): + > return False + > EOF + $ beforemerge + [merge-tools] + false.whatever= + true.priority=1 + true.executable=cat + # hg update -C 1 + $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:myworkingmergefn" + merging f + 0 files updated, 1 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ aftermerge + # cat f + revision 1 + space + # hg stat + M f + # hg resolve --list + R f + +executable set to python script that fails: + + $ cat > "$TESTTMP/mybrokenmerge.py" <<EOF + > def mybrokenmergefn(ui, repo, args, **kwargs): + > ui.write(b"some fail message\n") + > return True + > EOF + $ beforemerge + [merge-tools] + false.whatever= + true.priority=1 + true.executable=cat + # hg update -C 1 + $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/mybrokenmerge.py:mybrokenmergefn" + merging f + some fail message + abort: $TESTTMP/mybrokenmerge.py hook failed + [255] + $ aftermerge + # cat f + revision 1 + space + # hg stat + ? f.orig + # hg resolve --list + U f + +executable set to python script that is missing function: + + $ beforemerge + [merge-tools] + false.whatever= + true.priority=1 + true.executable=cat + # hg update -C 1 + $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:missingFunction" + merging f + abort: $TESTTMP/myworkingmerge.py does not have function: missingFunction + [255] + $ aftermerge + # cat f + revision 1 + space + # hg stat + ? f.orig + # hg resolve --list + U f + +executable set to missing python script: + + $ beforemerge + [merge-tools] + false.whatever= + true.priority=1 + true.executable=cat + # hg update -C 1 + $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/missingpythonscript.py:mergefn" + merging f + abort: loading python merge script failed: $TESTTMP/missingpythonscript.py + [255] + $ aftermerge + # cat f + revision 1 + space + # hg stat + ? f.orig + # hg resolve --list + U f + +executable set to python script but callable function is missing: + + $ beforemerge + [merge-tools] + false.whatever= + true.priority=1 + true.executable=cat + # hg update -C 1 + $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py" + abort: invalid 'python:' syntax: python:$TESTTMP/myworkingmerge.py + [255] + $ aftermerge + # cat f + revision 1 + space + # hg stat + # hg resolve --list + U f + +executable set to python script but callable function is empty string: + + $ beforemerge + [merge-tools] + false.whatever= + true.priority=1 + true.executable=cat + # hg update -C 1 + $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:" + abort: invalid 'python:' syntax: python:$TESTTMP/myworkingmerge.py: + [255] + $ aftermerge + # cat f + revision 1 + space + # hg stat + # hg resolve --list + U f + +executable set to python script but callable function is missing and path contains colon: + + $ beforemerge + [merge-tools] + false.whatever= + true.priority=1 + true.executable=cat + # hg update -C 1 + $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/some:dir/myworkingmerge.py" + abort: invalid 'python:' syntax: python:$TESTTMP/some:dir/myworkingmerge.py + [255] + $ aftermerge + # cat f + revision 1 + space + # hg stat + # hg resolve --list + U f + +executable set to python script filename that contains spaces: + + $ mkdir -p "$TESTTMP/my path" + $ cat > "$TESTTMP/my path/my working merge with spaces in filename.py" <<EOF + > def myworkingmergefn(ui, repo, args, **kwargs): + > return False + > EOF + $ beforemerge + [merge-tools] + false.whatever= + true.priority=1 + true.executable=cat + # hg update -C 1 + $ hg merge -r 2 --config "merge-tools.true.executable=python:$TESTTMP/my path/my working merge with spaces in filename.py:myworkingmergefn" + merging f + 0 files updated, 1 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ aftermerge + # cat f + revision 1 + space + # hg stat + M f + # hg resolve --list + R f + #if unix-permissions environment variables in true.executable are handled: @@ -1446,7 +1624,7 @@ merging f labellocal: "working copy" labelother: "merge rev" - output (arg): "$TESTTMP/f" + output (arg): "$TESTTMP/repo/f" output (contents): <<<<<<< working copy: uitmpl 1 revision 1 @@ -1485,7 +1663,7 @@ merging f labellocal: "working copy: tooltmpl ef83787e2614" labelother: "merge rev: tooltmpl 0185f4e0cf02" - output (arg): "$TESTTMP/f" + output (arg): "$TESTTMP/repo/f" output (contents): <<<<<<< working copy: tooltmpl ef83787e2614 revision 1 @@ -1585,7 +1763,7 @@ $ hg update -q -C 2 $ hg merge -y -r tip --tool echo --config merge-tools.echo.args='$base $local $other $output' merging f and f.txt to f.txt - */f~base.* */f~local.*.txt */f~other.*.txt $TESTTMP/f.txt (glob) + */f~base.* */f~local.*.txt */f~other.*.txt $TESTTMP/repo/f.txt (glob) 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -1600,7 +1778,7 @@ > --config merge-tools.echo.args='$base $local $other $output' \ > --config experimental.mergetempdirprefix=$TESTTMP/hgmerge. merging f and f.txt to f.txt - $TESTTMP/hgmerge.*/f~base $TESTTMP/hgmerge.*/f~local.txt $TESTTMP/hgmerge.*/f~other.txt $TESTTMP/f.txt (glob) + $TESTTMP/hgmerge.*/f~base $TESTTMP/hgmerge.*/f~local.txt $TESTTMP/hgmerge.*/f~other.txt $TESTTMP/repo/f.txt (glob) 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -1668,3 +1846,5 @@ couldn't find merge tool true (for pattern f) couldn't find merge tool true f = false + + $ cd ..
--- a/tests/test-merge6.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-merge6.t Thu Jul 19 13:55:54 2018 -0400 @@ -42,6 +42,7 @@ adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) new changesets b90e70beeb58 + 1 local changesets published (run 'hg heads' to see heads, 'hg merge' to merge) $ hg merge 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-minifileset.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-minifileset.py Thu Jul 19 13:55:54 2018 -0400 @@ -18,21 +18,24 @@ if f(*args): print('unexpected: %r should exclude %r' % (text, args)) -check('all()', [('a.php', 123), ('b.txt', 0)], []) -check('none()', [], [('a.php', 123), ('b.txt', 0)]) -check('!!!!((!(!!all())))', [], [('a.php', 123), ('b.txt', 0)]) +check(b'all()', [(b'a.php', 123), (b'b.txt', 0)], []) +check(b'none()', [], [(b'a.php', 123), (b'b.txt', 0)]) +check(b'!!!!((!(!!all())))', [], [(b'a.php', 123), (b'b.txt', 0)]) -check('"path:a" & (**.b | **.c)', [('a/b.b', 0), ('a/c.c', 0)], [('b/c.c', 0)]) -check('(path:a & **.b) | **.c', - [('a/b.b', 0), ('a/c.c', 0), ('b/c.c', 0)], []) +check(b'"path:a" & (**.b | **.c)', + [(b'a/b.b', 0), (b'a/c.c', 0)], [(b'b/c.c', 0)]) +check(b'(path:a & **.b) | **.c', + [(b'a/b.b', 0), (b'a/c.c', 0), (b'b/c.c', 0)], []) -check('**.bin - size("<20B")', [('b.bin', 21)], [('a.bin', 11), ('b.txt', 21)]) +check(b'**.bin - size("<20B")', + [(b'b.bin', 21)], [(b'a.bin', 11), (b'b.txt', 21)]) -check('!!**.bin or size(">20B") + "path:bin" or !size(">10")', - [('a.bin', 11), ('b.txt', 21), ('bin/abc', 11)], - [('a.notbin', 11), ('b.txt', 11), ('bin2/abc', 11)]) +check(b'!!**.bin or size(">20B") + "path:bin" or !size(">10")', + [(b'a.bin', 11), (b'b.txt', 21), (b'bin/abc', 11)], + [(b'a.notbin', 11), (b'b.txt', 11), (b'bin2/abc', 11)]) -check('(**.php and size(">10KB")) | **.zip | ("path:bin" & !"path:bin/README") ' - ' | size(">1M")', - [('a.php', 15000), ('a.zip', 0), ('bin/a', 0), ('bin/README', 1e7)], - [('a.php', 5000), ('b.zip2', 0), ('t/bin/a', 0), ('bin/README', 1)]) +check( + b'(**.php and size(">10KB")) | **.zip | ("path:bin" & !"path:bin/README") ' + b' | size(">1M")', + [(b'a.php', 15000), (b'a.zip', 0), (b'bin/a', 0), (b'bin/README', 1e7)], + [(b'a.php', 5000), (b'b.zip2', 0), (b't/bin/a', 0), (b'bin/README', 1)])
--- a/tests/test-minirst.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-minirst.py Thu Jul 19 13:55:54 2018 -0400 @@ -1,11 +1,13 @@ from __future__ import absolute_import, print_function -import pprint from mercurial import ( minirst, ) +from mercurial.utils import ( + stringutil, +) def debugformat(text, form, **kwargs): - if form == 'html': + if form == b'html': print("html format:") out = minirst.format(text, style=form, **kwargs) else: @@ -14,11 +16,11 @@ print("-" * 70) if type(out) == tuple: - print(out[0][:-1]) + print(out[0][:-1].decode('utf8')) print("-" * 70) - pprint.pprint(out[1]) + print(stringutil.pprint(out[1]).decode('utf8')) else: - print(out[:-1]) + print(out[:-1].decode('utf8')) print("-" * 70) print() @@ -26,7 +28,7 @@ print("== %s ==" % title) debugformat(text, 60, **kwargs) debugformat(text, 30, **kwargs) - debugformat(text, 'html', **kwargs) + debugformat(text, b'html', **kwargs) paragraphs = b""" This is some text in the first paragraph. @@ -37,7 +39,7 @@ \n \n \nThe third and final paragraph. """ -debugformats(b'paragraphs', paragraphs) +debugformats('paragraphs', paragraphs) definitions = b""" A Term @@ -52,7 +54,7 @@ Definition. """ -debugformats(b'definitions', definitions) +debugformats('definitions', definitions) literals = br""" The fully minimized form is the most @@ -76,7 +78,7 @@ with '::' disappears in the final output. """ -debugformats(b'literals', literals) +debugformats('literals', literals) lists = b""" - This is the first list item. @@ -127,7 +129,7 @@ * This is the third bullet """ -debugformats(b'lists', lists) +debugformats('lists', lists) options = b""" There is support for simple option lists, @@ -153,7 +155,7 @@ --foo bar baz """ -debugformats(b'options', options) +debugformats('options', options) fields = b""" :a: First item. @@ -166,7 +168,7 @@ :much too large: This key is big enough to get its own line. """ -debugformats(b'fields', fields) +debugformats('fields', fields) containers = b""" Normal output. @@ -184,14 +186,14 @@ Debug output. """ -debugformats(b'containers (normal)', containers) -debugformats(b'containers (verbose)', containers, keep=['verbose']) -debugformats(b'containers (debug)', containers, keep=['debug']) -debugformats(b'containers (verbose debug)', containers, - keep=['verbose', 'debug']) +debugformats('containers (normal)', containers) +debugformats('containers (verbose)', containers, keep=[b'verbose']) +debugformats('containers (debug)', containers, keep=[b'debug']) +debugformats('containers (verbose debug)', containers, + keep=[b'verbose', b'debug']) roles = b"""Please see :hg:`add`.""" -debugformats(b'roles', roles) +debugformats('roles', roles) sections = b""" @@ -207,7 +209,7 @@ Markup: ``foo`` and :hg:`help` ------------------------------ """ -debugformats(b'sections', sections) +debugformats('sections', sections) admonitions = b""" @@ -225,7 +227,7 @@ This is danger """ -debugformats(b'admonitions', admonitions) +debugformats('admonitions', admonitions) comments = b""" Some text. @@ -241,7 +243,7 @@ Empty comment above """ -debugformats(b'comments', comments) +debugformats('comments', comments) data = [[b'a', b'b', b'c'], @@ -251,9 +253,9 @@ rst = minirst.maketable(data, 2, True) table = b''.join(rst) -print(table) +print(table.decode('utf8')) -debugformats(b'table', table) +debugformats('table', table) data = [[b's', b'long', b'line\ngoes on here'], [b'', b'xy', b'tried to fix here\n by indenting']] @@ -261,7 +263,6 @@ rst = minirst.maketable(data, 1, False) table = b''.join(rst) -print(table) +print(table.decode('utf8')) -debugformats(b'table+nl', table) - +debugformats('table+nl', table)
--- a/tests/test-mq-qclone-http.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-mq-qclone-http.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - hide outer repo $ hg init
--- a/tests/test-mq-qimport.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-mq-qimport.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - $ cat > writelines.py <<EOF > import sys > path = sys.argv[1]
--- a/tests/test-narrow-commit.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-narrow-commit.t Thu Jul 19 13:55:54 2018 -0400 @@ -49,8 +49,9 @@ $ mkdir outside $ touch outside/f1 - $ hg debugwalk -I 'relglob:f1' - matcher: <includematcher includes='(?:(?:|.*/)f1(?:/|$))'> + $ hg debugwalk -v -I 'relglob:f1' + * matcher: + <includematcher includes='(?:(?:|.*/)f1(?:/|$))'> f inside/f1 inside/f1 $ hg add outside/f1 abort: cannot track 'outside/f1' - it is outside the narrow clone
--- a/tests/test-narrow-expanddirstate.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-narrow-expanddirstate.t Thu Jul 19 13:55:54 2018 -0400 @@ -72,29 +72,31 @@ > for f in repo[b'.'].manifest().walk(added): > repo.dirstate.normallookup(f) > - > def makeds(ui, repo): - > def wrapds(orig, self): - > ds = orig(self) - > class expandingdirstate(ds.__class__): - > @hgutil.propertycache - > def _map(self): - > ret = super(expandingdirstate, self)._map - > with repo.wlock(), repo.lock(), repo.transaction( - > b'expandnarrowspec'): - > expandnarrowspec(ui, repo, - > encoding.environ.get(b'DIRSTATEINCLUDES')) - > return ret - > ds.__class__ = expandingdirstate - > return ds - > return wrapds + > def wrapds(ui, repo, ds): + > class expandingdirstate(ds.__class__): + > @hgutil.propertycache + > def _map(self): + > ret = super(expandingdirstate, self)._map + > with repo.wlock(), repo.lock(), repo.transaction( + > b'expandnarrowspec'): + > expandnarrowspec(ui, repo, + > encoding.environ.get(b'DIRSTATEINCLUDES')) + > return ret + > ds.__class__ = expandingdirstate + > return ds > > def reposetup(ui, repo): - > extensions.wrapfilecache(localrepo.localrepository, b'dirstate', - > makeds(ui, repo)) - > def overridepatch(orig, *args, **kwargs): + > class expandingrepo(repo.__class__): + > def _makedirstate(self): + > dirstate = super(expandingrepo, self)._makedirstate() + > return wrapds(ui, repo, dirstate) + > repo.__class__ = expandingrepo + > + > def extsetup(unused_ui): + > def overridepatch(orig, ui, repo, *args, **kwargs): > with repo.wlock(): > expandnarrowspec(ui, repo, encoding.environ.get(b'PATCHINCLUDES')) - > return orig(*args, **kwargs) + > return orig(ui, repo, *args, **kwargs) > > extensions.wrapfunction(patch, b'patch', overridepatch) > EOF
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-nointerrupt.t Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,83 @@ +Dummy extension simulating unsafe long running command + $ cat > sleepext.py <<EOF + > import itertools + > import time + > + > from mercurial.i18n import _ + > from mercurial import registrar + > + > cmdtable = {} + > command = registrar.command(cmdtable) + > + > @command(b'sleep', [], _(b'TIME'), norepo=True) + > def sleep(ui, sleeptime=b"1", **opts): + > with ui.uninterruptable(): + > for _i in itertools.repeat(None, int(sleeptime)): + > time.sleep(1) + > ui.warn(b"end of unsafe operation\n") + > ui.warn(b"%s second(s) passed\n" % sleeptime) + > EOF + +Kludge to emulate timeout(1) which is not generally available. + $ cat > timeout.py <<EOF + > from __future__ import print_function + > import argparse + > import signal + > import subprocess + > import sys + > import time + > + > ap = argparse.ArgumentParser() + > ap.add_argument('-s', nargs=1, default='SIGTERM') + > ap.add_argument('duration', nargs=1, type=int) + > ap.add_argument('argv', nargs='*') + > opts = ap.parse_args() + > try: + > sig = int(opts.s[0]) + > except ValueError: + > sname = opts.s[0] + > if not sname.startswith('SIG'): + > sname = 'SIG' + sname + > sig = getattr(signal, sname) + > proc = subprocess.Popen(opts.argv) + > time.sleep(opts.duration[0]) + > proc.poll() + > if proc.returncode is None: + > proc.send_signal(sig) + > proc.wait() + > sys.exit(124) + > EOF + +Set up repository + $ hg init repo + $ cd repo + $ cat >> $HGRCPATH << EOF + > [extensions] + > sleepext = ../sleepext.py + > EOF + +Test ctrl-c + $ python $TESTTMP/timeout.py -s INT 1 hg sleep 2 + interrupted! + [124] + + $ cat >> $HGRCPATH << EOF + > [experimental] + > nointerrupt = yes + > EOF + + $ python $TESTTMP/timeout.py -s INT 1 hg sleep 2 + interrupted! + [124] + + $ cat >> $HGRCPATH << EOF + > [experimental] + > nointerrupt-interactiveonly = False + > EOF + + $ python $TESTTMP/timeout.py -s INT 1 hg sleep 2 + shutting down cleanly + press ^C again to terminate immediately (dangerous) + end of unsafe operation + interrupted! + [124]
--- a/tests/test-notify.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-notify.t Thu Jul 19 13:55:54 2018 -0400 @@ -131,6 +131,9 @@ notify.diffstat Set to True to include a diffstat before diff content. Default: True. + notify.showfunc + If set, override "diff.showfunc" for the diff content. Default: None. + notify.merge If True, send notifications for merge changesets. Default: True. @@ -647,3 +650,99 @@ To: baz@test.com, foo@bar with template + +showfunc diff + $ cat <<EOF >> $HGRCPATH + > showfunc = True + > template = + > maxdiff = -1 + > EOF + $ cd a + $ cat > f1 << EOF + > int main() { + > int a = 0; + > int b = 1; + > int c = 2; + > int d = 3; + > return a + b + c + d; + > } + > EOF + $ hg commit -Am addfunction + adding f1 + $ hg --cwd ../b pull ../a + pulling from ../a + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + new changesets b86bc16ff894 + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + Date: * (glob) + Subject: addfunction + From: test@test.com + X-Hg-Notification: changeset b86bc16ff894 + Message-Id: <hg.b86bc16ff894.*.*@*> (glob) + To: baz@test.com, foo@bar + + changeset b86bc16ff894 + diffs (11 lines): + + diff -r 14721b538ae3 -r b86bc16ff894 f1 + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/f1 Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,7 @@ + +int main() { + + int a = 0; + + int b = 1; + + int c = 2; + + int d = 3; + + return a + b + c + d; + +} + (run 'hg update' to get a working copy) + $ cat > f1 << EOF + > int main() { + > int a = 0; + > int b = 1; + > int c = 2; + > int e = 3; + > return a + b + c + e; + > } + > EOF + $ hg commit -m changefunction + $ hg --cwd ../b --config notify.showfunc=True pull ../a + pulling from ../a + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + new changesets e81040e9838c + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + Date: * (glob) + Subject: changefunction + From: test@test.com + X-Hg-Notification: changeset e81040e9838c + Message-Id: <hg.e81040e9838c.*.*@*> (glob) + To: baz@test.com, foo@bar + + changeset e81040e9838c + diffs (12 lines): + + diff -r b86bc16ff894 -r e81040e9838c f1 + --- a/f1 Thu Jan 01 00:00:00 1970 +0000 + +++ b/f1 Thu Jan 01 00:00:00 1970 +0000 + @@ -2,6 +2,6 @@ int main() { + int a = 0; + int b = 1; + int c = 2; + - int d = 3; + - return a + b + c + d; + + int e = 3; + + return a + b + c + e; + } + (run 'hg update' to get a working copy)
--- a/tests/test-obshistory.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-obshistory.t Thu Jul 19 13:55:54 2018 -0400 @@ -55,9 +55,9 @@ (use --hidden to access hidden revisions) [255] $ hg update --hidden "desc(A0)" - updating to a hidden changeset 471f378eab4c + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' was rewritten as: 4ae3a4151de9) - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Test output with pruned commit ============================== @@ -118,9 +118,9 @@ (use --hidden to access hidden revisions) [255] $ hg up --hidden -r 'desc(B0)' - updating to a hidden changeset 0dec01379d3b + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 0dec01379d3b (hidden revision '0dec01379d3b' is pruned) - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Test output with splitted commit ================================ @@ -195,9 +195,9 @@ (use --hidden to access hidden revisions) [255] $ hg update --hidden 'min(desc(A0))' - updating to a hidden changeset 471597cad322 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 471597cad322 (hidden revision '471597cad322' was split as: 337fec4d2edc, f257fde29c7a) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved Test output with lots of splitted commit ======================================== @@ -294,9 +294,9 @@ (use --hidden to access hidden revisions) [255] $ hg update --hidden 'min(desc(A0))' - updating to a hidden changeset de7290d8b885 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset de7290d8b885 (hidden revision 'de7290d8b885' was split as: 337fec4d2edc, f257fde29c7a and 2 more) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved Test output with folded commit ============================== @@ -373,17 +373,17 @@ (use --hidden to access hidden revisions) [255] $ hg update --hidden 'desc(A0)' - updating to a hidden changeset 471f378eab4c + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' was rewritten as: eb5a0daa2192) - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg update 0dec01379d3b abort: hidden revision '0dec01379d3b' was rewritten as: eb5a0daa2192! (use --hidden to access hidden revisions) [255] $ hg update --hidden 'desc(B0)' - updating to a hidden changeset 0dec01379d3b + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 0dec01379d3b (hidden revision '0dec01379d3b' was rewritten as: eb5a0daa2192) - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Test output with divergence =========================== @@ -416,9 +416,9 @@ summary: ROOT $ hg update --hidden 'desc(A0)' - updating to a hidden changeset 471f378eab4c + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' was rewritten as: fdf9bde5129a) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg commit --amend -m "A2" 2 new content-divergent changesets $ hg log --hidden -G @@ -456,9 +456,9 @@ (use --hidden to access hidden revisions) [255] $ hg update --hidden 'desc(A0)' - updating to a hidden changeset 471f378eab4c + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' has diverged) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved Test output with amended + folded commit ======================================== @@ -551,13 +551,13 @@ (use --hidden to access hidden revisions) [255] $ hg update --hidden 'desc(A0)' - updating to a hidden changeset 471f378eab4c + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' was rewritten as: eb5a0daa2192) - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg update --hidden 0dec01379d3b - updating to a hidden changeset 0dec01379d3b + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 0dec01379d3b (hidden revision '0dec01379d3b' was rewritten as: eb5a0daa2192) - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg update 0dec01379d3b 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg update --hidden 'desc(B0)'
--- a/tests/test-obsmarker-template.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-obsmarker-template.t Thu Jul 19 13:55:54 2018 -0400 @@ -75,9 +75,9 @@ Check templates --------------- $ hg up 'desc(A0)' --hidden - updating to a hidden changeset 471f378eab4c + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' was rewritten as: d004c8f274b9) - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Predecessors template should show current revision as it is the working copy $ hg tlog @@ -148,9 +148,9 @@ summary: ROOT $ hg up 'desc(A1)' --hidden - updating to a hidden changeset a468dc9b3633 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset a468dc9b3633 (hidden revision 'a468dc9b3633' was rewritten as: d004c8f274b9) - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Predecessors template should show current revision as it is the working copy $ hg tlog @@ -417,9 +417,9 @@ --------------- $ hg up 'obsolete()' --hidden - updating to a hidden changeset 471597cad322 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 471597cad322 (hidden revision '471597cad322' was split as: 337fec4d2edc, f257fde29c7a) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved Predecessors template should show current revision as it is the working copy $ hg tlog @@ -627,9 +627,9 @@ --------------- $ hg up 'desc(A0)' --hidden - updating to a hidden changeset 471f378eab4c + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' was rewritten as: eb5a0daa2192) - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved Predecessors template should show current revision as it is the working copy $ hg tlog @@ -653,9 +653,9 @@ o ea207398892e $ hg up 'desc(B0)' --hidden - updating to a hidden changeset 0dec01379d3b + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 0dec01379d3b (hidden revision '0dec01379d3b' was rewritten as: eb5a0daa2192) - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Predecessors template should show both predecessors as they should be both displayed @@ -820,9 +820,9 @@ summary: ROOT $ hg update --hidden 'desc(A0)' - updating to a hidden changeset 471f378eab4c + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' was rewritten as: fdf9bde5129a) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg commit --amend -m "A2" 2 new content-divergent changesets $ hg log --hidden -G @@ -894,9 +894,9 @@ --------------- $ hg up 'desc(A0)' --hidden - updating to a hidden changeset 471f378eab4c + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' has diverged) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved Predecessors template should show current revision as it is the working copy $ hg tlog @@ -1161,9 +1161,9 @@ --------------- $ hg up 'desc(A0)' --hidden - updating to a hidden changeset 471f378eab4c + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' was rewritten as: eb5a0daa2192) - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved Predecessors template should show current revision as it is the working copy $ hg tlog @@ -1187,9 +1187,9 @@ o ea207398892e $ hg up 'desc(B0)' --hidden - updating to a hidden changeset 0dec01379d3b + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 0dec01379d3b (hidden revision '0dec01379d3b' was rewritten as: eb5a0daa2192) - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Predecessors template should both predecessors as they are visible $ hg tlog @@ -1219,9 +1219,9 @@ o ea207398892e $ hg up 'desc(B1)' --hidden - updating to a hidden changeset b7ea6d14e664 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset b7ea6d14e664 (hidden revision 'b7ea6d14e664' was rewritten as: eb5a0daa2192) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved Predecessors template should both predecessors as they are visible $ hg tlog @@ -1623,9 +1623,9 @@ $ hg up -r "desc(B0)" --hidden - updating to a hidden changeset 0dec01379d3b + 2 files updated, 0 files merged, 1 files removed, 0 files unresolved + updated to hidden changeset 0dec01379d3b (hidden revision '0dec01379d3b' is pruned) - 2 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg tlog o f897c6137566 | Predecessors: 2:0dec01379d3b @@ -2087,9 +2087,9 @@ o ea207398892e $ hg up --hidden 4 - updating to a hidden changeset 9bd10a0775e4 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 9bd10a0775e4 (hidden revision '9bd10a0775e4' has diverged) - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg rebase -r 7 -d 8 --config extensions.rebase= rebasing 7:ba2ed02b0c9a "Add A,B,C" $ hg tlog @@ -2333,9 +2333,9 @@ obsoleted 1 changesets $ hg up -r "desc(A0)" --hidden - updating to a hidden changeset 471f378eab4c + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' is pruned) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg commit --amend -m "A2" $ hg debugobsolete --record-parent `getid "."` obsoleted 1 changesets @@ -2344,9 +2344,9 @@ ------------ $ hg up "desc(A0)" --hidden - updating to a hidden changeset 471f378eab4c + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 471f378eab4c (hidden revision '471f378eab4c' is pruned) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg tlog @ 471f378eab4c | @@ -2499,9 +2499,9 @@ --------------- $ hg up 'desc("A0")' --hidden - updating to a hidden changeset 471597cad322 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 471597cad322 (hidden revision '471597cad322' is pruned) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved # todo: the obsfate output is not ideal $ hg fatelog @@ -2512,9 +2512,9 @@ o ea207398892e $ hg up -r 'desc("A2")' --hidden - updating to a hidden changeset 0d0ef4bdf70e + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset 0d0ef4bdf70e (hidden revision '0d0ef4bdf70e' is pruned) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg fatelog --hidden @ 0d0ef4bdf70e @@ -2581,3 +2581,61 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: ROOT + +Test metadata encoding (issue5754) +================================== + + $ hg init $TESTTMP/metadata-encoding + $ cd $TESTTMP/metadata-encoding + $ cat <<'EOF' >> .hg/hgrc + > [extensions] + > amend = + > EOF + $ $PYTHON <<'EOF' + > with open('test1', 'wb') as f: + > f.write(b't\xe8st1') and None + > with open('test2', 'wb') as f: + > f.write(b't\xe8st2') and None + > EOF + $ mkcommit ROOT + $ HGENCODING=latin-1 HGUSER="`cat test1`" mkcommit A0 + $ echo 42 >> A0 + $ hg amend -m "A1" --note "`cat test2`" + $ HGENCODING=latin-1 hg amend -m "A2" \ + > --config devel.user.obsmarker="`cat test2`" + $ mkcommit B0 + $ HGENCODING=latin-1 hg debugobsolete -u "`cat test2`" "`getid 'desc(B0)'`" + obsoleted 1 changesets + +metadata should be stored in UTF-8, and debugobsolete doesn't decode it to +local encoding since the command is supposed to show unmodified content: + + $ HGENCODING=latin-1 hg debugobsolete + 5f66a482f0bb2fcaccfc215554ad5eb9f40b50f5 718c0d00cee1429bdb73064e0d88908c601507a8 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '9', 'note': 't\xc3\xa8st2', 'operation': 'amend', 'user': 't\xc3\xa8st1'} + 718c0d00cee1429bdb73064e0d88908c601507a8 1132562159b35bb27e1d6b80c80ee94a1659a4da 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 't\xc3\xa8st2'} + e1724525bc3bec4472d7915a02811b938004a7a2 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 't\xc3\xa8st2'} + +metadata should be converted back to local encoding when displaying: + + $ HGENCODING=latin-1 hg fatelog --hidden + @ e1724525bc3b + | Obsfate: pruned by t\xe8st2 (at 1970-01-01 00:00 +0000); (esc) + o 1132562159b3 + | + | x 718c0d00cee1 + |/ Obsfate: rewritten using amend as 3:1132562159b3 by t\xe8st2 (at 1970-01-01 00:00 +0000); (esc) + | x 5f66a482f0bb + |/ Obsfate: rewritten using amend as 2:718c0d00cee1 by t\xe8st1 (at 1970-01-01 00:00 +0000); (esc) + o ea207398892e + + $ HGENCODING=utf-8 hg fatelog --hidden + @ e1724525bc3b + | Obsfate: pruned by t\xc3\xa8st2 (at 1970-01-01 00:00 +0000); (esc) + o 1132562159b3 + | + | x 718c0d00cee1 + |/ Obsfate: rewritten using amend as 3:1132562159b3 by t\xc3\xa8st2 (at 1970-01-01 00:00 +0000); (esc) + | x 5f66a482f0bb + |/ Obsfate: rewritten using amend as 2:718c0d00cee1 by t\xc3\xa8st1 (at 1970-01-01 00:00 +0000); (esc) + o ea207398892e +
--- a/tests/test-obsolete-checkheads.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-obsolete-checkheads.t Thu Jul 19 13:55:54 2018 -0400 @@ -80,6 +80,7 @@ pulling from $TESTTMP/remote searching for changes no changes found + 1 local changesets published $ hg log -G --hidden @ 71e3228bffe1 (draft) add new |
--- a/tests/test-obsolete-divergent.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-obsolete-divergent.t Thu Jul 19 13:55:54 2018 -0400 @@ -704,16 +704,16 @@ > from mercurial import registrar, scmutil > cmdtable = {} > command = registrar.command(cmdtable) - > @command('cleanup') + > @command(b'cleanup') > def cleanup(ui, repo): > def node(expr): > unfi = repo.unfiltered() > rev = unfi.revs(expr).first() > return unfi.changelog.node(rev) - > with repo.wlock(), repo.lock(), repo.transaction('delayedstrip'): - > mapping = {node('desc(B1)'): [node('desc(B3)')], - > node('desc(B3)'): [node('desc(B4)')]} - > scmutil.cleanupnodes(repo, mapping, 'test') + > with repo.wlock(), repo.lock(), repo.transaction(b'delayedstrip'): + > mapping = {node(b'desc(B1)'): [node(b'desc(B3)')], + > node(b'desc(B3)'): [node(b'desc(B4)')]} + > scmutil.cleanupnodes(repo, mapping, b'test') > EOF $ rm .hg/localtags
--- a/tests/test-obsolete.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-obsolete.t Thu Jul 19 13:55:54 2018 -0400 @@ -62,7 +62,7 @@ $ hg tip -1:000000000000 (public) [tip ] $ hg up --hidden tip --quiet - updating to a hidden changeset 97b7c2d76b18 + updated to hidden changeset 97b7c2d76b18 (hidden revision '97b7c2d76b18' is pruned) Killing a single changeset with itself should fail @@ -931,7 +931,7 @@ $ echo "evolution.exchange=True" >> $HGRCPATH $ echo "evolution.createmarkers=True" >> $HGRCPATH - $ rm hg.pid access.log errors.log + $ rm access.log errors.log #endif Several troubles on the same changeset (create an unstable and bumped changeset) @@ -1318,16 +1318,18 @@ $ cat >$TESTTMP/test_extension.py << EOF > from __future__ import absolute_import > from mercurial.i18n import _ - > from mercurial import cmdutil, registrar + > from mercurial import cmdutil, pycompat, registrar + > from mercurial.utils import stringutil > > cmdtable = {} > command = registrar.command(cmdtable) > @command(b"amendtransient",[], _(b'hg amendtransient [rev]')) > def amend(ui, repo, *pats, **opts): - > opts['message'] = 'Test' - > opts['logfile'] = None - > cmdutil.amend(ui, repo, repo['.'], {}, pats, opts) - > ui.write(b'%s\n' % repo.changelog.headrevs()) + > opts = pycompat.byteskwargs(opts) + > opts[b'message'] = b'Test' + > opts[b'logfile'] = None + > cmdutil.amend(ui, repo, repo[b'.'], {}, pats, opts) + > ui.write(b'%s\n' % stringutil.pprint(repo.changelog.headrevs())) > EOF $ cat >> $HGRCPATH << EOF > [extensions] @@ -1365,7 +1367,7 @@ > hidden = repoview.filterrevs(repo, b'visible') > if sorted(hidden1) != sorted(hidden): > print("cache inconsistency") - > bkmstoreinst._repo.currenttransaction().addpostclose('test_extension', trhook) + > bkmstoreinst._repo.currenttransaction().addpostclose(b'test_extension', trhook) > orig(bkmstoreinst, *args, **kwargs) > def extsetup(ui): > extensions.wrapfunction(bookmarks.bmstore, '_recordchange',
--- a/tests/test-pager-legacy.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-pager-legacy.t Thu Jul 19 13:55:54 2018 -0400 @@ -247,9 +247,9 @@ > from mercurial import registrar, commands > cmdtable = {} > command = registrar.command(cmdtable) - > @command(b'fortytwo', [], 'fortytwo', norepo=True) + > @command(b'fortytwo', [], b'fortytwo', norepo=True) > def fortytwo(ui, *opts): - > ui.write('42\n') + > ui.write(b'42\n') > return 42 > EOF
--- a/tests/test-pager.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-pager.t Thu Jul 19 13:55:54 2018 -0400 @@ -260,9 +260,9 @@ > from mercurial import commands, registrar > cmdtable = {} > command = registrar.command(cmdtable) - > @command(b'fortytwo', [], 'fortytwo', norepo=True) + > @command(b'fortytwo', [], b'fortytwo', norepo=True) > def fortytwo(ui, *opts): - > ui.write('42\n') + > ui.write(b'42\n') > return 42 > EOF @@ -377,8 +377,8 @@ $ cat > $TESTTMP/pushbufferpager.py <<EOF > def uisetup(ui): > ui.pushbuffer() - > ui.pager('mycmd') - > ui.write('content\n') + > ui.pager(b'mycmd') + > ui.write(b'content\n') > ui.write(ui.popbuffer()) > EOF
--- a/tests/test-parse-date.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-parse-date.t Thu Jul 19 13:55:54 2018 -0400 @@ -243,8 +243,8 @@ >>> today = datetime.date.today().strftime("%b %d") >>> yesterday = (datetime.date.today() - datetime.timedelta(days=1)).strftime("%b %d") >>> dates = open('dates', 'w') - >>> dates.write(today + '\n') - >>> dates.write(yesterday + '\n') + >>> dates.write(today + '\n') and None + >>> dates.write(yesterday + '\n') and None >>> dates.close() $ hg ci -d "`sed -n '1p' dates`" -m "today is a good day to code" $ hg log -d today --template '{desc}\n'
--- a/tests/test-parseindex.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-parseindex.t Thu Jul 19 13:55:54 2018 -0400 @@ -26,6 +26,7 @@ summary: change foo $ cat >> test.py << EOF + > from __future__ import print_function > from mercurial import changelog, vfs > from mercurial.node import * > @@ -56,9 +57,9 @@ > return wrapper > > cl = changelog.changelog(opener('.hg/store')) - > print len(cl), 'revisions:' + > print(len(cl), 'revisions:') > for r in cl: - > print short(cl.node(r)) + > print(short(cl.node(r))) > EOF $ $PYTHON test.py 2 revisions: @@ -74,33 +75,34 @@ $ cd a $ $PYTHON <<EOF + > from __future__ import print_function > from mercurial import changelog, vfs > cl = changelog.changelog(vfs.vfs('.hg/store')) - > print 'good heads:' + > print('good heads:') > for head in [0, len(cl) - 1, -1]: - > print'%s: %r' % (head, cl.reachableroots(0, [head], [0])) - > print 'bad heads:' + > print('%s: %r' % (head, cl.reachableroots(0, [head], [0]))) + > print('bad heads:') > for head in [len(cl), 10000, -2, -10000, None]: - > print '%s:' % head, + > print('%s:' % head, end=' ') > try: > cl.reachableroots(0, [head], [0]) - > print 'uncaught buffer overflow?' + > print('uncaught buffer overflow?') > except (IndexError, TypeError) as inst: - > print inst - > print 'good roots:' + > print(inst) + > print('good roots:') > for root in [0, len(cl) - 1, -1]: - > print '%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root])) - > print 'out-of-range roots are ignored:' + > print('%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root]))) + > print('out-of-range roots are ignored:') > for root in [len(cl), 10000, -2, -10000]: - > print '%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root])) - > print 'bad roots:' + > print('%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root]))) + > print('bad roots:') > for root in [None]: - > print '%s:' % root, + > print('%s:' % root, end=' ') > try: > cl.reachableroots(root, [len(cl) - 1], [root]) - > print 'uncaught error?' + > print('uncaught error?') > except TypeError as inst: - > print inst + > print(inst) > EOF good heads: 0: [0] @@ -137,10 +139,10 @@ $ $PYTHON <<EOF > data = open("limit/.hg/store/00changelog.i", "rb").read() - > for n, p in [('limit', '\0\0\0\x02'), ('segv', '\0\x01\0\0')]: + > for n, p in [(b'limit', b'\0\0\0\x02'), (b'segv', b'\0\x01\0\0')]: > # corrupt p1 at rev0 and p2 at rev1 > d = data[:24] + p + data[28:127 + 28] + p + data[127 + 32:] - > open(n + "/.hg/store/00changelog.i", "wb").write(d) + > open(n + b"/.hg/store/00changelog.i", "wb").write(d) > EOF $ hg -R limit debugindex -f1 -c @@ -164,6 +166,7 @@ 1 2 1 -1 base 66 65 66 1.01538 66 0 0.00000 $ cat <<EOF > test.py + > from __future__ import print_function > import sys > from mercurial import changelog, vfs > cl = changelog.changelog(vfs.vfs(sys.argv[1])) @@ -177,12 +180,12 @@ > ('find_deepest', lambda: cl.ancestor(n0, n1)), > ] > for l, f in ops: - > print l + ':', + > print(l + ':', end=' ') > try: > f() - > print 'uncaught buffer overflow?' - > except ValueError, inst: - > print inst + > print('uncaught buffer overflow?') + > except ValueError as inst: + > print(inst) > EOF $ $PYTHON test.py limit/.hg/store
--- a/tests/test-parseindex2.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-parseindex2.py Thu Jul 19 13:55:54 2018 -0400 @@ -15,6 +15,7 @@ ) from mercurial import ( policy, + pycompat, ) parsers = policy.importmod(r'parsers') @@ -24,7 +25,7 @@ return int(q & 0xFFFF) def offset_type(offset, type): - return long(long(offset) << 16 | type) + return int(int(offset) << 16 | type) indexformatng = ">Qiiiiii20s12x" @@ -65,46 +66,50 @@ return index, cache -data_inlined = '\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x8c' \ - '\x00\x00\x04\x07\x00\x00\x00\x00\x00\x00\x15\x15\xff\xff\xff' \ - '\xff\xff\xff\xff\xff\xebG\x97\xb7\x1fB\x04\xcf\x13V\x81\tw\x1b' \ - 'w\xdduR\xda\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ - 'x\x9c\x9d\x93?O\xc30\x10\xc5\xf7|\x8a\xdb\x9a\xa8m\x06\xd8*\x95' \ - '\x81B\xa1\xa2\xa2R\xcb\x86Pd\x9a\x0b5$vd_\x04\xfd\xf6\x9c\xff@' \ - '\x11!\x0b\xd9\xec\xf7\xbbw\xe7gG6\xad6\x04\xdaN\xc0\x92\xa0$)' \ - '\xb1\x82\xa2\xd1%\x16\xa4\x8b7\xa9\xca\xd4-\xb2Y\x02\xfc\xc9' \ - '\xcaS\xf9\xaeX\xed\xb6\xd77Q\x02\x83\xd4\x19\xf5--Y\xea\xe1W' \ - '\xab\xed\x10\xceR\x0f_\xdf\xdf\r\xe1,\xf5\xf0\xcb\xf5 \xceR\x0f' \ - '_\xdc\x0e\x0e\xc3R\x0f_\xae\x96\x9b!\x9e\xa5\x1e\xbf\xdb,\x06' \ - '\xc7q\x9a/\x88\x82\xc3B\xea\xb5\xb4TJ\x93\xb6\x82\x0e\xe16\xe6' \ - 'KQ\xdb\xaf\xecG\xa3\xd1 \x01\xd3\x0b_^\xe8\xaa\xa0\xae\xad\xd1' \ - '&\xbef\x1bz\x08\xb0|\xc9Xz\x06\xf6Z\x91\x90J\xaa\x17\x90\xaa' \ - '\xd2\xa6\x11$5C\xcf\xba#\xa0\x03\x02*2\x92-\xfc\xb1\x94\xdf\xe2' \ - '\xae\xb8\'m\x8ey0^\x85\xd3\x82\xb4\xf0`:\x9c\x00\x8a\xfd\x01' \ - '\xb0\xc6\x86\x8b\xdd\xae\x80\xf3\xa9\x9fd\x16\n\x00R%\x1a\x06' \ - '\xe9\xd8b\x98\x1d\xf4\xf3+\x9bf\x01\xd8p\x1b\xf3.\xed\x9f^g\xc3' \ - '^\xd9W81T\xdb\xd5\x04sx|\xf2\xeb\xd6`%?x\xed"\x831\xbf\xf3\xdc' \ - 'b\xeb%gaY\xe1\xad\x9f\xb9f\'1w\xa9\xa5a\x83s\x82J\xb98\xbc4\x8b' \ - '\x83\x00\x9f$z\xb8#\xa5\xb1\xdf\x98\xd9\xec\x1b\x89O\xe3Ts\x9a4' \ - '\x17m\x8b\xfc\x8f\xa5\x95\x9a\xfc\xfa\xed,\xe5|\xa1\xfe\x15\xb9' \ - '\xbc\xb2\x93\x1f\xf2\x95\xff\xdf,\x1a\xc5\xe7\x17*\x93Oz:>\x0e' +data_inlined = ( + b'\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x8c' + b'\x00\x00\x04\x07\x00\x00\x00\x00\x00\x00\x15\x15\xff\xff\xff' + b'\xff\xff\xff\xff\xff\xebG\x97\xb7\x1fB\x04\xcf\x13V\x81\tw\x1b' + b'w\xdduR\xda\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'x\x9c\x9d\x93?O\xc30\x10\xc5\xf7|\x8a\xdb\x9a\xa8m\x06\xd8*\x95' + b'\x81B\xa1\xa2\xa2R\xcb\x86Pd\x9a\x0b5$vd_\x04\xfd\xf6\x9c\xff@' + b'\x11!\x0b\xd9\xec\xf7\xbbw\xe7gG6\xad6\x04\xdaN\xc0\x92\xa0$)' + b'\xb1\x82\xa2\xd1%\x16\xa4\x8b7\xa9\xca\xd4-\xb2Y\x02\xfc\xc9' + b'\xcaS\xf9\xaeX\xed\xb6\xd77Q\x02\x83\xd4\x19\xf5--Y\xea\xe1W' + b'\xab\xed\x10\xceR\x0f_\xdf\xdf\r\xe1,\xf5\xf0\xcb\xf5 \xceR\x0f' + b'_\xdc\x0e\x0e\xc3R\x0f_\xae\x96\x9b!\x9e\xa5\x1e\xbf\xdb,\x06' + b'\xc7q\x9a/\x88\x82\xc3B\xea\xb5\xb4TJ\x93\xb6\x82\x0e\xe16\xe6' + b'KQ\xdb\xaf\xecG\xa3\xd1 \x01\xd3\x0b_^\xe8\xaa\xa0\xae\xad\xd1' + b'&\xbef\x1bz\x08\xb0|\xc9Xz\x06\xf6Z\x91\x90J\xaa\x17\x90\xaa' + b'\xd2\xa6\x11$5C\xcf\xba#\xa0\x03\x02*2\x92-\xfc\xb1\x94\xdf\xe2' + b'\xae\xb8\'m\x8ey0^\x85\xd3\x82\xb4\xf0`:\x9c\x00\x8a\xfd\x01' + b'\xb0\xc6\x86\x8b\xdd\xae\x80\xf3\xa9\x9fd\x16\n\x00R%\x1a\x06' + b'\xe9\xd8b\x98\x1d\xf4\xf3+\x9bf\x01\xd8p\x1b\xf3.\xed\x9f^g\xc3' + b'^\xd9W81T\xdb\xd5\x04sx|\xf2\xeb\xd6`%?x\xed"\x831\xbf\xf3\xdc' + b'b\xeb%gaY\xe1\xad\x9f\xb9f\'1w\xa9\xa5a\x83s\x82J\xb98\xbc4\x8b' + b'\x83\x00\x9f$z\xb8#\xa5\xb1\xdf\x98\xd9\xec\x1b\x89O\xe3Ts\x9a4' + b'\x17m\x8b\xfc\x8f\xa5\x95\x9a\xfc\xfa\xed,\xe5|\xa1\xfe\x15\xb9' + b'\xbc\xb2\x93\x1f\xf2\x95\xff\xdf,\x1a\xc5\xe7\x17*\x93Oz:>\x0e' + ) -data_non_inlined = '\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19' \ - '\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff' \ - '\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d' \ - '\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ - '\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00' \ - '\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff' \ - '\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh' \ - '\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ - '\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00' \ - '\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n' \ - '\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00' \ - '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F' \ - '\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01' \ - '\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1' \ - '\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00' \ - '\x00\x00\x00\x00\x00\x00\x00\x00\x00' +data_non_inlined = ( + b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19' + b'\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff' + b'\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d' + b'\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00' + b'\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff' + b'\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh' + b'\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00' + b'\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n' + b'\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F' + b'\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01' + b'\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1' + b'\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00' + ) def parse_index2(data, inline): index, chunkcache = parsers.parse_index2(data, inline) @@ -145,7 +150,7 @@ def testversionfail(testnumber, hexversion): stdout, stderr = importparsers(hexversion) # We include versionerrortext to distinguish from other ImportErrors. - errtext = "ImportError: %s" % parsers.versionerrortext + errtext = b"ImportError: %s" % pycompat.sysbytes(parsers.versionerrortext) if errtext not in stdout: printhexfail(testnumber, hexversion, stdout, expected="stdout to contain %r" % errtext)
--- a/tests/test-patch-offset.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-patch-offset.t Thu Jul 19 13:55:54 2018 -0400 @@ -8,7 +8,7 @@ > fp = open(path, 'wb') > for pattern in patterns: > count = int(pattern[0:-1]) - > char = pattern[-1] + '\n' + > char = pattern[-1].encode('utf8') + b'\n' > fp.write(char*count) > fp.close() > EOF
--- a/tests/test-pathencode.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-pathencode.py Thu Jul 19 13:55:54 2018 -0400 @@ -16,6 +16,7 @@ import sys import time from mercurial import ( + pycompat, store, ) @@ -24,15 +25,15 @@ except NameError: xrange = range -validchars = set(map(chr, range(0, 256))) +validchars = set(map(pycompat.bytechr, range(0, 256))) alphanum = range(ord('A'), ord('Z')) -for c in '\0/': +for c in (b'\0', b'/'): validchars.remove(c) -winreserved = ('aux con prn nul'.split() + - ['com%d' % i for i in xrange(1, 10)] + - ['lpt%d' % i for i in xrange(1, 10)]) +winreserved = (b'aux con prn nul'.split() + + [b'com%d' % i for i in xrange(1, 10)] + + [b'lpt%d' % i for i in xrange(1, 10)]) def casecombinations(names): '''Build all case-diddled combinations of names.''' @@ -44,7 +45,7 @@ for c in itertools.combinations(xrange(len(r)), i): d = r for j in c: - d = ''.join((d[:j], d[j].upper(), d[j + 1:])) + d = b''.join((d[:j], d[j:j + 1].upper(), d[j + 1:])) combos.add(d) return sorted(combos) @@ -78,19 +79,19 @@ # looking at filelog names from a real-world, very large repo. probtable = ( - ('t', 9.828), ('e', 9.042), ('s', 8.011), ('a', 6.801), ('i', 6.618), - ('g', 5.053), ('r', 5.030), ('o', 4.887), ('p', 4.363), ('n', 4.258), - ('l', 3.830), ('h', 3.693), ('_', 3.659), ('.', 3.377), ('m', 3.194), - ('u', 2.364), ('d', 2.296), ('c', 2.163), ('b', 1.739), ('f', 1.625), - ('6', 0.666), ('j', 0.610), ('y', 0.554), ('x', 0.487), ('w', 0.477), - ('k', 0.476), ('v', 0.473), ('3', 0.336), ('1', 0.335), ('2', 0.326), - ('4', 0.310), ('5', 0.305), ('9', 0.302), ('8', 0.300), ('7', 0.299), - ('q', 0.298), ('0', 0.250), ('z', 0.223), ('-', 0.118), ('C', 0.095), - ('T', 0.087), ('F', 0.085), ('B', 0.077), ('S', 0.076), ('P', 0.076), - ('L', 0.059), ('A', 0.058), ('N', 0.051), ('D', 0.049), ('M', 0.046), - ('E', 0.039), ('I', 0.035), ('R', 0.035), ('G', 0.028), ('U', 0.026), - ('W', 0.025), ('O', 0.017), ('V', 0.015), ('H', 0.013), ('Q', 0.011), - ('J', 0.007), ('K', 0.005), ('+', 0.004), ('X', 0.003), ('Y', 0.001), + (b't', 9.828), (b'e', 9.042), (b's', 8.011), (b'a', 6.801), (b'i', 6.618), + (b'g', 5.053), (b'r', 5.030), (b'o', 4.887), (b'p', 4.363), (b'n', 4.258), + (b'l', 3.830), (b'h', 3.693), (b'_', 3.659), (b'.', 3.377), (b'm', 3.194), + (b'u', 2.364), (b'd', 2.296), (b'c', 2.163), (b'b', 1.739), (b'f', 1.625), + (b'6', 0.666), (b'j', 0.610), (b'y', 0.554), (b'x', 0.487), (b'w', 0.477), + (b'k', 0.476), (b'v', 0.473), (b'3', 0.336), (b'1', 0.335), (b'2', 0.326), + (b'4', 0.310), (b'5', 0.305), (b'9', 0.302), (b'8', 0.300), (b'7', 0.299), + (b'q', 0.298), (b'0', 0.250), (b'z', 0.223), (b'-', 0.118), (b'C', 0.095), + (b'T', 0.087), (b'F', 0.085), (b'B', 0.077), (b'S', 0.076), (b'P', 0.076), + (b'L', 0.059), (b'A', 0.058), (b'N', 0.051), (b'D', 0.049), (b'M', 0.046), + (b'E', 0.039), (b'I', 0.035), (b'R', 0.035), (b'G', 0.028), (b'U', 0.026), + (b'W', 0.025), (b'O', 0.017), (b'V', 0.015), (b'H', 0.013), (b'Q', 0.011), + (b'J', 0.007), (b'K', 0.005), (b'+', 0.004), (b'X', 0.003), (b'Y', 0.001), ) for c, _ in probtable: @@ -121,12 +122,12 @@ # Special suffixes. -internalsuffixcombos = casecombinations('.hg .i .d'.split()) +internalsuffixcombos = casecombinations(b'.hg .i .d'.split()) # The last component of a path, before a slash or at the end of a name. lasttable = resttable + ( - (lambda rng: '', 95), + (lambda rng: b'', 95), (lambda rng: rng.choice(internalsuffixcombos), 5), ) @@ -142,13 +143,13 @@ l += len(p) ps.append(p) ps.append(pickfrom(rng, lasttable)(rng)) - return ''.join(ps) + return b''.join(ps) def makepath(rng, j, k): '''Construct a complete pathname.''' - return ('data/' + '/'.join(makepart(rng, k) for _ in xrange(j)) + - rng.choice(['.d', '.i'])) + return (b'data/' + b'/'.join(makepart(rng, k) for _ in xrange(j)) + + rng.choice([b'.d', b'.i'])) def genpath(rng, count): '''Generate random pathnames with gradually increasing lengths.'''
--- a/tests/test-phases-exchange.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-phases-exchange.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,7 +1,6 @@ -#require killdaemons - $ cat >> $HGRCPATH << EOF > [extensions] + > drawdag=$TESTDIR/drawdag.py > phasereport=$TESTDIR/testlib/ext-phase-report.py > EOF @@ -169,6 +168,7 @@ pulling from ../alpha searching for changes no changes found + 1 local changesets published test-debug-phase: move rev 2: 1 -> 0 $ hgph o 4 public a-D - b555f63b6063 @@ -285,6 +285,7 @@ adding file changes added 1 changesets with 1 changes to 1 files new changesets b555f63b6063 + 3 local changesets published test-debug-phase: move rev 0: 1 -> 0 test-debug-phase: move rev 1: 1 -> 0 test-debug-phase: move rev 2: 1 -> 0 @@ -333,6 +334,7 @@ adding file changes added 2 changesets with 2 changes to 2 files new changesets d6bcb4f74035:145e75495359 + 4 local changesets published test-debug-phase: move rev 0: 1 -> 0 test-debug-phase: move rev 1: 1 -> 0 test-debug-phase: move rev 3: 1 -> 0 @@ -406,6 +408,7 @@ pulling from ../alpha searching for changes no changes found + 3 local changesets published test-debug-phase: move rev 3: 1 -> 0 test-debug-phase: move rev 5: 1 -> 0 test-debug-phase: move rev 6: 1 -> 0 @@ -1172,6 +1175,8 @@ $ hg phase f54f1bb90ff3 2: draft + $ killdaemons.py + put the changeset in the draft state again (first test after this one expect to be able to copy) @@ -1377,3 +1382,180 @@ o 9 draft a-G - 3e27b6f1eee1 | ~ + +Test phases exchange when a phaseroot is on a merge + + $ hg init mergetest + $ cd mergetest + > cat > .hg/hgrc << EOF + > [phases] + > publish = false + > EOF + + $ hg debugdrawdag << EOF + > E Z + > |\| + > D Y + > | | + > C X + > |/ + > B + > | + > A + > EOF + test-debug-phase: new rev 0: x -> 1 + test-debug-phase: new rev 1: x -> 1 + test-debug-phase: new rev 2: x -> 1 + test-debug-phase: new rev 3: x -> 1 + test-debug-phase: new rev 4: x -> 1 + test-debug-phase: new rev 5: x -> 1 + test-debug-phase: new rev 6: x -> 1 + test-debug-phase: new rev 7: x -> 1 + + $ hg phase --public -r D + test-debug-phase: move rev 0: 1 -> 0 + test-debug-phase: move rev 1: 1 -> 0 + test-debug-phase: move rev 2: 1 -> 0 + test-debug-phase: move rev 4: 1 -> 0 + + $ hg log -G -T '{shortest(node, 5)} {phase}' + o bb947 draft + | + | o 5ac28 draft + |/| + o | 13b7b draft + | | + | o f5853 public + | | + o | c67c4 draft + | | + | o 26805 public + |/ + o 11247 public + | + o 426ba public + + $ cd .. + +Works with default settings + + $ hg -R mergetest serve -p $HGPORT -d --pid-file=hg.pid + $ cat hg.pid >> $DAEMON_PIDS + + $ hg clone -U http://localhost:$HGPORT mergetest-normal + requesting all changes + adding changesets + adding manifests + adding file changes + added 8 changesets with 7 changes to 7 files (+1 heads) + new changesets 426bada5c675:bb94757e651a + test-debug-phase: new rev 0: x -> 0 + test-debug-phase: new rev 1: x -> 0 + test-debug-phase: new rev 2: x -> 0 + test-debug-phase: new rev 3: x -> 1 + test-debug-phase: new rev 4: x -> 0 + test-debug-phase: new rev 5: x -> 1 + test-debug-phase: new rev 6: x -> 1 + test-debug-phase: new rev 7: x -> 1 + + $ hg -R mergetest-normal log -G -T '{shortest(node, 5)} {phase}' + o bb947 draft + | + | o 5ac28 draft + |/| + o | 13b7b draft + | | + | o f5853 public + | | + o | c67c4 draft + | | + | o 26805 public + |/ + o 11247 public + | + o 426ba public + + $ killdaemons.py + +With legacy listkeys over bundle2 +(issue 5939: public phase was lost on 26805 and f5853 before, due to a bug +of phase heads computation) + + $ hg -R mergetest --config devel.legacy.exchange=phases serve -p $HGPORT -d --pid-file=hg.pid + $ cat hg.pid >> $DAEMON_PIDS + + $ hg clone -U http://localhost:$HGPORT mergetest-nobinarypart + requesting all changes + adding changesets + adding manifests + adding file changes + added 8 changesets with 7 changes to 7 files (+1 heads) + new changesets 426bada5c675:bb94757e651a + test-debug-phase: new rev 0: x -> 0 + test-debug-phase: new rev 1: x -> 0 + test-debug-phase: new rev 2: x -> 0 + test-debug-phase: new rev 3: x -> 1 + test-debug-phase: new rev 4: x -> 0 + test-debug-phase: new rev 5: x -> 1 + test-debug-phase: new rev 6: x -> 1 + test-debug-phase: new rev 7: x -> 1 + + $ hg -R mergetest-nobinarypart log -G -T '{shortest(node, 5)} {phase}' + o bb947 draft + | + | o 5ac28 draft + |/| + o | 13b7b draft + | | + | o f5853 public + | | + o | c67c4 draft + | | + | o 26805 public + |/ + o 11247 public + | + o 426ba public + + $ killdaemons.py + +Without bundle2 +(issue 5939: public phase was lost on 26805 and f5853 before, due to a bug +of phase heads computation) + + $ hg -R mergetest serve -p $HGPORT -d --pid-file=hg.pid + $ cat hg.pid >> $DAEMON_PIDS + + $ hg --config devel.legacy.exchange=bundle1 clone -U http://localhost:$HGPORT mergetest-bundle1 + requesting all changes + adding changesets + adding manifests + adding file changes + added 8 changesets with 7 changes to 7 files (+1 heads) + new changesets 426bada5c675:bb94757e651a + test-debug-phase: new rev 0: x -> 0 + test-debug-phase: new rev 1: x -> 0 + test-debug-phase: new rev 2: x -> 0 + test-debug-phase: new rev 3: x -> 1 + test-debug-phase: new rev 4: x -> 0 + test-debug-phase: new rev 5: x -> 1 + test-debug-phase: new rev 6: x -> 1 + test-debug-phase: new rev 7: x -> 1 + + $ hg -R mergetest-bundle1 log -G -T '{shortest(node, 5)} {phase}' + o bb947 draft + | + | o 5ac28 draft + |/| + o | 13b7b draft + | | + | o f5853 public + | | + o | c67c4 draft + | | + | o 26805 public + |/ + o 11247 public + | + o 426ba public +
--- a/tests/test-profile.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-profile.t Thu Jul 19 13:55:54 2018 -0400 @@ -69,7 +69,7 @@ > from mercurial import registrar, commands > cmdtable = {} > command = registrar.command(cmdtable) - > @command(b'sleep', [], 'hg sleep') + > @command(b'sleep', [], b'hg sleep') > def sleep(ui, *args, **kwargs): > time.sleep(0.1) > EOF @@ -123,13 +123,13 @@ > yield > print('fooprof: end profile') > def extsetup(ui): - > ui.write('fooprof: loaded\n') + > ui.write(b'fooprof: loaded\n') > EOF $ cat > otherextension.py <<EOF > from __future__ import absolute_import > def extsetup(ui): - > ui.write('otherextension: loaded\n') + > ui.write(b'otherextension: loaded\n') > EOF $ hg init b
--- a/tests/test-progress.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-progress.t Thu Jul 19 13:55:54 2018 -0400 @@ -16,10 +16,10 @@ > time.time = incrementingtime() > > @command(b'loop', - > [('', 'total', '', 'override for total'), - > ('', 'nested', False, 'show nested results'), - > ('', 'parallel', False, 'show parallel sets of results')], - > 'hg loop LOOPS', + > [(b'', b'total', b'', b'override for total'), + > (b'', b'nested', False, b'show nested results'), + > (b'', b'parallel', False, b'show parallel sets of results')], + > b'hg loop LOOPS', > norepo=True) > def loop(ui, loops, **opts): > loops = int(loops) @@ -33,20 +33,22 @@ > nested = True > loops = abs(loops) > + > progress = ui.makeprogress(topiclabel, unit='loopnum', total=total) + > other = ui.makeprogress('other', unit='othernum', total=total) > for i in range(loops): - > ui.progress(topiclabel, i, getloopitem(i), 'loopnum', total) + > progress.update(i, item=getloopitem(i)) > if opts.get('parallel'): - > ui.progress('other', i, 'other.%d' % i, 'othernum', total) + > other.update(i, item='other.%d' % i) > if nested: > nested_steps = 2 > if i and i % 4 == 0: > nested_steps = 5 + > nested = ui.makeprogress('nested', unit='nestnum', + > total=nested_steps) > for j in range(nested_steps): - > ui.progress( - > 'nested', j, 'nested.%d' % j, 'nestnum', nested_steps) - > ui.progress( - > 'nested', None, 'nested.done', 'nestnum', nested_steps) - > ui.progress(topiclabel, None, 'loop.done', 'loopnum', total) + > nested.update(j, item='nested.%d' % j) + > nested.complete() + > progress.complete() > > topiclabel = 'loop' > def getloopitem(i):
--- a/tests/test-pull-branch.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-pull-branch.t Thu Jul 19 13:55:54 2018 -0400 @@ -170,6 +170,7 @@ adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) new changesets 7d8ffa4c0b22 + 13 local changesets published (run 'hg heads' to see heads) Make changes on default and branchC on tt @@ -183,6 +184,7 @@ adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) new changesets 2b94b54b6b5f + 1 local changesets published (run 'hg heads' to see heads) $ hg up -C default 2 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -219,6 +221,7 @@ adding file changes added 2 changesets with 2 changes to 2 files (+2 heads) new changesets eed40c14b407:e634733b0309 + 1 local changesets published (run 'hg heads .' to see heads, 'hg merge' to merge) $ cd ..
--- a/tests/test-pull-bundle.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-pull-bundle.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,3 +1,5 @@ +#require no-chg + $ hg init repo $ cd repo $ echo foo > foo @@ -44,9 +46,9 @@ $ hg bundle --base 1 -r 2 .hg/2.hg 1 changesets found $ cat <<EOF > .hg/pullbundles.manifest - > 2.hg heads=effea6de0384e684f44435651cb7bd70b8735bd4 bases=bbd179dfa0a71671c253b3ae0aa1513b60d199fa - > 1.hg heads=ed1b79f46b9a29f5a6efa59cf12fcfca43bead5a bases=bbd179dfa0a71671c253b3ae0aa1513b60d199fa - > 0.hg heads=bbd179dfa0a71671c253b3ae0aa1513b60d199fa + > 2.hg BUNDLESPEC=none-v2 heads=effea6de0384e684f44435651cb7bd70b8735bd4 bases=bbd179dfa0a71671c253b3ae0aa1513b60d199fa + > 1.hg BUNDLESPEC=bzip2-v2 heads=ed1b79f46b9a29f5a6efa59cf12fcfca43bead5a bases=bbd179dfa0a71671c253b3ae0aa1513b60d199fa + > 0.hg BUNDLESPEC=gzip-v2 heads=bbd179dfa0a71671c253b3ae0aa1513b60d199fa > EOF $ hg --config blackbox.track=debug --debug serve -p $HGPORT2 -d --pid-file=../repo.pid listening at http://*:$HGPORT2/ (bound to $LOCALIP:$HGPORT2) (glob) (?)
--- a/tests/test-pull-http.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-pull-http.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - #if no-windows For debugging: this is a pretty simple test that is a good candidate for tracking down network-related bugs. Sometimes a command in this
--- a/tests/test-pull-update.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-pull-update.t Thu Jul 19 13:55:54 2018 -0400 @@ -27,6 +27,7 @@ adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) new changesets 107cefe13e42 + 1 local changesets published abort: uncommitted changes [255] $ hg --config extensions.strip= strip --no-backup tip @@ -58,6 +59,7 @@ adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) new changesets 800c91d5bfc1 + 1 local changesets published 0 files updated, 0 files merged, 0 files removed, 0 files unresolved updated to "107cefe13e42: m" 1 other heads for branch "default" @@ -80,6 +82,7 @@ adding file changes added 1 changesets with 1 changes to 1 files (-1 heads) new changesets 483b76ad4309 + 1 local changesets published 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Similarity between "hg update" and "hg pull -u" in handling bookmark @@ -108,6 +111,7 @@ added 1 changesets with 1 changes to 1 files adding remote bookmark active-after-pull new changesets f815b3da6163 + 1 local changesets published 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (activating bookmark active-after-pull) @@ -137,6 +141,7 @@ added 1 changesets with 1 changes to 1 files adding remote bookmark active-after-pull new changesets f815b3da6163 + 1 local changesets published 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (activating bookmark active-after-pull) @@ -175,6 +180,7 @@ adding file changes added 2 changesets with 1 changes to 1 files new changesets f815b3da6163:b5e4babfaaa7 + 1 local changesets published 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (leaving bookmark active-before-pull) @@ -202,6 +208,7 @@ adding file changes added 2 changesets with 1 changes to 1 files new changesets f815b3da6163:b5e4babfaaa7 + 1 local changesets published 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (leaving bookmark active-before-pull) @@ -229,6 +236,7 @@ adding file changes added 2 changesets with 1 changes to 1 files new changesets f815b3da6163:b5e4babfaaa7 + 1 local changesets published 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (leaving bookmark active-before-pull)
--- a/tests/test-pull.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-pull.t Thu Jul 19 13:55:54 2018 -0400 @@ -109,12 +109,12 @@ It's tricky to make file:// URLs working on every platform with regular shell commands. - $ URL=`$PYTHON -c "import os; print 'file://foobar' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test'"` + $ URL=`$PYTHON -c "from __future__ import print_function; import os; print('file://foobar' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test')"` $ hg pull -q "$URL" abort: file:// URLs can only refer to localhost [255] - $ URL=`$PYTHON -c "import os; print 'file://localhost' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test'"` + $ URL=`$PYTHON -c "from __future__ import print_function; import os; print('file://localhost' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test')"` $ hg pull -q "$URL" SEC: check for unsafe ssh url
--- a/tests/test-push-http.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-push-http.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,4 +1,4 @@ -#require killdaemons +#require no-chg #testcases bundle1 bundle2
--- a/tests/test-py3-commands.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-py3-commands.t Thu Jul 19 13:55:54 2018 -0400 @@ -198,28 +198,28 @@ $ $PYTHON3 $HGBIN log -Tjson [ { - "rev": 1, - "node": "e1e9167203d450ca2f558af628955b5f5afd4489", + "bookmarks": [], "branch": "default", - "phase": "draft", - "user": "test", "date": [0, 0], "desc": "message", - "bookmarks": [], + "node": "e1e9167203d450ca2f558af628955b5f5afd4489", + "parents": ["71c96e924262969ff0d8d3d695b0f75412ccc3d8"], + "phase": "draft", + "rev": 1, "tags": ["tip"], - "parents": ["71c96e924262969ff0d8d3d695b0f75412ccc3d8"] + "user": "test" }, { - "rev": 0, - "node": "71c96e924262969ff0d8d3d695b0f75412ccc3d8", + "bookmarks": [], "branch": "default", - "phase": "draft", - "user": "test", "date": [0, 0], "desc": "commit performed in Python 3", - "bookmarks": [], + "node": "71c96e924262969ff0d8d3d695b0f75412ccc3d8", + "parents": ["0000000000000000000000000000000000000000"], + "phase": "draft", + "rev": 0, "tags": [], - "parents": ["0000000000000000000000000000000000000000"] + "user": "test" } ]
--- a/tests/test-rebase-conflicts.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-rebase-conflicts.t Thu Jul 19 13:55:54 2018 -0400 @@ -80,8 +80,8 @@ # # To mark files as resolved: hg resolve --mark FILE - # To continue: hg rebase --continue - # To abort: hg rebase --abort + # To continue: hg rebase --continue + # To abort: hg rebase --abort Try to continue without solving the conflict:
--- a/tests/test-rebase-inmemory.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-rebase-inmemory.t Thu Jul 19 13:55:54 2018 -0400 @@ -4,6 +4,7 @@ > amend= > rebase= > debugdrawdag=$TESTDIR/drawdag.py + > strip= > [rebase] > experimental.inmemory=1 > [diff] @@ -155,4 +156,356 @@ |/ o 0: b173517d0057 'a' +Test dry-run rebasing + $ hg init repo3 + $ cd repo3 + $ echo a>a + $ hg ci -Aqma + $ echo b>b + $ hg ci -Aqmb + $ echo c>c + $ hg ci -Aqmc + $ echo d>d + $ hg ci -Aqmd + $ echo e>e + $ hg ci -Aqme + $ hg up 1 -q + $ echo f>f + $ hg ci -Amf + adding f + created new head + $ echo g>g + $ hg ci -Aqmg + $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n" + @ 6:baf10c5166d4 test + | g + | + o 5:6343ca3eff20 test + | f + | + | o 4:e860deea161a test + | | e + | | + | o 3:055a42cdd887 test + | | d + | | + | o 2:177f92b77385 test + |/ c + | + o 1:d2ae7f538514 test + | b + | + o 0:cb9a9f314b8b test + a + +Make sure it throws error while passing --continue or --abort with --dry-run + $ hg rebase -s 2 -d 6 -n --continue + abort: cannot specify both --dry-run and --continue + [255] + $ hg rebase -s 2 -d 6 -n --abort + abort: cannot specify both --dry-run and --abort + [255] + +Check dryrun gives correct results when there is no conflict in rebasing + $ hg rebase -s 2 -d 6 -n + starting dry-run rebase; repository will not be changed + rebasing 2:177f92b77385 "c" + rebasing 3:055a42cdd887 "d" + rebasing 4:e860deea161a "e" + dry-run rebase completed successfully; run without -n/--dry-run to perform this rebase + + $ hg diff + $ hg status + + $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n" + @ 6:baf10c5166d4 test + | g + | + o 5:6343ca3eff20 test + | f + | + | o 4:e860deea161a test + | | e + | | + | o 3:055a42cdd887 test + | | d + | | + | o 2:177f92b77385 test + |/ c + | + o 1:d2ae7f538514 test + | b + | + o 0:cb9a9f314b8b test + a + +Check dryrun working with --collapse when there is no conflict + $ hg rebase -s 2 -d 6 -n --collapse + starting dry-run rebase; repository will not be changed + rebasing 2:177f92b77385 "c" + rebasing 3:055a42cdd887 "d" + rebasing 4:e860deea161a "e" + dry-run rebase completed successfully; run without -n/--dry-run to perform this rebase + +Check dryrun gives correct results when there is conflict in rebasing +Make a conflict: + $ hg up 6 -q + $ echo conflict>e + $ hg ci -Aqm "conflict with e" + $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n" + @ 7:d2c195b28050 test + | conflict with e + | + o 6:baf10c5166d4 test + | g + | + o 5:6343ca3eff20 test + | f + | + | o 4:e860deea161a test + | | e + | | + | o 3:055a42cdd887 test + | | d + | | + | o 2:177f92b77385 test + |/ c + | + o 1:d2ae7f538514 test + | b + | + o 0:cb9a9f314b8b test + a + + $ hg rebase -s 2 -d 7 -n + starting dry-run rebase; repository will not be changed + rebasing 2:177f92b77385 "c" + rebasing 3:055a42cdd887 "d" + rebasing 4:e860deea161a "e" + merging e + transaction abort! + rollback completed + hit a merge conflict + [1] + $ hg diff + $ hg status + $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n" + @ 7:d2c195b28050 test + | conflict with e + | + o 6:baf10c5166d4 test + | g + | + o 5:6343ca3eff20 test + | f + | + | o 4:e860deea161a test + | | e + | | + | o 3:055a42cdd887 test + | | d + | | + | o 2:177f92b77385 test + |/ c + | + o 1:d2ae7f538514 test + | b + | + o 0:cb9a9f314b8b test + a + +Check dryrun working with --collapse when there is conflicts + $ hg rebase -s 2 -d 7 -n --collapse + starting dry-run rebase; repository will not be changed + rebasing 2:177f92b77385 "c" + rebasing 3:055a42cdd887 "d" + rebasing 4:e860deea161a "e" + merging e + hit a merge conflict + [1] + +========================== +Test for --confirm option| +========================== + $ cd .. + $ hg clone repo3 repo4 -q + $ cd repo4 + $ hg strip 7 -q + $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n" + @ 6:baf10c5166d4 test + | g + | + o 5:6343ca3eff20 test + | f + | + | o 4:e860deea161a test + | | e + | | + | o 3:055a42cdd887 test + | | d + | | + | o 2:177f92b77385 test + |/ c + | + o 1:d2ae7f538514 test + | b + | + o 0:cb9a9f314b8b test + a + +Check it gives error when both --dryrun and --confirm is used: + $ hg rebase -s 2 -d . --confirm --dry-run + abort: cannot specify both --confirm and --dry-run + [255] + $ hg rebase -s 2 -d . --confirm --abort + abort: cannot specify both --confirm and --abort + [255] + $ hg rebase -s 2 -d . --confirm --continue + abort: cannot specify both --confirm and --continue + [255] + +Test --confirm option when there are no conflicts: + $ hg rebase -s 2 -d . --keep --config ui.interactive=True --confirm << EOF + > n + > EOF + starting in-memory rebase + rebasing 2:177f92b77385 "c" + rebasing 3:055a42cdd887 "d" + rebasing 4:e860deea161a "e" + rebase completed successfully + apply changes (yn)? n + $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n" + @ 6:baf10c5166d4 test + | g + | + o 5:6343ca3eff20 test + | f + | + | o 4:e860deea161a test + | | e + | | + | o 3:055a42cdd887 test + | | d + | | + | o 2:177f92b77385 test + |/ c + | + o 1:d2ae7f538514 test + | b + | + o 0:cb9a9f314b8b test + a + + $ hg rebase -s 2 -d . --keep --config ui.interactive=True --confirm << EOF + > y + > EOF + starting in-memory rebase + rebasing 2:177f92b77385 "c" + rebasing 3:055a42cdd887 "d" + rebasing 4:e860deea161a "e" + rebase completed successfully + apply changes (yn)? y + $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n" + o 9:9fd28f55f6dc test + | e + | + o 8:12cbf031f469 test + | d + | + o 7:c83b1da5b1ae test + | c + | + @ 6:baf10c5166d4 test + | g + | + o 5:6343ca3eff20 test + | f + | + | o 4:e860deea161a test + | | e + | | + | o 3:055a42cdd887 test + | | d + | | + | o 2:177f92b77385 test + |/ c + | + o 1:d2ae7f538514 test + | b + | + o 0:cb9a9f314b8b test + a + +Test --confirm option when there is a conflict + $ hg up tip -q + $ echo ee>e + $ hg ci --amend -m "conflict with e" -q + $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n" + @ 9:906d72f66a59 test + | conflict with e + | + o 8:12cbf031f469 test + | d + | + o 7:c83b1da5b1ae test + | c + | + o 6:baf10c5166d4 test + | g + | + o 5:6343ca3eff20 test + | f + | + | o 4:e860deea161a test + | | e + | | + | o 3:055a42cdd887 test + | | d + | | + | o 2:177f92b77385 test + |/ c + | + o 1:d2ae7f538514 test + | b + | + o 0:cb9a9f314b8b test + a + + $ hg rebase -s 4 -d . --keep --confirm + starting in-memory rebase + rebasing 4:e860deea161a "e" + merging e + hit a merge conflict + [1] + $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n" + @ 9:906d72f66a59 test + | conflict with e + | + o 8:12cbf031f469 test + | d + | + o 7:c83b1da5b1ae test + | c + | + o 6:baf10c5166d4 test + | g + | + o 5:6343ca3eff20 test + | f + | + | o 4:e860deea161a test + | | e + | | + | o 3:055a42cdd887 test + | | d + | | + | o 2:177f92b77385 test + |/ c + | + o 1:d2ae7f538514 test + | b + | + o 0:cb9a9f314b8b test + a +
--- a/tests/test-rebase-interruptions.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-rebase-interruptions.t Thu Jul 19 13:55:54 2018 -0400 @@ -333,12 +333,8 @@ $ cp -R a3 hook-pretxncommit $ cd hook-pretxncommit -#if windows - $ NODE="%HG_NODE%" -#else - $ NODE="\$HG_NODE" -#endif - $ hg rebase --source 2 --dest 5 --tool internal:other --config "hooks.pretxncommit=hg log -r $NODE | grep \"summary: C\"" + $ hg rebase --source 2 --dest 5 --tool internal:other \ + > --config 'hooks.tonative.pretxncommit=True' --config 'hooks.pretxncommit=hg log -r $HG_NODE | grep "summary: C"' rebasing 2:965c486023db "C" summary: C rebasing 6:a0b2430ebfb8 "F" (tip)
--- a/tests/test-rebase-obsolete.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-rebase-obsolete.t Thu Jul 19 13:55:54 2018 -0400 @@ -281,7 +281,7 @@ even though it is hidden (until we're moved there). $ hg --hidden up -qr 'first(hidden())' - updating to a hidden changeset 42ccdea3bb16 + updated to hidden changeset 42ccdea3bb16 (hidden revision '42ccdea3bb16' is pruned) $ hg rebase --rev 13 --dest 15 rebasing 13:98f6af4ee953 "C" @@ -642,9 +642,9 @@ Test hidden changesets in the rebase set (issue4504) $ hg up --hidden 9 - updating to a hidden changeset 4bde274eefcf + 3 files updated, 0 files merged, 1 files removed, 0 files unresolved + updated to hidden changeset 4bde274eefcf (hidden revision '4bde274eefcf' was rewritten as: acd174b7ab39) - 3 files updated, 0 files merged, 1 files removed, 0 files unresolved $ echo J > J $ hg add J $ hg commit -m J @@ -764,9 +764,9 @@ $ hg commit --amend -m B1 $ hg commit --amend -m B2 $ hg up --hidden 'desc(B0)' - updating to a hidden changeset a8b11f55fb19 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + updated to hidden changeset a8b11f55fb19 (hidden revision 'a8b11f55fb19' was rewritten as: 261e70097290) - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ echo C > C $ hg add C $ hg commit -m C @@ -790,9 +790,9 @@ Even when the chain include missing node $ hg up --hidden 'desc(B0)' - updating to a hidden changeset a8b11f55fb19 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + updated to hidden changeset a8b11f55fb19 (hidden revision 'a8b11f55fb19' was rewritten as: 261e70097290) - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved $ echo D > D $ hg add D $ hg commit -m D @@ -909,9 +909,9 @@ $ hg add bar $ hg commit --amend -m "10'" $ hg up 10 --hidden - updating to a hidden changeset 121d9e3bc4c6 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + updated to hidden changeset 121d9e3bc4c6 (hidden revision '121d9e3bc4c6' was rewritten as: 77d874d096a2) - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved $ echo "bar" > foo $ hg add foo $ hg commit -m "bar foo" @@ -1738,7 +1738,7 @@ $ rm .hg/localtags $ hg update -q $C --hidden - updating to a hidden changeset 7829726be4dc + updated to hidden changeset 7829726be4dc (hidden revision '7829726be4dc' is pruned) $ hg rebase -s $B -d $D rebasing 1:2ec65233581b "B"
--- a/tests/test-rebuildstate.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-rebuildstate.t Thu Jul 19 13:55:54 2018 -0400 @@ -4,9 +4,9 @@ > cmdtable = {} > command = registrar.command(cmdtable) > @command(b'debugadddrop', - > [('', 'drop', False, 'drop file from dirstate', 'FILE'), - > ('', 'normal-lookup', False, 'add file to dirstate', 'FILE')], - > 'hg debugadddrop') + > [(b'', b'drop', False, b'drop file from dirstate', b'FILE'), + > (b'', b'normal-lookup', False, b'add file to dirstate', b'FILE')], + > b'hg debugadddrop') > def debugadddrop(ui, repo, *pats, **opts): > '''Add or drop unnamed arguments to or from the dirstate''' > drop = opts.get('drop')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-removeemptydirs.t Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,330 @@ +Tests for experimental.removeemptydirs + + $ NO_RM=--config=experimental.removeemptydirs=0 + $ isdir() { if [ -d $1 ]; then echo yes; else echo no; fi } + $ isfile() { if [ -f $1 ]; then echo yes; else echo no; fi } + +`hg rm` of the last file in a directory: + $ hg init hgrm + $ cd hgrm + $ mkdir somedir + $ echo hi > somedir/foo + $ hg ci -qAm foo + $ isdir somedir + yes + $ hg rm somedir/foo + $ isdir somedir + no + $ hg revert -qa + $ isdir somedir + yes + $ hg $NO_RM rm somedir/foo + $ isdir somedir + yes + $ ls somedir + $ cd $TESTTMP + +`hg mv` of the last file in a directory: + $ hg init hgmv + $ cd hgmv + $ mkdir somedir + $ mkdir destdir + $ echo hi > somedir/foo + $ hg ci -qAm foo + $ isdir somedir + yes + $ hg mv somedir/foo destdir/foo + $ isdir somedir + no + $ hg revert -qa +(revert doesn't get rid of destdir/foo?) + $ rm destdir/foo + $ isdir somedir + yes + $ hg $NO_RM mv somedir/foo destdir/foo + $ isdir somedir + yes + $ ls somedir + $ cd $TESTTMP + +Updating to a commit that doesn't have the directory: + $ hg init hgupdate + $ cd hgupdate + $ echo hi > r0 + $ hg ci -qAm r0 + $ mkdir somedir + $ echo hi > somedir/foo + $ hg ci -qAm r1 + $ isdir somedir + yes + $ hg co -q -r ".^" + $ isdir somedir + no + $ hg co -q tip + $ isdir somedir + yes + $ hg $NO_RM co -q -r ".^" + $ isdir somedir + yes + $ ls somedir + $ cd $TESTTMP + +Rebasing across a commit that doesn't have the directory, from inside the +directory: + $ hg init hgrebase + $ cd hgrebase + $ echo hi > r0 + $ hg ci -qAm r0 + $ mkdir somedir + $ echo hi > somedir/foo + $ hg ci -qAm first_rebase_source + $ hg $NO_RM co -q -r ".^" + $ echo hi > somedir/bar + $ hg ci -qAm first_rebase_dest + $ hg $NO_RM co -q -r ".^" + $ echo hi > somedir/baz + $ hg ci -qAm second_rebase_dest + $ hg co -qr 'desc(first_rebase_source)' + $ cd $TESTTMP/hgrebase/somedir + $ hg --config extensions.rebase= rebase -qr . -d 'desc(first_rebase_dest)' + current directory was removed + (consider changing to repo root: $TESTTMP/hgrebase) + $ cd $TESTTMP/hgrebase/somedir +(The current node is the rebased first_rebase_source on top of +first_rebase_dest) +This should not output anything about current directory being removed: + $ hg $NO_RM --config extensions.rebase= rebase -qr . -d 'desc(second_rebase_dest)' + $ cd $TESTTMP + +Histediting across a commit that doesn't have the directory, from inside the +directory (reordering nodes): + $ hg init hghistedit + $ cd hghistedit + $ echo hi > r0 + $ hg ci -qAm r0 + $ echo hi > r1 + $ hg ci -qAm r1 + $ echo hi > r2 + $ hg ci -qAm r2 + $ mkdir somedir + $ echo hi > somedir/foo + $ hg ci -qAm migrating_revision + $ cat > histedit_commands <<EOF + > pick 89079fab8aee 0 r0 + > pick e6d271df3142 1 r1 + > pick 89e25aa83f0f 3 migrating_revision + > pick b550aa12d873 2 r2 + > EOF + $ cd $TESTTMP/hghistedit/somedir + $ hg --config extensions.histedit= histedit -q --commands ../histedit_commands + +histedit doesn't output anything when the current diretory is removed. We rely +on the tests being commonly run on machines where the current directory +disappearing from underneath us actually has an observable effect, such as an +error or no files listed +#if linuxormacos + $ isfile foo + no +#endif + $ cd $TESTTMP/hghistedit/somedir + $ isfile foo + yes + + $ cd $TESTTMP/hghistedit + $ cat > histedit_commands <<EOF + > pick 89079fab8aee 0 r0 + > pick 7c7a22c6009f 3 migrating_revision + > pick e6d271df3142 1 r1 + > pick 40a53c2d4276 2 r2 + > EOF + $ cd $TESTTMP/hghistedit/somedir + $ hg $NO_RM --config extensions.histedit= histedit -q --commands ../histedit_commands +Regardless of system, we should always get a 'yes' here. + $ isfile foo + yes + $ cd $TESTTMP + +This is essentially the exact test from issue5826, just cleaned up a little: + + $ hg init issue5826_withrm + $ cd issue5826_withrm + +Let's only turn this on for this repo so that we don't contaminate later tests. + $ cat >> .hg/hgrc <<EOF + > [extensions] + > histedit = + > EOF +Commit three revisions that each create a directory: + + $ mkdir foo + $ touch foo/bar + $ hg commit -qAm "add foo" + + $ mkdir bar + $ touch bar/bar + $ hg commit -qAm "add bar" + + $ mkdir baz + $ touch baz/bar + $ hg commit -qAm "add baz" + +Enter the first directory: + + $ cd foo + +Histedit doing 'pick, pick, fold': + + $ hg histedit --commands /dev/stdin <<EOF + > pick 6274c77c93c3 1 add bar + > pick ff70a87b588f 0 add foo + > fold 9992bb0ac0db 2 add baz + > EOF + abort: $ENOENT$ + [255] + +Go back to the repo root after losing it as part of that operation: + $ cd $TESTTMP/issue5826_withrm + +Note the lack of a non-zero exit code from this function - it exits +successfully, but doesn't really do anything. + $ hg histedit --continue + 9992bb0ac0db: cannot fold - working copy is not a descendant of previous commit 5c806432464a + saved backup bundle to $TESTTMP/issue5826_withrm/.hg/strip-backup/ff70a87b588f-e94f9789-histedit.hg + + $ hg log -T '{rev}:{node|short} {desc}\n' + 2:94e3f9fae1d6 fold-temp-revision 9992bb0ac0db + 1:5c806432464a add foo + 0:d17db4b0303a add bar + +Now test that again with experimental.removeemptydirs=false: + $ hg init issue5826_norm + $ cd issue5826_norm + +Let's only turn this on for this repo so that we don't contaminate later tests. + $ cat >> .hg/hgrc <<EOF + > [extensions] + > histedit = + > [experimental] + > removeemptydirs = false + > EOF +Commit three revisions that each create a directory: + + $ mkdir foo + $ touch foo/bar + $ hg commit -qAm "add foo" + + $ mkdir bar + $ touch bar/bar + $ hg commit -qAm "add bar" + + $ mkdir baz + $ touch baz/bar + $ hg commit -qAm "add baz" + +Enter the first directory: + + $ cd foo + +Histedit doing 'pick, pick, fold': + + $ hg histedit --commands /dev/stdin <<EOF + > pick 6274c77c93c3 1 add bar + > pick ff70a87b588f 0 add foo + > fold 9992bb0ac0db 2 add baz + > EOF + saved backup bundle to $TESTTMP/issue5826_withrm/issue5826_norm/.hg/strip-backup/5c806432464a-cd4c8d86-histedit.hg + +Note the lack of a 'cd' being necessary here, and we don't need to 'histedit +--continue' + + $ hg log -T '{rev}:{node|short} {desc}\n' + 1:b9eddaa97cbc add foo + *** + add baz + 0:d17db4b0303a add bar + + $ cd $TESTTMP + +Testing `hg split` being run from inside of a directory that was created in the +commit being split: + + $ hg init hgsplit + $ cd hgsplit + $ cat >> .hg/hgrc << EOF + > [ui] + > interactive = 1 + > [extensions] + > split = + > EOF + $ echo anchor > anchor.txt + $ hg ci -qAm anchor + +Create a changeset with '/otherfile_in_root' and 'somedir/foo', then try to +split it. + $ echo otherfile > otherfile_in_root + $ mkdir somedir + $ cd somedir + $ echo hi > foo + $ hg ci -qAm split_me +(Note: need to make this file not in this directory, or else the bug doesn't +reproduce; we're using a separate file due to concerns of portability on +`echo -e`) + $ cat > ../split_commands << EOF + > n + > y + > y + > a + > EOF + $ cat ../split_commands | hg split + current directory was removed + (consider changing to repo root: $TESTTMP/hgsplit) + diff --git a/otherfile_in_root b/otherfile_in_root + new file mode 100644 + examine changes to 'otherfile_in_root'? [Ynesfdaq?] n + + diff --git a/somedir/foo b/somedir/foo + new file mode 100644 + examine changes to 'somedir/foo'? [Ynesfdaq?] y + + @@ -0,0 +1,1 @@ + +hi + record change 2/2 to 'somedir/foo'? [Ynesfdaq?] y + + abort: $ENOENT$ + [255] + +Let's try that again without the rmdir + $ cd $TESTTMP/hgsplit/somedir +Show that the previous split didn't do anything + $ hg log -T '{rev}:{node|short} {desc}\n' + 1:e26b22a4f0b7 split_me + 0:7e53273730c0 anchor + $ hg status + ? split_commands +Try again + $ cat ../split_commands | hg $NO_RM split + diff --git a/otherfile_in_root b/otherfile_in_root + new file mode 100644 + examine changes to 'otherfile_in_root'? [Ynesfdaq?] n + + diff --git a/somedir/foo b/somedir/foo + new file mode 100644 + examine changes to 'somedir/foo'? [Ynesfdaq?] y + + @@ -0,0 +1,1 @@ + +hi + record change 2/2 to 'somedir/foo'? [Ynesfdaq?] y + + created new head + diff --git a/otherfile_in_root b/otherfile_in_root + new file mode 100644 + examine changes to 'otherfile_in_root'? [Ynesfdaq?] a + + saved backup bundle to $TESTTMP/hgsplit/.hg/strip-backup/*-split.hg (glob) +Show that this split did something + $ hg log -T '{rev}:{node|short} {desc}\n' + 2:a440f24fca4f split_me + 1:c994f20276ab split_me + 0:7e53273730c0 anchor + $ hg status + ? split_commands
--- a/tests/test-rename-after-merge.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-rename-after-merge.t Thu Jul 19 13:55:54 2018 -0400 @@ -36,6 +36,7 @@ adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) new changesets d2ae7f538514 + 1 local changesets published (run 'hg heads' to see heads, 'hg merge' to merge) $ hg merge
--- a/tests/test-rename-dir-merge.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-rename-dir-merge.t Thu Jul 19 13:55:54 2018 -0400 @@ -219,6 +219,7 @@ adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) new changesets 7d51ed18da25 + 1 local changesets published (run 'hg heads' to see heads, 'hg merge' to merge) $ hg merge
--- a/tests/test-resolve.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-resolve.t Thu Jul 19 13:55:54 2018 -0400 @@ -162,6 +162,10 @@ } ] + $ hg resolve -l -T '{path} {status} {p1rev} {p2rev}\n' + file1 R 2 1 + file2 U 2 1 + resolve -m without paths should mark all resolved $ hg resolve -m
--- a/tests/test-revert-interactive.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-revert-interactive.t Thu Jul 19 13:55:54 2018 -0400 @@ -22,16 +22,16 @@ $ mkdir -p a/folder1 a/folder2 $ cd a $ hg init - >>> open('f', 'wb').write("1\n2\n3\n4\n5\n") + >>> open('f', 'wb').write(b"1\n2\n3\n4\n5\n") and None $ hg add f ; hg commit -m "adding f" $ cat f > folder1/g ; hg add folder1/g ; hg commit -m "adding folder1/g" $ cat f > folder2/h ; hg add folder2/h ; hg commit -m "adding folder2/h" $ cat f > folder1/i ; hg add folder1/i ; hg commit -m "adding folder1/i" - >>> open('f', 'wb').write("a\n1\n2\n3\n4\n5\nb\n") + >>> open('f', 'wb').write(b"a\n1\n2\n3\n4\n5\nb\n") and None $ hg commit -m "modifying f" - >>> open('folder1/g', 'wb').write("c\n1\n2\n3\n4\n5\nd\n") + >>> open('folder1/g', 'wb').write(b"c\n1\n2\n3\n4\n5\nd\n") and None $ hg commit -m "modifying folder1/g" - >>> open('folder2/h', 'wb').write("e\n1\n2\n3\n4\n5\nf\n") + >>> open('folder2/h', 'wb').write(b"e\n1\n2\n3\n4\n5\nf\n") and None $ hg commit -m "modifying folder2/h" $ hg tip changeset: 6:59dd6e4ab63a @@ -182,7 +182,7 @@ $ ls folder1/ g - >>> open('folder1/g', 'wb').write("1\n2\n3\n4\n5\nd\n") + >>> open('folder1/g', 'wb').write(b"1\n2\n3\n4\n5\nd\n") and None $ hg update -C 6
--- a/tests/test-revlog-raw.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-revlog-raw.py Thu Jul 19 13:55:54 2018 -0400 @@ -13,10 +13,10 @@ ) # TESTTMP is optional. This makes it convenient to run without run-tests.py -tvfs = vfs.vfs(encoding.environ.get('TESTTMP', b'/tmp')) +tvfs = vfs.vfs(encoding.environ.get(b'TESTTMP', b'/tmp')) # Enable generaldelta otherwise revlog won't use delta as expected by the test -tvfs.options = {'generaldelta': True, 'revlogv1': True} +tvfs.options = {b'generaldelta': True, b'revlogv1': True} # The test wants to control whether to use delta explicitly, based on # "storedeltachains". @@ -116,21 +116,21 @@ deltaparent = min(0, parentrev) if not rlog.candelta(deltaparent, r): deltaparent = -1 - return {'node': rlog.node(r), 'p1': pnode, 'p2': node.nullid, - 'cs': rlog.node(rlog.linkrev(r)), 'flags': rlog.flags(r), - 'deltabase': rlog.node(deltaparent), - 'delta': rlog.revdiff(deltaparent, r)} + return {b'node': rlog.node(r), b'p1': pnode, b'p2': node.nullid, + b'cs': rlog.node(rlog.linkrev(r)), b'flags': rlog.flags(r), + b'deltabase': rlog.node(deltaparent), + b'delta': rlog.revdiff(deltaparent, r)} def deltaiter(self): chain = None for chunkdata in iter(lambda: self.deltachunk(chain), {}): - node = chunkdata['node'] - p1 = chunkdata['p1'] - p2 = chunkdata['p2'] - cs = chunkdata['cs'] - deltabase = chunkdata['deltabase'] - delta = chunkdata['delta'] - flags = chunkdata['flags'] + node = chunkdata[b'node'] + p1 = chunkdata[b'p1'] + p2 = chunkdata[b'p2'] + cs = chunkdata[b'cs'] + deltabase = chunkdata[b'deltabase'] + delta = chunkdata[b'delta'] + flags = chunkdata[b'flags'] chain = node @@ -166,9 +166,9 @@ flags = rlog.flags(r) ifh = dfh = None try: - ifh = dlog.opener(dlog.indexfile, 'a+') + ifh = dlog.opener(dlog.indexfile, b'a+') if not dlog._inline: - dfh = dlog.opener(dlog.datafile, 'a+') + dfh = dlog.opener(dlog.datafile, b'a+') dlog._addrevision(rlog.node(r), text, tr, r, p1, p2, flags, cachedelta, ifh, dfh) finally: @@ -305,7 +305,7 @@ checkrevlog(rl2, expected) print('addgroupcopy test passed') # Copy via revlog.clone - rl3 = newrevlog(name='_destrevlog3.i', recreate=True) + rl3 = newrevlog(name=b'_destrevlog3.i', recreate=True) rl.clone(tr, rl3) checkrevlog(rl3, expected) print('clone test passed')
--- a/tests/test-revlog-v2.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-revlog-v2.t Thu Jul 19 13:55:54 2018 -0400 @@ -31,7 +31,7 @@ Unknown flags to revlog are rejected >>> with open('.hg/store/00changelog.i', 'wb') as fh: - ... fh.write(b'\x00\x04\xde\xad') + ... fh.write(b'\x00\x04\xde\xad') and None $ hg log abort: unknown flags (0x04) in version 57005 revlog 00changelog.i!
--- a/tests/test-revlog.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-revlog.t Thu Jul 19 13:55:54 2018 -0400 @@ -4,7 +4,7 @@ Flags on revlog version 0 are rejected >>> with open('.hg/store/00changelog.i', 'wb') as fh: - ... fh.write(b'\x00\x01\x00\x00') + ... fh.write(b'\x00\x01\x00\x00') and None $ hg log abort: unknown flags (0x01) in version 0 revlog 00changelog.i! @@ -13,7 +13,7 @@ Unknown flags on revlog version 1 are rejected >>> with open('.hg/store/00changelog.i', 'wb') as fh: - ... fh.write(b'\x00\x04\x00\x01') + ... fh.write(b'\x00\x04\x00\x01') and None $ hg log abort: unknown flags (0x04) in version 1 revlog 00changelog.i! @@ -22,7 +22,7 @@ Unknown version is rejected >>> with open('.hg/store/00changelog.i', 'wb') as fh: - ... fh.write(b'\x00\x00\x00\x02') + ... fh.write(b'\x00\x00\x00\x02') and None $ hg log abort: unknown version (2) in revlog 00changelog.i!
--- a/tests/test-revset.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-revset.t Thu Jul 19 13:55:54 2018 -0400 @@ -42,8 +42,8 @@ > registrar, > revset, > revsetlang, - > smartset, > ) + > from mercurial.utils import stringutil > cmdtable = {} > command = registrar.command(cmdtable) > @command(b'debugrevlistspec', @@ -63,7 +63,7 @@ > func = revset.match(ui, expr, lookup=revset.lookupfn(repo)) > revs = func(repo) > if ui.verbose: - > ui.note(b"* set:\n", smartset.prettyformat(revs), b"\n") + > ui.note(b"* set:\n", stringutil.prettyrepr(revs), b"\n") > for c in revs: > ui.write(b"%d\n" % c) > EOF @@ -1041,6 +1041,34 @@ 2 3 +test common ancestors + + $ hg log -T '{rev}\n' -r 'commonancestors(7 + 9)' + 0 + 1 + 2 + 4 + + $ hg log -T '{rev}\n' -r 'commonancestors(head())' + 0 + 1 + 2 + 4 + + $ hg log -T '{rev}\n' -r 'commonancestors(9)' + 0 + 1 + 2 + 4 + 8 + 9 + +test ancestor variants of empty revision + + $ log 'ancestor(none())' + $ log 'ancestors(none())' + $ log 'commonancestors(none())' + test ancestors with depth limit (depth=0 selects the node itself) @@ -1357,8 +1385,40 @@ 6 7 9 + +Test heads + $ log 'heads(6::)' 7 + + heads() can be computed in subset '9:' + + $ hg debugrevspec -s '9: & heads(all())' + * set: + <filteredset + <filteredset + <baseset [9]>, + <spanset+ 0:10>>, + <not + <filteredset + <baseset [9]>, set([0, 1, 2, 3, 4, 5, 6, 8])>>> + 9 + + but should follow the order of the subset + + $ log 'heads(all())' + 7 + 9 + $ log 'heads(tip:0)' + 7 + 9 + $ log 'tip:0 & heads(all())' + 9 + 7 + $ log 'tip:0 & heads(0:tip)' + 9 + 7 + $ log 'keyword(issue)' 6 $ log 'keyword("test a")' @@ -1713,8 +1773,6 @@ Test hexadecimal revision $ log 'id(2)' - abort: 00changelog.i@2: ambiguous identifier! - [255] $ log 'id(23268)' 4 $ log 'id(2785f51eece)' @@ -1783,6 +1841,16 @@ 6 7 2147483647 + $ hg debugrevspec '0:wdir() & ancestor(wdir())' + 2147483647 + $ hg debugrevspec '0:wdir() & ancestor(.:wdir())' + 4 + $ hg debugrevspec '0:wdir() & ancestor(wdir(), wdir())' + 2147483647 + $ hg debugrevspec '0:wdir() & ancestor(wdir(), tip)' + 4 + $ hg debugrevspec 'null:wdir() & ancestor(wdir(), null)' + -1 $ hg debugrevspec 'wdir()~0' 2147483647 $ hg debugrevspec 'p1(wdir())' @@ -1876,9 +1944,9 @@ $ hg debugrevspec '0:wdir() & fffb' abort: 00changelog.i@fffb: ambiguous identifier! [255] -BROKEN should be '2' (node lookup uses unfiltered repo since dc25ed84bee8) +BROKEN should be '2' (node lookup uses unfiltered repo) $ hg debugrevspec '0:wdir() & id(fffb)' - 2 +BROKEN should be '2' (node lookup uses unfiltered repo) $ hg debugrevspec '0:wdir() & ffff8' 4 $ hg debugrevspec '0:wdir() & fffff'
--- a/tests/test-revset2.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-revset2.t Thu Jul 19 13:55:54 2018 -0400 @@ -584,6 +584,9 @@ hg: parse error: empty string is not a valid revision [255] +test empty revset + $ hg log 'none()' + we can use patterns when searching for tags $ log 'tag("1..*")' @@ -1589,11 +1592,11 @@ > > revsetpredicate = registrar.revsetpredicate() > - > @revsetpredicate('custom1()') + > @revsetpredicate(b'custom1()') > def custom1(repo, subset, x): > return revset.baseset([1]) > - > raise error.Abort('intentional failure of loading extension') + > raise error.Abort(b'intentional failure of loading extension') > EOF $ cat <<EOF > .hg/hgrc > [extensions] @@ -1611,14 +1614,14 @@ > from mercurial import encoding, registrar > cmdtable = {} > command = registrar.command(cmdtable) - > @command('printprevset') + > @command(b'printprevset') > def printprevset(ui, repo): > alias = {} - > p = encoding.environ.get('P') + > p = encoding.environ.get(b'P') > if p: - > alias['P'] = p - > revs = repo.anyrevs(['P'], user=True, localalias=alias) - > ui.write('P=%r\n' % list(revs)) + > alias[b'P'] = p + > revs = repo.anyrevs([b'P'], user=True, localalias=alias) + > ui.write(b'P=%r\n' % list(revs)) > EOF $ cat >> .hg/hgrc <<EOF @@ -1831,3 +1834,21 @@ (keyvalue (symbol 'depth') (symbol '1'))))) + +test commonancestors and its optimization + + $ hg debugrevspec --verify -p analyzed -p optimized 'heads(commonancestors(head()))' + * analyzed: + (func + (symbol 'heads') + (func + (symbol 'commonancestors') + (func + (symbol 'head') + None))) + * optimized: + (func + (symbol '_commonancestorheads') + (func + (symbol 'head') + None))
--- a/tests/test-run-tests.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-run-tests.py Thu Jul 19 13:55:54 2018 -0400 @@ -40,7 +40,7 @@ assert not re.search(br'[^ \w\\/\r\n()*?]', expected + output), \ b'single backslash or unknown char' test = run_tests.TTest(b'test-run-test.t', b'.', b'.') - match = test.linematch(expected, output) + match, exact = test.linematch(expected, output) if isinstance(match, str): return 'special: ' + match elif isinstance(match, bytes):
--- a/tests/test-run-tests.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-run-tests.t Thu Jul 19 13:55:54 2018 -0400 @@ -120,6 +120,49 @@ python hash seed: * (glob) [1] +test how multiple globs gets matched with lines in output + $ cat > test-failure-globs.t <<EOF + > $ echo "context"; echo "context"; \ + > echo "key: 1"; echo "value: not a"; \ + > echo "key: 2"; echo "value: not b"; \ + > echo "key: 3"; echo "value: c"; \ + > echo "key: 4"; echo "value: d" + > context + > context + > key: 1 + > value: a + > key: 2 + > value: b + > key: 3 + > value: * (glob) + > key: 4 + > value: * (glob) + > EOF + $ rt test-failure-globs.t + + --- $TESTTMP/test-failure-globs.t + +++ $TESTTMP/test-failure-globs.t.err + @@ -2,9 +2,9 @@ + context + context + key: 1 + - value: a + + value: not a + key: 2 + - value: b + + value: not b + key: 3 + value: * (glob) + key: 4 + + ERROR: test-failure-globs.t output changed + ! + Failed test-failure-globs.t: output changed + # Ran 1 tests, 0 skipped, 1 failed. + python hash seed: * (glob) + [1] + $ rm test-failure-globs.t + test diff colorisation #if no-windows pygments @@ -374,6 +417,7 @@ $ cat .testtimes test-empty.t * (glob) + test-failure-globs.t * (glob) test-failure-unicode.t * (glob) test-failure.t * (glob) test-success.t * (glob) @@ -574,7 +618,6 @@ # Ran 1 tests, 0 skipped, 0 failed. $ rm test-serve-inuse.t $ killdaemons.py $DAEMON_PIDS - $ rm $DAEMON_PIDS Running In Debug Mode ====================== @@ -1203,6 +1246,17 @@ $ echo dead:beef::1 $LOCALIP (glob) +Add support for external test formatter +======================================= + + $ CUSTOM_TEST_RESULT=basic_test_result $PYTHON $TESTDIR/run-tests.py --with-hg=`which hg` "$@" test-success.t test-failure.t + + # Ran 2 tests, 0 skipped, 0 failed. + ON_START! <__main__.TestSuite tests=[<__main__.TTest testMethod=test-failure.t>, <__main__.TTest testMethod=test-success.t>]> + FAILURE! test-failure.t output changed + SUCCESS! test-success.t + ON_END! + Test reusability for third party tools ====================================== @@ -1497,9 +1551,9 @@ $ [ $V = C ] #endif - ERROR: test-cases-abc.t (case B) output changed + ERROR: test-cases-abc.t#B output changed !. - Failed test-cases-abc.t (case B): output changed + Failed test-cases-abc.t#B: output changed # Ran 3 tests, 0 skipped, 1 failed. python hash seed: * (glob) [1] @@ -1520,9 +1574,9 @@ $ [ $V = C ] #endif - ERROR: test-cases-abc.t (case B) output changed + ERROR: test-cases-abc.t#B output changed !. - Failed test-cases-abc.t (case B): output changed + Failed test-cases-abc.t#B: output changed # Ran 2 tests, 0 skipped, 1 failed. python hash seed: * (glob) [1] @@ -1545,9 +1599,9 @@ $ [ $V = C ] #endif - ERROR: test-cases-abc.t (case B) output changed + ERROR: test-cases-abc.t#B output changed !. - Failed test-cases-abc.t (case B): output changed + Failed test-cases-abc.t#B: output changed # Ran 2 tests, 0 skipped, 1 failed. python hash seed: * (glob) [1] @@ -1572,7 +1626,152 @@ .. # Ran 2 tests, 0 skipped, 0 failed. +Support running a specific test case + + $ rt "test-cases-abc.t#B" + + --- $TESTTMP/anothertests/cases/test-cases-abc.t + +++ $TESTTMP/anothertests/cases/test-cases-abc.t.B.err + @@ -7,7 +7,7 @@ + $ V=C + #endif + $ echo $V | sed 's/A/C/' + - C + + B + #if C + $ [ $V = C ] + #endif + + ERROR: test-cases-abc.t#B output changed + ! + Failed test-cases-abc.t#B: output changed + # Ran 1 tests, 0 skipped, 1 failed. + python hash seed: * (glob) + [1] + +Support running multiple test cases in the same file + + $ rt test-cases-abc.t#B test-cases-abc.t#C + + --- $TESTTMP/anothertests/cases/test-cases-abc.t + +++ $TESTTMP/anothertests/cases/test-cases-abc.t.B.err + @@ -7,7 +7,7 @@ + $ V=C + #endif + $ echo $V | sed 's/A/C/' + - C + + B + #if C + $ [ $V = C ] + #endif + + ERROR: test-cases-abc.t#B output changed + !. + Failed test-cases-abc.t#B: output changed + # Ran 2 tests, 0 skipped, 1 failed. + python hash seed: * (glob) + [1] + +Support ignoring invalid test cases + + $ rt test-cases-abc.t#B test-cases-abc.t#D + + --- $TESTTMP/anothertests/cases/test-cases-abc.t + +++ $TESTTMP/anothertests/cases/test-cases-abc.t.B.err + @@ -7,7 +7,7 @@ + $ V=C + #endif + $ echo $V | sed 's/A/C/' + - C + + B + #if C + $ [ $V = C ] + #endif + + ERROR: test-cases-abc.t#B output changed + ! + Failed test-cases-abc.t#B: output changed + # Ran 1 tests, 0 skipped, 1 failed. + python hash seed: * (glob) + [1] + +Support running complex test cases names + + $ cat > test-cases-advanced-cases.t <<'EOF' + > #testcases simple case-with-dashes casewith_-.chars + > $ echo $TESTCASE + > simple + > EOF + + $ cat test-cases-advanced-cases.t + #testcases simple case-with-dashes casewith_-.chars + $ echo $TESTCASE + simple + + $ rt test-cases-advanced-cases.t + + --- $TESTTMP/anothertests/cases/test-cases-advanced-cases.t + +++ $TESTTMP/anothertests/cases/test-cases-advanced-cases.t.case-with-dashes.err + @@ -1,3 +1,3 @@ + #testcases simple case-with-dashes casewith_-.chars + $ echo $TESTCASE + - simple + + case-with-dashes + + ERROR: test-cases-advanced-cases.t#case-with-dashes output changed + ! + --- $TESTTMP/anothertests/cases/test-cases-advanced-cases.t + +++ $TESTTMP/anothertests/cases/test-cases-advanced-cases.t.casewith_-.chars.err + @@ -1,3 +1,3 @@ + #testcases simple case-with-dashes casewith_-.chars + $ echo $TESTCASE + - simple + + casewith_-.chars + + ERROR: test-cases-advanced-cases.t#casewith_-.chars output changed + !. + Failed test-cases-advanced-cases.t#case-with-dashes: output changed + Failed test-cases-advanced-cases.t#casewith_-.chars: output changed + # Ran 3 tests, 0 skipped, 2 failed. + python hash seed: * (glob) + [1] + + $ rt "test-cases-advanced-cases.t#case-with-dashes" + + --- $TESTTMP/anothertests/cases/test-cases-advanced-cases.t + +++ $TESTTMP/anothertests/cases/test-cases-advanced-cases.t.case-with-dashes.err + @@ -1,3 +1,3 @@ + #testcases simple case-with-dashes casewith_-.chars + $ echo $TESTCASE + - simple + + case-with-dashes + + ERROR: test-cases-advanced-cases.t#case-with-dashes output changed + ! + Failed test-cases-advanced-cases.t#case-with-dashes: output changed + # Ran 1 tests, 0 skipped, 1 failed. + python hash seed: * (glob) + [1] + + $ rt "test-cases-advanced-cases.t#casewith_-.chars" + + --- $TESTTMP/anothertests/cases/test-cases-advanced-cases.t + +++ $TESTTMP/anothertests/cases/test-cases-advanced-cases.t.casewith_-.chars.err + @@ -1,3 +1,3 @@ + #testcases simple case-with-dashes casewith_-.chars + $ echo $TESTCASE + - simple + + casewith_-.chars + + ERROR: test-cases-advanced-cases.t#casewith_-.chars output changed + ! + Failed test-cases-advanced-cases.t#casewith_-.chars: output changed + # Ran 1 tests, 0 skipped, 1 failed. + python hash seed: * (glob) + [1] + Test automatic pattern replacement +================================== $ cat << EOF >> common-pattern.py > substitutions = [
--- a/tests/test-serve.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-serve.t Thu Jul 19 13:55:54 2018 -0400 @@ -6,10 +6,11 @@ > | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \ > -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \ > -e 's/http:\/\/[^/]*\//http:\/\/localhost\//' - > cat hg.pid >> "$DAEMON_PIDS" + > if [ -f hg.pid ]; then + > killdaemons.py hg.pid + > fi > echo % errors > cat errors.log - > killdaemons.py hg.pid > } $ hg init test
--- a/tests/test-share.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-share.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - $ echo "[extensions]" >> $HGRCPATH $ echo "share = " >> $HGRCPATH @@ -362,6 +360,7 @@ searching for changes no changes found adding remote bookmark bm3 + 1 local changesets published $ hg boo bm1 3:b87954705719 * bm3 4:62f4ded848e4
--- a/tests/test-shelve.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-shelve.t Thu Jul 19 13:55:54 2018 -0400 @@ -68,9 +68,12 @@ -l --list list current shelves -m --message TEXT use text as shelve message -n --name NAME use the given name for the shelved commit - -p --patch show patch + -p --patch output patches for changes (provide the names of the + shelved changes as positional arguments) -i --interactive interactive mode, only works while creating a shelve - --stat output diffstat-style summary of changes + --stat output diffstat-style summary of changes (provide + the names of the shelved changes as positional + arguments) -I --include PATTERN [+] include names matching the given patterns -X --exclude PATTERN [+] exclude names matching the given patterns --mq operate on patch repository @@ -136,9 +139,7 @@ cleaning the branches made for name checking tests $ hg up default -q - $ hg strip 3 -q - $ hg strip 2 -q - $ hg strip 1 -q + $ hg strip e9177275307e+6a6d231f43d+882bae7c62c2 -q create an mq patch - shelving should work fine with a patch applied @@ -215,7 +216,6 @@ unshelving change 'default-01' temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 4:32c69314e062 "changes to: [mq]: second.patch" (tip) merging a/a $ hg revert --all -q @@ -337,7 +337,6 @@ unshelving change 'default' temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 5:32c69314e062 "changes to: [mq]: second.patch" (tip) merging a/a warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark') unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') @@ -356,8 +355,8 @@ # # To mark files as resolved: hg resolve --mark FILE - # To continue: hg unshelve --continue - # To abort: hg unshelve --abort + # To continue: hg unshelve --continue + # To abort: hg unshelve --abort ensure that we have a merge with unresolved conflicts @@ -380,11 +379,11 @@ +++ b/a/a @@ -1,2 +1,6 @@ a - +<<<<<<< dest: * - shelve: pending changes temporary commit (glob) + +<<<<<<< shelve: 562f7831e574 - shelve: pending changes temporary commit c +======= +a - +>>>>>>> source: 32c69314e062 - shelve: changes to: [mq]: second.patch + +>>>>>>> working-copy: 32c69314e062 - shelve: changes to: [mq]: second.patch diff --git a/b/b b/b.rename/b rename from b/b rename to b.rename/b @@ -408,7 +407,6 @@ R b/b ? a/a.orig $ hg unshelve -a - rebase aborted unshelve of 'default' aborted $ hg heads -q 3:2e69b451d1ea @@ -462,7 +460,6 @@ (continue: hg unshelve --continue) [255] $ hg unshelve -c - rebasing 5:32c69314e062 "changes to: [mq]: second.patch" (tip) unshelve of 'default' complete ensure the repo is as we hope @@ -533,7 +530,6 @@ unshelving change 'default' temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 6:2f694dd83a13 "changes to: second" (tip) merging a/a $ hg parents -q 4:33f7f61e6c5e @@ -556,9 +552,8 @@ unshelving change 'default' temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 6:2f694dd83a13 "changes to: second" (tip) merging a/a - note: rebase of 6:2f694dd83a13 created no changes to commit + note: unshelved changes already existed in the working copy $ hg parents -q 4:33f7f61e6c5e $ hg shelve -l @@ -632,7 +627,7 @@ $ hg commit -Aqm xy $ echo z >> x $ hg commit -Aqm z - $ hg up 0 + $ hg up 5c4c67fb7dce 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ printf 'a\nx\ny\nz\n' > x $ hg commit -Aqm xyz @@ -640,14 +635,13 @@ $ hg shelve shelved as default 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg rebase -d 1 --config extensions.rebase= + $ hg rebase -d 6c103be8f4e4 --config extensions.rebase= rebasing 2:323bfa07f744 "xyz" (tip) merging x saved backup bundle to $TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-78114325-rebase.hg $ hg unshelve unshelving change 'default' rebasing shelved changes - rebasing 4:82a0d7d6ba61 "changes to: xyz" (tip) $ hg status M z @@ -661,7 +655,7 @@ $ hg ci -Aqm a $ touch b $ hg ci -Aqm b - $ hg up -q 0 + $ hg up -q 3903775176ed $ touch c $ hg ci -Aqm c @@ -670,11 +664,10 @@ $ hg shelve shelved as default 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg up -q 1 + $ hg up -q 0e067c57feba $ hg unshelve unshelving change 'default' rebasing shelved changes - rebasing 3:958bcbd1776e "changes to: c" (tip) $ hg status A d @@ -683,12 +676,11 @@ $ hg shelve shelved as default 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg up 0 + $ hg up 3903775176ed 0 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg unshelve unshelving change 'default' rebasing shelved changes - rebasing 3:013284d9655e "changes to: b" (tip) $ hg status A d @@ -701,7 +693,7 @@ $ hg shelve shelved as default 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg debugobsolete `hg --debug id -i -r 1` + $ hg debugobsolete `hg log -r 0e067c57feba -T '{node}'` obsoleted 1 changesets $ hg unshelve unshelving change 'default' @@ -790,7 +782,6 @@ unshelving change 'default' temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 5:81152db69da7 "changes to: commit stuff" (tip) merging f warning: conflicts while merging f! (edit, then use 'hg resolve --mark') unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') @@ -810,16 +801,15 @@ M f ? f.orig $ cat f - <<<<<<< dest: 5f6b880e719b - shelve: pending changes temporary commit + <<<<<<< shelve: 5f6b880e719b - shelve: pending changes temporary commit g ======= f - >>>>>>> source: 81152db69da7 - shelve: changes to: commit stuff + >>>>>>> working-copy: 81152db69da7 - shelve: changes to: commit stuff $ cat f.orig g $ hg unshelve --abort -t false tool option will be ignored - rebase aborted unshelve of 'default' aborted $ hg st M a @@ -830,7 +820,6 @@ unshelving change 'default' temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 5:81152db69da7 "changes to: commit stuff" (tip) $ hg st M a A f @@ -846,7 +835,6 @@ $ hg unshelve unshelving change 'default' rebasing shelved changes - rebasing 5:81152db69da7 "changes to: commit stuff" (tip) merging f warning: conflicts while merging f! (edit, then use 'hg resolve --mark') unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') @@ -855,15 +843,14 @@ M f ? f.orig $ cat f - <<<<<<< dest: * - test: intermediate other change (glob) + <<<<<<< shelve: 6b563750f973 - test: intermediate other change g ======= f - >>>>>>> source: 81152db69da7 - shelve: changes to: commit stuff + >>>>>>> working-copy: 81152db69da7 - shelve: changes to: commit stuff $ cat f.orig g $ hg unshelve --abort - rebase aborted unshelve of 'default' aborted $ hg st ? f.orig @@ -874,7 +861,7 @@ Recreate some conflict again $ cd ../repo - $ hg up -C -r 3 + $ hg up -C -r 2e69b451d1ea 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (leaving bookmark test) $ echo y >> a/a @@ -889,7 +876,6 @@ $ hg unshelve unshelving change 'default' rebasing shelved changes - rebasing 5:e42a7da90865 "changes to: second" (tip) merging a/a warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark') unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') @@ -906,8 +892,7 @@ (no more unresolved files) continue: hg unshelve --continue $ hg unshelve -c - rebasing 5:e42a7da90865 "changes to: second" (tip) - note: rebase of 5:e42a7da90865 created no changes to commit + note: unshelved changes already existed in the working copy unshelve of 'default' complete $ hg bookmark * test 4:33f7f61e6c5e @@ -1000,7 +985,6 @@ unshelving change 'test' temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 6:96a1354f65f6 "changes to: create conflict" (tip) merging a/a $ hg bookmark * test 4:33f7f61e6c5e @@ -1073,8 +1057,33 @@ $ hg shelve --patch default nonexistentshelf abort: cannot find shelf nonexistentshelf [255] + +when the user asks for a patch, we assume they want the most recent shelve if +they don't provide a shelve name + $ hg shelve --patch - abort: --patch expects at least one shelf + default-01 (*)* changes to: create conflict (glob) + + diff --git a/shelf-patch-b b/shelf-patch-b + new file mode 100644 + --- /dev/null + +++ b/shelf-patch-b + @@ -0,0 +1,1 @@ + +patch b + + $ cd .. + +you shouldn't be able to ask for the patch/stats of the most recent shelve if +there are no shelves + + $ hg init noshelves + $ cd noshelves + + $ hg shelve --patch + abort: there are no shelves to show + [255] + $ hg shelve --stat + abort: there are no shelves to show [255] $ cd .. @@ -1177,13 +1186,12 @@ $ hg unshelve --keep default temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 7:206bf5d4f922 "changes to: create conflict" (tip) ==== preupdate: VISIBLE 6:66b86db80ee4 ACTUAL 5:703117a2acfb ==== ==== preupdate: - VISIBLE 8:a0e04704317e + VISIBLE 8:92fdbb7b4de7 ACTUAL 5:703117a2acfb ==== ==== preupdate: @@ -1204,7 +1212,7 @@ == test visibility to external update hook - $ hg update -q -C 5 + $ hg update -q -C 703117a2acfb $ cat >> .hg/hgrc <<EOF > [hooks] @@ -1222,7 +1230,6 @@ $ hg unshelve --keep default temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 7:206bf5d4f922 "changes to: create conflict" (tip) ==== update: VISIBLE 6:66b86db80ee4 VISIBLE 7:206bf5d4f922 @@ -1269,30 +1276,19 @@ test Abort unshelve always gets user out of the unshelved state --------------------------------------------------------------- -Wreak havoc on the unshelve process - $ rm .hg/unshelverebasestate - $ hg unshelve --abort + +with a corrupted shelve state file + $ sed 's/ae8c668541e8/123456789012/' .hg/shelvedstate > ../corrupt-shelvedstate + $ mv ../corrupt-shelvedstate .hg/shelvestate + $ hg unshelve --abort 2>&1 | grep 'aborted' unshelve of 'default' aborted - abort: $ENOENT$* (glob) - [255] -Can the user leave the current state? - $ hg up -C . - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - -Try again but with a corrupted shelve state file - $ hg strip -r 2 -r 1 -q - $ hg up -r 0 -q - $ echo '' > root - $ hg shelve -q - $ echo 'contADDent' > root - $ hg unshelve -q - warning: conflicts while merging root! (edit, then use 'hg resolve --mark') - unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') - [1] - $ sed 's/ae8c668541e8/123456789012/' .hg/shelvedstate > ../corrupt-shelvedstate - $ mv ../corrupt-shelvedstate .hg/histedit-state - $ hg unshelve --abort 2>&1 | grep 'rebase aborted' - rebase aborted + $ hg summary + parent: 0:ae8c668541e8 tip + root + branch: default + commit: 1 modified + update: (current) + phases: 1 draft $ hg up -C . 1 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -1373,7 +1369,6 @@ unshelving change 'default' temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 1:098df96e7410 "(changes in empty repository)" (tip) merging unknown $ hg status A unknown @@ -1394,7 +1389,6 @@ $ hg unshelve unshelving change 'default' rebasing shelved changes - rebasing 1:098df96e7410 "(changes in empty repository)" (tip) merging unknown $ hg status M unknown @@ -1527,7 +1521,6 @@ unshelving change 'default' temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 2:425c97ef07f3 "changes to: a" (tip) merging a warning: conflicts while merging a! (edit, then use 'hg resolve --mark') unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') @@ -1538,7 +1531,6 @@ (no more unresolved files) continue: hg unshelve --continue $ hg unshelve --continue - rebasing 2:425c97ef07f3 "changes to: a" (tip) marked working directory as branch test unshelve of 'default' complete $ cat a @@ -1558,12 +1550,11 @@ $ hg shelve shelved as test 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg update -r default + $ hg update -r 7049e48789d7 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg unshelve unshelving change 'test' rebasing shelved changes - rebasing 2:357525f34729 "changes to: test-commit" (tip) $ hg status A b $ hg branch @@ -1604,7 +1595,6 @@ unshelving change 'default' temporarily committing pending changes (restore with 'hg unshelve --abort') rebasing shelved changes - rebasing 2:425c97ef07f3 "changes to: a" (tip) merging a warning: conflicts while merging a! (edit, then use 'hg resolve --mark') unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') @@ -1622,7 +1612,6 @@ (no more unresolved files) continue: hg unshelve --continue $ hg unshelve --continue - rebasing 2:425c97ef07f3 "changes to: a" (tip) unshelve of 'default' complete $ cat a aaabbbccc @@ -1678,7 +1667,6 @@ $ hg unshelve unshelving change 'default' rebasing shelved changes - rebasing 1:396ea74229f9 "(changes in empty repository)" (tip) merging file warning: conflicts while merging file! (edit, then use 'hg resolve --mark') unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') @@ -1717,7 +1705,6 @@ $ hg unshelve --keep unshelving change 'default' rebasing shelved changes - rebasing 2:3fbe6fbb0bef "changes to: 1" (tip) merging file warning: conflicts while merging file! (edit, then use 'hg resolve --mark') unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') @@ -1726,7 +1713,6 @@ (no more unresolved files) continue: hg unshelve --continue $ hg unshelve --continue - rebasing 2:3fbe6fbb0bef "changes to: 1" (tip) unshelve of 'default' complete $ hg shelve --list default (*s ago) * changes to: 1 (glob) @@ -1776,7 +1762,6 @@ $ hg unshelve unshelving change 'ashelve' rebasing shelved changes - rebasing 2:003d2d94241c "changes to: root" (tip) merging a warning: conflicts while merging a! (edit, then use 'hg resolve --mark') unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') @@ -1799,7 +1784,6 @@ continue: hg unshelve --continue mercurial does not crash $ hg unshelve --continue - rebasing 2:003d2d94241c "changes to: root" (tip) unshelve of 'ashelve' complete $ cd ..
--- a/tests/test-simple-update.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-simple-update.t Thu Jul 19 13:55:54 2018 -0400 @@ -31,6 +31,7 @@ adding file changes added 1 changesets with 1 changes to 1 files new changesets 30aff43faee1 + 1 local changesets published (run 'hg update' to get a working copy) $ hg verify @@ -64,7 +65,7 @@ $ cat <<EOF > forceworker.py > from mercurial import extensions, worker - > def nocost(orig, ui, costperop, nops): + > def nocost(orig, ui, costperop, nops, threadsafe=True): > return worker._numworkers(ui) > 1 > def uisetup(ui): > extensions.wrapfunction(worker, 'worthwhile', nocost)
--- a/tests/test-simplekeyvaluefile.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-simplekeyvaluefile.py Thu Jul 19 13:55:54 2018 -0400 @@ -44,11 +44,11 @@ self.vfs = mockvfs() def testbasicwritingiandreading(self): - dw = {'key1': 'value1', 'Key2': 'value2'} - scmutil.simplekeyvaluefile(self.vfs, 'kvfile').write(dw) - self.assertEqual(sorted(self.vfs.read('kvfile').split('\n')), - ['', 'Key2=value2', 'key1=value1']) - dr = scmutil.simplekeyvaluefile(self.vfs, 'kvfile').read() + dw = {b'key1': b'value1', b'Key2': b'value2'} + scmutil.simplekeyvaluefile(self.vfs, b'kvfile').write(dw) + self.assertEqual(sorted(self.vfs.read(b'kvfile').split(b'\n')), + [b'', b'Key2=value2', b'key1=value1']) + dr = scmutil.simplekeyvaluefile(self.vfs, b'kvfile').read() self.assertEqual(dr, dw) if not getattr(unittest.TestCase, 'assertRaisesRegex', False): @@ -58,33 +58,33 @@ unittest.TestCase.assertRaisesRegexp) def testinvalidkeys(self): - d = {'0key1': 'value1', 'Key2': 'value2'} + d = {b'0key1': b'value1', b'Key2': b'value2'} with self.assertRaisesRegex(error.ProgrammingError, 'keys must start with a letter.*'): - scmutil.simplekeyvaluefile(self.vfs, 'kvfile').write(d) + scmutil.simplekeyvaluefile(self.vfs, b'kvfile').write(d) - d = {'key1@': 'value1', 'Key2': 'value2'} + d = {b'key1@': b'value1', b'Key2': b'value2'} with self.assertRaisesRegex(error.ProgrammingError, 'invalid key.*'): - scmutil.simplekeyvaluefile(self.vfs, 'kvfile').write(d) + scmutil.simplekeyvaluefile(self.vfs, b'kvfile').write(d) def testinvalidvalues(self): - d = {'key1': 'value1', 'Key2': 'value2\n'} + d = {b'key1': b'value1', b'Key2': b'value2\n'} with self.assertRaisesRegex(error.ProgrammingError, 'invalid val.*'): - scmutil.simplekeyvaluefile(self.vfs, 'kvfile').write(d) + scmutil.simplekeyvaluefile(self.vfs, b'kvfile').write(d) def testcorruptedfile(self): - self.vfs.contents['badfile'] = 'ababagalamaga\n' + self.vfs.contents[b'badfile'] = b'ababagalamaga\n' with self.assertRaisesRegex(error.CorruptedState, 'dictionary.*element.*'): - scmutil.simplekeyvaluefile(self.vfs, 'badfile').read() + scmutil.simplekeyvaluefile(self.vfs, b'badfile').read() def testfirstline(self): - dw = {'key1': 'value1'} - scmutil.simplekeyvaluefile(self.vfs, 'fl').write(dw, firstline='1.0') - self.assertEqual(self.vfs.read('fl'), '1.0\nkey1=value1\n') - dr = scmutil.simplekeyvaluefile(self.vfs, 'fl')\ + dw = {b'key1': b'value1'} + scmutil.simplekeyvaluefile(self.vfs, b'fl').write(dw, firstline=b'1.0') + self.assertEqual(self.vfs.read(b'fl'), b'1.0\nkey1=value1\n') + dr = scmutil.simplekeyvaluefile(self.vfs, b'fl')\ .read(firstlinenonkeyval=True) - self.assertEqual(dr, {'__firstline': '1.0', 'key1': 'value1'}) + self.assertEqual(dr, {b'__firstline': b'1.0', b'key1': b'value1'}) if __name__ == "__main__": silenttestrunner.main(__name__)
--- a/tests/test-simplemerge.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-simplemerge.py Thu Jul 19 13:55:54 2018 -0400 @@ -35,12 +35,12 @@ incorporating the changes from both BASE->OTHER and BASE->THIS. All three will typically be sequences of lines.""" def __init__(self, base, a, b): - basetext = '\n'.join([i.strip('\n') for i in base] + ['']) - atext = '\n'.join([i.strip('\n') for i in a] + ['']) - btext = '\n'.join([i.strip('\n') for i in b] + ['']) + basetext = b'\n'.join([i.strip(b'\n') for i in base] + [b'']) + atext = b'\n'.join([i.strip(b'\n') for i in a] + [b'']) + btext = b'\n'.join([i.strip(b'\n') for i in b] + [b'']) if (stringutil.binary(basetext) or stringutil.binary(atext) or stringutil.binary(btext)): - raise error.Abort("don't know how to merge binary files") + raise error.Abort(b"don't know how to merge binary files") simplemerge.Merge3Text.__init__(self, basetext, atext, btext, base, a, b) @@ -52,7 +52,7 @@ ############################################################ # test case data from the gnu diffutils manual # common base -TZU = split_lines(""" The Nameless is the origin of Heaven and Earth; +TZU = split_lines(b""" The Nameless is the origin of Heaven and Earth; The named is the mother of all things. Therefore let there always be non-being, @@ -67,7 +67,7 @@ The door of all subtleties! """) -LAO = split_lines(""" The Way that can be told of is not the eternal Way; +LAO = split_lines(b""" The Way that can be told of is not the eternal Way; The name that can be named is not the eternal name. The Nameless is the origin of Heaven and Earth; The Named is the mother of all things. @@ -81,7 +81,7 @@ """) -TAO = split_lines(""" The Way that can be told of is not the eternal Way; +TAO = split_lines(b""" The Way that can be told of is not the eternal Way; The name that can be named is not the eternal name. The Nameless is the origin of Heaven and Earth; The named is the mother of all things. @@ -98,7 +98,7 @@ """) -MERGED_RESULT = split_lines("""\ +MERGED_RESULT = split_lines(b"""\ The Way that can be told of is not the eternal Way; The name that can be named is not the eternal name. The Nameless is the origin of Heaven and Earth; @@ -109,13 +109,13 @@ so we may see their result. The two are the same, But after they are produced, - they have different names. -<<<<<<< LAO -======= + they have different names.\ +\n<<<<<<< LAO\ +\n======= -- The Way of Lao-Tzu, tr. Wing-tsit Chan - ->>>>>>> TAO +\ +\n>>>>>>> TAO """) class TestMerge3(TestCase): @@ -124,167 +124,167 @@ def test_no_changes(self): """No conflicts because nothing changed""" - m3 = Merge3(['aaa', 'bbb'], - ['aaa', 'bbb'], - ['aaa', 'bbb']) + m3 = Merge3([b'aaa', b'bbb'], + [b'aaa', b'bbb'], + [b'aaa', b'bbb']) - self.assertEquals(m3.find_unconflicted(), - [(0, 2)]) + self.assertEqual(m3.find_unconflicted(), + [(0, 2)]) - self.assertEquals(list(m3.find_sync_regions()), - [(0, 2, - 0, 2, - 0, 2), - (2, 2, 2, 2, 2, 2)]) + self.assertEqual(list(m3.find_sync_regions()), + [(0, 2, + 0, 2, + 0, 2), + (2, 2, 2, 2, 2, 2)]) - self.assertEquals(list(m3.merge_regions()), - [('unchanged', 0, 2)]) + self.assertEqual(list(m3.merge_regions()), + [(b'unchanged', 0, 2)]) - self.assertEquals(list(m3.merge_groups()), - [('unchanged', ['aaa', 'bbb'])]) + self.assertEqual(list(m3.merge_groups()), + [(b'unchanged', [b'aaa', b'bbb'])]) def test_front_insert(self): - m3 = Merge3(['zz'], - ['aaa', 'bbb', 'zz'], - ['zz']) + m3 = Merge3([b'zz'], + [b'aaa', b'bbb', b'zz'], + [b'zz']) # todo: should use a sentinel at end as from get_matching_blocks # to match without zz - self.assertEquals(list(m3.find_sync_regions()), - [(0, 1, 2, 3, 0, 1), - (1, 1, 3, 3, 1, 1)]) + self.assertEqual(list(m3.find_sync_regions()), + [(0, 1, 2, 3, 0, 1), + (1, 1, 3, 3, 1, 1)]) - self.assertEquals(list(m3.merge_regions()), - [('a', 0, 2), - ('unchanged', 0, 1)]) + self.assertEqual(list(m3.merge_regions()), + [(b'a', 0, 2), + (b'unchanged', 0, 1)]) - self.assertEquals(list(m3.merge_groups()), - [('a', ['aaa', 'bbb']), - ('unchanged', ['zz'])]) + self.assertEqual(list(m3.merge_groups()), + [(b'a', [b'aaa', b'bbb']), + (b'unchanged', [b'zz'])]) def test_null_insert(self): m3 = Merge3([], - ['aaa', 'bbb'], + [b'aaa', b'bbb'], []) # todo: should use a sentinel at end as from get_matching_blocks # to match without zz - self.assertEquals(list(m3.find_sync_regions()), - [(0, 0, 2, 2, 0, 0)]) + self.assertEqual(list(m3.find_sync_regions()), + [(0, 0, 2, 2, 0, 0)]) - self.assertEquals(list(m3.merge_regions()), - [('a', 0, 2)]) + self.assertEqual(list(m3.merge_regions()), + [(b'a', 0, 2)]) - self.assertEquals(list(m3.merge_lines()), - ['aaa', 'bbb']) + self.assertEqual(list(m3.merge_lines()), + [b'aaa', b'bbb']) def test_no_conflicts(self): """No conflicts because only one side changed""" - m3 = Merge3(['aaa', 'bbb'], - ['aaa', '111', 'bbb'], - ['aaa', 'bbb']) + m3 = Merge3([b'aaa', b'bbb'], + [b'aaa', b'111', b'bbb'], + [b'aaa', b'bbb']) - self.assertEquals(m3.find_unconflicted(), - [(0, 1), (1, 2)]) + self.assertEqual(m3.find_unconflicted(), + [(0, 1), (1, 2)]) - self.assertEquals(list(m3.find_sync_regions()), - [(0, 1, 0, 1, 0, 1), - (1, 2, 2, 3, 1, 2), - (2, 2, 3, 3, 2, 2)]) + self.assertEqual(list(m3.find_sync_regions()), + [(0, 1, 0, 1, 0, 1), + (1, 2, 2, 3, 1, 2), + (2, 2, 3, 3, 2, 2)]) - self.assertEquals(list(m3.merge_regions()), - [('unchanged', 0, 1), - ('a', 1, 2), - ('unchanged', 1, 2)]) + self.assertEqual(list(m3.merge_regions()), + [(b'unchanged', 0, 1), + (b'a', 1, 2), + (b'unchanged', 1, 2)]) def test_append_a(self): - m3 = Merge3(['aaa\n', 'bbb\n'], - ['aaa\n', 'bbb\n', '222\n'], - ['aaa\n', 'bbb\n']) + m3 = Merge3([b'aaa\n', b'bbb\n'], + [b'aaa\n', b'bbb\n', b'222\n'], + [b'aaa\n', b'bbb\n']) - self.assertEquals(''.join(m3.merge_lines()), - 'aaa\nbbb\n222\n') + self.assertEqual(b''.join(m3.merge_lines()), + b'aaa\nbbb\n222\n') def test_append_b(self): - m3 = Merge3(['aaa\n', 'bbb\n'], - ['aaa\n', 'bbb\n'], - ['aaa\n', 'bbb\n', '222\n']) + m3 = Merge3([b'aaa\n', b'bbb\n'], + [b'aaa\n', b'bbb\n'], + [b'aaa\n', b'bbb\n', b'222\n']) - self.assertEquals(''.join(m3.merge_lines()), - 'aaa\nbbb\n222\n') + self.assertEqual(b''.join(m3.merge_lines()), + b'aaa\nbbb\n222\n') def test_append_agreement(self): - m3 = Merge3(['aaa\n', 'bbb\n'], - ['aaa\n', 'bbb\n', '222\n'], - ['aaa\n', 'bbb\n', '222\n']) + m3 = Merge3([b'aaa\n', b'bbb\n'], + [b'aaa\n', b'bbb\n', b'222\n'], + [b'aaa\n', b'bbb\n', b'222\n']) - self.assertEquals(''.join(m3.merge_lines()), - 'aaa\nbbb\n222\n') + self.assertEqual(b''.join(m3.merge_lines()), + b'aaa\nbbb\n222\n') def test_append_clash(self): - m3 = Merge3(['aaa\n', 'bbb\n'], - ['aaa\n', 'bbb\n', '222\n'], - ['aaa\n', 'bbb\n', '333\n']) + m3 = Merge3([b'aaa\n', b'bbb\n'], + [b'aaa\n', b'bbb\n', b'222\n'], + [b'aaa\n', b'bbb\n', b'333\n']) - ml = m3.merge_lines(name_a='a', - name_b='b', - start_marker='<<', - mid_marker='--', - end_marker='>>') - self.assertEquals(''.join(ml), - 'aaa\n' - 'bbb\n' - '<< a\n' - '222\n' - '--\n' - '333\n' - '>> b\n' + ml = m3.merge_lines(name_a=b'a', + name_b=b'b', + start_marker=b'<<', + mid_marker=b'--', + end_marker=b'>>') + self.assertEqual(b''.join(ml), + b'aaa\n' + b'bbb\n' + b'<< a\n' + b'222\n' + b'--\n' + b'333\n' + b'>> b\n' ) def test_insert_agreement(self): - m3 = Merge3(['aaa\n', 'bbb\n'], - ['aaa\n', '222\n', 'bbb\n'], - ['aaa\n', '222\n', 'bbb\n']) + m3 = Merge3([b'aaa\n', b'bbb\n'], + [b'aaa\n', b'222\n', b'bbb\n'], + [b'aaa\n', b'222\n', b'bbb\n']) - ml = m3.merge_lines(name_a='a', - name_b='b', - start_marker='<<', - mid_marker='--', - end_marker='>>') - self.assertEquals(''.join(ml), 'aaa\n222\nbbb\n') + ml = m3.merge_lines(name_a=b'a', + name_b=b'b', + start_marker=b'<<', + mid_marker=b'--', + end_marker=b'>>') + self.assertEqual(b''.join(ml), b'aaa\n222\nbbb\n') def test_insert_clash(self): """Both try to insert lines in the same place.""" - m3 = Merge3(['aaa\n', 'bbb\n'], - ['aaa\n', '111\n', 'bbb\n'], - ['aaa\n', '222\n', 'bbb\n']) + m3 = Merge3([b'aaa\n', b'bbb\n'], + [b'aaa\n', b'111\n', b'bbb\n'], + [b'aaa\n', b'222\n', b'bbb\n']) - self.assertEquals(m3.find_unconflicted(), - [(0, 1), (1, 2)]) + self.assertEqual(m3.find_unconflicted(), + [(0, 1), (1, 2)]) - self.assertEquals(list(m3.find_sync_regions()), - [(0, 1, 0, 1, 0, 1), - (1, 2, 2, 3, 2, 3), - (2, 2, 3, 3, 3, 3)]) + self.assertEqual(list(m3.find_sync_regions()), + [(0, 1, 0, 1, 0, 1), + (1, 2, 2, 3, 2, 3), + (2, 2, 3, 3, 3, 3)]) - self.assertEquals(list(m3.merge_regions()), - [('unchanged', 0, 1), - ('conflict', 1, 1, 1, 2, 1, 2), - ('unchanged', 1, 2)]) + self.assertEqual(list(m3.merge_regions()), + [(b'unchanged', 0, 1), + (b'conflict', 1, 1, 1, 2, 1, 2), + (b'unchanged', 1, 2)]) - self.assertEquals(list(m3.merge_groups()), - [('unchanged', ['aaa\n']), - ('conflict', [], ['111\n'], ['222\n']), - ('unchanged', ['bbb\n']), - ]) + self.assertEqual(list(m3.merge_groups()), + [(b'unchanged', [b'aaa\n']), + (b'conflict', [], [b'111\n'], [b'222\n']), + (b'unchanged', [b'bbb\n']), + ]) - ml = m3.merge_lines(name_a='a', - name_b='b', - start_marker='<<', - mid_marker='--', - end_marker='>>') - self.assertEquals(''.join(ml), -'''aaa + ml = m3.merge_lines(name_a=b'a', + name_b=b'b', + start_marker=b'<<', + mid_marker=b'--', + end_marker=b'>>') + self.assertEqual(b''.join(ml), +b'''aaa << a 111 -- @@ -295,64 +295,64 @@ def test_replace_clash(self): """Both try to insert lines in the same place.""" - m3 = Merge3(['aaa', '000', 'bbb'], - ['aaa', '111', 'bbb'], - ['aaa', '222', 'bbb']) + m3 = Merge3([b'aaa', b'000', b'bbb'], + [b'aaa', b'111', b'bbb'], + [b'aaa', b'222', b'bbb']) - self.assertEquals(m3.find_unconflicted(), - [(0, 1), (2, 3)]) + self.assertEqual(m3.find_unconflicted(), + [(0, 1), (2, 3)]) - self.assertEquals(list(m3.find_sync_regions()), - [(0, 1, 0, 1, 0, 1), + self.assertEqual(list(m3.find_sync_regions()), + [(0, 1, 0, 1, 0, 1), (2, 3, 2, 3, 2, 3), (3, 3, 3, 3, 3, 3)]) def test_replace_multi(self): """Replacement with regions of different size.""" - m3 = Merge3(['aaa', '000', '000', 'bbb'], - ['aaa', '111', '111', '111', 'bbb'], - ['aaa', '222', '222', '222', '222', 'bbb']) + m3 = Merge3([b'aaa', b'000', b'000', b'bbb'], + [b'aaa', b'111', b'111', b'111', b'bbb'], + [b'aaa', b'222', b'222', b'222', b'222', b'bbb']) - self.assertEquals(m3.find_unconflicted(), - [(0, 1), (3, 4)]) + self.assertEqual(m3.find_unconflicted(), + [(0, 1), (3, 4)]) - self.assertEquals(list(m3.find_sync_regions()), - [(0, 1, 0, 1, 0, 1), - (3, 4, 4, 5, 5, 6), - (4, 4, 5, 5, 6, 6)]) + self.assertEqual(list(m3.find_sync_regions()), + [(0, 1, 0, 1, 0, 1), + (3, 4, 4, 5, 5, 6), + (4, 4, 5, 5, 6, 6)]) def test_merge_poem(self): """Test case from diff3 manual""" m3 = Merge3(TZU, LAO, TAO) - ml = list(m3.merge_lines('LAO', 'TAO')) - self.log('merge result:') - self.log(''.join(ml)) - self.assertEquals(ml, MERGED_RESULT) + ml = list(m3.merge_lines(b'LAO', b'TAO')) + self.log(b'merge result:') + self.log(b''.join(ml)) + self.assertEqual(ml, MERGED_RESULT) def test_binary(self): with self.assertRaises(error.Abort): - Merge3(['\x00'], ['a'], ['b']) + Merge3([b'\x00'], [b'a'], [b'b']) def test_dos_text(self): - base_text = 'a\r\n' - this_text = 'b\r\n' - other_text = 'c\r\n' + base_text = b'a\r\n' + this_text = b'b\r\n' + other_text = b'c\r\n' m3 = Merge3(base_text.splitlines(True), other_text.splitlines(True), this_text.splitlines(True)) - m_lines = m3.merge_lines('OTHER', 'THIS') - self.assertEqual('<<<<<<< OTHER\r\nc\r\n=======\r\nb\r\n' - '>>>>>>> THIS\r\n'.splitlines(True), list(m_lines)) + m_lines = m3.merge_lines(b'OTHER', b'THIS') + self.assertEqual(b'<<<<<<< OTHER\r\nc\r\n=======\r\nb\r\n' + b'>>>>>>> THIS\r\n'.splitlines(True), list(m_lines)) def test_mac_text(self): - base_text = 'a\r' - this_text = 'b\r' - other_text = 'c\r' + base_text = b'a\r' + this_text = b'b\r' + other_text = b'c\r' m3 = Merge3(base_text.splitlines(True), other_text.splitlines(True), this_text.splitlines(True)) - m_lines = m3.merge_lines('OTHER', 'THIS') - self.assertEqual('<<<<<<< OTHER\rc\r=======\rb\r' - '>>>>>>> THIS\r'.splitlines(True), list(m_lines)) + m_lines = m3.merge_lines(b'OTHER', b'THIS') + self.assertEqual(b'<<<<<<< OTHER\rc\r=======\rb\r' + b'>>>>>>> THIS\r'.splitlines(True), list(m_lines)) if __name__ == '__main__': # hide the timer
--- a/tests/test-split.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-split.t Thu Jul 19 13:55:54 2018 -0400 @@ -91,10 +91,11 @@ $ hg forget dirty $ rm dirty -Split a head +Make a clean directory for future tests to build off of - $ cp -R . ../b - $ cp -R . ../c + $ cp -R . ../clean + +Split a head $ hg bookmark r3 @@ -263,6 +264,7 @@ Split a head while working parent is not that head + $ cp -R $TESTTMP/clean $TESTTMP/b $ cd $TESTTMP/b $ hg up 0 -q @@ -302,6 +304,7 @@ Split a non-head + $ cp -R $TESTTMP/clean $TESTTMP/c $ cd $TESTTMP/c $ echo d > d $ hg ci -m d1 -A d @@ -529,3 +532,36 @@ o 0:426bada5c675 A #endif + +Preserve secret phase in split + + $ cp -R $TESTTMP/clean $TESTTMP/phases1 + $ cd $TESTTMP/phases1 + $ hg phase --secret -fr tip + $ hg log -T '{short(node)} {phase}\n' + 1df0d5c5a3ab secret + a61bcde8c529 draft + $ runsplit tip >/dev/null + $ hg log -T '{short(node)} {phase}\n' + 00eebaf8d2e2 secret + a09ad58faae3 secret + e704349bd21b secret + a61bcde8c529 draft + +Do not move things to secret even if phases.new-commit=secret + + $ cp -R $TESTTMP/clean $TESTTMP/phases2 + $ cd $TESTTMP/phases2 + $ cat >> .hg/hgrc <<EOF + > [phases] + > new-commit=secret + > EOF + $ hg log -T '{short(node)} {phase}\n' + 1df0d5c5a3ab draft + a61bcde8c529 draft + $ runsplit tip >/dev/null + $ hg log -T '{short(node)} {phase}\n' + 00eebaf8d2e2 draft + a09ad58faae3 draft + e704349bd21b draft + a61bcde8c529 draft
--- a/tests/test-ssh-bundle1.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-ssh-bundle1.t Thu Jul 19 13:55:54 2018 -0400 @@ -444,11 +444,11 @@ > > def wrappedpush(orig, repo, *args, **kwargs): > res = orig(repo, *args, **kwargs) - > repo.ui.write('local stdout\n') + > repo.ui.write(b'local stdout\n') > return res > > def extsetup(ui): - > extensions.wrapfunction(exchange, 'push', wrappedpush) + > extensions.wrapfunction(exchange, b'push', wrappedpush) > EOF $ cat >> .hg/hgrc << EOF @@ -537,7 +537,7 @@ $ cat > $TESTTMP/failhook << EOF > def hook(ui, repo, **kwargs): - > ui.write('hook failure!\n') + > ui.write(b'hook failure!\n') > ui.flush() > return 1 > EOF
--- a/tests/test-ssh-proto-unbundle.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-ssh-proto-unbundle.t Thu Jul 19 13:55:54 2018 -0400 @@ -93,7 +93,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 115: e> abort: incompatible Mercurial client; bundle2 required\n e> (see https://www.mercurial-scm.org/wiki/IncompatibleClient)\n @@ -144,7 +143,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 115: e> abort: incompatible Mercurial client; bundle2 required\n e> (see https://www.mercurial-scm.org/wiki/IncompatibleClient)\n @@ -161,10 +159,12 @@ > import sys > def hook1line(ui, repo, **kwargs): > ui.write(b'ui.write 1 line\n') + > ui.flush() > return 1 > def hook2lines(ui, repo, **kwargs): > ui.write(b'ui.write 2 lines 1\n') > ui.write(b'ui.write 2 lines 2\n') + > ui.flush() > return 1 > def hook1lineflush(ui, repo, **kwargs): > ui.write(b'ui.write 1 line flush\n') @@ -181,21 +181,31 @@ > ui.write_err(b'ui.write_err 1\n') > ui.write(b'ui.write 2\n') > ui.write_err(b'ui.write_err 2\n') + > ui.flush() > return 1 > def hookprintstdout(ui, repo, **kwargs): > print('printed line') + > sys.stdout.flush() > return 1 > def hookprintandwrite(ui, repo, **kwargs): > print('print 1') + > sys.stdout.flush() > ui.write(b'ui.write 1\n') + > ui.flush() > print('print 2') + > sys.stdout.flush() > ui.write(b'ui.write 2\n') + > ui.flush() > return 1 > def hookprintstderrandstdout(ui, repo, **kwargs): > print('stdout 1') + > sys.stdout.flush() > print('stderr 1', file=sys.stderr) + > sys.stderr.flush() > print('stdout 2') + > sys.stdout.flush() > print('stderr 2', file=sys.stderr) + > sys.stderr.flush() > return 1 > EOF @@ -262,7 +272,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 196: e> adding changesets\n e> adding manifests\n @@ -319,7 +328,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 196: e> adding changesets\n e> adding manifests\n @@ -390,7 +398,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 218: e> adding changesets\n e> adding manifests\n @@ -448,7 +455,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 218: e> adding changesets\n e> adding manifests\n @@ -520,7 +526,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 202: e> adding changesets\n e> adding manifests\n @@ -577,7 +582,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 202: e> adding changesets\n e> adding manifests\n @@ -648,7 +652,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 206: e> adding changesets\n e> adding manifests\n @@ -706,7 +709,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 206: e> adding changesets\n e> adding manifests\n @@ -778,7 +780,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 232: e> adding changesets\n e> adding manifests\n @@ -838,7 +839,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 232: e> adding changesets\n e> adding manifests\n @@ -912,7 +912,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 193: e> adding changesets\n e> adding manifests\n @@ -969,7 +968,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 193: e> adding changesets\n e> adding manifests\n @@ -1040,16 +1038,15 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 218: e> adding changesets\n e> adding manifests\n e> adding file changes\n e> added 1 changesets with 1 changes to 1 files\n + e> print 1\n e> ui.write 1\n + e> print 2\n e> ui.write 2\n - e> print 1\n - e> print 2\n e> transaction abort!\n e> rollback completed\n e> abort: pretxnchangegroup.fail hook failed\n @@ -1100,16 +1097,15 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 218: e> adding changesets\n e> adding manifests\n e> adding file changes\n e> added 1 changesets with 1 changes to 1 files\n + e> print 1\n e> ui.write 1\n + e> print 2\n e> ui.write 2\n - e> print 1\n - e> print 2\n e> transaction abort!\n e> rollback completed\n e> abort: pretxnchangegroup.fail hook failed\n @@ -1174,16 +1170,15 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 216: e> adding changesets\n e> adding manifests\n e> adding file changes\n e> added 1 changesets with 1 changes to 1 files\n + e> stdout 1\n e> stderr 1\n + e> stdout 2\n e> stderr 2\n - e> stdout 1\n - e> stdout 2\n e> transaction abort!\n e> rollback completed\n e> abort: pretxnchangegroup.fail hook failed\n @@ -1234,16 +1229,15 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 216: e> adding changesets\n e> adding manifests\n e> adding file changes\n e> added 1 changesets with 1 changes to 1 files\n + e> stdout 1\n e> stderr 1\n + e> stdout 2\n e> stderr 2\n - e> stdout 1\n - e> stdout 2\n e> transaction abort!\n e> rollback completed\n e> abort: pretxnchangegroup.fail hook failed\n @@ -1314,7 +1308,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 212: e> adding changesets\n e> adding manifests\n @@ -1372,7 +1365,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 212: e> adding changesets\n e> adding manifests\n @@ -1445,7 +1437,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 212: e> adding changesets\n e> adding manifests\n @@ -1503,7 +1494,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 212: e> adding changesets\n e> adding manifests\n @@ -1578,7 +1568,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 230: e> adding changesets\n e> adding manifests\n @@ -1638,7 +1627,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 230: e> adding changesets\n e> adding manifests\n @@ -1721,7 +1709,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 273: e> adding changesets\n e> adding manifests\n @@ -1731,10 +1718,10 @@ e> shell stderr 1\n e> shell stdout 2\n e> shell stderr 2\n + e> stdout 1\n e> stderr 1\n + e> stdout 2\n e> stderr 2\n - e> stdout 1\n - e> stdout 2\n e> transaction abort!\n e> rollback completed\n e> abort: pretxnchangegroup.b hook failed\n @@ -1785,7 +1772,6 @@ o> read(1) -> 1: 0 result: 0 remote output: - o> read(-1) -> 0: e> read(-1) -> 273: e> adding changesets\n e> adding manifests\n @@ -1795,10 +1781,10 @@ e> shell stderr 1\n e> shell stdout 2\n e> shell stderr 2\n + e> stdout 1\n e> stderr 1\n + e> stdout 2\n e> stderr 2\n - e> stdout 1\n - e> stdout 2\n e> transaction abort!\n e> rollback completed\n e> abort: pretxnchangegroup.b hook failed\n @@ -1863,7 +1849,6 @@ o> read(1) -> 1: 1 result: 1 remote output: - o> read(-1) -> 0: e> read(-1) -> 100: e> adding changesets\n e> adding manifests\n @@ -1916,7 +1901,6 @@ o> read(1) -> 1: 1 result: 1 remote output: - o> read(-1) -> 0: e> read(-1) -> 100: e> adding changesets\n e> adding manifests\n @@ -1995,7 +1979,6 @@ o> read(1) -> 1: 1 result: 1 remote output: - o> read(-1) -> 0: e> read(-1) -> 152: e> adding changesets\n e> adding manifests\n @@ -2052,7 +2035,6 @@ o> read(1) -> 1: 1 result: 1 remote output: - o> read(-1) -> 0: e> read(-1) -> 152: e> adding changesets\n e> adding manifests\n
--- a/tests/test-ssh-proto.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-ssh-proto.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,3 +1,5 @@ +#require no-chg + $ cat > hgrc-sshv2 << EOF > %include $HGRCPATH > [experimental] @@ -1152,7 +1154,6 @@ i> hello\n o> readline() -> 1: o> \n - o> read(-1) -> 0: e> read(-1) -> 42: e> cannot upgrade protocols multiple times\n e> -\n @@ -1244,7 +1245,6 @@ i> invalid\n o> readline() -> 1: o> \n - o> read(-1) -> 0: e> read(-1) -> 46: e> malformed handshake protocol: missing hello\n e> -\n @@ -1264,7 +1264,6 @@ i> invalid\n o> readline() -> 1: o> \n - o> read(-1) -> 0: e> read(-1) -> 48: e> malformed handshake protocol: missing between\n e> -\n @@ -1286,7 +1285,6 @@ i> invalid\n o> readline() -> 1: o> \n - o> read(-1) -> 0: e> read(-1) -> 49: e> malformed handshake protocol: missing pairs 81\n e> -\n
--- a/tests/test-ssh.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-ssh.t Thu Jul 19 13:55:54 2018 -0400 @@ -230,7 +230,7 @@ namespaces phases $ hg book foo -r 0 - $ hg out -B + $ hg out -B --config paths.default=bogus://invalid --config paths.default:pushurl=`hg paths default` comparing with ssh://user@dummy/remote searching for changed bookmarks foo 1160648e36ce @@ -272,12 +272,14 @@ $ cat <<EOF > $TESTTMP/badhook > import sys > sys.stdout.write("KABOOM\n") + > sys.stdout.flush() > EOF $ cat <<EOF > $TESTTMP/badpyhook.py > import sys > def hook(ui, repo, hooktype, **kwargs): > sys.stdout.write("KABOOM IN PROCESS\n") + > sys.stdout.flush() > EOF $ cat <<EOF >> ../remote/.hg/hgrc @@ -455,11 +457,12 @@ > > def wrappedpush(orig, repo, *args, **kwargs): > res = orig(repo, *args, **kwargs) - > repo.ui.write('local stdout\n') + > repo.ui.write(b'local stdout\n') + > repo.ui.flush() > return res > > def extsetup(ui): - > extensions.wrapfunction(exchange, 'push', wrappedpush) + > extensions.wrapfunction(exchange, b'push', wrappedpush) > EOF $ cat >> .hg/hgrc << EOF @@ -569,7 +572,7 @@ $ cat > $TESTTMP/failhook << EOF > def hook(ui, repo, **kwargs): - > ui.write('hook failure!\n') + > ui.write(b'hook failure!\n') > ui.flush() > return 1 > EOF
--- a/tests/test-static-http.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-static-http.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,4 +1,4 @@ -#require killdaemons no-reposimplestore +#require no-reposimplestore $ hg clone http://localhost:$HGPORT/ copy abort: * (glob)
--- a/tests/test-status-inprocess.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-status-inprocess.py Thu Jul 19 13:55:54 2018 -0400 @@ -1,12 +1,24 @@ #!/usr/bin/env python from __future__ import absolute_import, print_function +import sys + from mercurial import ( commands, localrepo, ui as uimod, ) +print_ = print +def print(*args, **kwargs): + """print() wrapper that flushes stdout buffers to avoid py3 buffer issues + + We could also just write directly to sys.stdout.buffer the way the + ui object will, but this was easier for porting the test. + """ + print_(*args, **kwargs) + sys.stdout.flush() + u = uimod.ui.load() print('% creating repo')
--- a/tests/test-status-terse.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-status-terse.t Thu Jul 19 13:55:54 2018 -0400 @@ -67,6 +67,48 @@ ? x/ ? y/ +Run from subdirectory + $ hg status --terse u --cwd x/l + ? .hgignore + ? a + ? b + ? x/ + ? y/ + $ relstatus() { + > hg status --terse u --config commands.status.relative=1 "$@"; + > } +This should probably have {"l/", "m/", "n/"} instead of {"."}. They should +probably come after "../y/". + $ relstatus --cwd x + ? ../.hgignore + ? ../a + ? ../b + ? . + ? ../y/ +This should probably have {"u/", "../m/", "../n/"} instead of {"../"}. + $ relstatus --cwd x/l + ? ../../.hgignore + ? ../../a + ? ../../b + ? ../ + ? ../../y/ +This should probably have {"a/", "bb", "../aa", "../../m/", "../../n/"} +instead of {"../../"}. + $ relstatus --cwd x/l/u + ? ../../../.hgignore + ? ../../../a + ? ../../../b + ? ../../ + ? ../../../y/ +This should probably have {"bb", "../bb", "../../aa", "../../../m/", +"../../../n/"} instead of {"../../../"}. + $ relstatus --cwd x/l/u/a + ? ../../../../.hgignore + ? ../../../../a + ? ../../../../b + ? ../../../ + ? ../../../../y/ + $ hg add x/aa x/bb .hgignore $ hg status --terse au A .hgignore @@ -183,3 +225,55 @@ $ hg status --terse marduic --rev 0 --rev 1 abort: cannot use --terse with --rev [255] + +Config item to set the default terseness + $ cat <<EOF >> $HGRCPATH + > [commands] + > status.terse = u + > EOF + $ hg status -mu + M x/aa + M x/bb + ? a + ? b + ? x/l/ + ? x/m/ + ? x/n/ + ? y/ + +Command line flag overrides the default + $ hg status --terse= + M x/aa + M x/bb + ? a + ? b + ? x/l/aa + ? x/l/u/a/bb + ? x/l/u/bb + ? x/m/aa + ? x/n/aa + ? y/l + ? y/m + $ hg status --terse=mardu + M x/aa + M x/bb + ? a + ? b + ? x/l/ + ? x/m/ + ? x/n/ + ? y/ + +Specifying --rev should still work, with the terseness disabled. + $ hg status --rev 0 + M x/aa + M x/bb + ? a + ? b + ? x/l/aa + ? x/l/u/a/bb + ? x/l/u/bb + ? x/m/aa + ? x/n/aa + ? y/l + ? y/m
--- a/tests/test-status.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-status.t Thu Jul 19 13:55:54 2018 -0400 @@ -109,11 +109,8 @@ tweaking defaults works $ hg status --cwd a --config ui.tweakdefaults=yes - ? 1/in_a_1 - ? in_a - ? ../b/1/in_b_1 - ? ../b/2/in_b_2 - ? ../b/in_b + ? . + ? ../b/ ? ../in_root $ HGPLAIN=1 hg status --cwd a --config ui.tweakdefaults=yes ? a/1/in_a_1 (glob) @@ -123,11 +120,8 @@ ? b/in_b (glob) ? in_root $ HGPLAINEXCEPT=tweakdefaults hg status --cwd a --config ui.tweakdefaults=yes - ? 1/in_a_1 (glob) - ? in_a - ? ../b/1/in_b_1 (glob) - ? ../b/2/in_b_2 (glob) - ? ../b/in_b (glob) + ? . + ? ../b/ ? ../in_root (glob) relative paths can be requested @@ -157,11 +151,8 @@ > status.relative = False > EOF $ hg status --cwd a --config ui.tweakdefaults=yes - ? a/1/in_a_1 - ? a/in_a - ? b/1/in_b_1 - ? b/2/in_b_2 - ? b/in_b + ? a/ + ? b/ ? in_root $ cd .. @@ -222,6 +213,16 @@ C .hgignore C modified + $ hg status -A -T '{status} {path} {node|shortest}\n' + A added ffff + A copied ffff + R removed ffff + ! deleted ffff + ? unknown ffff + I ignored ffff + C .hgignore ffff + C modified ffff + $ hg status -A -Tjson [ { @@ -465,12 +466,12 @@ $ hg init repo5 $ cd repo5 - >>> open("010a", r"wb").write(b"\1\nfoo") + >>> open("010a", r"wb").write(b"\1\nfoo") and None $ hg ci -q -A -m 'initial checkin' $ hg status -A C 010a - >>> open("010a", r"wb").write(b"\1\nbar") + >>> open("010a", r"wb").write(b"\1\nbar") and None $ hg status -A M 010a $ hg ci -q -m 'modify 010a'
--- a/tests/test-strip.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-strip.t Thu Jul 19 13:55:54 2018 -0400 @@ -719,7 +719,7 @@ revisions without this option) -f --force force removal of changesets, discard uncommitted changes (no backup) - --no-backup no backups + --no-backup do not save backup bundle -k --keep do not modify working directory during strip -B --bookmark VALUE [+] remove revs only reachable from given bookmark --mq operate on patch repository
--- a/tests/test-subrepo-deep-nested-change.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-subrepo-deep-nested-change.t Thu Jul 19 13:55:54 2018 -0400 @@ -379,7 +379,7 @@ $ touch bar/abc $ hg addremove -S .. \r (no-eol) (esc) - searching for exact renames [ ] 0/1\r (no-eol) (esc) + searching for exact renames [========================>] 1/1\r (no-eol) (esc) \r (no-eol) (esc) adding ../sub1/sub2/folder/test.txt removing ../sub1/sub2/test.txt
--- a/tests/test-subrepo-paths.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-subrepo-paths.t Thu Jul 19 13:55:54 2018 -0400 @@ -55,7 +55,7 @@ > .* = \1 > EOF $ hg debugsub - abort: bad subrepository pattern in $TESTTMP/outer/.hg/hgrc:2: invalid group reference + abort: bad subrepository pattern in $TESTTMP/outer/.hg/hgrc:2: invalid group reference* (glob) [255] $ cd ..
--- a/tests/test-subrepo-relative-path.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-subrepo-relative-path.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - Preparing the subrepository 'sub' $ hg init sub @@ -58,6 +56,30 @@ new changesets 863c1745b441 3 files updated, 0 files merged, 0 files removed, 0 files unresolved +Ensure that subrepos pay attention to default:pushurl + + $ cat > cloned/.hg/hgrc << EOF + > [paths] + > default:pushurl = http://localhost:$HGPORT/main + > EOF + + $ hg -R cloned out -S --config paths.default=bogus://invalid + comparing with http://localhost:$HGPORT/main + searching for changes + no changes found + comparing with http://localhost:$HGPORT/sub + searching for changes + no changes found + [1] + + $ hg -R cloned push --config paths.default=bogus://invalid + pushing to http://localhost:$HGPORT/main + no changes made to subrepo sub since last push to http://localhost:$HGPORT/sub + searching for changes + no changes found + abort: HTTP Error 403: ssl required + [255] + Checking cloned repo ids $ hg id -R cloned
--- a/tests/test-symlink-os-yes-fs-no.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-symlink-os-yes-fs-no.py Thu Jul 19 13:55:54 2018 -0400 @@ -6,6 +6,7 @@ from mercurial import ( commands, hg, + pycompat, ui as uimod, util, ) @@ -19,13 +20,13 @@ u = uimod.ui.load() # hide outer repo -hg.peer(u, {}, '.', create=True) +hg.peer(u, {}, b'.', create=True) # unbundle with symlink support -hg.peer(u, {}, 'test0', create=True) +hg.peer(u, {}, b'test0', create=True) -repo = hg.repository(u, 'test0') -commands.unbundle(u, repo, BUNDLEPATH, update=True) +repo = hg.repository(u, b'test0') +commands.unbundle(u, repo, pycompat.fsencode(BUNDLEPATH), update=True) # wait a bit, or the status call wont update the dirstate time.sleep(1) @@ -42,7 +43,7 @@ # dereference links as if a Samba server has exported this to a # Windows client -for f in 'test0/a.lnk', 'test0/d/b.lnk': +for f in b'test0/a.lnk', b'test0/d/b.lnk': os.unlink(f) fp = open(f, 'wb') fp.write(util.readfile(f[:-4])) @@ -50,11 +51,11 @@ # reload repository u = uimod.ui.load() -repo = hg.repository(u, 'test0') +repo = hg.repository(u, b'test0') commands.status(u, repo) # try unbundling a repo which contains symlinks u = uimod.ui.load() -repo = hg.repository(u, 'test1', create=True) -commands.unbundle(u, repo, BUNDLEPATH, update=True) +repo = hg.repository(u, b'test1', create=True) +commands.unbundle(u, repo, pycompat.fsencode(BUNDLEPATH), update=True)
--- a/tests/test-symlink-placeholder.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-symlink-placeholder.t Thu Jul 19 13:55:54 2018 -0400 @@ -50,13 +50,13 @@ Write binary data to the placeholder: - >>> open('b', 'w').write('this is a binary\0') + >>> open('b', 'w').write('this is a binary\0') and None $ hg --config extensions.n=$TESTTMP/nolink.py st --debug ignoring suspect symlink placeholder "b" Write a long string to the placeholder: - >>> open('b', 'w').write('this' * 1000) + >>> open('b', 'w').write('this' * 1000) and None $ hg --config extensions.n=$TESTTMP/nolink.py st --debug ignoring suspect symlink placeholder "b" @@ -68,7 +68,7 @@ Write a valid string to the placeholder: - >>> open('b', 'w').write('this') + >>> open('b', 'w').write('this') and None $ hg --config extensions.n=$TESTTMP/nolink.py st --debug M b $ hg --config extensions.n=$TESTTMP/nolink.py ci -m1
--- a/tests/test-tags.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-tags.t Thu Jul 19 13:55:54 2018 -0400 @@ -610,6 +610,27 @@ localtag 0:bbd179dfa0a7 local globaltag 0:bbd179dfa0a7 +Templated output: + + (immediate values) + + $ hg tags -T '{pad(tag, 9)} {rev}:{node} ({type})\n' + tip 1:a0b6fe111088c8c29567d3876cc466aa02927cae () + localtag 0:bbd179dfa0a71671c253b3ae0aa1513b60d199fa (local) + globaltag 0:bbd179dfa0a71671c253b3ae0aa1513b60d199fa () + + (ctx/revcache dependent) + + $ hg tags -T '{pad(tag, 9)} {rev} {file_adds}\n' + tip 1 .hgtags + localtag 0 foo + globaltag 0 foo + + $ hg tags -T '{pad(tag, 9)} {rev}:{node|shortest}\n' + tip 1:a0b6 + localtag 0:bbd1 + globaltag 0:bbd1 + Test for issue3911 $ hg tag -r 0 -l localtag2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-template-basic.t Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,1088 @@ +Test template syntax and basic functionality +============================================ + + $ hg init a + $ cd a + $ echo a > a + $ hg add a + $ echo line 1 > b + $ echo line 2 >> b + $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>' + + $ hg add b + $ echo other 1 > c + $ echo other 2 >> c + $ echo >> c + $ echo other 3 >> c + $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>' + + $ hg add c + $ hg commit -m 'no person' -d '1200000 0' -u 'other@place' + $ echo c >> c + $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person' + + $ echo foo > .hg/branch + $ hg commit -m 'new branch' -d '1400000 0' -u 'person' + + $ hg co -q 3 + $ echo other 4 >> d + $ hg add d + $ hg commit -m 'new head' -d '1500000 0' -u 'person' + + $ hg merge -q foo + $ hg commit -m 'merge' -d '1500001 0' -u 'person' + +Test arithmetic operators have the right precedence: + + $ hg log -l 1 -T '{date(date, "%Y") + 5 * 10} {date(date, "%Y") - 2 * 3}\n' + 2020 1964 + $ hg log -l 1 -T '{date(date, "%Y") * 5 + 10} {date(date, "%Y") * 3 - 2}\n' + 9860 5908 + +Test division: + + $ hg debugtemplate -r0 -v '{5 / 2} {mod(5, 2)}\n' + (template + (/ + (integer '5') + (integer '2')) + (string ' ') + (func + (symbol 'mod') + (list + (integer '5') + (integer '2'))) + (string '\n')) + * keywords: + * functions: mod + 2 1 + $ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n' + (template + (/ + (integer '5') + (negate + (integer '2'))) + (string ' ') + (func + (symbol 'mod') + (list + (integer '5') + (negate + (integer '2')))) + (string '\n')) + * keywords: + * functions: mod + -3 -1 + $ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n' + (template + (/ + (negate + (integer '5')) + (integer '2')) + (string ' ') + (func + (symbol 'mod') + (list + (negate + (integer '5')) + (integer '2'))) + (string '\n')) + * keywords: + * functions: mod + -3 1 + $ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n' + (template + (/ + (negate + (integer '5')) + (negate + (integer '2'))) + (string ' ') + (func + (symbol 'mod') + (list + (negate + (integer '5')) + (negate + (integer '2')))) + (string '\n')) + * keywords: + * functions: mod + 2 -1 + +Filters bind closer than arithmetic: + + $ hg debugtemplate -r0 -v '{revset(".")|count - 1}\n' + (template + (- + (| + (func + (symbol 'revset') + (string '.')) + (symbol 'count')) + (integer '1')) + (string '\n')) + * keywords: + * functions: count, revset + 0 + +But negate binds closer still: + + $ hg debugtemplate -r0 -v '{1-3|stringify}\n' + (template + (- + (integer '1') + (| + (integer '3') + (symbol 'stringify'))) + (string '\n')) + * keywords: + * functions: stringify + hg: parse error: arithmetic only defined on integers + [255] + $ hg debugtemplate -r0 -v '{-3|stringify}\n' + (template + (| + (negate + (integer '3')) + (symbol 'stringify')) + (string '\n')) + * keywords: + * functions: stringify + -3 + +Filters bind as close as map operator: + + $ hg debugtemplate -r0 -v '{desc|splitlines % "{line}\n"}' + (template + (% + (| + (symbol 'desc') + (symbol 'splitlines')) + (template + (symbol 'line') + (string '\n')))) + * keywords: desc, line + * functions: splitlines + line 1 + line 2 + +Keyword arguments: + + $ hg debugtemplate -r0 -v '{foo=bar|baz}' + (template + (keyvalue + (symbol 'foo') + (| + (symbol 'bar') + (symbol 'baz')))) + * keywords: bar, foo + * functions: baz + hg: parse error: can't use a key-value pair in this context + [255] + + $ hg debugtemplate '{pad("foo", width=10, left=true)}\n' + foo + +Call function which takes named arguments by filter syntax: + + $ hg debugtemplate '{" "|separate}' + $ hg debugtemplate '{("not", "an", "argument", "list")|separate}' + hg: parse error: unknown method 'list' + [255] + +Second branch starting at nullrev: + + $ hg update null + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + $ echo second > second + $ hg add second + $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>' + created new head + + $ echo third > third + $ hg add third + $ hg mv second fourth + $ hg commit -m third -d "2020-01-01 10:01" + + $ hg log --template '{join(file_copies, ",\n")}\n' -r . + fourth (second) + $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r . + second -> fourth + $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7 + 8 t + 7 f + +Internal resources shouldn't be exposed (issue5699): + + $ hg log -r. -T '{cache}{ctx}{repo}{revcache}{templ}{ui}' + +Never crash on internal resource not available: + + $ hg --cwd .. debugtemplate '{"c0bebeef"|shortest}\n' + abort: template resource not available: repo + [255] + + $ hg config -T '{author}' + +Quoting for ui.logtemplate + + $ hg tip --config "ui.logtemplate={rev}\n" + 8 + $ hg tip --config "ui.logtemplate='{rev}\n'" + 8 + $ hg tip --config 'ui.logtemplate="{rev}\n"' + 8 + $ hg tip --config 'ui.logtemplate=n{rev}\n' + n8 + +Check that recursive reference does not fall into RuntimeError (issue4758): + + common mistake: + + $ cat << EOF > issue4758 + > changeset = '{changeset}\n' + > EOF + $ hg log --style ./issue4758 + abort: recursive reference 'changeset' in template + [255] + + circular reference: + + $ cat << EOF > issue4758 + > changeset = '{foo}' + > foo = '{changeset}' + > EOF + $ hg log --style ./issue4758 + abort: recursive reference 'foo' in template + [255] + + buildmap() -> gettemplate(), where no thunk was made: + + $ cat << EOF > issue4758 + > changeset = '{files % changeset}\n' + > EOF + $ hg log --style ./issue4758 + abort: recursive reference 'changeset' in template + [255] + + not a recursion if a keyword of the same name exists: + + $ cat << EOF > issue4758 + > changeset = '{tags % rev}' + > rev = '{rev} {tag}\n' + > EOF + $ hg log --style ./issue4758 -r tip + 8 tip + +Set up phase: + + $ hg phase -r 5 --public + $ hg phase -r 7 --secret --force + +Add a dummy commit to make up for the instability of the above: + + $ echo a > a + $ hg add a + $ hg ci -m future + +Add a commit that does all possible modifications at once + + $ echo modify >> third + $ touch b + $ hg add b + $ hg mv fourth fifth + $ hg rm a + $ hg ci -m "Modify, add, remove, rename" + +Error on syntax: + + $ cat <<EOF > t + > changeset = '{c}' + > c = q + > x = "f + > EOF + $ echo '[ui]' > .hg/hgrc + $ echo 'style = t' >> .hg/hgrc + $ hg log + hg: parse error at t:3: unmatched quotes + [255] + + $ hg log -T '{date' + hg: parse error at 1: unterminated template expansion + ({date + ^ here) + [255] + $ hg log -T '{date(}' + hg: parse error at 6: not a prefix: end + ({date(} + ^ here) + [255] + $ hg log -T '{date)}' + hg: parse error at 5: invalid token + ({date)} + ^ here) + [255] + $ hg log -T '{date date}' + hg: parse error at 6: invalid token + ({date date} + ^ here) + [255] + + $ hg log -T '{}' + hg: parse error at 1: not a prefix: end + ({} + ^ here) + [255] + $ hg debugtemplate -v '{()}' + (template + (group + None)) + * keywords: + * functions: + hg: parse error: missing argument + [255] + +Behind the scenes, this would throw TypeError without intype=bytes + + $ hg log -l 3 --template '{date|obfuscate}\n' + 0.00 + 0.00 + 1577872860.00 + +Behind the scenes, this will throw a ValueError + + $ hg log -l 3 --template 'line: {desc|shortdate}\n' + hg: parse error: invalid date: 'Modify, add, remove, rename' + (template filter 'shortdate' is not compatible with keyword 'desc') + [255] + +Behind the scenes, this would throw AttributeError without intype=bytes + + $ hg log -l 3 --template 'line: {date|escape}\n' + line: 0.00 + line: 0.00 + line: 1577872860.00 + + $ hg log -l 3 --template 'line: {extras|localdate}\n' + hg: parse error: localdate expects a date information + [255] + +Behind the scenes, this will throw ValueError + + $ hg tip --template '{author|email|date}\n' + hg: parse error: date expects a date information + [255] + + $ hg tip -T '{author|email|shortdate}\n' + hg: parse error: invalid date: 'test' + (template filter 'shortdate' is not compatible with keyword 'author') + [255] + + $ hg tip -T '{get(extras, "branch")|shortdate}\n' + hg: parse error: invalid date: 'default' + (incompatible use of template filter 'shortdate') + [255] + +Error in nested template: + + $ hg log -T '{"date' + hg: parse error at 2: unterminated string + ({"date + ^ here) + [255] + + $ hg log -T '{"foo{date|?}"}' + hg: parse error at 11: syntax error + ({"foo{date|?}"} + ^ here) + [255] + +Thrown an error if a template function doesn't exist + + $ hg tip --template '{foo()}\n' + hg: parse error: unknown function 'foo' + [255] + + $ cd .. + +Set up latesttag repository: + + $ hg init latesttag + $ cd latesttag + + $ echo a > file + $ hg ci -Am a -d '0 0' + adding file + + $ echo b >> file + $ hg ci -m b -d '1 0' + + $ echo c >> head1 + $ hg ci -Am h1c -d '2 0' + adding head1 + + $ hg update -q 1 + $ echo d >> head2 + $ hg ci -Am h2d -d '3 0' + adding head2 + created new head + + $ echo e >> head2 + $ hg ci -m h2e -d '4 0' + + $ hg merge -q + $ hg ci -m merge -d '5 -3600' + + $ hg tag -r 1 -m t1 -d '6 0' t1 + $ hg tag -r 2 -m t2 -d '7 0' t2 + $ hg tag -r 3 -m t3 -d '8 0' t3 + $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter + $ hg tag -r 5 -m t5 -d '9 0' t5 + $ hg tag -r 3 -m at3 -d '10 0' at3 + + $ cd .. + +Test new-style inline templating: + + $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n' + modified files: .hgtags + + + $ hg log -R latesttag -r tip -T '{rev % "a"}\n' + hg: parse error: 11 is not iterable of mappings + (keyword 'rev' does not support map operation) + [255] + $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n' + hg: parse error: None is not iterable of mappings + [255] + $ hg log -R latesttag -r tip -T '{extras % "{key}\n" % "{key}\n"}' + hg: parse error: list of strings is not mappable + [255] + +Test new-style inline templating of non-list/dict type: + + $ hg log -R latesttag -r tip -T '{manifest}\n' + 11:2bc6e9006ce2 + $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n' + string length: 15 + $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n' + 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc + + $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}' + branch: default + $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}' + hg: parse error: None is not iterable of mappings + [255] + $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}' + branch: default + $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}' + 0:ce3cec86e6c2 + $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}' + 9:fbc7cd862e9c + +Test dot operator precedence: + + $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n' + (template + (| + (. + (symbol 'manifest') + (symbol 'node')) + (symbol 'short')) + (string '\n')) + * keywords: manifest, node, rev + * functions: formatnode, short + 89f4071fec70 + + (the following examples are invalid, but seem natural in parsing POV) + + $ hg debugtemplate -R latesttag -r0 -v '{foo|bar.baz}\n' 2> /dev/null + (template + (| + (symbol 'foo') + (. + (symbol 'bar') + (symbol 'baz'))) + (string '\n')) + [255] + $ hg debugtemplate -R latesttag -r0 -v '{foo.bar()}\n' 2> /dev/null + (template + (. + (symbol 'foo') + (func + (symbol 'bar') + None)) + (string '\n')) + * keywords: foo + * functions: bar + [255] + +Test evaluation of dot operator: + + $ hg log -R latesttag -l1 -T '{min(revset("0:9")).node}\n' + ce3cec86e6c26bd9bdfc590a6b92abc9680f1796 + $ hg log -R latesttag -r0 -T '{extras.branch}\n' + default + $ hg log -R latesttag -r0 -T '{date.unixtime} {localdate(date, "+0200").tzoffset}\n' + 0 -7200 + + $ hg log -R latesttag -l1 -T '{author.invalid}\n' + hg: parse error: 'test' is not a dictionary + (keyword 'author' does not support member operation) + [255] + $ hg log -R latesttag -l1 -T '{min("abc").invalid}\n' + hg: parse error: 'a' is not a dictionary + [255] + +Test integer literal: + + $ hg debugtemplate -v '{(0)}\n' + (template + (group + (integer '0')) + (string '\n')) + * keywords: + * functions: + 0 + $ hg debugtemplate -v '{(123)}\n' + (template + (group + (integer '123')) + (string '\n')) + * keywords: + * functions: + 123 + $ hg debugtemplate -v '{(-4)}\n' + (template + (group + (negate + (integer '4'))) + (string '\n')) + * keywords: + * functions: + -4 + $ hg debugtemplate '{(-)}\n' + hg: parse error at 3: not a prefix: ) + ({(-)}\n + ^ here) + [255] + $ hg debugtemplate '{(-a)}\n' + hg: parse error: negation needs an integer argument + [255] + +top-level integer literal is interpreted as symbol (i.e. variable name): + + $ hg debugtemplate -D 1=one -v '{1}\n' + (template + (integer '1') + (string '\n')) + * keywords: + * functions: + one + $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n' + (template + (func + (symbol 'if') + (list + (string 't') + (template + (integer '1')))) + (string '\n')) + * keywords: + * functions: if + one + $ hg debugtemplate -D 1=one -v '{1|stringify}\n' + (template + (| + (integer '1') + (symbol 'stringify')) + (string '\n')) + * keywords: + * functions: stringify + one + +unless explicit symbol is expected: + + $ hg log -Ra -r0 -T '{desc|1}\n' + hg: parse error: expected a symbol, got 'integer' + [255] + $ hg log -Ra -r0 -T '{1()}\n' + hg: parse error: expected a symbol, got 'integer' + [255] + +Test string literal: + + $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n' + (template + (string 'string with no template fragment') + (string '\n')) + * keywords: + * functions: + string with no template fragment + $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n' + (template + (template + (string 'template: ') + (symbol 'rev')) + (string '\n')) + * keywords: rev + * functions: + template: 0 + $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n' + (template + (string 'rawstring: {rev}') + (string '\n')) + * keywords: + * functions: + rawstring: {rev} + $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n' + (template + (% + (symbol 'files') + (string 'rawstring: {file}')) + (string '\n')) + * keywords: files + * functions: + rawstring: {file} + +Test string escaping: + + $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n' + > + <>\n<[> + <>\n<]> + <>\n< + + $ hg log -R latesttag -r 0 \ + > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n' + > + <>\n<[> + <>\n<]> + <>\n< + + $ hg log -R latesttag -r 0 -T esc \ + > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n' + > + <>\n<[> + <>\n<]> + <>\n< + + $ cat <<'EOF' > esctmpl + > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n' + > EOF + $ hg log -R latesttag -r 0 --style ./esctmpl + > + <>\n<[> + <>\n<]> + <>\n< + +Test string escaping of quotes: + + $ hg log -Ra -r0 -T '{"\""}\n' + " + $ hg log -Ra -r0 -T '{"\\\""}\n' + \" + $ hg log -Ra -r0 -T '{r"\""}\n' + \" + $ hg log -Ra -r0 -T '{r"\\\""}\n' + \\\" + + + $ hg log -Ra -r0 -T '{"\""}\n' + " + $ hg log -Ra -r0 -T '{"\\\""}\n' + \" + $ hg log -Ra -r0 -T '{r"\""}\n' + \" + $ hg log -Ra -r0 -T '{r"\\\""}\n' + \\\" + +Test exception in quoted template. single backslash before quotation mark is +stripped before parsing: + + $ cat <<'EOF' > escquotetmpl + > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n" + > EOF + $ cd latesttag + $ hg log -r 2 --style ../escquotetmpl + " \" \" \\" head1 + + $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"' + valid + $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'" + valid + +Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested +_evalifliteral() templates (issue4733): + + $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n' + "2 + $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n' + "2 + $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n' + "2 + + $ hg log -r 2 -T '{if(rev, "\\\"")}\n' + \" + $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n' + \" + $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n' + \" + + $ hg log -r 2 -T '{if(rev, r"\\\"")}\n' + \\\" + $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n' + \\\" + $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n' + \\\" + +escaped single quotes and errors: + + $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n' + foo + $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n' + foo + $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n' + hg: parse error at 21: unterminated string + ({if(rev, "{if(rev, \")}")}\n + ^ here) + [255] + $ hg log -r 2 -T '{if(rev, \"\\"")}\n' + hg: parse error: trailing \ in string + [255] + $ hg log -r 2 -T '{if(rev, r\"\\"")}\n' + hg: parse error: trailing \ in string + [255] + + $ cd .. + +Test leading backslashes: + + $ cd latesttag + $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n' + {rev} {file} + $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n' + \2 \head1 + $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n' + \{rev} \{file} + $ cd .. + +Test leading backslashes in "if" expression (issue4714): + + $ cd latesttag + $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n' + {rev} \{rev} + $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n' + \2 \\{rev} + $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n' + \{rev} \\\{rev} + $ cd .. + +"string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice) + + $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n' + \x6e + $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n' + \x5c\x786e + $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n' + \x6e + $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n' + \x5c\x786e + + $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n' + \x6e + $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n' + \x5c\x786e + $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n' + \x6e + $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n' + \x5c\x786e + + $ hg log -R a -r 8 --template '{join(files, "\n")}\n' + fourth + second + third + $ hg log -R a -r 8 --template '{join(files, r"\n")}\n' + fourth\nsecond\nthird + + $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}' + <p> + 1st + </p> + <p> + 2nd + </p> + $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}' + <p> + 1st\n\n2nd + </p> + $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}' + 1st + + 2nd + + $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n' + o perso + $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n' + no person + $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n' + o perso + $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n' + no perso + + $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n' + -o perso- + $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n' + no person + $ hg log -R a -r 2 --template '{sub("n", r"\\x2d", desc)}\n' + \x2do perso\x2d + $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n' + -o perso- + $ hg log -R a -r 2 --template '{sub("n", r"\\x2d", r"no perso\x6e")}\n' + \x2do perso\x6e + + $ hg log -R a -r 8 --template '{files % "{file}\n"}' + fourth + second + third + +Test string escaping in nested expression: + + $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n' + fourth\x6esecond\x6ethird + $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n' + fourth\x6esecond\x6ethird + + $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n' + fourth\x6esecond\x6ethird + $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n' + fourth\x5c\x786esecond\x5c\x786ethird + + $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n' + 3:\x6eo user, \x6eo domai\x6e + 4:\x5c\x786eew bra\x5c\x786ech + +Test quotes in nested expression are evaluated just like a $(command) +substitution in POSIX shells: + + $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n' + 8:95c24699272e + $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n' + {8} "95c24699272e" + +Test recursive evaluation: + + $ hg init r + $ cd r + $ echo a > a + $ hg ci -Am '{rev}' + adding a + $ hg log -r 0 --template '{if(rev, desc)}\n' + {rev} + $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n' + test 0 + + $ hg branch -q 'text.{rev}' + $ echo aa >> aa + $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped' + + $ hg log -l1 --template '{fill(desc, "20", author, branch)}' + {node|short}desc to + text.{rev}be wrapped + text.{rev}desc to be + text.{rev}wrapped (no-eol) + $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}' + bcc7ff960b8e:desc to + text.1:be wrapped + text.1:desc to be + text.1:wrapped (no-eol) + $ hg log -l1 -T '{fill(desc, date, "", "")}\n' + hg: parse error: fill expects an integer width + [255] + + $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}' + {node|short} (no-eol) + $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}' + bcc-ff---b-e (no-eol) + + $ cat >> .hg/hgrc <<EOF + > [extensions] + > color= + > [color] + > mode=ansi + > text.{rev} = red + > text.1 = green + > EOF + $ hg log --color=always -l 1 --template '{label(branch, "text\n")}' + \x1b[0;31mtext\x1b[0m (esc) + $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}' + \x1b[0;32mtext\x1b[0m (esc) + + $ cd .. + +Test bad template with better error message + + $ hg log -Gv -R a --template '{desc|user()}' + hg: parse error: expected a symbol, got 'func' + [255] + +Test broken string escapes: + + $ hg log -T "bogus\\" -R a + hg: parse error: trailing \ in string + [255] + $ hg log -T "\\xy" -R a + hg: parse error: invalid \x escape* (glob) + [255] + +Templater supports aliases of symbol and func() styles: + + $ hg clone -q a aliases + $ cd aliases + $ cat <<EOF >> .hg/hgrc + > [templatealias] + > r = rev + > rn = "{r}:{node|short}" + > status(c, files) = files % "{c} {file}\n" + > utcdate(d) = localdate(d, "UTC") + > EOF + + $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n' + (template + (symbol 'rn') + (string ' ') + (| + (func + (symbol 'utcdate') + (symbol 'date')) + (symbol 'isodate')) + (string '\n')) + * expanded: + (template + (template + (symbol 'rev') + (string ':') + (| + (symbol 'node') + (symbol 'short'))) + (string ' ') + (| + (func + (symbol 'localdate') + (list + (symbol 'date') + (string 'UTC'))) + (symbol 'isodate')) + (string '\n')) + * keywords: date, node, rev + * functions: isodate, localdate, short + 0:1e4e1b8f71e0 1970-01-12 13:46 +0000 + + $ hg debugtemplate -vr0 '{status("A", file_adds)}' + (template + (func + (symbol 'status') + (list + (string 'A') + (symbol 'file_adds')))) + * expanded: + (template + (% + (symbol 'file_adds') + (template + (string 'A') + (string ' ') + (symbol 'file') + (string '\n')))) + * keywords: file, file_adds + * functions: + A a + +A unary function alias can be called as a filter: + + $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n' + (template + (| + (| + (symbol 'date') + (symbol 'utcdate')) + (symbol 'isodate')) + (string '\n')) + * expanded: + (template + (| + (func + (symbol 'localdate') + (list + (symbol 'date') + (string 'UTC'))) + (symbol 'isodate')) + (string '\n')) + * keywords: date + * functions: isodate, localdate + 1970-01-12 13:46 +0000 + +Aliases should be applied only to command arguments and templates in hgrc. +Otherwise, our stock styles and web templates could be corrupted: + + $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n' + 0:1e4e1b8f71e0 1970-01-12 13:46 +0000 + + $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"' + 0:1e4e1b8f71e0 1970-01-12 13:46 +0000 + + $ cat <<EOF > tmpl + > changeset = 'nothing expanded:{rn}\n' + > EOF + $ hg log -r0 --style ./tmpl + nothing expanded: + +Aliases in formatter: + + $ hg branches -T '{pad(branch, 7)} {rn}\n' + default 6:d41e714fe50d + foo 4:bbe44766e73d + +Aliases should honor HGPLAIN: + + $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n' + nothing expanded: + $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n' + 0:1e4e1b8f71e0 + +Unparsable alias: + + $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}' + (template + (symbol 'bad')) + abort: bad definition of template alias "bad": at 2: not a prefix: end + [255] + $ hg log --config templatealias.bad='x(' -T '{bad}' + abort: bad definition of template alias "bad": at 2: not a prefix: end + [255] + + $ cd .. + +Test that template function in extension is registered as expected + + $ cd a + + $ cat <<EOF > $TESTTMP/customfunc.py + > from mercurial import registrar + > + > templatefunc = registrar.templatefunc() + > + > @templatefunc(b'custom()') + > def custom(context, mapping, args): + > return b'custom' + > EOF + $ cat <<EOF > .hg/hgrc + > [extensions] + > customfunc = $TESTTMP/customfunc.py + > EOF + + $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true + custom + + $ cd ..
--- a/tests/test-template-engine.t Sun Jul 01 23:36:53 2018 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ - - $ cat > engine.py << EOF - > - > from mercurial import ( - > pycompat, - > templater, - > templateutil, - > ) - > - > class mytemplater(templater.engine): - > def _load(self, t): - > return self._loader(t) - > - > def process(self, t, map): - > tmpl = self._load(t) - > props = self._defaults.copy() - > props.update(map) - > for k, v in props.items(): - > if b'{{%s}}' % k not in tmpl: - > continue - > if callable(v) and getattr(v, '_requires', None) is None: - > props = self._resources.copy() - > props.update(map) - > v = v(**pycompat.strkwargs(props)) - > elif callable(v): - > v = v(self, props) - > v = templateutil.stringify(self, props, v) - > tmpl = tmpl.replace(b'{{%s}}' % k, v) - > yield tmpl - > - > templater.engines[b'my'] = mytemplater - > EOF - $ hg init test - $ echo '[extensions]' > test/.hg/hgrc - $ echo "engine = `pwd`/engine.py" >> test/.hg/hgrc - $ cd test - $ cat > mymap << EOF - > changeset = my:changeset.txt - > EOF - $ cat > changeset.txt << EOF - > {{rev}} {{node}} {{author}} - > EOF - $ hg ci -Ama - adding changeset.txt - adding mymap - $ hg log --style=./mymap - 0 97e5f848f0936960273bbf75be6388cd0350a32b test - - $ cat > changeset.txt << EOF - > {{p1rev}} {{p1node}} {{p2rev}} {{p2node}} - > EOF - $ hg ci -Ama - $ hg log --style=./mymap - 0 97e5f848f0936960273bbf75be6388cd0350a32b -1 0000000000000000000000000000000000000000 - -1 0000000000000000000000000000000000000000 -1 0000000000000000000000000000000000000000 - -invalid engine type: - - $ echo 'changeset = unknown:changeset.txt' > unknownenginemap - $ hg log --style=./unknownenginemap - abort: invalid template engine: unknown - [255] - - $ cd ..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-template-functions.t Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,1430 @@ +Test template filters and functions +=================================== + + $ hg init a + $ cd a + $ echo a > a + $ hg add a + $ echo line 1 > b + $ echo line 2 >> b + $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>' + + $ hg add b + $ echo other 1 > c + $ echo other 2 >> c + $ echo >> c + $ echo other 3 >> c + $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>' + + $ hg add c + $ hg commit -m 'no person' -d '1200000 0' -u 'other@place' + $ echo c >> c + $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person' + + $ echo foo > .hg/branch + $ hg commit -m 'new branch' -d '1400000 0' -u 'person' + + $ hg co -q 3 + $ echo other 4 >> d + $ hg add d + $ hg commit -m 'new head' -d '1500000 0' -u 'person' + + $ hg merge -q foo + $ hg commit -m 'merge' -d '1500001 0' -u 'person' + +Second branch starting at nullrev: + + $ hg update null + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + $ echo second > second + $ hg add second + $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>' + created new head + + $ echo third > third + $ hg add third + $ hg mv second fourth + $ hg commit -m third -d "2020-01-01 10:01" + + $ hg phase -r 5 --public + $ hg phase -r 7 --secret --force + +Filters work: + + $ hg log --template '{author|domain}\n' + + hostname + + + + + place + place + hostname + + $ hg log --template '{author|person}\n' + test + User Name + person + person + person + person + other + A. N. Other + User Name + + $ hg log --template '{author|user}\n' + test + user + person + person + person + person + other + other + user + + $ hg log --template '{date|date}\n' + Wed Jan 01 10:01:00 2020 +0000 + Mon Jan 12 13:46:40 1970 +0000 + Sun Jan 18 08:40:01 1970 +0000 + Sun Jan 18 08:40:00 1970 +0000 + Sat Jan 17 04:53:20 1970 +0000 + Fri Jan 16 01:06:40 1970 +0000 + Wed Jan 14 21:20:00 1970 +0000 + Tue Jan 13 17:33:20 1970 +0000 + Mon Jan 12 13:46:40 1970 +0000 + + $ hg log --template '{date|isodate}\n' + 2020-01-01 10:01 +0000 + 1970-01-12 13:46 +0000 + 1970-01-18 08:40 +0000 + 1970-01-18 08:40 +0000 + 1970-01-17 04:53 +0000 + 1970-01-16 01:06 +0000 + 1970-01-14 21:20 +0000 + 1970-01-13 17:33 +0000 + 1970-01-12 13:46 +0000 + + $ hg log --template '{date|isodatesec}\n' + 2020-01-01 10:01:00 +0000 + 1970-01-12 13:46:40 +0000 + 1970-01-18 08:40:01 +0000 + 1970-01-18 08:40:00 +0000 + 1970-01-17 04:53:20 +0000 + 1970-01-16 01:06:40 +0000 + 1970-01-14 21:20:00 +0000 + 1970-01-13 17:33:20 +0000 + 1970-01-12 13:46:40 +0000 + + $ hg log --template '{date|rfc822date}\n' + Wed, 01 Jan 2020 10:01:00 +0000 + Mon, 12 Jan 1970 13:46:40 +0000 + Sun, 18 Jan 1970 08:40:01 +0000 + Sun, 18 Jan 1970 08:40:00 +0000 + Sat, 17 Jan 1970 04:53:20 +0000 + Fri, 16 Jan 1970 01:06:40 +0000 + Wed, 14 Jan 1970 21:20:00 +0000 + Tue, 13 Jan 1970 17:33:20 +0000 + Mon, 12 Jan 1970 13:46:40 +0000 + + $ hg log --template '{desc|firstline}\n' + third + second + merge + new head + new branch + no user, no domain + no person + other 1 + line 1 + + $ hg log --template '{node|short}\n' + 95c24699272e + 29114dbae42b + d41e714fe50d + 13207e5a10d9 + bbe44766e73d + 10e46f2dcbf4 + 97054abb4ab8 + b608e9d1a3f0 + 1e4e1b8f71e0 + + $ hg log --template '<changeset author="{author|xmlescape}"/>\n' + <changeset author="test"/> + <changeset author="User Name <user@hostname>"/> + <changeset author="person"/> + <changeset author="person"/> + <changeset author="person"/> + <changeset author="person"/> + <changeset author="other@place"/> + <changeset author="A. N. Other <other@place>"/> + <changeset author="User Name <user@hostname>"/> + + $ hg log --template '{rev}: {children}\n' + 8: + 7: 8:95c24699272e + 6: + 5: 6:d41e714fe50d + 4: 6:d41e714fe50d + 3: 4:bbe44766e73d 5:13207e5a10d9 + 2: 3:10e46f2dcbf4 + 1: 2:97054abb4ab8 + 0: 1:b608e9d1a3f0 + +Formatnode filter works: + + $ hg -q log -r 0 --template '{node|formatnode}\n' + 1e4e1b8f71e0 + + $ hg log -r 0 --template '{node|formatnode}\n' + 1e4e1b8f71e0 + + $ hg -v log -r 0 --template '{node|formatnode}\n' + 1e4e1b8f71e0 + + $ hg --debug log -r 0 --template '{node|formatnode}\n' + 1e4e1b8f71e05681d422154f5421e385fec3454f + +Age filter: + + $ hg init unstable-hash + $ cd unstable-hash + $ hg log --template '{date|age}\n' > /dev/null || exit 1 + + >>> from __future__ import absolute_import + >>> import datetime + >>> fp = open('a', 'wb') + >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7) + >>> fp.write(b'%d-%d-%d 00:00' % (n.year, n.month, n.day)) and None + >>> fp.close() + $ hg add a + $ hg commit -m future -d "`cat a`" + + $ hg log -l1 --template '{date|age}\n' + 7 years from now + + $ cd .. + $ rm -rf unstable-hash + +Filename filters: + + $ hg debugtemplate '{"foo/bar"|basename}|{"foo/"|basename}|{"foo"|basename}|\n' + bar||foo| + $ hg debugtemplate '{"foo/bar"|dirname}|{"foo/"|dirname}|{"foo"|dirname}|\n' + foo|foo|| + $ hg debugtemplate '{"foo/bar"|stripdir}|{"foo/"|stripdir}|{"foo"|stripdir}|\n' + foo|foo|foo| + +commondir() filter: + + $ hg debugtemplate '{""|splitlines|commondir}\n' + + $ hg debugtemplate '{"foo/bar\nfoo/baz\nfoo/foobar\n"|splitlines|commondir}\n' + foo + $ hg debugtemplate '{"foo/bar\nfoo/bar\n"|splitlines|commondir}\n' + foo + $ hg debugtemplate '{"/foo/bar\n/foo/bar\n"|splitlines|commondir}\n' + foo + $ hg debugtemplate '{"/foo\n/foo\n"|splitlines|commondir}\n' + + $ hg debugtemplate '{"foo/bar\nbar/baz"|splitlines|commondir}\n' + + $ hg debugtemplate '{"foo/bar\nbar/baz\nbar/foo\n"|splitlines|commondir}\n' + + $ hg debugtemplate '{"foo/../bar\nfoo/bar"|splitlines|commondir}\n' + foo + $ hg debugtemplate '{"foo\n/foo"|splitlines|commondir}\n' + + + $ hg log -r null -T '{rev|commondir}' + hg: parse error: argument is not a list of text + (template filter 'commondir' is not compatible with keyword 'rev') + [255] + +Add a dummy commit to make up for the instability of the above: + + $ echo a > a + $ hg add a + $ hg ci -m future + +Count filter: + + $ hg log -l1 --template '{node|count} {node|short|count}\n' + 40 12 + + $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n' + 0 1 4 + + $ hg log -G --template '{rev}: children: {children|count}, \ + > tags: {tags|count}, file_adds: {file_adds|count}, \ + > ancestors: {revset("ancestors(%s)", rev)|count}' + @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3 + | + o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2 + | + o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1 + + o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7 + |\ + | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5 + | | + o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5 + |/ + o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4 + | + o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3 + | + o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2 + | + o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1 + + + $ hg log -l1 -T '{termwidth|count}\n' + hg: parse error: not countable + (template filter 'count' is not compatible with keyword 'termwidth') + [255] + +Upper/lower filters: + + $ hg log -r0 --template '{branch|upper}\n' + DEFAULT + $ hg log -r0 --template '{author|lower}\n' + user name <user@hostname> + $ hg log -r0 --template '{date|upper}\n' + 1000000.00 + +Add a commit that does all possible modifications at once + + $ echo modify >> third + $ touch b + $ hg add b + $ hg mv fourth fifth + $ hg rm a + $ hg ci -m "Modify, add, remove, rename" + +Pass generator object created by template function to filter + + $ hg log -l 1 --template '{if(author, author)|user}\n' + test + +Test diff function: + + $ hg diff -c 8 + diff -r 29114dbae42b -r 95c24699272e fourth + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/fourth Wed Jan 01 10:01:00 2020 +0000 + @@ -0,0 +1,1 @@ + +second + diff -r 29114dbae42b -r 95c24699272e second + --- a/second Mon Jan 12 13:46:40 1970 +0000 + +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +0,0 @@ + -second + diff -r 29114dbae42b -r 95c24699272e third + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/third Wed Jan 01 10:01:00 2020 +0000 + @@ -0,0 +1,1 @@ + +third + + $ hg log -r 8 -T "{diff()}" + diff -r 29114dbae42b -r 95c24699272e fourth + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/fourth Wed Jan 01 10:01:00 2020 +0000 + @@ -0,0 +1,1 @@ + +second + diff -r 29114dbae42b -r 95c24699272e second + --- a/second Mon Jan 12 13:46:40 1970 +0000 + +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +0,0 @@ + -second + diff -r 29114dbae42b -r 95c24699272e third + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/third Wed Jan 01 10:01:00 2020 +0000 + @@ -0,0 +1,1 @@ + +third + + $ hg log -r 8 -T "{diff('glob:f*')}" + diff -r 29114dbae42b -r 95c24699272e fourth + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/fourth Wed Jan 01 10:01:00 2020 +0000 + @@ -0,0 +1,1 @@ + +second + + $ hg log -r 8 -T "{diff('', 'glob:f*')}" + diff -r 29114dbae42b -r 95c24699272e second + --- a/second Mon Jan 12 13:46:40 1970 +0000 + +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +0,0 @@ + -second + diff -r 29114dbae42b -r 95c24699272e third + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/third Wed Jan 01 10:01:00 2020 +0000 + @@ -0,0 +1,1 @@ + +third + + $ hg log -r 8 -T "{diff('FOURTH'|lower)}" + diff -r 29114dbae42b -r 95c24699272e fourth + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/fourth Wed Jan 01 10:01:00 2020 +0000 + @@ -0,0 +1,1 @@ + +second + + $ cd .. + +latesttag() function: + + $ hg init latesttag + $ cd latesttag + + $ echo a > file + $ hg ci -Am a -d '0 0' + adding file + + $ echo b >> file + $ hg ci -m b -d '1 0' + + $ echo c >> head1 + $ hg ci -Am h1c -d '2 0' + adding head1 + + $ hg update -q 1 + $ echo d >> head2 + $ hg ci -Am h2d -d '3 0' + adding head2 + created new head + + $ echo e >> head2 + $ hg ci -m h2e -d '4 0' + + $ hg merge -q + $ hg ci -m merge -d '5 -3600' + + $ hg tag -r 1 -m t1 -d '6 0' t1 + $ hg tag -r 2 -m t2 -d '7 0' t2 + $ hg tag -r 3 -m t3 -d '8 0' t3 + $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter + $ hg tag -r 5 -m t5 -d '9 0' t5 + $ hg tag -r 3 -m at3 -d '10 0' at3 + + $ 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 .. + +Test filter() empty values: + + $ hg log -R a -r 1 -T '{filter(desc|splitlines) % "{line}\n"}' + other 1 + other 2 + other 3 + $ hg log -R a -r 0 -T '{filter(dict(a=0, b=1) % "{ifeq(key, "a", "{value}\n")}")}' + 0 + + 0 should not be falsy + + $ hg log -R a -r 0 -T '{filter(revset("0:2"))}\n' + 0 1 2 + +Test filter() by expression: + + $ hg log -R a -r 1 -T '{filter(desc|splitlines, ifcontains("1", line, "t"))}\n' + other 1 + $ hg log -R a -r 0 -T '{filter(dict(a=0, b=1), ifeq(key, "b", "t"))}\n' + b=1 + +Test filter() shouldn't crash: + + $ hg log -R a -r 0 -T '{filter(extras)}\n' + branch=default + $ hg log -R a -r 0 -T '{filter(files)}\n' + a + +Test filter() unsupported arguments: + + $ hg log -R a -r 0 -T '{filter()}\n' + hg: parse error: filter expects one or two arguments + [255] + $ hg log -R a -r 0 -T '{filter(date)}\n' + hg: parse error: date is not iterable + [255] + $ hg log -R a -r 0 -T '{filter(rev)}\n' + hg: parse error: 0 is not iterable + [255] + $ hg log -R a -r 0 -T '{filter(desc|firstline)}\n' + hg: parse error: 'line 1' is not filterable + [255] + $ hg log -R a -r 0 -T '{filter(manifest)}\n' + hg: parse error: '0:a0c8bcbbb45c' is not filterable + [255] + $ hg log -R a -r 0 -T '{filter(succsandmarkers)}\n' + hg: parse error: not filterable without template + [255] + $ hg log -R a -r 0 -T '{filter(desc|splitlines % "{line}", "")}\n' + hg: parse error: not filterable by expression + [255] + +Test manifest/get() can be join()-ed as string, though it's silly: + + $ hg log -R latesttag -r tip -T '{join(manifest, ".")}\n' + 1.1.:.2.b.c.6.e.9.0.0.6.c.e.2 + $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), ".")}\n' + d.e.f.a.u.l.t + +Test join() over string + + $ hg log -R latesttag -r tip -T '{join(rev|stringify, ".")}\n' + 1.1 + +Test join() over uniterable + + $ hg log -R latesttag -r tip -T '{join(rev, "")}\n' + hg: parse error: 11 is not iterable + [255] + +Test min/max of integers + + $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n' + 9 + $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n' + 10 + +Test min/max over map operation: + + $ hg log -R latesttag -r3 -T '{min(tags % "{tag}")}\n' + at3 + $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n' + t3 + +Test min/max of strings: + + $ hg log -R latesttag -l1 -T '{min(desc)}\n' + 3 + $ hg log -R latesttag -l1 -T '{max(desc)}\n' + t + +Test min/max of non-iterable: + + $ hg debugtemplate '{min(1)}' + hg: parse error: 1 is not iterable + (min first argument should be an iterable) + [255] + $ hg debugtemplate '{max(2)}' + hg: parse error: 2 is not iterable + (max first argument should be an iterable) + [255] + + $ hg log -R latesttag -l1 -T '{min(date)}' + hg: parse error: date is not iterable + (min first argument should be an iterable) + [255] + $ hg log -R latesttag -l1 -T '{max(date)}' + hg: parse error: date is not iterable + (max first argument should be an iterable) + [255] + +Test min/max of empty sequence: + + $ hg debugtemplate '{min("")}' + hg: parse error: empty string + (min first argument should be an iterable) + [255] + $ hg debugtemplate '{max("")}' + hg: parse error: empty string + (max first argument should be an iterable) + [255] + $ hg debugtemplate '{min(dict())}' + hg: parse error: empty sequence + (min first argument should be an iterable) + [255] + $ hg debugtemplate '{max(dict())}' + hg: parse error: empty sequence + (max first argument should be an iterable) + [255] + $ hg debugtemplate '{min(dict() % "")}' + hg: parse error: empty sequence + (min first argument should be an iterable) + [255] + $ hg debugtemplate '{max(dict() % "")}' + hg: parse error: empty sequence + (max first argument should be an iterable) + [255] + +Test min/max of if() result + + $ cd latesttag + $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n' + 9 + $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n' + 10 + $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n' + 9 + $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n' + 10 + $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n' + 9 + $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n' + 10 + $ cd .. + +Test laziness of if() then/else clause + + $ hg debugtemplate '{count(0)}' + hg: parse error: not countable + (incompatible use of template filter 'count') + [255] + $ hg debugtemplate '{if(true, "", count(0))}' + $ hg debugtemplate '{if(false, count(0), "")}' + $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}' + $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}' + $ hg debugtemplate '{ifeq(0, 0, "", count(0))}' + $ hg debugtemplate '{ifeq(0, 1, count(0), "")}' + +Test the sub function of templating for expansion: + + $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n' + xx + + $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n' + hg: parse error: sub got an invalid pattern: [ + [255] + $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n' + hg: parse error: sub got an invalid replacement: \1 + [255] + +Test the strip function with chars specified: + + $ hg log -R latesttag --template '{desc}\n' + at3 + t5 + t4 + t3 + t2 + t1 + merge + h2e + h2d + h1c + b + a + + $ hg log -R latesttag --template '{strip(desc, "te")}\n' + at3 + 5 + 4 + 3 + 2 + 1 + merg + h2 + h2d + h1c + b + a + +Test date format: + + $ 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 + date: 70 01 01 05 +0100 + date: 70 01 01 04 +0000 + date: 70 01 01 03 +0000 + date: 70 01 01 02 +0000 + date: 70 01 01 01 +0000 + date: 70 01 01 00 +0000 + +Test invalid date: + + $ hg log -R latesttag -T '{date(rev)}\n' + hg: parse error: date expects a date information + [255] + +Set up repository containing template fragments in commit metadata: + + $ hg init r + $ cd r + $ echo a > a + $ hg ci -Am '{rev}' + adding a + + $ hg branch -q 'text.{rev}' + $ echo aa >> aa + $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped' + +color effect can be specified without quoting: + + $ hg log --color=always -l 1 --template '{label(red, "text\n")}' + \x1b[0;31mtext\x1b[0m (esc) + +color effects can be nested (issue5413) + + $ hg debugtemplate --color=always \ + > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n' + \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc) + +pad() should interact well with color codes (issue5416) + + $ hg debugtemplate --color=always \ + > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n' + \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc) + +label should be no-op if color is disabled: + + $ hg log --color=never -l 1 --template '{label(red, "text\n")}' + text + $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}' + text + +Test branches inside if statement: + + $ hg log -r 0 --template '{if(branches, "yes", "no")}\n' + no + +Test dict constructor: + + $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n' + y=f7769ec2ab97 x=0 + $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}' + x=0 + y=f7769ec2ab97 + $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n' + {"x": 0, "y": "f7769ec2ab97"} + $ hg log -r 0 -T '{dict()|json}\n' + {} + + $ hg log -r 0 -T '{dict(rev, node=node|short)}\n' + rev=0 node=f7769ec2ab97 + $ hg log -r 0 -T '{dict(rev, node|short)}\n' + rev=0 node=f7769ec2ab97 + + $ hg log -r 0 -T '{dict(rev, rev=rev)}\n' + hg: parse error: duplicated dict key 'rev' inferred + [255] + $ hg log -r 0 -T '{dict(node, node|short)}\n' + hg: parse error: duplicated dict key 'node' inferred + [255] + $ hg log -r 0 -T '{dict(1 + 2)}' + hg: parse error: dict key cannot be inferred + [255] + + $ hg log -r 0 -T '{dict(x=rev, x=node)}' + hg: parse error: dict got multiple values for keyword argument 'x' + [255] + +Test get function: + + $ hg log -r 0 --template '{get(extras, "branch")}\n' + default + $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n' + default + $ hg log -r 0 --template '{get(files, "should_fail")}\n' + hg: parse error: not a dictionary + (get() expects a dict as first argument) + [255] + +Test json filter applied to wrapped object: + + $ hg log -r0 -T '{files|json}\n' + ["a"] + $ hg log -r0 -T '{extras|json}\n' + {"branch": "default"} + $ hg log -r0 -T '{date|json}\n' + [0, 0] + +Test json filter applied to map result: + + $ hg log -r0 -T '{json(extras % "{key}")}\n' + ["branch"] + +Test localdate(date, tz) function: + + $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n' + 1970-01-01 09:00 +0900 + $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n' + 1970-01-01 00:00 +0000 + $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n' + hg: parse error: localdate expects a timezone + [255] + $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n' + 1970-01-01 02:00 +0200 + $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n' + 1970-01-01 00:00 +0000 + $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n' + 1970-01-01 00:00 +0000 + $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n' + hg: parse error: localdate expects a timezone + [255] + $ hg log -r0 -T '{localdate(date, date)|isodate}\n' + hg: parse error: localdate expects a timezone + [255] + +Test shortest(node) function: + + $ echo b > b + $ hg ci -qAm b + $ hg log --template '{shortest(node)}\n' + e777 + bcc7 + f776 + $ hg log --template '{shortest(node, 10)}\n' + e777603221 + bcc7ff960b + f7769ec2ab + $ hg log --template '{node|shortest}\n' -l1 + e777 + + $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n' + f7769ec2ab + $ hg log -r 0 -T '{shortest(node, "not an int")}\n' + hg: parse error: shortest() expects an integer minlength + [255] + + $ hg log -r 'wdir()' -T '{node|shortest}\n' + ffff + + $ hg log --template '{shortest("f")}\n' -l1 + f + + $ hg log --template '{shortest("0123456789012345678901234567890123456789")}\n' -l1 + 0123456789012345678901234567890123456789 + + $ hg log --template '{shortest("01234567890123456789012345678901234567890123456789")}\n' -l1 + 01234567890123456789012345678901234567890123456789 + + $ hg log --template '{shortest("not a hex string")}\n' -l1 + not a hex string + + $ hg log --template '{shortest("not a hex string, but it'\''s 40 bytes long")}\n' -l1 + not a hex string, but it's 40 bytes long + + $ hg log --template '{shortest("ffffffffffffffffffffffffffffffffffffffff")}\n' -l1 + ffff + + $ hg log --template '{shortest("fffffff")}\n' -l1 + ffff + + $ hg log --template '{shortest("ff")}\n' -l1 + ffff + + $ cd .. + +Test shortest(node) with the repo having short hash collision: + + $ hg init hashcollision + $ cd hashcollision + $ cat <<EOF >> .hg/hgrc + > [experimental] + > evolution.createmarkers=True + > EOF + $ echo 0 > a + $ hg ci -qAm 0 + $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do + > hg up -q 0 + > echo $i > a + > hg ci -qm $i + > done + $ hg up -q null + $ hg log -r0: -T '{rev}:{node}\n' + 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a + 1:11424df6dc1dd4ea255eae2b58eaca7831973bbc + 2:11407b3f1b9c3e76a79c1ec5373924df096f0499 + 3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4 + 4:10776689e627b465361ad5c296a20a487e153ca4 + 5:a00be79088084cb3aff086ab799f8790e01a976b + 6:a0b0acd79b4498d0052993d35a6a748dd51d13e6 + 7:a0457b3450b8e1b778f1163b31a435802987fe5d + 8:c56256a09cd28e5764f32e8e2810d0f01e2e357a + 9:c5623987d205cd6d9d8389bfc40fff9dbb670b48 + 10:c562ddd9c94164376c20b86b0b4991636a3bf84f + $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b + obsoleted 1 changesets + $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48 + obsoleted 1 changesets + $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f + obsoleted 1 changesets + + nodes starting with '11' (we don't have the revision number '11' though) + + $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n' + 1:1142 + 2:1140 + 3:11d + + '5:a00' is hidden, but still we have two nodes starting with 'a0' + + $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n' + 6:a0b + 7:a04 + + node '10' conflicts with the revision number '10' even if it is hidden + (we could exclude hidden revision numbers, but currently we don't) + + $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' + 4:107 + $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden + 4:107 + + node 'c562' should be unique if the other 'c562' nodes are hidden + (but we don't try the slow path to filter out hidden nodes for now) + + $ hg log -r 8 -T '{rev}:{node|shortest}\n' + 8:c5625 + $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden + 8:c5625 + 9:c5623 + 10:c562d + + $ cd .. + +Test pad function + + $ cd r + + $ hg log --template '{pad(rev, 20)} {author|user}\n' + 2 test + 1 {node|short} + 0 test + + $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n' + 2 test + 1 {node|short} + 0 test + + $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n' + 2------------------- test + 1------------------- {node|short} + 0------------------- test + +Test template string in pad function + + $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n' + {0} test + + $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n' + \{rev} test + +Test width argument passed to pad function + + $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n' + 0 test + $ hg log -r 0 -T '{pad(rev, "not an int")}\n' + hg: parse error: pad() expects an integer width + [255] + +Test invalid fillchar passed to pad function + + $ hg log -r 0 -T '{pad(rev, 10, "")}\n' + hg: parse error: pad() expects a single fill character + [255] + $ hg log -r 0 -T '{pad(rev, 10, "--")}\n' + hg: parse error: pad() expects a single fill character + [255] + +Test boolean argument passed to pad function + + no crash + + $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n' + ---------0 + + string/literal + + $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n' + ---------0 + $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n' + 0--------- + $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n' + 0--------- + + unknown keyword is evaluated to '' + + $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n' + 0--------- + +Test separate function + + $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n' + a-b-c + $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n' + 0:f7769ec2ab97 test default + $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n' + a \x1b[0;31mb\x1b[0m c d (esc) + +Test boolean expression/literal passed to if function + + $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n' + rev 0 is True + $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n' + literal 0 is True as well + $ hg log -r 0 -T '{if(min(revset(r"0")), "0 of hybriditem is also True")}\n' + 0 of hybriditem is also True + $ hg log -r 0 -T '{if("", "", "empty string is False")}\n' + empty string is False + $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n' + empty list is False + $ hg log -r 0 -T '{if(revset(r"0"), "non-empty list is True")}\n' + non-empty list is True + $ hg log -r 0 -T '{if(revset(r"0") % "", "list of empty strings is True")}\n' + list of empty strings is True + $ hg log -r 0 -T '{if(true, "true is True")}\n' + true is True + $ hg log -r 0 -T '{if(false, "", "false is False")}\n' + false is False + $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n' + non-empty string is True + +Test ifcontains function + + $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n' + 2 is in the string + 1 is not + 0 is in the string + + $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n' + 2 is in the string + 1 is not + 0 is in the string + + $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n' + 2 did not add a + 1 did not add a + 0 added a + + $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n' + 2 is parent of 1 + 1 + 0 + + $ hg log -l1 -T '{ifcontains("branch", extras, "t", "f")}\n' + t + $ hg log -l1 -T '{ifcontains("branch", extras % "{key}", "t", "f")}\n' + t + $ hg log -l1 -T '{ifcontains("branc", extras % "{key}", "t", "f")}\n' + f + $ hg log -l1 -T '{ifcontains("branc", stringify(extras % "{key}"), "t", "f")}\n' + t + +Test revset function + + $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n' + 2 current rev + 1 not current rev + 0 not current rev + + $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n' + 2 match rev + 1 match rev + 0 not match rev + + $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1 + type not match + + $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n' + 2 Parents: 1 + 1 Parents: 0 + 0 Parents: + + $ cat >> .hg/hgrc <<EOF + > [revsetalias] + > myparents(\$1) = parents(\$1) + > EOF + $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n' + 2 Parents: 1 + 1 Parents: 0 + 0 Parents: + + $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n' + Rev: 2 + Ancestor: 0 + Ancestor: 1 + Ancestor: 2 + + Rev: 1 + Ancestor: 0 + Ancestor: 1 + + Rev: 0 + Ancestor: 0 + + $ hg log --template '{revset("TIP"|lower)}\n' -l1 + 2 + + $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1 + 2 + + a list template is evaluated for each item of revset/parents + + $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n' + 2 p: 1:bcc7ff960b8e + 1 p: 0:f7769ec2ab97 + 0 p: + + $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n' + 2 p: 1:bcc7ff960b8e -1:000000000000 + 1 p: 0:f7769ec2ab97 -1:000000000000 + 0 p: -1:000000000000 -1:000000000000 + + therefore, 'revcache' should be recreated for each rev + + $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n' + 2 aa b + p + 1 + p a + 0 a + p + + $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n' + 2 aa b + p + 1 + p a + 0 a + p + +a revset item must be evaluated as an integer revision, not an offset from tip + + $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n' + -1:000000000000 + $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n' + -1:000000000000 + +join() should pick '{rev}' from revset items: + + $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6 + 4, 5 + +on the other hand, parents are formatted as '{rev}:{node|formatnode}' by +default. join() should agree with the default formatting: + + $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6 + 5:13207e5a10d9, 4:bbe44766e73d + + $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug + 5:13207e5a10d9fd28ec424934298e176197f2c67f, + 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74 + +Invalid arguments passed to revset() + + $ hg log -T '{revset("%whatever", 0)}\n' + hg: parse error: unexpected revspec format character w + [255] + $ hg log -T '{revset("%lwhatever", files)}\n' + hg: parse error: unexpected revspec format character w + [255] + $ hg log -T '{revset("%s %s", 0)}\n' + hg: parse error: missing argument for revspec + [255] + $ hg log -T '{revset("", 0)}\n' + hg: parse error: too many revspec arguments specified + [255] + $ hg log -T '{revset("%s", 0, 1)}\n' + hg: parse error: too many revspec arguments specified + [255] + $ hg log -T '{revset("%", 0)}\n' + hg: parse error: incomplete revspec format character + [255] + $ hg log -T '{revset("%l", 0)}\n' + hg: parse error: incomplete revspec format character + [255] + $ hg log -T '{revset("%d", 'foo')}\n' + hg: parse error: invalid argument for revspec + [255] + $ hg log -T '{revset("%ld", files)}\n' + hg: parse error: invalid argument for revspec + [255] + $ hg log -T '{revset("%ls", 0)}\n' + hg: parse error: invalid argument for revspec + [255] + $ hg log -T '{revset("%b", 'foo')}\n' + hg: parse error: invalid argument for revspec + [255] + $ hg log -T '{revset("%lb", files)}\n' + hg: parse error: invalid argument for revspec + [255] + $ hg log -T '{revset("%r", 0)}\n' + hg: parse error: invalid argument for revspec + [255] + +Test files function + + $ hg log -T "{rev}\n{join(files('*'), '\n')}\n" + 2 + a + aa + b + 1 + a + 0 + a + + $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n" + 2 + aa + 1 + + 0 + + $ hg rm a + $ hg log -r "wdir()" -T "{rev}\n{join(files('*'), '\n')}\n" + 2147483647 + aa + b + $ hg revert a + +Test relpath function + + $ hg log -r0 -T '{files % "{file|relpath}\n"}' + a + $ cd .. + $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}' + r/a + +Test stringify on sub expressions + + $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n' + fourth, second, third + $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n' + abc + +Test splitlines + + $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}" + @ foo Modify, add, remove, rename + | + o foo future + | + o foo third + | + o foo second + + o foo merge + |\ + | o foo new head + | | + o | foo new branch + |/ + o foo no user, no domain + | + o foo no person + | + o foo other 1 + | foo other 2 + | foo + | foo other 3 + o foo line 1 + foo line 2 + + $ hg log -R a -r0 -T '{desc|splitlines}\n' + line 1 line 2 + $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n' + line 1|line 2 + +Test startswith + $ hg log -Gv -R a --template "{startswith(desc)}" + hg: parse error: startswith expects two arguments + [255] + + $ hg log -Gv -R a --template "{startswith('line', desc)}" + @ + | + o + | + o + | + o + + o + |\ + | o + | | + o | + |/ + o + | + o + | + o + | + o line 1 + line 2 + +Test word function (including index out of bounds graceful failure) + + $ hg log -Gv -R a --template "{word('1', desc)}" + @ add, + | + o + | + o + | + o + + o + |\ + | o head + | | + o | branch + |/ + o user, + | + o person + | + o 1 + | + o 1 + + +Test word third parameter used as splitter + + $ hg log -Gv -R a --template "{word('0', desc, 'o')}" + @ M + | + o future + | + o third + | + o sec + + o merge + |\ + | o new head + | | + o | new branch + |/ + o n + | + o n + | + o + | + o line 1 + line 2 + +Test word error messages for not enough and too many arguments + + $ hg log -Gv -R a --template "{word('0')}" + hg: parse error: word expects two or three arguments, got 1 + [255] + + $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}" + hg: parse error: word expects two or three arguments, got 7 + [255] + +Test word for integer literal + + $ hg log -R a --template "{word(2, desc)}\n" -r0 + line + +Test word for invalid numbers + + $ hg log -Gv -R a --template "{word('a', desc)}" + hg: parse error: word expects an integer index + [255] + +Test word for out of range + + $ hg log -R a --template "{word(10000, desc)}" + $ hg log -R a --template "{word(-10000, desc)}" + +Test indent and not adding to empty lines + + $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a + ----- + > line 1 + >> line 2 + ----- + > other 1 + >> other 2 + + >> other 3 + +Test with non-strings like dates + + $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a + 1200000.00 + 1300000.00 + +json filter should escape HTML tags so that the output can be embedded in hgweb: + + $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1 + "\u003cfoo@example.org\u003e" + +Set up repository for non-ascii encoding tests: + + $ hg init nonascii + $ cd nonascii + $ $PYTHON <<EOF + > open('latin1', 'wb').write(b'\xe9') + > open('utf-8', 'wb').write(b'\xc3\xa9') + > EOF + $ HGENCODING=utf-8 hg branch -q `cat utf-8` + $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8 + +json filter should try round-trip conversion to utf-8: + + $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0 + "\u00e9" + $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0 + "non-ascii branch: \u00e9" + +json filter should take input as utf-8 if it was converted from utf-8: + + $ HGENCODING=latin-1 hg log -T "{branch|json}\n" -r0 + "\u00e9" + $ HGENCODING=latin-1 hg log -T "{desc|json}\n" -r0 + "non-ascii branch: \u00e9" + +json filter takes input as utf-8b: + + $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1 + "\u00e9" + $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1 + "\udce9" + +utf8 filter: + + $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0 + round-trip: c3a9 + $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1 + decoded: c3a9 + $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1 + abort: decoding near * (glob) + [255] + $ hg log -T "coerced to string: {rev|utf8}\n" -r0 + coerced to string: 0 + +pad width: + + $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n" + \xc3\xa9- (esc) + + $ cd ..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-template-keywords.t Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,1195 @@ +Test template keywords +====================== + + $ hg init a + $ cd a + $ echo a > a + $ hg add a + $ echo line 1 > b + $ echo line 2 >> b + $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>' + + $ hg add b + $ echo other 1 > c + $ echo other 2 >> c + $ echo >> c + $ echo other 3 >> c + $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>' + + $ hg add c + $ hg commit -m 'no person' -d '1200000 0' -u 'other@place' + $ echo c >> c + $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person' + + $ echo foo > .hg/branch + $ hg commit -m 'new branch' -d '1400000 0' -u 'person' + + $ hg co -q 3 + $ echo other 4 >> d + $ hg add d + $ hg commit -m 'new head' -d '1500000 0' -u 'person' + + $ hg merge -q foo + $ hg commit -m 'merge' -d '1500001 0' -u 'person' + +Second branch starting at nullrev: + + $ hg update null + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + $ echo second > second + $ hg add second + $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>' + created new head + + $ echo third > third + $ hg add third + $ hg mv second fourth + $ hg commit -m third -d "2020-01-01 10:01" + +Working-directory revision has special identifiers, though they are still +experimental: + + $ hg log -r 'wdir()' -T '{rev}:{node}\n' + 2147483647:ffffffffffffffffffffffffffffffffffffffff + +Some keywords are invalid for working-directory revision, but they should +never cause crash: + + $ hg log -r 'wdir()' -T '{manifest}\n' + + +Check that {phase} works correctly on parents: + + $ cat << EOF > parentphase + > changeset_debug = '{rev} ({phase}):{parents}\n' + > parent = ' {rev} ({phase})' + > EOF + $ hg phase -r 5 --public + $ hg phase -r 7 --secret --force + $ hg log --debug -G --style ./parentphase + @ 8 (secret): 7 (secret) -1 (public) + | + o 7 (secret): -1 (public) -1 (public) + + o 6 (draft): 5 (public) 4 (draft) + |\ + | o 5 (public): 3 (public) -1 (public) + | | + o | 4 (draft): 3 (public) -1 (public) + |/ + o 3 (public): 2 (public) -1 (public) + | + o 2 (public): 1 (public) -1 (public) + | + o 1 (public): 0 (public) -1 (public) + | + o 0 (public): -1 (public) -1 (public) + + +Keys work: + + $ for key in author branch branches date desc file_adds file_dels file_mods \ + > file_copies file_copies_switch files \ + > manifest node parents rev tags diffstat extras \ + > p1rev p2rev p1node p2node; do + > for mode in '' --verbose --debug; do + > hg log $mode --template "$key$mode: {$key}\n" + > done + > done + author: test + author: User Name <user@hostname> + author: person + author: person + author: person + author: person + author: other@place + author: A. N. Other <other@place> + author: User Name <user@hostname> + author--verbose: test + author--verbose: User Name <user@hostname> + author--verbose: person + author--verbose: person + author--verbose: person + author--verbose: person + author--verbose: other@place + author--verbose: A. N. Other <other@place> + author--verbose: User Name <user@hostname> + author--debug: test + author--debug: User Name <user@hostname> + author--debug: person + author--debug: person + author--debug: person + author--debug: person + author--debug: other@place + author--debug: A. N. Other <other@place> + author--debug: User Name <user@hostname> + branch: default + branch: default + branch: default + branch: default + branch: foo + branch: default + branch: default + branch: default + branch: default + branch--verbose: default + branch--verbose: default + branch--verbose: default + branch--verbose: default + branch--verbose: foo + branch--verbose: default + branch--verbose: default + branch--verbose: default + branch--verbose: default + branch--debug: default + branch--debug: default + branch--debug: default + branch--debug: default + branch--debug: foo + branch--debug: default + branch--debug: default + branch--debug: default + branch--debug: default + branches: + branches: + branches: + branches: + branches: foo + branches: + branches: + branches: + branches: + branches--verbose: + branches--verbose: + branches--verbose: + branches--verbose: + branches--verbose: foo + branches--verbose: + branches--verbose: + branches--verbose: + branches--verbose: + branches--debug: + branches--debug: + branches--debug: + branches--debug: + branches--debug: foo + branches--debug: + branches--debug: + branches--debug: + branches--debug: + date: 1577872860.00 + date: 1000000.00 + date: 1500001.00 + date: 1500000.00 + date: 1400000.00 + date: 1300000.00 + date: 1200000.00 + date: 1100000.00 + date: 1000000.00 + date--verbose: 1577872860.00 + date--verbose: 1000000.00 + date--verbose: 1500001.00 + date--verbose: 1500000.00 + date--verbose: 1400000.00 + date--verbose: 1300000.00 + date--verbose: 1200000.00 + date--verbose: 1100000.00 + date--verbose: 1000000.00 + date--debug: 1577872860.00 + date--debug: 1000000.00 + date--debug: 1500001.00 + date--debug: 1500000.00 + date--debug: 1400000.00 + date--debug: 1300000.00 + date--debug: 1200000.00 + date--debug: 1100000.00 + date--debug: 1000000.00 + desc: third + desc: second + desc: merge + desc: new head + desc: new branch + desc: no user, no domain + desc: no person + desc: other 1 + other 2 + + other 3 + desc: line 1 + line 2 + desc--verbose: third + desc--verbose: second + desc--verbose: merge + desc--verbose: new head + desc--verbose: new branch + desc--verbose: no user, no domain + desc--verbose: no person + desc--verbose: other 1 + other 2 + + other 3 + desc--verbose: line 1 + line 2 + desc--debug: third + desc--debug: second + desc--debug: merge + desc--debug: new head + desc--debug: new branch + desc--debug: no user, no domain + desc--debug: no person + desc--debug: other 1 + other 2 + + other 3 + desc--debug: line 1 + line 2 + file_adds: fourth third + file_adds: second + file_adds: + file_adds: d + file_adds: + file_adds: + file_adds: c + file_adds: b + file_adds: a + file_adds--verbose: fourth third + file_adds--verbose: second + file_adds--verbose: + file_adds--verbose: d + file_adds--verbose: + file_adds--verbose: + file_adds--verbose: c + file_adds--verbose: b + file_adds--verbose: a + file_adds--debug: fourth third + file_adds--debug: second + file_adds--debug: + file_adds--debug: d + file_adds--debug: + file_adds--debug: + file_adds--debug: c + file_adds--debug: b + file_adds--debug: a + file_dels: second + file_dels: + file_dels: + file_dels: + file_dels: + file_dels: + file_dels: + file_dels: + file_dels: + file_dels--verbose: second + file_dels--verbose: + file_dels--verbose: + file_dels--verbose: + file_dels--verbose: + file_dels--verbose: + file_dels--verbose: + file_dels--verbose: + file_dels--verbose: + file_dels--debug: second + file_dels--debug: + file_dels--debug: + file_dels--debug: + file_dels--debug: + file_dels--debug: + file_dels--debug: + file_dels--debug: + file_dels--debug: + file_mods: + file_mods: + file_mods: + file_mods: + file_mods: + file_mods: c + file_mods: + file_mods: + file_mods: + file_mods--verbose: + file_mods--verbose: + file_mods--verbose: + file_mods--verbose: + file_mods--verbose: + file_mods--verbose: c + file_mods--verbose: + file_mods--verbose: + file_mods--verbose: + file_mods--debug: + file_mods--debug: + file_mods--debug: + file_mods--debug: + file_mods--debug: + file_mods--debug: c + file_mods--debug: + file_mods--debug: + file_mods--debug: + file_copies: fourth (second) + file_copies: + file_copies: + file_copies: + file_copies: + file_copies: + file_copies: + file_copies: + file_copies: + file_copies--verbose: fourth (second) + file_copies--verbose: + file_copies--verbose: + file_copies--verbose: + file_copies--verbose: + file_copies--verbose: + file_copies--verbose: + file_copies--verbose: + file_copies--verbose: + file_copies--debug: fourth (second) + file_copies--debug: + file_copies--debug: + file_copies--debug: + file_copies--debug: + file_copies--debug: + file_copies--debug: + file_copies--debug: + file_copies--debug: + file_copies_switch: + file_copies_switch: + file_copies_switch: + file_copies_switch: + file_copies_switch: + file_copies_switch: + file_copies_switch: + file_copies_switch: + file_copies_switch: + file_copies_switch--verbose: + file_copies_switch--verbose: + file_copies_switch--verbose: + file_copies_switch--verbose: + file_copies_switch--verbose: + file_copies_switch--verbose: + file_copies_switch--verbose: + file_copies_switch--verbose: + file_copies_switch--verbose: + file_copies_switch--debug: + file_copies_switch--debug: + file_copies_switch--debug: + file_copies_switch--debug: + file_copies_switch--debug: + file_copies_switch--debug: + file_copies_switch--debug: + file_copies_switch--debug: + file_copies_switch--debug: + files: fourth second third + files: second + files: + files: d + files: + files: c + files: c + files: b + files: a + files--verbose: fourth second third + files--verbose: second + files--verbose: + files--verbose: d + files--verbose: + files--verbose: c + files--verbose: c + files--verbose: b + files--verbose: a + files--debug: fourth second third + files--debug: second + files--debug: + files--debug: d + files--debug: + files--debug: c + files--debug: c + files--debug: b + files--debug: a + manifest: 6:94961b75a2da + manifest: 5:f2dbc354b94e + manifest: 4:4dc3def4f9b4 + manifest: 4:4dc3def4f9b4 + manifest: 3:cb5a1327723b + manifest: 3:cb5a1327723b + manifest: 2:6e0e82995c35 + manifest: 1:4e8d705b1e53 + manifest: 0:a0c8bcbbb45c + manifest--verbose: 6:94961b75a2da + manifest--verbose: 5:f2dbc354b94e + manifest--verbose: 4:4dc3def4f9b4 + manifest--verbose: 4:4dc3def4f9b4 + manifest--verbose: 3:cb5a1327723b + manifest--verbose: 3:cb5a1327723b + manifest--verbose: 2:6e0e82995c35 + manifest--verbose: 1:4e8d705b1e53 + manifest--verbose: 0:a0c8bcbbb45c + manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64 + manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf + manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216 + manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216 + manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc + manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc + manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1 + manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55 + manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 + node: 95c24699272ef57d062b8bccc32c878bf841784a + node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 + node: d41e714fe50d9e4a5f11b4d595d543481b5f980b + node: 13207e5a10d9fd28ec424934298e176197f2c67f + node: bbe44766e73d5f11ed2177f1838de10c53ef3e74 + node: 10e46f2dcbf4823578cf180f33ecf0b957964c47 + node: 97054abb4ab824450e9164180baf491ae0078465 + node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 + node: 1e4e1b8f71e05681d422154f5421e385fec3454f + node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a + node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 + node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b + node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f + node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74 + node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47 + node--verbose: 97054abb4ab824450e9164180baf491ae0078465 + node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 + node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f + node--debug: 95c24699272ef57d062b8bccc32c878bf841784a + node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 + node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b + node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f + node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74 + node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47 + node--debug: 97054abb4ab824450e9164180baf491ae0078465 + node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 + node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f + parents: + parents: -1:000000000000 + parents: 5:13207e5a10d9 4:bbe44766e73d + parents: 3:10e46f2dcbf4 + parents: + parents: + parents: + parents: + parents: + parents--verbose: + parents--verbose: -1:000000000000 + parents--verbose: 5:13207e5a10d9 4:bbe44766e73d + parents--verbose: 3:10e46f2dcbf4 + parents--verbose: + parents--verbose: + parents--verbose: + parents--verbose: + parents--verbose: + parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000 + parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000 + parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74 + parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000 + parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000 + parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000 + parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000 + parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000 + parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000 + rev: 8 + rev: 7 + rev: 6 + rev: 5 + rev: 4 + rev: 3 + rev: 2 + rev: 1 + rev: 0 + rev--verbose: 8 + rev--verbose: 7 + rev--verbose: 6 + rev--verbose: 5 + rev--verbose: 4 + rev--verbose: 3 + rev--verbose: 2 + rev--verbose: 1 + rev--verbose: 0 + rev--debug: 8 + rev--debug: 7 + rev--debug: 6 + rev--debug: 5 + rev--debug: 4 + rev--debug: 3 + rev--debug: 2 + rev--debug: 1 + rev--debug: 0 + tags: tip + tags: + tags: + tags: + tags: + tags: + tags: + tags: + tags: + tags--verbose: tip + tags--verbose: + tags--verbose: + tags--verbose: + tags--verbose: + tags--verbose: + tags--verbose: + tags--verbose: + tags--verbose: + tags--debug: tip + tags--debug: + tags--debug: + tags--debug: + tags--debug: + tags--debug: + tags--debug: + tags--debug: + tags--debug: + diffstat: 3: +2/-1 + diffstat: 1: +1/-0 + diffstat: 0: +0/-0 + diffstat: 1: +1/-0 + diffstat: 0: +0/-0 + diffstat: 1: +1/-0 + diffstat: 1: +4/-0 + diffstat: 1: +2/-0 + diffstat: 1: +1/-0 + diffstat--verbose: 3: +2/-1 + diffstat--verbose: 1: +1/-0 + diffstat--verbose: 0: +0/-0 + diffstat--verbose: 1: +1/-0 + diffstat--verbose: 0: +0/-0 + diffstat--verbose: 1: +1/-0 + diffstat--verbose: 1: +4/-0 + diffstat--verbose: 1: +2/-0 + diffstat--verbose: 1: +1/-0 + diffstat--debug: 3: +2/-1 + diffstat--debug: 1: +1/-0 + diffstat--debug: 0: +0/-0 + diffstat--debug: 1: +1/-0 + diffstat--debug: 0: +0/-0 + diffstat--debug: 1: +1/-0 + diffstat--debug: 1: +4/-0 + diffstat--debug: 1: +2/-0 + diffstat--debug: 1: +1/-0 + extras: branch=default + extras: branch=default + extras: branch=default + extras: branch=default + extras: branch=foo + extras: branch=default + extras: branch=default + extras: branch=default + extras: branch=default + extras--verbose: branch=default + extras--verbose: branch=default + extras--verbose: branch=default + extras--verbose: branch=default + extras--verbose: branch=foo + extras--verbose: branch=default + extras--verbose: branch=default + extras--verbose: branch=default + extras--verbose: branch=default + extras--debug: branch=default + extras--debug: branch=default + extras--debug: branch=default + extras--debug: branch=default + extras--debug: branch=foo + extras--debug: branch=default + extras--debug: branch=default + extras--debug: branch=default + extras--debug: branch=default + p1rev: 7 + p1rev: -1 + p1rev: 5 + p1rev: 3 + p1rev: 3 + p1rev: 2 + p1rev: 1 + p1rev: 0 + p1rev: -1 + p1rev--verbose: 7 + p1rev--verbose: -1 + p1rev--verbose: 5 + p1rev--verbose: 3 + p1rev--verbose: 3 + p1rev--verbose: 2 + p1rev--verbose: 1 + p1rev--verbose: 0 + p1rev--verbose: -1 + p1rev--debug: 7 + p1rev--debug: -1 + p1rev--debug: 5 + p1rev--debug: 3 + p1rev--debug: 3 + p1rev--debug: 2 + p1rev--debug: 1 + p1rev--debug: 0 + p1rev--debug: -1 + p2rev: -1 + p2rev: -1 + p2rev: 4 + p2rev: -1 + p2rev: -1 + p2rev: -1 + p2rev: -1 + p2rev: -1 + p2rev: -1 + p2rev--verbose: -1 + p2rev--verbose: -1 + p2rev--verbose: 4 + p2rev--verbose: -1 + p2rev--verbose: -1 + p2rev--verbose: -1 + p2rev--verbose: -1 + p2rev--verbose: -1 + p2rev--verbose: -1 + p2rev--debug: -1 + p2rev--debug: -1 + p2rev--debug: 4 + p2rev--debug: -1 + p2rev--debug: -1 + p2rev--debug: -1 + p2rev--debug: -1 + p2rev--debug: -1 + p2rev--debug: -1 + p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 + p1node: 0000000000000000000000000000000000000000 + p1node: 13207e5a10d9fd28ec424934298e176197f2c67f + p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47 + p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47 + p1node: 97054abb4ab824450e9164180baf491ae0078465 + p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 + p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f + p1node: 0000000000000000000000000000000000000000 + p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 + p1node--verbose: 0000000000000000000000000000000000000000 + p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f + p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47 + p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47 + p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465 + p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 + p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f + p1node--verbose: 0000000000000000000000000000000000000000 + p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453 + p1node--debug: 0000000000000000000000000000000000000000 + p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f + p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47 + p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47 + p1node--debug: 97054abb4ab824450e9164180baf491ae0078465 + p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965 + p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f + p1node--debug: 0000000000000000000000000000000000000000 + p2node: 0000000000000000000000000000000000000000 + p2node: 0000000000000000000000000000000000000000 + p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74 + p2node: 0000000000000000000000000000000000000000 + p2node: 0000000000000000000000000000000000000000 + p2node: 0000000000000000000000000000000000000000 + p2node: 0000000000000000000000000000000000000000 + p2node: 0000000000000000000000000000000000000000 + p2node: 0000000000000000000000000000000000000000 + p2node--verbose: 0000000000000000000000000000000000000000 + p2node--verbose: 0000000000000000000000000000000000000000 + p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74 + p2node--verbose: 0000000000000000000000000000000000000000 + p2node--verbose: 0000000000000000000000000000000000000000 + p2node--verbose: 0000000000000000000000000000000000000000 + p2node--verbose: 0000000000000000000000000000000000000000 + p2node--verbose: 0000000000000000000000000000000000000000 + p2node--verbose: 0000000000000000000000000000000000000000 + p2node--debug: 0000000000000000000000000000000000000000 + p2node--debug: 0000000000000000000000000000000000000000 + p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74 + p2node--debug: 0000000000000000000000000000000000000000 + p2node--debug: 0000000000000000000000000000000000000000 + p2node--debug: 0000000000000000000000000000000000000000 + p2node--debug: 0000000000000000000000000000000000000000 + p2node--debug: 0000000000000000000000000000000000000000 + p2node--debug: 0000000000000000000000000000000000000000 + +Add a dummy commit to make up for the instability of the above: + + $ echo a > a + $ hg add a + $ hg ci -m future + +Add a commit that does all possible modifications at once + + $ echo modify >> third + $ touch b + $ hg add b + $ hg mv fourth fifth + $ hg rm a + $ hg ci -m "Modify, add, remove, rename" + +Test index keyword: + + $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n' + 10 0:a 1:b 2:fifth 3:fourth 4:third + 11 0:a + + $ hg branches -T '{index} {branch}\n' + 0 default + 1 foo + +ui verbosity: + + $ hg log -l1 -T '{verbosity}\n' + + $ hg log -l1 -T '{verbosity}\n' --debug + debug + $ hg log -l1 -T '{verbosity}\n' --quiet + quiet + $ hg log -l1 -T '{verbosity}\n' --verbose + verbose + + $ cd .. + +latesttag: + + $ hg init latesttag + $ cd latesttag + + $ echo a > file + $ hg ci -Am a -d '0 0' + adding file + + $ echo b >> file + $ hg ci -m b -d '1 0' + + $ echo c >> head1 + $ hg ci -Am h1c -d '2 0' + adding head1 + + $ hg update -q 1 + $ echo d >> head2 + $ hg ci -Am h2d -d '3 0' + adding head2 + created new head + + $ echo e >> head2 + $ hg ci -m h2e -d '4 0' + + $ hg merge -q + $ hg ci -m merge -d '5 -3600' + +No tag set: + + $ 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 -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 -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 -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 -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 + + + $ cd .. + +Set up repository containing template fragments in commit metadata: + + $ hg init r + $ cd r + $ echo a > a + $ hg ci -Am '{rev}' + adding a + + $ hg branch -q 'text.{rev}' + $ echo aa >> aa + $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped' + +Test termwidth: + + $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}' + bcc7ff960b8e:desc to be + termwidth.1:wrapped desc + termwidth.1:to be wrapped (no-eol) + +Just one more commit: + + $ echo b > b + $ hg ci -qAm b + +Test 'originalnode' + + $ hg log -r 1 -T '{revset("null") % "{node|short} {originalnode|short}"}\n' + 000000000000 bcc7ff960b8e + $ hg log -r 0 -T '{manifest % "{node} {originalnode}"}\n' + a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 f7769ec2ab975ad19684098ad1ffd9b81ecc71a1 + +Test active bookmark templating + + $ hg book foo + $ hg book bar + $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n" + 2 bar* foo + 1 + 0 + $ hg log --template "{rev} {activebookmark}\n" + 2 bar + 1 + 0 + $ hg bookmarks --inactive bar + $ hg log --template "{rev} {activebookmark}\n" + 2 + 1 + 0 + $ hg book -r1 baz + $ hg log --template "{rev} {join(bookmarks, ' ')}\n" + 2 bar foo + 1 baz + 0 + $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n" + 2 t + 1 f + 0 f + +Test namespaces dict + + $ hg --config extensions.revnamesext=$TESTDIR/revnamesext.py log -T '{rev}\n{namespaces % " {namespace} color={colorname} builtin={builtin}\n {join(names, ",")}\n"}\n' + 2 + bookmarks color=bookmark builtin=True + bar,foo + tags color=tag builtin=True + tip + branches color=branch builtin=True + text.{rev} + revnames color=revname builtin=False + r2 + + 1 + bookmarks color=bookmark builtin=True + baz + tags color=tag builtin=True + + branches color=branch builtin=True + text.{rev} + revnames color=revname builtin=False + r1 + + 0 + bookmarks color=bookmark builtin=True + + tags color=tag builtin=True + + branches color=branch builtin=True + default + revnames color=revname builtin=False + r0 + + $ hg log -r2 -T '{namespaces % "{namespace}: {names}\n"}' + bookmarks: bar foo + tags: tip + branches: text.{rev} + $ hg log -r2 -T '{namespaces % "{namespace}:\n{names % " {name}\n"}"}' + bookmarks: + bar + foo + tags: + tip + branches: + text.{rev} + $ hg log -r2 -T '{get(namespaces, "bookmarks") % "{name}\n"}' + bar + foo + $ hg log -r2 -T '{namespaces.bookmarks % "{bookmark}\n"}' + bar + foo + + $ cd .. + +Test 'graphwidth' in 'hg log' on various topologies. The key here is that the +printed graphwidths 3, 5, 7, etc. should all line up in their respective +columns. We don't care about other aspects of the graph rendering here. + + $ hg init graphwidth + $ cd graphwidth + + $ wrappabletext="a a a a a a a a a a a a" + + $ printf "first\n" > file + $ hg add file + $ hg commit -m "$wrappabletext" + + $ printf "first\nsecond\n" > file + $ hg commit -m "$wrappabletext" + + $ hg checkout 0 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ printf "third\nfirst\n" > file + $ hg commit -m "$wrappabletext" + created new head + + $ hg merge + merging file + 0 files updated, 1 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + + $ hg log --graph -T "{graphwidth}" + @ 3 + | + | @ 5 + |/ + o 3 + + $ hg commit -m "$wrappabletext" + + $ hg log --graph -T "{graphwidth}" + @ 5 + |\ + | o 5 + | | + o | 5 + |/ + o 3 + + + $ hg checkout 0 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ printf "third\nfirst\nsecond\n" > file + $ hg commit -m "$wrappabletext" + created new head + + $ hg log --graph -T "{graphwidth}" + @ 3 + | + | o 7 + | |\ + +---o 7 + | | + | o 5 + |/ + o 3 + + + $ hg log --graph -T "{graphwidth}" -r 3 + o 5 + |\ + ~ ~ + + $ hg log --graph -T "{graphwidth}" -r 1 + o 3 + | + ~ + + $ hg merge + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg commit -m "$wrappabletext" + + $ printf "seventh\n" >> file + $ hg commit -m "$wrappabletext" + + $ hg log --graph -T "{graphwidth}" + @ 3 + | + o 5 + |\ + | o 5 + | | + o | 7 + |\ \ + | o | 7 + | |/ + o / 5 + |/ + o 3 + + +The point of graphwidth is to allow wrapping that accounts for the space taken +by the graph. + + $ COLUMNS=10 hg log --graph -T "{fill(desc, termwidth - graphwidth)}" + @ a a a a + | a a a a + | a a a a + o a a a + |\ a a a + | | a a a + | | a a a + | o a a a + | | a a a + | | a a a + | | a a a + o | a a + |\ \ a a + | | | a a + | | | a a + | | | a a + | | | a a + | o | a a + | |/ a a + | | a a + | | a a + | | a a + | | a a + o | a a a + |/ a a a + | a a a + | a a a + o a a a a + a a a a + a a a a + +Something tricky happens when there are elided nodes; the next drawn row of +edges can be more than one column wider, but the graph width only increases by +one column. The remaining columns are added in between the nodes. + + $ hg log --graph -T "{graphwidth}" -r "0|2|4|5" + o 5 + |\ + | \ + | :\ + o : : 7 + :/ / + : o 5 + :/ + o 3 + + + $ cd ..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-template-map.t Thu Jul 19 13:55:54 2018 -0400 @@ -0,0 +1,1760 @@ +Test template map files and styles +================================== + + $ hg init a + $ cd a + $ echo a > a + $ hg add a + $ echo line 1 > b + $ echo line 2 >> b + $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>' + + $ hg add b + $ echo other 1 > c + $ echo other 2 >> c + $ echo >> c + $ echo other 3 >> c + $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>' + + $ hg add c + $ hg commit -m 'no person' -d '1200000 0' -u 'other@place' + $ echo c >> c + $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person' + + $ echo foo > .hg/branch + $ hg commit -m 'new branch' -d '1400000 0' -u 'person' + + $ hg co -q 3 + $ echo other 4 >> d + $ hg add d + $ hg commit -m 'new head' -d '1500000 0' -u 'person' + + $ hg merge -q foo + $ hg commit -m 'merge' -d '1500001 0' -u 'person' + +Second branch starting at nullrev: + + $ hg update null + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + $ echo second > second + $ hg add second + $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>' + created new head + + $ echo third > third + $ hg add third + $ hg mv second fourth + $ hg commit -m third -d "2020-01-01 10:01" + +Make sure user/global hgrc does not affect tests + + $ echo '[ui]' > .hg/hgrc + $ echo 'logtemplate =' >> .hg/hgrc + $ echo 'style =' >> .hg/hgrc + +Add some simple styles to settings + + $ cat <<'EOF' >> .hg/hgrc + > [templates] + > simple = "{rev}\n" + > simple2 = {rev}\n + > rev = "should not precede {rev} keyword\n" + > EOF + + $ hg log -l1 -Tsimple + 8 + $ hg log -l1 -Tsimple2 + 8 + $ hg log -l1 -Trev + should not precede 8 keyword + $ hg log -l1 -T '{simple}' + 8 + +Map file shouldn't see user templates: + + $ cat <<EOF > tmpl + > changeset = 'nothing expanded:{simple}\n' + > EOF + $ hg log -l1 --style ./tmpl + nothing expanded: + +Test templates and style maps in files: + + $ echo "{rev}" > tmpl + $ hg log -l1 -T./tmpl + 8 + $ hg log -l1 -Tblah/blah + blah/blah (no-eol) + + $ printf 'changeset = "{rev}\\n"\n' > map-simple + $ hg log -l1 -T./map-simple + 8 + + a map file may have [templates] and [templatealias] sections: + + $ cat <<'EOF' > map-simple + > [templates] + > changeset = "{a}\n" + > [templatealias] + > a = rev + > EOF + $ hg log -l1 -T./map-simple + 8 + + so it can be included in hgrc + + $ cat <<EOF > myhgrc + > %include $HGRCPATH + > %include map-simple + > [templates] + > foo = "{changeset}" + > EOF + $ HGRCPATH=./myhgrc hg log -l1 -Tfoo + 8 + $ HGRCPATH=./myhgrc hg log -l1 -T'{a}\n' + 8 + +Test template map inheritance + + $ echo "__base__ = map-cmdline.default" > map-simple + $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple + $ hg log -l1 -T./map-simple + changeset: ***8*** + tag: tip + user: test + date: Wed Jan 01 10:01:00 2020 +0000 + summary: third + + +Test docheader, docfooter and separator in template map + + $ cat <<'EOF' > map-myjson + > docheader = '\{\n' + > docfooter = '\n}\n' + > separator = ',\n' + > changeset = ' {dict(rev, node|short)|json}' + > EOF + $ hg log -l2 -T./map-myjson + { + {"node": "95c24699272e", "rev": 8}, + {"node": "29114dbae42b", "rev": 7} + } + +Test docheader, docfooter and separator in [templates] section + + $ cat <<'EOF' >> .hg/hgrc + > [templates] + > myjson = ' {dict(rev, node|short)|json}' + > myjson:docheader = '\{\n' + > myjson:docfooter = '\n}\n' + > myjson:separator = ',\n' + > :docheader = 'should not be selected as a docheader for literal templates\n' + > EOF + $ hg log -l2 -Tmyjson + { + {"node": "95c24699272e", "rev": 8}, + {"node": "29114dbae42b", "rev": 7} + } + $ hg log -l1 -T'{rev}\n' + 8 + +Template should precede style option + + $ hg log -l1 --style default -T '{rev}\n' + 8 + +Add a commit with empty description, to ensure that the templates +below will omit the description line. + + $ echo c >> c + $ hg add c + $ hg commit -qm ' ' + +Default style is like normal output. Phases style should be the same +as default style, except for extra phase lines. + + $ hg log > log.out + $ hg log --style default > style.out + $ cmp log.out style.out || diff -u log.out style.out + $ hg log -T phases > phases.out + $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@' + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + + $ hg log -v > log.out + $ hg log -v --style default > style.out + $ cmp log.out style.out || diff -u log.out style.out + $ hg log -v -T phases > phases.out + $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@' + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + +phase: draft + + $ hg log -q > log.out + $ hg log -q --style default > style.out + $ cmp log.out style.out || diff -u log.out style.out + $ hg log -q -T phases > phases.out + $ cmp log.out phases.out || diff -u log.out phases.out + + $ hg log --debug > log.out + $ hg log --debug --style default > style.out + $ cmp log.out style.out || diff -u log.out style.out + $ hg log --debug -T phases > phases.out + $ cmp log.out phases.out || diff -u log.out phases.out + +Default style of working-directory revision should also be the same (but +date may change while running tests): + + $ hg log -r 'wdir()' | sed 's|^date:.*|date:|' > log.out + $ hg log -r 'wdir()' --style default | sed 's|^date:.*|date:|' > style.out + $ cmp log.out style.out || diff -u log.out style.out + + $ hg log -r 'wdir()' -v | sed 's|^date:.*|date:|' > log.out + $ hg log -r 'wdir()' -v --style default | sed 's|^date:.*|date:|' > style.out + $ cmp log.out style.out || diff -u log.out style.out + + $ hg log -r 'wdir()' -q > log.out + $ hg log -r 'wdir()' -q --style default > style.out + $ cmp log.out style.out || diff -u log.out style.out + + $ hg log -r 'wdir()' --debug | sed 's|^date:.*|date:|' > log.out + $ hg log -r 'wdir()' --debug --style default \ + > | sed 's|^date:.*|date:|' > style.out + $ cmp log.out style.out || diff -u log.out style.out + +Default style should also preserve color information (issue2866): + + $ cp $HGRCPATH $HGRCPATH-bak + $ cat <<EOF >> $HGRCPATH + > [extensions] + > color= + > EOF + + $ hg --color=debug log > log.out + $ hg --color=debug log --style default > style.out + $ cmp log.out style.out || diff -u log.out style.out + $ hg --color=debug log -T phases > phases.out + $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@' + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + + $ hg --color=debug -v log > log.out + $ hg --color=debug -v log --style default > style.out + $ cmp log.out style.out || diff -u log.out style.out + $ hg --color=debug -v log -T phases > phases.out + $ diff -U 0 log.out phases.out | egrep -v '^---|^\+\+\+|^@@' + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + +[log.phase|phase: draft] + + $ hg --color=debug -q log > log.out + $ hg --color=debug -q log --style default > style.out + $ cmp log.out style.out || diff -u log.out style.out + $ hg --color=debug -q log -T phases > phases.out + $ cmp log.out phases.out || diff -u log.out phases.out + + $ hg --color=debug --debug log > log.out + $ hg --color=debug --debug log --style default > style.out + $ cmp log.out style.out || diff -u log.out style.out + $ hg --color=debug --debug log -T phases > phases.out + $ cmp log.out phases.out || diff -u log.out phases.out + + $ mv $HGRCPATH-bak $HGRCPATH + +Remove commit with empty commit message, so as to not pollute further +tests. + + $ hg --config extensions.strip= strip -q . + +Revision with no copies (used to print a traceback): + + $ hg tip -v --template '\n' + + +Compact style works: + + $ hg log -Tcompact + 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test + third + + 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user + second + + 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person + merge + + 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person + new head + + 4 bbe44766e73d 1970-01-17 04:53 +0000 person + new branch + + 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person + no user, no domain + + 2 97054abb4ab8 1970-01-14 21:20 +0000 other + no person + + 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other + other 1 + + 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user + line 1 + + + $ hg log -v --style compact + 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test + third + + 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname> + second + + 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person + merge + + 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person + new head + + 4 bbe44766e73d 1970-01-17 04:53 +0000 person + new branch + + 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person + no user, no domain + + 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place + no person + + 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place> + other 1 + other 2 + + other 3 + + 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname> + line 1 + line 2 + + + $ hg log --debug --style compact + 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test + third + + 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname> + second + + 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person + merge + + 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person + new head + + 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person + new branch + + 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person + no user, no domain + + 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place + no person + + 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place> + other 1 + other 2 + + other 3 + + 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname> + line 1 + line 2 + + +Test xml styles: + + $ hg log --style xml -r 'not all()' + <?xml version="1.0"?> + <log> + </log> + + $ hg log --style xml + <?xml version="1.0"?> + <log> + <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a"> + <tag>tip</tag> + <author email="test">test</author> + <date>2020-01-01T10:01:00+00:00</date> + <msg xml:space="preserve">third</msg> + </logentry> + <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453"> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <author email="user@hostname">User Name</author> + <date>1970-01-12T13:46:40+00:00</date> + <msg xml:space="preserve">second</msg> + </logentry> + <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b"> + <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" /> + <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" /> + <author email="person">person</author> + <date>1970-01-18T08:40:01+00:00</date> + <msg xml:space="preserve">merge</msg> + </logentry> + <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f"> + <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" /> + <author email="person">person</author> + <date>1970-01-18T08:40:00+00:00</date> + <msg xml:space="preserve">new head</msg> + </logentry> + <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74"> + <branch>foo</branch> + <author email="person">person</author> + <date>1970-01-17T04:53:20+00:00</date> + <msg xml:space="preserve">new branch</msg> + </logentry> + <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47"> + <author email="person">person</author> + <date>1970-01-16T01:06:40+00:00</date> + <msg xml:space="preserve">no user, no domain</msg> + </logentry> + <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465"> + <author email="other@place">other</author> + <date>1970-01-14T21:20:00+00:00</date> + <msg xml:space="preserve">no person</msg> + </logentry> + <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965"> + <author email="other@place">A. N. Other</author> + <date>1970-01-13T17:33:20+00:00</date> + <msg xml:space="preserve">other 1 + other 2 + + other 3</msg> + </logentry> + <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f"> + <author email="user@hostname">User Name</author> + <date>1970-01-12T13:46:40+00:00</date> + <msg xml:space="preserve">line 1 + line 2</msg> + </logentry> + </log> + + $ hg log -v --style xml + <?xml version="1.0"?> + <log> + <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a"> + <tag>tip</tag> + <author email="test">test</author> + <date>2020-01-01T10:01:00+00:00</date> + <msg xml:space="preserve">third</msg> + <paths> + <path action="A">fourth</path> + <path action="A">third</path> + <path action="R">second</path> + </paths> + <copies> + <copy source="second">fourth</copy> + </copies> + </logentry> + <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453"> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <author email="user@hostname">User Name</author> + <date>1970-01-12T13:46:40+00:00</date> + <msg xml:space="preserve">second</msg> + <paths> + <path action="A">second</path> + </paths> + </logentry> + <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b"> + <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" /> + <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" /> + <author email="person">person</author> + <date>1970-01-18T08:40:01+00:00</date> + <msg xml:space="preserve">merge</msg> + <paths> + </paths> + </logentry> + <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f"> + <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" /> + <author email="person">person</author> + <date>1970-01-18T08:40:00+00:00</date> + <msg xml:space="preserve">new head</msg> + <paths> + <path action="A">d</path> + </paths> + </logentry> + <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74"> + <branch>foo</branch> + <author email="person">person</author> + <date>1970-01-17T04:53:20+00:00</date> + <msg xml:space="preserve">new branch</msg> + <paths> + </paths> + </logentry> + <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47"> + <author email="person">person</author> + <date>1970-01-16T01:06:40+00:00</date> + <msg xml:space="preserve">no user, no domain</msg> + <paths> + <path action="M">c</path> + </paths> + </logentry> + <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465"> + <author email="other@place">other</author> + <date>1970-01-14T21:20:00+00:00</date> + <msg xml:space="preserve">no person</msg> + <paths> + <path action="A">c</path> + </paths> + </logentry> + <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965"> + <author email="other@place">A. N. Other</author> + <date>1970-01-13T17:33:20+00:00</date> + <msg xml:space="preserve">other 1 + other 2 + + other 3</msg> + <paths> + <path action="A">b</path> + </paths> + </logentry> + <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f"> + <author email="user@hostname">User Name</author> + <date>1970-01-12T13:46:40+00:00</date> + <msg xml:space="preserve">line 1 + line 2</msg> + <paths> + <path action="A">a</path> + </paths> + </logentry> + </log> + + $ hg log --debug --style xml + <?xml version="1.0"?> + <log> + <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a"> + <tag>tip</tag> + <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" /> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <author email="test">test</author> + <date>2020-01-01T10:01:00+00:00</date> + <msg xml:space="preserve">third</msg> + <paths> + <path action="A">fourth</path> + <path action="A">third</path> + <path action="R">second</path> + </paths> + <copies> + <copy source="second">fourth</copy> + </copies> + <extra key="branch">default</extra> + </logentry> + <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453"> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <author email="user@hostname">User Name</author> + <date>1970-01-12T13:46:40+00:00</date> + <msg xml:space="preserve">second</msg> + <paths> + <path action="A">second</path> + </paths> + <extra key="branch">default</extra> + </logentry> + <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b"> + <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" /> + <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" /> + <author email="person">person</author> + <date>1970-01-18T08:40:01+00:00</date> + <msg xml:space="preserve">merge</msg> + <paths> + </paths> + <extra key="branch">default</extra> + </logentry> + <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f"> + <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" /> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <author email="person">person</author> + <date>1970-01-18T08:40:00+00:00</date> + <msg xml:space="preserve">new head</msg> + <paths> + <path action="A">d</path> + </paths> + <extra key="branch">default</extra> + </logentry> + <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74"> + <branch>foo</branch> + <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" /> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <author email="person">person</author> + <date>1970-01-17T04:53:20+00:00</date> + <msg xml:space="preserve">new branch</msg> + <paths> + </paths> + <extra key="branch">foo</extra> + </logentry> + <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47"> + <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" /> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <author email="person">person</author> + <date>1970-01-16T01:06:40+00:00</date> + <msg xml:space="preserve">no user, no domain</msg> + <paths> + <path action="M">c</path> + </paths> + <extra key="branch">default</extra> + </logentry> + <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465"> + <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" /> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <author email="other@place">other</author> + <date>1970-01-14T21:20:00+00:00</date> + <msg xml:space="preserve">no person</msg> + <paths> + <path action="A">c</path> + </paths> + <extra key="branch">default</extra> + </logentry> + <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965"> + <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" /> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <author email="other@place">A. N. Other</author> + <date>1970-01-13T17:33:20+00:00</date> + <msg xml:space="preserve">other 1 + other 2 + + other 3</msg> + <paths> + <path action="A">b</path> + </paths> + <extra key="branch">default</extra> + </logentry> + <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f"> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <parent revision="-1" node="0000000000000000000000000000000000000000" /> + <author email="user@hostname">User Name</author> + <date>1970-01-12T13:46:40+00:00</date> + <msg xml:space="preserve">line 1 + line 2</msg> + <paths> + <path action="A">a</path> + </paths> + <extra key="branch">default</extra> + </logentry> + </log> + + +Test JSON style: + + $ hg log -k nosuch -Tjson + [ + ] + + $ hg log -qr . -Tjson + [ + { + "node": "95c24699272ef57d062b8bccc32c878bf841784a", + "rev": 8 + } + ] + + $ hg log -vpr . -Tjson --stat + [ + { + "bookmarks": [], + "branch": "default", + "date": [1577872860, 0], + "desc": "third", + "diff": "diff -r 29114dbae42b -r 95c24699272e fourth\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/fourth\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+second\ndiff -r 29114dbae42b -r 95c24699272e second\n--- a/second\tMon Jan 12 13:46:40 1970 +0000\n+++ /dev/null\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +0,0 @@\n-second\ndiff -r 29114dbae42b -r 95c24699272e third\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/third\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+third\n", + "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n", + "files": ["fourth", "second", "third"], + "node": "95c24699272ef57d062b8bccc32c878bf841784a", + "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], + "phase": "draft", + "rev": 8, + "tags": ["tip"], + "user": "test" + } + ] + +honor --git but not format-breaking diffopts + $ hg --config diff.noprefix=True log --git -vpr . -Tjson + [ + { + "bookmarks": [], + "branch": "default", + "date": [1577872860, 0], + "desc": "third", + "diff": "diff --git a/second b/fourth\nrename from second\nrename to fourth\ndiff --git a/third b/third\nnew file mode 100644\n--- /dev/null\n+++ b/third\n@@ -0,0 +1,1 @@\n+third\n", + "files": ["fourth", "second", "third"], + "node": "95c24699272ef57d062b8bccc32c878bf841784a", + "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], + "phase": "draft", + "rev": 8, + "tags": ["tip"], + "user": "test" + } + ] + + $ hg log -T json + [ + { + "bookmarks": [], + "branch": "default", + "date": [1577872860, 0], + "desc": "third", + "node": "95c24699272ef57d062b8bccc32c878bf841784a", + "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], + "phase": "draft", + "rev": 8, + "tags": ["tip"], + "user": "test" + }, + { + "bookmarks": [], + "branch": "default", + "date": [1000000, 0], + "desc": "second", + "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453", + "parents": ["0000000000000000000000000000000000000000"], + "phase": "draft", + "rev": 7, + "tags": [], + "user": "User Name <user@hostname>" + }, + { + "bookmarks": [], + "branch": "default", + "date": [1500001, 0], + "desc": "merge", + "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b", + "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"], + "phase": "draft", + "rev": 6, + "tags": [], + "user": "person" + }, + { + "bookmarks": [], + "branch": "default", + "date": [1500000, 0], + "desc": "new head", + "node": "13207e5a10d9fd28ec424934298e176197f2c67f", + "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"], + "phase": "draft", + "rev": 5, + "tags": [], + "user": "person" + }, + { + "bookmarks": [], + "branch": "foo", + "date": [1400000, 0], + "desc": "new branch", + "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74", + "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"], + "phase": "draft", + "rev": 4, + "tags": [], + "user": "person" + }, + { + "bookmarks": [], + "branch": "default", + "date": [1300000, 0], + "desc": "no user, no domain", + "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47", + "parents": ["97054abb4ab824450e9164180baf491ae0078465"], + "phase": "draft", + "rev": 3, + "tags": [], + "user": "person" + }, + { + "bookmarks": [], + "branch": "default", + "date": [1200000, 0], + "desc": "no person", + "node": "97054abb4ab824450e9164180baf491ae0078465", + "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"], + "phase": "draft", + "rev": 2, + "tags": [], + "user": "other@place" + }, + { + "bookmarks": [], + "branch": "default", + "date": [1100000, 0], + "desc": "other 1\nother 2\n\nother 3", + "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965", + "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"], + "phase": "draft", + "rev": 1, + "tags": [], + "user": "A. N. Other <other@place>" + }, + { + "bookmarks": [], + "branch": "default", + "date": [1000000, 0], + "desc": "line 1\nline 2", + "node": "1e4e1b8f71e05681d422154f5421e385fec3454f", + "parents": ["0000000000000000000000000000000000000000"], + "phase": "draft", + "rev": 0, + "tags": [], + "user": "User Name <user@hostname>" + } + ] + + $ hg heads -v -Tjson + [ + { + "bookmarks": [], + "branch": "default", + "date": [1577872860, 0], + "desc": "third", + "files": ["fourth", "second", "third"], + "node": "95c24699272ef57d062b8bccc32c878bf841784a", + "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], + "phase": "draft", + "rev": 8, + "tags": ["tip"], + "user": "test" + }, + { + "bookmarks": [], + "branch": "default", + "date": [1500001, 0], + "desc": "merge", + "files": [], + "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b", + "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"], + "phase": "draft", + "rev": 6, + "tags": [], + "user": "person" + }, + { + "bookmarks": [], + "branch": "foo", + "date": [1400000, 0], + "desc": "new branch", + "files": [], + "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74", + "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"], + "phase": "draft", + "rev": 4, + "tags": [], + "user": "person" + } + ] + + $ hg log --debug -Tjson + [ + { + "added": ["fourth", "third"], + "bookmarks": [], + "branch": "default", + "date": [1577872860, 0], + "desc": "third", + "extra": {"branch": "default"}, + "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64", + "modified": [], + "node": "95c24699272ef57d062b8bccc32c878bf841784a", + "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], + "phase": "draft", + "removed": ["second"], + "rev": 8, + "tags": ["tip"], + "user": "test" + }, + { + "added": ["second"], + "bookmarks": [], + "branch": "default", + "date": [1000000, 0], + "desc": "second", + "extra": {"branch": "default"}, + "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf", + "modified": [], + "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453", + "parents": ["0000000000000000000000000000000000000000"], + "phase": "draft", + "removed": [], + "rev": 7, + "tags": [], + "user": "User Name <user@hostname>" + }, + { + "added": [], + "bookmarks": [], + "branch": "default", + "date": [1500001, 0], + "desc": "merge", + "extra": {"branch": "default"}, + "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216", + "modified": [], + "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b", + "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"], + "phase": "draft", + "removed": [], + "rev": 6, + "tags": [], + "user": "person" + }, + { + "added": ["d"], + "bookmarks": [], + "branch": "default", + "date": [1500000, 0], + "desc": "new head", + "extra": {"branch": "default"}, + "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216", + "modified": [], + "node": "13207e5a10d9fd28ec424934298e176197f2c67f", + "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"], + "phase": "draft", + "removed": [], + "rev": 5, + "tags": [], + "user": "person" + }, + { + "added": [], + "bookmarks": [], + "branch": "foo", + "date": [1400000, 0], + "desc": "new branch", + "extra": {"branch": "foo"}, + "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc", + "modified": [], + "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74", + "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"], + "phase": "draft", + "removed": [], + "rev": 4, + "tags": [], + "user": "person" + }, + { + "added": [], + "bookmarks": [], + "branch": "default", + "date": [1300000, 0], + "desc": "no user, no domain", + "extra": {"branch": "default"}, + "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc", + "modified": ["c"], + "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47", + "parents": ["97054abb4ab824450e9164180baf491ae0078465"], + "phase": "draft", + "removed": [], + "rev": 3, + "tags": [], + "user": "person" + }, + { + "added": ["c"], + "bookmarks": [], + "branch": "default", + "date": [1200000, 0], + "desc": "no person", + "extra": {"branch": "default"}, + "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1", + "modified": [], + "node": "97054abb4ab824450e9164180baf491ae0078465", + "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"], + "phase": "draft", + "removed": [], + "rev": 2, + "tags": [], + "user": "other@place" + }, + { + "added": ["b"], + "bookmarks": [], + "branch": "default", + "date": [1100000, 0], + "desc": "other 1\nother 2\n\nother 3", + "extra": {"branch": "default"}, + "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55", + "modified": [], + "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965", + "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"], + "phase": "draft", + "removed": [], + "rev": 1, + "tags": [], + "user": "A. N. Other <other@place>" + }, + { + "added": ["a"], + "bookmarks": [], + "branch": "default", + "date": [1000000, 0], + "desc": "line 1\nline 2", + "extra": {"branch": "default"}, + "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0", + "modified": [], + "node": "1e4e1b8f71e05681d422154f5421e385fec3454f", + "parents": ["0000000000000000000000000000000000000000"], + "phase": "draft", + "removed": [], + "rev": 0, + "tags": [], + "user": "User Name <user@hostname>" + } + ] + +Error if style not readable: + +#if unix-permissions no-root + $ touch q + $ chmod 0 q + $ hg log --style ./q + abort: Permission denied: ./q + [255] +#endif + +Error if no style: + + $ hg log --style notexist + abort: style 'notexist' not found + (available styles: bisect, changelog, compact, default, phases, show, status, xml) + [255] + + $ hg log -T list + available styles: bisect, changelog, compact, default, phases, show, status, xml + abort: specify a template + [255] + +Error if style missing key: + + $ echo 'q = q' > t + $ hg log --style ./t + abort: "changeset" not in template map + [255] + +Error if style missing value: + + $ echo 'changeset =' > t + $ hg log --style t + hg: parse error at t:1: missing value + [255] + +Error if include fails: + + $ echo 'changeset = q' >> t +#if unix-permissions no-root + $ hg log --style ./t + abort: template file ./q: Permission denied + [255] + $ rm -f q +#endif + +Include works: + + $ echo '{rev}' > q + $ hg log --style ./t + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 + + $ hg phase -r 5 --public + $ hg phase -r 7 --secret --force + +Missing non-standard names give no error (backward compatibility): + + $ echo "changeset = '{c}'" > t + $ hg log --style ./t + +Defining non-standard name works: + + $ cat <<EOF > t + > changeset = '{c}' + > c = q + > EOF + $ hg log --style ./t + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 + +ui.style works: + + $ echo '[ui]' > .hg/hgrc + $ echo 'style = t' >> .hg/hgrc + $ hg log + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 + +Issue338: + + $ hg log --style=changelog > changelog + + $ cat changelog + 2020-01-01 test <test> + + * fourth, second, third: + third + [95c24699272e] [tip] + + 1970-01-12 User Name <user@hostname> + + * second: + second + [29114dbae42b] + + 1970-01-18 person <person> + + * merge + [d41e714fe50d] + + * d: + new head + [13207e5a10d9] + + 1970-01-17 person <person> + + * new branch + [bbe44766e73d] <foo> + + 1970-01-16 person <person> + + * c: + no user, no domain + [10e46f2dcbf4] + + 1970-01-14 other <other@place> + + * c: + no person + [97054abb4ab8] + + 1970-01-13 A. N. Other <other@place> + + * b: + other 1 other 2 + + other 3 + [b608e9d1a3f0] + + 1970-01-12 User Name <user@hostname> + + * a: + line 1 line 2 + [1e4e1b8f71e0] + + +Issue2130: xml output for 'hg heads' is malformed + + $ hg heads --style changelog + 2020-01-01 test <test> + + * fourth, second, third: + third + [95c24699272e] [tip] + + 1970-01-18 person <person> + + * merge + [d41e714fe50d] + + 1970-01-17 person <person> + + * new branch + [bbe44766e73d] <foo> + + +Add a dummy commit to make up for the instability of the above: + + $ echo a > a + $ hg add a + $ hg ci -m future + +Add a commit that does all possible modifications at once + + $ echo modify >> third + $ touch b + $ hg add b + $ hg mv fourth fifth + $ hg rm a + $ hg ci -m "Modify, add, remove, rename" + +Check the status template + + $ cat <<EOF >> $HGRCPATH + > [extensions] + > color= + > EOF + + $ hg log -T status -r 10 + changeset: 10:0f9759ec227a + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: Modify, add, remove, rename + files: + M third + A b + A fifth + R a + R fourth + + $ hg log -T status -C -r 10 + changeset: 10:0f9759ec227a + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: Modify, add, remove, rename + files: + M third + A b + A fifth + fourth + R a + R fourth + + $ hg log -T status -C -r 10 -v + changeset: 10:0f9759ec227a + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + description: + Modify, add, remove, rename + + files: + M third + A b + A fifth + fourth + R a + R fourth + + $ hg log -T status -C -r 10 --debug + changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c + tag: tip + phase: secret + parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066 + parent: -1:0000000000000000000000000000000000000000 + manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + extra: branch=default + description: + Modify, add, remove, rename + + files: + M third + A b + A fifth + fourth + R a + R fourth + + $ hg log -T status -C -r 10 --quiet + 10:0f9759ec227a + $ hg --color=debug log -T status -r 10 + [log.changeset changeset.secret|changeset: 10:0f9759ec227a] + [log.tag|tag: tip] + [log.user|user: test] + [log.date|date: Thu Jan 01 00:00:00 1970 +0000] + [log.summary|summary: Modify, add, remove, rename] + [ui.note log.files|files:] + [status.modified|M third] + [status.added|A b] + [status.added|A fifth] + [status.removed|R a] + [status.removed|R fourth] + + $ hg --color=debug log -T status -C -r 10 + [log.changeset changeset.secret|changeset: 10:0f9759ec227a] + [log.tag|tag: tip] + [log.user|user: test] + [log.date|date: Thu Jan 01 00:00:00 1970 +0000] + [log.summary|summary: Modify, add, remove, rename] + [ui.note log.files|files:] + [status.modified|M third] + [status.added|A b] + [status.added|A fifth] + [status.copied| fourth] + [status.removed|R a] + [status.removed|R fourth] + + $ hg --color=debug log -T status -C -r 10 -v + [log.changeset changeset.secret|changeset: 10:0f9759ec227a] + [log.tag|tag: tip] + [log.user|user: test] + [log.date|date: Thu Jan 01 00:00:00 1970 +0000] + [ui.note log.description|description:] + [ui.note log.description|Modify, add, remove, rename] + + [ui.note log.files|files:] + [status.modified|M third] + [status.added|A b] + [status.added|A fifth] + [status.copied| fourth] + [status.removed|R a] + [status.removed|R fourth] + + $ hg --color=debug log -T status -C -r 10 --debug + [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c] + [log.tag|tag: tip] + [log.phase|phase: secret] + [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066] + [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] + [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567] + [log.user|user: test] + [log.date|date: Thu Jan 01 00:00:00 1970 +0000] + [ui.debug log.extra|extra: branch=default] + [ui.note log.description|description:] + [ui.note log.description|Modify, add, remove, rename] + + [ui.note log.files|files:] + [status.modified|M third] + [status.added|A b] + [status.added|A fifth] + [status.copied| fourth] + [status.removed|R a] + [status.removed|R fourth] + + $ hg --color=debug log -T status -C -r 10 --quiet + [log.node|10:0f9759ec227a] + +Check the bisect template + + $ hg bisect -g 1 + $ hg bisect -b 3 --noupdate + Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests) + $ hg log -T bisect -r 0:4 + changeset: 0:1e4e1b8f71e0 + bisect: good (implicit) + user: User Name <user@hostname> + date: Mon Jan 12 13:46:40 1970 +0000 + summary: line 1 + + changeset: 1:b608e9d1a3f0 + bisect: good + user: A. N. Other <other@place> + date: Tue Jan 13 17:33:20 1970 +0000 + summary: other 1 + + changeset: 2:97054abb4ab8 + bisect: untested + user: other@place + date: Wed Jan 14 21:20:00 1970 +0000 + summary: no person + + changeset: 3:10e46f2dcbf4 + bisect: bad + user: person + date: Fri Jan 16 01:06:40 1970 +0000 + summary: no user, no domain + + changeset: 4:bbe44766e73d + bisect: bad (implicit) + branch: foo + user: person + date: Sat Jan 17 04:53:20 1970 +0000 + summary: new branch + + $ hg log --debug -T bisect -r 0:4 + changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f + bisect: good (implicit) + phase: public + parent: -1:0000000000000000000000000000000000000000 + parent: -1:0000000000000000000000000000000000000000 + manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 + user: User Name <user@hostname> + date: Mon Jan 12 13:46:40 1970 +0000 + files+: a + extra: branch=default + description: + line 1 + line 2 + + + changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 + bisect: good + phase: public + parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f + parent: -1:0000000000000000000000000000000000000000 + manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55 + user: A. N. Other <other@place> + date: Tue Jan 13 17:33:20 1970 +0000 + files+: b + extra: branch=default + description: + other 1 + other 2 + + other 3 + + + changeset: 2:97054abb4ab824450e9164180baf491ae0078465 + bisect: untested + phase: public + parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 + parent: -1:0000000000000000000000000000000000000000 + manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1 + user: other@place + date: Wed Jan 14 21:20:00 1970 +0000 + files+: c + extra: branch=default + description: + no person + + + changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 + bisect: bad + phase: public + parent: 2:97054abb4ab824450e9164180baf491ae0078465 + parent: -1:0000000000000000000000000000000000000000 + manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc + user: person + date: Fri Jan 16 01:06:40 1970 +0000 + files: c + extra: branch=default + description: + no user, no domain + + + changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74 + bisect: bad (implicit) + branch: foo + phase: draft + parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 + parent: -1:0000000000000000000000000000000000000000 + manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc + user: person + date: Sat Jan 17 04:53:20 1970 +0000 + extra: branch=foo + description: + new branch + + + $ hg log -v -T bisect -r 0:4 + changeset: 0:1e4e1b8f71e0 + bisect: good (implicit) + user: User Name <user@hostname> + date: Mon Jan 12 13:46:40 1970 +0000 + files: a + description: + line 1 + line 2 + + + changeset: 1:b608e9d1a3f0 + bisect: good + user: A. N. Other <other@place> + date: Tue Jan 13 17:33:20 1970 +0000 + files: b + description: + other 1 + other 2 + + other 3 + + + changeset: 2:97054abb4ab8 + bisect: untested + user: other@place + date: Wed Jan 14 21:20:00 1970 +0000 + files: c + description: + no person + + + changeset: 3:10e46f2dcbf4 + bisect: bad + user: person + date: Fri Jan 16 01:06:40 1970 +0000 + files: c + description: + no user, no domain + + + changeset: 4:bbe44766e73d + bisect: bad (implicit) + branch: foo + user: person + date: Sat Jan 17 04:53:20 1970 +0000 + description: + new branch + + + $ hg --color=debug log -T bisect -r 0:4 + [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0] + [log.bisect bisect.good|bisect: good (implicit)] + [log.user|user: User Name <user@hostname>] + [log.date|date: Mon Jan 12 13:46:40 1970 +0000] + [log.summary|summary: line 1] + + [log.changeset changeset.public|changeset: 1:b608e9d1a3f0] + [log.bisect bisect.good|bisect: good] + [log.user|user: A. N. Other <other@place>] + [log.date|date: Tue Jan 13 17:33:20 1970 +0000] + [log.summary|summary: other 1] + + [log.changeset changeset.public|changeset: 2:97054abb4ab8] + [log.bisect bisect.untested|bisect: untested] + [log.user|user: other@place] + [log.date|date: Wed Jan 14 21:20:00 1970 +0000] + [log.summary|summary: no person] + + [log.changeset changeset.public|changeset: 3:10e46f2dcbf4] + [log.bisect bisect.bad|bisect: bad] + [log.user|user: person] + [log.date|date: Fri Jan 16 01:06:40 1970 +0000] + [log.summary|summary: no user, no domain] + + [log.changeset changeset.draft|changeset: 4:bbe44766e73d] + [log.bisect bisect.bad|bisect: bad (implicit)] + [log.branch|branch: foo] + [log.user|user: person] + [log.date|date: Sat Jan 17 04:53:20 1970 +0000] + [log.summary|summary: new branch] + + $ hg --color=debug log --debug -T bisect -r 0:4 + [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f] + [log.bisect bisect.good|bisect: good (implicit)] + [log.phase|phase: public] + [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] + [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] + [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0] + [log.user|user: User Name <user@hostname>] + [log.date|date: Mon Jan 12 13:46:40 1970 +0000] + [ui.debug log.files|files+: a] + [ui.debug log.extra|extra: branch=default] + [ui.note log.description|description:] + [ui.note log.description|line 1 + line 2] + + + [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965] + [log.bisect bisect.good|bisect: good] + [log.phase|phase: public] + [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f] + [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] + [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55] + [log.user|user: A. N. Other <other@place>] + [log.date|date: Tue Jan 13 17:33:20 1970 +0000] + [ui.debug log.files|files+: b] + [ui.debug log.extra|extra: branch=default] + [ui.note log.description|description:] + [ui.note log.description|other 1 + other 2 + + other 3] + + + [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465] + [log.bisect bisect.untested|bisect: untested] + [log.phase|phase: public] + [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965] + [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] + [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1] + [log.user|user: other@place] + [log.date|date: Wed Jan 14 21:20:00 1970 +0000] + [ui.debug log.files|files+: c] + [ui.debug log.extra|extra: branch=default] + [ui.note log.description|description:] + [ui.note log.description|no person] + + + [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47] + [log.bisect bisect.bad|bisect: bad] + [log.phase|phase: public] + [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465] + [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] + [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc] + [log.user|user: person] + [log.date|date: Fri Jan 16 01:06:40 1970 +0000] + [ui.debug log.files|files: c] + [ui.debug log.extra|extra: branch=default] + [ui.note log.description|description:] + [ui.note log.description|no user, no domain] + + + [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74] + [log.bisect bisect.bad|bisect: bad (implicit)] + [log.branch|branch: foo] + [log.phase|phase: draft] + [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47] + [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000] + [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc] + [log.user|user: person] + [log.date|date: Sat Jan 17 04:53:20 1970 +0000] + [ui.debug log.extra|extra: branch=foo] + [ui.note log.description|description:] + [ui.note log.description|new branch] + + + $ hg --color=debug log -v -T bisect -r 0:4 + [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0] + [log.bisect bisect.good|bisect: good (implicit)] + [log.user|user: User Name <user@hostname>] + [log.date|date: Mon Jan 12 13:46:40 1970 +0000] + [ui.note log.files|files: a] + [ui.note log.description|description:] + [ui.note log.description|line 1 + line 2] + + + [log.changeset changeset.public|changeset: 1:b608e9d1a3f0] + [log.bisect bisect.good|bisect: good] + [log.user|user: A. N. Other <other@place>] + [log.date|date: Tue Jan 13 17:33:20 1970 +0000] + [ui.note log.files|files: b] + [ui.note log.description|description:] + [ui.note log.description|other 1 + other 2 + + other 3] + + + [log.changeset changeset.public|changeset: 2:97054abb4ab8] + [log.bisect bisect.untested|bisect: untested] + [log.user|user: other@place] + [log.date|date: Wed Jan 14 21:20:00 1970 +0000] + [ui.note log.files|files: c] + [ui.note log.description|description:] + [ui.note log.description|no person] + + + [log.changeset changeset.public|changeset: 3:10e46f2dcbf4] + [log.bisect bisect.bad|bisect: bad] + [log.user|user: person] + [log.date|date: Fri Jan 16 01:06:40 1970 +0000] + [ui.note log.files|files: c] + [ui.note log.description|description:] + [ui.note log.description|no user, no domain] + + + [log.changeset changeset.draft|changeset: 4:bbe44766e73d] + [log.bisect bisect.bad|bisect: bad (implicit)] + [log.branch|branch: foo] + [log.user|user: person] + [log.date|date: Sat Jan 17 04:53:20 1970 +0000] + [ui.note log.description|description:] + [ui.note log.description|new branch] + + + $ hg bisect --reset + + $ cd .. + +Set up latesttag repository: + + $ hg init latesttag + $ cd latesttag + + $ echo a > file + $ hg ci -Am a -d '0 0' + adding file + + $ echo b >> file + $ hg ci -m b -d '1 0' + + $ echo c >> head1 + $ hg ci -Am h1c -d '2 0' + adding head1 + + $ hg update -q 1 + $ echo d >> head2 + $ hg ci -Am h2d -d '3 0' + adding head2 + created new head + + $ echo e >> head2 + $ hg ci -m h2e -d '4 0' + + $ hg merge -q + $ hg ci -m merge -d '5 -3600' + + $ hg tag -r 1 -m t1 -d '6 0' t1 + $ hg tag -r 2 -m t2 -d '7 0' t2 + $ hg tag -r 3 -m t3 -d '8 0' t3 + $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter + $ hg tag -r 5 -m t5 -d '9 0' t5 + $ hg tag -r 3 -m at3 -d '10 0' at3 + + $ cd .. + +Style path expansion: issue1948 - ui.style option doesn't work on OSX +if it is a relative path + + $ mkdir -p home/styles + + $ cat > home/styles/teststyle <<EOF + > changeset = 'test {rev}:{node|short}\n' + > EOF + + $ HOME=`pwd`/home; export HOME + + $ cat > latesttag/.hg/hgrc <<EOF + > [ui] + > style = ~/styles/teststyle + > EOF + + $ hg -R latesttag tip + test 11:97e5943b523a + +Test recursive showlist template (issue1989): + + $ cat > style1989 <<EOF + > changeset = '{file_mods}{manifest}{extras}' + > file_mod = 'M|{author|person}\n' + > manifest = '{rev},{author}\n' + > extra = '{key}: {author}\n' + > EOF + + $ hg -R latesttag log -r tip --style=style1989 + M|test + 11,test + branch: test
--- a/tests/test-transplant.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-transplant.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - $ cat <<EOF >> $HGRCPATH > [extensions] > transplant= @@ -838,9 +836,9 @@ $ cd binarysource $ echo a > a $ hg ci -Am adda a - >>> open('b', 'wb').write(b'\0b1') + >>> open('b', 'wb').write(b'\0b1') and None $ hg ci -Am addb b - >>> open('b', 'wb').write(b'\0b2') + >>> open('b', 'wb').write(b'\0b2') and None $ hg ci -m changeb b $ cd ..
--- a/tests/test-treediscovery-legacy.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-treediscovery-legacy.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - Tests discovery against servers without getbundle support: $ cat >> $HGRCPATH <<EOF @@ -356,6 +354,7 @@ pulling from http://localhost:$HGPORT/ searching for changes no changes found + 1 local changesets published $ hg push $remote pushing to http://localhost:$HGPORT/ searching for changes
--- a/tests/test-treediscovery.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-treediscovery.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - Tests discovery against servers without getbundle support: $ CAP="getbundle bundle2"
--- a/tests/test-treemanifest.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-treemanifest.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - $ cat << EOF >> $HGRCPATH > [ui] > ssh=$PYTHON "$TESTDIR/dummyssh"
--- a/tests/test-ui-color.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-ui-color.py Thu Jul 19 13:55:54 2018 -0400 @@ -5,6 +5,9 @@ dispatch, ui as uimod, ) +from mercurial.utils import ( + stringutil, +) # ensure errors aren't buffered testui = uimod.ui() @@ -12,7 +15,7 @@ testui.write((b'buffered\n')) testui.warn((b'warning\n')) testui.write_err(b'error\n') -print(repr(testui.popbuffer())) +print(stringutil.pprint(testui.popbuffer(), bprefix=True).decode('ascii')) # test dispatch.dispatch with the same ui object hgrc = open(os.environ["HGRCPATH"], 'wb') @@ -35,4 +38,3 @@ print("colored? %s" % (ui_._colormode is not None)) runcmd() print("colored? %s" % (ui_._colormode is not None)) -
--- a/tests/test-ui-color.py.out Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-ui-color.py.out Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,5 @@ warning error -'buffered\n' +b'buffered\n' colored? True colored? True
--- a/tests/test-ui-config.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-ui-config.py Thu Jul 19 13:55:54 2018 -0400 @@ -2,8 +2,12 @@ from mercurial import ( dispatch, error, + pycompat, ui as uimod, ) +from mercurial.utils import ( + stringutil, +) testui = uimod.ui.load() @@ -46,59 +50,62 @@ b'date.invalid=0' ]) -print(repr(testui.configitems(b'values'))) -print(repr(testui.configitems(b'lists'))) +def pprint(obj): + return stringutil.pprint(obj).decode('ascii') + +print(pprint(testui.configitems(b'values'))) +print(pprint(testui.configitems(b'lists'))) print("---") -print(repr(testui.config(b'values', b'string'))) -print(repr(testui.config(b'values', b'bool1'))) -print(repr(testui.config(b'values', b'bool2'))) -print(repr(testui.config(b'values', b'unknown'))) +print(pprint(testui.config(b'values', b'string'))) +print(pprint(testui.config(b'values', b'bool1'))) +print(pprint(testui.config(b'values', b'bool2'))) +print(pprint(testui.config(b'values', b'unknown'))) print("---") try: - print(repr(testui.configbool(b'values', b'string'))) + print(pprint(testui.configbool(b'values', b'string'))) except error.ConfigError as inst: - print(inst) -print(repr(testui.configbool(b'values', b'bool1'))) -print(repr(testui.configbool(b'values', b'bool2'))) -print(repr(testui.configbool(b'values', b'bool2', True))) -print(repr(testui.configbool(b'values', b'unknown'))) -print(repr(testui.configbool(b'values', b'unknown', True))) + print(pprint(pycompat.bytestr(inst))) +print(pprint(testui.configbool(b'values', b'bool1'))) +print(pprint(testui.configbool(b'values', b'bool2'))) +print(pprint(testui.configbool(b'values', b'bool2', True))) +print(pprint(testui.configbool(b'values', b'unknown'))) +print(pprint(testui.configbool(b'values', b'unknown', True))) print("---") -print(repr(testui.configint(b'values', b'int1'))) -print(repr(testui.configint(b'values', b'int2'))) +print(pprint(testui.configint(b'values', b'int1'))) +print(pprint(testui.configint(b'values', b'int2'))) print("---") -print(repr(testui.configlist(b'lists', b'list1'))) -print(repr(testui.configlist(b'lists', b'list2'))) -print(repr(testui.configlist(b'lists', b'list3'))) -print(repr(testui.configlist(b'lists', b'list4'))) -print(repr(testui.configlist(b'lists', b'list4', [b'foo']))) -print(repr(testui.configlist(b'lists', b'list5'))) -print(repr(testui.configlist(b'lists', b'list6'))) -print(repr(testui.configlist(b'lists', b'list7'))) -print(repr(testui.configlist(b'lists', b'list8'))) -print(repr(testui.configlist(b'lists', b'list9'))) -print(repr(testui.configlist(b'lists', b'list10'))) -print(repr(testui.configlist(b'lists', b'list11'))) -print(repr(testui.configlist(b'lists', b'list12'))) -print(repr(testui.configlist(b'lists', b'list13'))) -print(repr(testui.configlist(b'lists', b'list14'))) -print(repr(testui.configlist(b'lists', b'list15'))) -print(repr(testui.configlist(b'lists', b'list16'))) -print(repr(testui.configlist(b'lists', b'list17'))) -print(repr(testui.configlist(b'lists', b'list18'))) -print(repr(testui.configlist(b'lists', b'unknown'))) -print(repr(testui.configlist(b'lists', b'unknown', b''))) -print(repr(testui.configlist(b'lists', b'unknown', b'foo'))) -print(repr(testui.configlist(b'lists', b'unknown', [b'foo']))) -print(repr(testui.configlist(b'lists', b'unknown', b'foo bar'))) -print(repr(testui.configlist(b'lists', b'unknown', b'foo, bar'))) -print(repr(testui.configlist(b'lists', b'unknown', [b'foo bar']))) -print(repr(testui.configlist(b'lists', b'unknown', [b'foo', b'bar']))) +print(pprint(testui.configlist(b'lists', b'list1'))) +print(pprint(testui.configlist(b'lists', b'list2'))) +print(pprint(testui.configlist(b'lists', b'list3'))) +print(pprint(testui.configlist(b'lists', b'list4'))) +print(pprint(testui.configlist(b'lists', b'list4', [b'foo']))) +print(pprint(testui.configlist(b'lists', b'list5'))) +print(pprint(testui.configlist(b'lists', b'list6'))) +print(pprint(testui.configlist(b'lists', b'list7'))) +print(pprint(testui.configlist(b'lists', b'list8'))) +print(pprint(testui.configlist(b'lists', b'list9'))) +print(pprint(testui.configlist(b'lists', b'list10'))) +print(pprint(testui.configlist(b'lists', b'list11'))) +print(pprint(testui.configlist(b'lists', b'list12'))) +print(pprint(testui.configlist(b'lists', b'list13'))) +print(pprint(testui.configlist(b'lists', b'list14'))) +print(pprint(testui.configlist(b'lists', b'list15'))) +print(pprint(testui.configlist(b'lists', b'list16'))) +print(pprint(testui.configlist(b'lists', b'list17'))) +print(pprint(testui.configlist(b'lists', b'list18'))) +print(pprint(testui.configlist(b'lists', b'unknown'))) +print(pprint(testui.configlist(b'lists', b'unknown', b''))) +print(pprint(testui.configlist(b'lists', b'unknown', b'foo'))) +print(pprint(testui.configlist(b'lists', b'unknown', [b'foo']))) +print(pprint(testui.configlist(b'lists', b'unknown', b'foo bar'))) +print(pprint(testui.configlist(b'lists', b'unknown', b'foo, bar'))) +print(pprint(testui.configlist(b'lists', b'unknown', [b'foo bar']))) +print(pprint(testui.configlist(b'lists', b'unknown', [b'foo', b'bar']))) print("---") -print(repr(testui.configdate(b'date', b'epoch'))) -print(repr(testui.configdate(b'date', b'birth'))) +print(pprint(testui.configdate(b'date', b'epoch'))) +print(pprint(testui.configdate(b'date', b'birth'))) -print(repr(testui.config(b'values', b'String'))) +print(pprint(testui.config(b'values', b'String'))) def function(): pass
--- a/tests/test-ui-config.py.out Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-ui-config.py.out Thu Jul 19 13:55:54 2018 -0400 @@ -6,7 +6,7 @@ 'false' None --- -values.string is not a boolean ('string value') +'values.string is not a boolean (\'string value\')' True False False
--- a/tests/test-ui-verbosity.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-ui-verbosity.py Thu Jul 19 13:55:54 2018 -0400 @@ -38,9 +38,9 @@ u = uimod.ui.load() if cmd_quiet or cmd_debug or cmd_verbose: - u.setconfig('ui', 'quiet', str(bool(cmd_quiet))) - u.setconfig('ui', 'verbose', str(bool(cmd_verbose))) - u.setconfig('ui', 'debug', str(bool(cmd_debug))) + u.setconfig(b'ui', b'quiet', pycompat.bytestr(bool(cmd_quiet))) + u.setconfig(b'ui', b'verbose', pycompat.bytestr(bool(cmd_verbose))) + u.setconfig(b'ui', b'debug', pycompat.bytestr(bool(cmd_debug))) check = '' if u.debugflag:
--- a/tests/test-unbundlehash.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-unbundlehash.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - Test wire protocol unbundle with hashed heads (capability: unbundlehash) $ cat << EOF >> $HGRCPATH
--- a/tests/test-uncommit.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-uncommit.t Thu Jul 19 13:55:54 2018 -0400 @@ -278,6 +278,13 @@ 12: draft $ hg commit -m 'update ab again' +Phase is preserved + + $ hg uncommit --keep --config phases.new-commit=secret + $ hg phase -r . + 15: draft + $ hg commit --amend -m 'update ab again' + Uncommit with public parent $ hg phase -p "::.^" @@ -294,7 +301,7 @@ $ hg status A xyz $ hg phase -r . - 16: draft + 18: draft $ hg phase -r ".^" 12: public
--- a/tests/test-unified-test.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-unified-test.t Thu Jul 19 13:55:54 2018 -0400 @@ -84,7 +84,7 @@ foo/bar\r (no-eol) (esc) #endif $ printf 'foo/bar\rfoo/bar\r' - foo.bar\r \(no-eol\) (re) (esc) + foo.bar\r [(]no-eol[)] (re) (esc) foo.bar\r \(no-eol\) (re) testing hghave
--- a/tests/test-update-branches.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-update-branches.t Thu Jul 19 13:55:54 2018 -0400 @@ -516,11 +516,33 @@ $ hg bookmarks * bm 5:ff252e8273df +Test that we abort before we warn about the hidden commit if the working +directory is dirty + $ echo conflict > a + $ hg up --hidden 3 + abort: uncommitted changes + (commit or update --clean to discard changes) + [255] + +Test that we still warn also when there are conflicts + $ hg up -m --hidden 3 + merging a + warning: conflicts while merging a! (edit, then use 'hg resolve --mark') + 0 files updated, 0 files merged, 0 files removed, 1 files unresolved + use 'hg resolve' to retry unresolved file merges + (leaving bookmark bm) + updated to hidden changeset 6efa171f091b + (hidden revision '6efa171f091b' was rewritten as: d047485b3896) + [1] + $ hg revert -r . a + $ hg resolve -m + (no more unresolved files) + Test that 4 is detected as the no-argument destination from 3 and also moves the bookmark with it $ hg up --quiet 0 # we should be able to update to 3 directly $ hg up --quiet --hidden 3 # but not implemented yet. - updating to a hidden changeset 6efa171f091b + updated to hidden changeset 6efa171f091b (hidden revision '6efa171f091b' was rewritten as: d047485b3896) $ hg book -f bm $ hg up @@ -532,7 +554,7 @@ Test that 5 is detected as a valid destination from 1 $ hg up --quiet 0 # we should be able to update to 3 directly $ hg up --quiet --hidden 3 # but not implemented yet. - updating to a hidden changeset 6efa171f091b + updated to hidden changeset 6efa171f091b (hidden revision '6efa171f091b' was rewritten as: d047485b3896) $ hg up 5 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-upgrade-repo.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-upgrade-repo.t Thu Jul 19 13:55:54 2018 -0400 @@ -56,6 +56,7 @@ fncache: yes dotencode: yes generaldelta: yes + sparserevlog: no plain-cl-delta: yes compression: zlib $ hg debugformat --verbose @@ -63,6 +64,7 @@ fncache: yes yes yes dotencode: yes yes yes generaldelta: yes yes yes + sparserevlog: no no no plain-cl-delta: yes yes yes compression: zlib zlib zlib $ hg debugformat --verbose --config format.usegfncache=no @@ -70,6 +72,7 @@ fncache: yes yes yes dotencode: yes yes yes generaldelta: yes yes yes + sparserevlog: no no no plain-cl-delta: yes yes yes compression: zlib zlib zlib $ hg debugformat --verbose --config format.usegfncache=no --color=debug @@ -77,6 +80,7 @@ [formatvariant.name.uptodate|fncache: ][formatvariant.repo.uptodate| yes][formatvariant.config.default| yes][formatvariant.default| yes] [formatvariant.name.uptodate|dotencode: ][formatvariant.repo.uptodate| yes][formatvariant.config.default| yes][formatvariant.default| yes] [formatvariant.name.uptodate|generaldelta: ][formatvariant.repo.uptodate| yes][formatvariant.config.default| yes][formatvariant.default| yes] + [formatvariant.name.uptodate|sparserevlog: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no] [formatvariant.name.uptodate|plain-cl-delta:][formatvariant.repo.uptodate| yes][formatvariant.config.default| yes][formatvariant.default| yes] [formatvariant.name.uptodate|compression: ][formatvariant.repo.uptodate| zlib][formatvariant.config.default| zlib][formatvariant.default| zlib] $ hg debugformat -Tjson @@ -100,6 +104,12 @@ "repo": true }, { + "config": false, + "default": false, + "name": "sparserevlog", + "repo": false + }, + { "config": true, "default": true, "name": "plain-cl-delta", @@ -170,6 +180,7 @@ fncache: no dotencode: no generaldelta: no + sparserevlog: no plain-cl-delta: yes compression: zlib $ hg debugformat --verbose @@ -177,6 +188,7 @@ fncache: no yes yes dotencode: no yes yes generaldelta: no yes yes + sparserevlog: no no no plain-cl-delta: yes yes yes compression: zlib zlib zlib $ hg debugformat --verbose --config format.usegeneraldelta=no @@ -184,6 +196,7 @@ fncache: no yes yes dotencode: no yes yes generaldelta: no no yes + sparserevlog: no no no plain-cl-delta: yes yes yes compression: zlib zlib zlib $ hg debugformat --verbose --config format.usegeneraldelta=no --color=debug @@ -191,6 +204,7 @@ [formatvariant.name.mismatchconfig|fncache: ][formatvariant.repo.mismatchconfig| no][formatvariant.config.default| yes][formatvariant.default| yes] [formatvariant.name.mismatchconfig|dotencode: ][formatvariant.repo.mismatchconfig| no][formatvariant.config.default| yes][formatvariant.default| yes] [formatvariant.name.mismatchdefault|generaldelta: ][formatvariant.repo.mismatchdefault| no][formatvariant.config.special| no][formatvariant.default| yes] + [formatvariant.name.uptodate|sparserevlog: ][formatvariant.repo.uptodate| no][formatvariant.config.default| no][formatvariant.default| no] [formatvariant.name.uptodate|plain-cl-delta:][formatvariant.repo.uptodate| yes][formatvariant.config.default| yes][formatvariant.default| yes] [formatvariant.name.uptodate|compression: ][formatvariant.repo.uptodate| zlib][formatvariant.config.default| zlib][formatvariant.default| zlib] $ hg debugupgraderepo @@ -697,3 +711,42 @@ > [format] > maxchainlen = 9001 > EOF + +Check upgrading a sparse-revlog repository +--------------------------------------- + + $ hg init sparserevlogrepo + $ cd sparserevlogrepo + $ touch foo + $ hg add foo + $ hg -q commit -m "foo" + $ cat .hg/requires + dotencode + fncache + generaldelta + revlogv1 + store + +Check that we can add the sparse-revlog format requirement + $ hg --config format.sparse-revlog=yes debugupgraderepo --run >/dev/null + copy of old repository backed up at $TESTTMP/sparserevlogrepo/.hg/upgradebackup.* (glob) + the old repository will not be deleted; remove it to free up disk space once the upgraded repository is verified + $ cat .hg/requires + dotencode + fncache + generaldelta + revlogv1 + sparserevlog + store + +Check that we can remove the sparse-revlog format requirement + $ hg --config format.sparse-revlog=no debugupgraderepo --run >/dev/null + copy of old repository backed up at $TESTTMP/sparserevlogrepo/.hg/upgradebackup.* (glob) + the old repository will not be deleted; remove it to free up disk space once the upgraded repository is verified + $ cat .hg/requires + dotencode + fncache + generaldelta + revlogv1 + store + $ cd ..
--- a/tests/test-url.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-url.py Thu Jul 19 13:55:54 2018 -0400 @@ -20,17 +20,17 @@ check(_verifycert(cert('example.com'), 'example.com'), None) check(_verifycert(cert('example.com'), 'www.example.com'), - 'certificate is for example.com') + b'certificate is for example.com') check(_verifycert(cert('www.example.com'), 'example.com'), - 'certificate is for www.example.com') + b'certificate is for www.example.com') # Test wildcard certificates check(_verifycert(cert('*.example.com'), 'www.example.com'), None) check(_verifycert(cert('*.example.com'), 'example.com'), - 'certificate is for *.example.com') + b'certificate is for *.example.com') check(_verifycert(cert('*.example.com'), 'w.w.example.com'), - 'certificate is for *.example.com') + b'certificate is for *.example.com') # Test subjectAltName san_cert = {'subject': ((('commonName', 'example.com'),),), @@ -42,7 +42,7 @@ None) # no fallback to subject commonName when subjectAltName has DNS check(_verifycert(san_cert, 'example.com'), - 'certificate is for *.example.net, example.net') + b'certificate is for *.example.net, example.net') # fallback to subject commonName when no DNS in subjectAltName san_cert = {'subject': ((('commonName', 'example.com'),),), 'subjectAltName': (('IP Address', '8.8.8.8'),)} @@ -50,84 +50,84 @@ # Avoid some pitfalls check(_verifycert(cert('*.foo'), 'foo'), - 'certificate is for *.foo') + b'certificate is for *.foo') check(_verifycert(cert('*o'), 'foo'), None) check(_verifycert({'subject': ()}, 'example.com'), - 'no commonName or subjectAltName found in certificate') + b'no commonName or subjectAltName found in certificate') check(_verifycert(None, 'example.com'), - 'no certificate received') + b'no certificate received') # Unicode (IDN) certname isn't supported check(_verifycert(cert(u'\u4f8b.jp'), 'example.jp'), - 'IDN in certificate not supported') + b'IDN in certificate not supported') # The following tests are from CPython's test_ssl.py. check(_verifycert(cert('example.com'), 'example.com'), None) check(_verifycert(cert('example.com'), 'ExAmple.cOm'), None) check(_verifycert(cert('example.com'), 'www.example.com'), - 'certificate is for example.com') + b'certificate is for example.com') check(_verifycert(cert('example.com'), '.example.com'), - 'certificate is for example.com') + b'certificate is for example.com') check(_verifycert(cert('example.com'), 'example.org'), - 'certificate is for example.com') + b'certificate is for example.com') check(_verifycert(cert('example.com'), 'exampleXcom'), - 'certificate is for example.com') + b'certificate is for example.com') check(_verifycert(cert('*.a.com'), 'foo.a.com'), None) check(_verifycert(cert('*.a.com'), 'bar.foo.a.com'), - 'certificate is for *.a.com') + b'certificate is for *.a.com') check(_verifycert(cert('*.a.com'), 'a.com'), - 'certificate is for *.a.com') + b'certificate is for *.a.com') check(_verifycert(cert('*.a.com'), 'Xa.com'), - 'certificate is for *.a.com') + b'certificate is for *.a.com') check(_verifycert(cert('*.a.com'), '.a.com'), - 'certificate is for *.a.com') + b'certificate is for *.a.com') # only match one left-most wildcard check(_verifycert(cert('f*.com'), 'foo.com'), None) check(_verifycert(cert('f*.com'), 'f.com'), None) check(_verifycert(cert('f*.com'), 'bar.com'), - 'certificate is for f*.com') + b'certificate is for f*.com') check(_verifycert(cert('f*.com'), 'foo.a.com'), - 'certificate is for f*.com') + b'certificate is for f*.com') check(_verifycert(cert('f*.com'), 'bar.foo.com'), - 'certificate is for f*.com') + b'certificate is for f*.com') # NULL bytes are bad, CVE-2013-4073 check(_verifycert(cert('null.python.org\x00example.org'), 'null.python.org\x00example.org'), None) check(_verifycert(cert('null.python.org\x00example.org'), 'example.org'), - 'certificate is for null.python.org\x00example.org') + b'certificate is for null.python.org\x00example.org') check(_verifycert(cert('null.python.org\x00example.org'), 'null.python.org'), - 'certificate is for null.python.org\x00example.org') + b'certificate is for null.python.org\x00example.org') # error cases with wildcards check(_verifycert(cert('*.*.a.com'), 'bar.foo.a.com'), - 'certificate is for *.*.a.com') + b'certificate is for *.*.a.com') check(_verifycert(cert('*.*.a.com'), 'a.com'), - 'certificate is for *.*.a.com') + b'certificate is for *.*.a.com') check(_verifycert(cert('*.*.a.com'), 'Xa.com'), - 'certificate is for *.*.a.com') + b'certificate is for *.*.a.com') check(_verifycert(cert('*.*.a.com'), '.a.com'), - 'certificate is for *.*.a.com') + b'certificate is for *.*.a.com') check(_verifycert(cert('a.*.com'), 'a.foo.com'), - 'certificate is for a.*.com') + b'certificate is for a.*.com') check(_verifycert(cert('a.*.com'), 'a..com'), - 'certificate is for a.*.com') + b'certificate is for a.*.com') check(_verifycert(cert('a.*.com'), 'a.com'), - 'certificate is for a.*.com') + b'certificate is for a.*.com') # wildcard doesn't match IDNA prefix 'xn--' idna = u'püthon.python.org'.encode('idna').decode('ascii') check(_verifycert(cert(idna), idna), None) check(_verifycert(cert('x*.python.org'), idna), - 'certificate is for x*.python.org') + b'certificate is for x*.python.org') check(_verifycert(cert('xn--p*.python.org'), idna), - 'certificate is for xn--p*.python.org') + b'certificate is for xn--p*.python.org') # wildcard in first fragment and IDNA A-labels in sequent fragments # are supported. @@ -140,10 +140,10 @@ None) check(_verifycert(cert(idna), u'ftp.pythön.org'.encode('idna').decode('ascii')), - 'certificate is for www*.xn--pythn-mua.org') + b'certificate is for www*.xn--pythn-mua.org') check(_verifycert(cert(idna), u'pythön.org'.encode('idna').decode('ascii')), - 'certificate is for www*.xn--pythn-mua.org') + b'certificate is for www*.xn--pythn-mua.org') c = { 'notAfter': 'Jun 26 21:41:46 2011 GMT', @@ -158,10 +158,10 @@ check(_verifycert(c, 'linuxfr.com'), None) # Not a "DNS" entry check(_verifycert(c, '<unsupported>'), - 'certificate is for linuxfr.org, linuxfr.com') + b'certificate is for linuxfr.org, linuxfr.com') # When there is a subjectAltName, commonName isn't used check(_verifycert(c, 'linuxfrz.org'), - 'certificate is for linuxfr.org, linuxfr.com') + b'certificate is for linuxfr.org, linuxfr.com') # A pristine real-world example c = { @@ -175,10 +175,10 @@ ), } check(_verifycert(c, 'mail.google.com'), None) -check(_verifycert(c, 'gmail.com'), 'certificate is for mail.google.com') +check(_verifycert(c, 'gmail.com'), b'certificate is for mail.google.com') # Only commonName is considered -check(_verifycert(c, 'California'), 'certificate is for mail.google.com') +check(_verifycert(c, 'California'), b'certificate is for mail.google.com') # Neither commonName nor subjectAltName c = { @@ -191,7 +191,7 @@ ), } check(_verifycert(c, 'mail.google.com'), - 'no commonName or subjectAltName found in certificate') + b'no commonName or subjectAltName found in certificate') # No DNS entry in subjectAltName but a commonName c = { @@ -218,25 +218,27 @@ 'subjectAltName': (('othername', 'blabla'),), } check(_verifycert(c, 'google.com'), - 'no commonName or subjectAltName found in certificate') + b'no commonName or subjectAltName found in certificate') # Empty cert / no cert -check(_verifycert(None, 'example.com'), 'no certificate received') -check(_verifycert({}, 'example.com'), 'no certificate received') +check(_verifycert(None, 'example.com'), b'no certificate received') +check(_verifycert({}, 'example.com'), b'no certificate received') # avoid denials of service by refusing more than one # wildcard per fragment. check(_verifycert({'subject': (((u'commonName', u'a*b.com'),),)}, 'axxb.com'), None) check(_verifycert({'subject': (((u'commonName', u'a*b.co*'),),)}, - 'axxb.com'), 'certificate is for a*b.co*') + 'axxb.com'), b'certificate is for a*b.co*') check(_verifycert({'subject': (((u'commonName', u'a*b*.com'),),)}, 'axxbxxc.com'), - 'too many wildcards in certificate DNS name: a*b*.com') + b'too many wildcards in certificate DNS name: a*b*.com') def test_url(): """ + >>> from mercurial import error, pycompat >>> from mercurial.util import url + >>> from mercurial.utils.stringutil import forcebytestr This tests for edge cases in url.URL's parsing algorithm. Most of these aren't useful for documentation purposes, so they aren't @@ -244,119 +246,120 @@ Query strings and fragments: - >>> url('http://host/a?b#c') + >>> url(b'http://host/a?b#c') <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'> - >>> url('http://host/a?') + >>> url(b'http://host/a?') <url scheme: 'http', host: 'host', path: 'a'> - >>> url('http://host/a#b#c') + >>> url(b'http://host/a#b#c') <url scheme: 'http', host: 'host', path: 'a', fragment: 'b#c'> - >>> url('http://host/a#b?c') + >>> url(b'http://host/a#b?c') <url scheme: 'http', host: 'host', path: 'a', fragment: 'b?c'> - >>> url('http://host/?a#b') + >>> url(b'http://host/?a#b') <url scheme: 'http', host: 'host', path: '', query: 'a', fragment: 'b'> - >>> url('http://host/?a#b', parsequery=False) + >>> url(b'http://host/?a#b', parsequery=False) <url scheme: 'http', host: 'host', path: '?a', fragment: 'b'> - >>> url('http://host/?a#b', parsefragment=False) + >>> url(b'http://host/?a#b', parsefragment=False) <url scheme: 'http', host: 'host', path: '', query: 'a#b'> - >>> url('http://host/?a#b', parsequery=False, parsefragment=False) + >>> url(b'http://host/?a#b', parsequery=False, parsefragment=False) <url scheme: 'http', host: 'host', path: '?a#b'> IPv6 addresses: - >>> url('ldap://[2001:db8::7]/c=GB?objectClass?one') + >>> url(b'ldap://[2001:db8::7]/c=GB?objectClass?one') <url scheme: 'ldap', host: '[2001:db8::7]', path: 'c=GB', query: 'objectClass?one'> - >>> url('ldap://joe:xxx@[2001:db8::7]:80/c=GB?objectClass?one') + >>> url(b'ldap://joe:xxx@[2001:db8::7]:80/c=GB?objectClass?one') <url scheme: 'ldap', user: 'joe', passwd: 'xxx', host: '[2001:db8::7]', port: '80', path: 'c=GB', query: 'objectClass?one'> Missing scheme, host, etc.: - >>> url('://192.0.2.16:80/') + >>> url(b'://192.0.2.16:80/') <url path: '://192.0.2.16:80/'> - >>> url('https://mercurial-scm.org') + >>> url(b'https://mercurial-scm.org') <url scheme: 'https', host: 'mercurial-scm.org'> - >>> url('/foo') + >>> url(b'/foo') <url path: '/foo'> - >>> url('bundle:/foo') + >>> url(b'bundle:/foo') <url scheme: 'bundle', path: '/foo'> - >>> url('a?b#c') + >>> url(b'a?b#c') <url path: 'a?b', fragment: 'c'> - >>> url('http://x.com?arg=/foo') + >>> url(b'http://x.com?arg=/foo') <url scheme: 'http', host: 'x.com', query: 'arg=/foo'> - >>> url('http://joe:xxx@/foo') + >>> url(b'http://joe:xxx@/foo') <url scheme: 'http', user: 'joe', passwd: 'xxx', path: 'foo'> Just a scheme and a path: - >>> url('mailto:John.Doe@example.com') + >>> url(b'mailto:John.Doe@example.com') <url scheme: 'mailto', path: 'John.Doe@example.com'> - >>> url('a:b:c:d') + >>> url(b'a:b:c:d') <url path: 'a:b:c:d'> - >>> url('aa:bb:cc:dd') + >>> url(b'aa:bb:cc:dd') <url scheme: 'aa', path: 'bb:cc:dd'> SSH examples: - >>> url('ssh://joe@host//home/joe') + >>> url(b'ssh://joe@host//home/joe') <url scheme: 'ssh', user: 'joe', host: 'host', path: '/home/joe'> - >>> url('ssh://joe:xxx@host/src') + >>> url(b'ssh://joe:xxx@host/src') <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', path: 'src'> - >>> url('ssh://joe:xxx@host') + >>> url(b'ssh://joe:xxx@host') <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host'> - >>> url('ssh://joe@host') + >>> url(b'ssh://joe@host') <url scheme: 'ssh', user: 'joe', host: 'host'> - >>> url('ssh://host') + >>> url(b'ssh://host') <url scheme: 'ssh', host: 'host'> - >>> url('ssh://') + >>> url(b'ssh://') <url scheme: 'ssh'> - >>> url('ssh:') + >>> url(b'ssh:') <url scheme: 'ssh'> Non-numeric port: - >>> url('http://example.com:dd') + >>> url(b'http://example.com:dd') <url scheme: 'http', host: 'example.com', port: 'dd'> - >>> url('ssh://joe:xxx@host:ssh/foo') + >>> url(b'ssh://joe:xxx@host:ssh/foo') <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', port: 'ssh', path: 'foo'> Bad authentication credentials: - >>> url('http://joe@joeville:123@4:@host/a?b#c') + >>> url(b'http://joe@joeville:123@4:@host/a?b#c') <url scheme: 'http', user: 'joe@joeville', passwd: '123@4:', host: 'host', path: 'a', query: 'b', fragment: 'c'> - >>> url('http://!*#?/@!*#?/:@host/a?b#c') + >>> url(b'http://!*#?/@!*#?/:@host/a?b#c') <url scheme: 'http', host: '!*', fragment: '?/@!*#?/:@host/a?b#c'> - >>> url('http://!*#?@!*#?:@host/a?b#c') + >>> url(b'http://!*#?@!*#?:@host/a?b#c') <url scheme: 'http', host: '!*', fragment: '?@!*#?:@host/a?b#c'> - >>> url('http://!*@:!*@@host/a?b#c') + >>> url(b'http://!*@:!*@@host/a?b#c') <url scheme: 'http', user: '!*@', passwd: '!*@', host: 'host', path: 'a', query: 'b', fragment: 'c'> File paths: - >>> url('a/b/c/d.g.f') + >>> url(b'a/b/c/d.g.f') <url path: 'a/b/c/d.g.f'> - >>> url('/x///z/y/') + >>> url(b'/x///z/y/') <url path: '/x///z/y/'> - >>> url('/foo:bar') + >>> url(b'/foo:bar') <url path: '/foo:bar'> - >>> url('\\\\foo:bar') + >>> url(b'\\\\foo:bar') <url path: '\\\\foo:bar'> - >>> url('./foo:bar') + >>> url(b'./foo:bar') <url path: './foo:bar'> Non-localhost file URL: - >>> u = url('file://mercurial-scm.org/foo') - Traceback (most recent call last): - File "<stdin>", line 1, in ? - Abort: file:// URLs can only refer to localhost + >>> try: + ... u = url(b'file://mercurial-scm.org/foo') + ... except error.Abort as e: + ... forcebytestr(e) + 'file:// URLs can only refer to localhost' Empty URL: - >>> u = url('') + >>> u = url(b'') >>> u <url path: ''> >>> str(u) @@ -364,54 +367,54 @@ Empty path with query string: - >>> str(url('http://foo/?bar')) + >>> str(url(b'http://foo/?bar')) 'http://foo/?bar' Invalid path: - >>> u = url('http://foo/bar') - >>> u.path = 'bar' + >>> u = url(b'http://foo/bar') + >>> u.path = b'bar' >>> str(u) 'http://foo/bar' - >>> u = url('file:/foo/bar/baz') + >>> u = url(b'file:/foo/bar/baz') >>> u <url scheme: 'file', path: '/foo/bar/baz'> >>> str(u) 'file:///foo/bar/baz' - >>> u.localpath() + >>> pycompat.bytestr(u.localpath()) '/foo/bar/baz' - >>> u = url('file:///foo/bar/baz') + >>> u = url(b'file:///foo/bar/baz') >>> u <url scheme: 'file', path: '/foo/bar/baz'> >>> str(u) 'file:///foo/bar/baz' - >>> u.localpath() + >>> pycompat.bytestr(u.localpath()) '/foo/bar/baz' - >>> u = url('file:///f:oo/bar/baz') + >>> u = url(b'file:///f:oo/bar/baz') >>> u <url scheme: 'file', path: 'f:oo/bar/baz'> >>> str(u) 'file:///f:oo/bar/baz' - >>> u.localpath() + >>> pycompat.bytestr(u.localpath()) 'f:oo/bar/baz' - >>> u = url('file://localhost/f:oo/bar/baz') + >>> u = url(b'file://localhost/f:oo/bar/baz') >>> u <url scheme: 'file', host: 'localhost', path: 'f:oo/bar/baz'> >>> str(u) 'file://localhost/f:oo/bar/baz' - >>> u.localpath() + >>> pycompat.bytestr(u.localpath()) 'f:oo/bar/baz' - >>> u = url('file:foo/bar/baz') + >>> u = url(b'file:foo/bar/baz') >>> u <url scheme: 'file', path: 'foo/bar/baz'> >>> str(u) 'file:foo/bar/baz' - >>> u.localpath() + >>> pycompat.bytestr(u.localpath()) 'foo/bar/baz' """
--- a/tests/test-walk.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-walk.t Thu Jul 19 13:55:54 2018 -0400 @@ -28,8 +28,9 @@ adding mammals/skunk $ hg commit -m "commit #0" - $ hg debugwalk - matcher: <alwaysmatcher> + $ hg debugwalk -v + * matcher: + <alwaysmatcher> f beans/black beans/black f beans/borlotti beans/borlotti f beans/kidney beans/kidney @@ -43,8 +44,9 @@ f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon f mammals/skunk mammals/skunk - $ hg debugwalk -I. - matcher: <includematcher includes='(?:)'> + $ hg debugwalk -v -I. + * matcher: + <includematcher includes='(?:)'> f beans/black beans/black f beans/borlotti beans/borlotti f beans/kidney beans/kidney @@ -60,8 +62,9 @@ f mammals/skunk mammals/skunk $ cd mammals - $ hg debugwalk - matcher: <alwaysmatcher> + $ hg debugwalk -v + * matcher: + <alwaysmatcher> f beans/black ../beans/black f beans/borlotti ../beans/borlotti f beans/kidney ../beans/kidney @@ -75,8 +78,11 @@ f mammals/Procyonidae/coatimundi Procyonidae/coatimundi f mammals/Procyonidae/raccoon Procyonidae/raccoon f mammals/skunk skunk - $ hg debugwalk -X ../beans - matcher: <differencematcher m1=<alwaysmatcher>, m2=<includematcher includes='(?:beans(?:/|$))'>> + $ hg debugwalk -v -X ../beans + * matcher: + <differencematcher + m1=<alwaysmatcher>, + m2=<includematcher includes='(?:beans(?:/|$))'>> f fennel ../fennel f fenugreek ../fenugreek f fiddlehead ../fiddlehead @@ -84,40 +90,50 @@ f mammals/Procyonidae/coatimundi Procyonidae/coatimundi f mammals/Procyonidae/raccoon Procyonidae/raccoon f mammals/skunk skunk - $ hg debugwalk -I '*k' - matcher: <includematcher includes='(?:mammals\\/[^/]*k(?:/|$))'> + $ hg debugwalk -v -I '*k' + * matcher: + <includematcher includes='(?:mammals/[^/]*k(?:/|$))'> f mammals/skunk skunk - $ hg debugwalk -I 'glob:*k' - matcher: <includematcher includes='(?:mammals\\/[^/]*k(?:/|$))'> + $ hg debugwalk -v -I 'glob:*k' + * matcher: + <includematcher includes='(?:mammals/[^/]*k(?:/|$))'> f mammals/skunk skunk - $ hg debugwalk -I 'relglob:*k' - matcher: <includematcher includes='(?:(?:|.*/)[^/]*k(?:/|$))'> + $ hg debugwalk -v -I 'relglob:*k' + * matcher: + <includematcher includes='(?:(?:|.*/)[^/]*k(?:/|$))'> f beans/black ../beans/black f fenugreek ../fenugreek f mammals/skunk skunk - $ hg debugwalk -I 'relglob:*k' . - matcher: <intersectionmatcher m1=<patternmatcher patterns='(?:mammals(?:/|$))'>, m2=<includematcher includes='(?:(?:|.*/)[^/]*k(?:/|$))'>> + $ hg debugwalk -v -I 'relglob:*k' . + * matcher: + <intersectionmatcher + m1=<patternmatcher patterns='(?:mammals(?:/|$))'>, + m2=<includematcher includes='(?:(?:|.*/)[^/]*k(?:/|$))'>> f mammals/skunk skunk - $ hg debugwalk -I 're:.*k$' - matcher: <includematcher includes='(?:.*k$)'> + $ hg debugwalk -v -I 're:.*k$' + * matcher: + <includematcher includes='(?:.*k$)'> f beans/black ../beans/black f fenugreek ../fenugreek f mammals/skunk skunk - $ hg debugwalk -I 'relre:.*k$' - matcher: <includematcher includes='(?:.*.*k$)'> + $ hg debugwalk -v -I 'relre:.*k$' + * matcher: + <includematcher includes='(?:.*.*k$)'> f beans/black ../beans/black f fenugreek ../fenugreek f mammals/skunk skunk - $ hg debugwalk -I 'path:beans' - matcher: <includematcher includes='(?:beans(?:/|$))'> + $ hg debugwalk -v -I 'path:beans' + * matcher: + <includematcher includes='(?:beans(?:/|$))'> f beans/black ../beans/black f beans/borlotti ../beans/borlotti f beans/kidney ../beans/kidney f beans/navy ../beans/navy f beans/pinto ../beans/pinto f beans/turtle ../beans/turtle - $ hg debugwalk -I 'relpath:detour/../../beans' - matcher: <includematcher includes='(?:beans(?:/|$))'> + $ hg debugwalk -v -I 'relpath:detour/../../beans' + * matcher: + <includematcher includes='(?:beans(?:/|$))'> f beans/black ../beans/black f beans/borlotti ../beans/borlotti f beans/kidney ../beans/kidney @@ -125,28 +141,35 @@ f beans/pinto ../beans/pinto f beans/turtle ../beans/turtle - $ hg debugwalk 'rootfilesin:' - matcher: <patternmatcher patterns='(?:[^/]+$)'> + $ hg debugwalk -v 'rootfilesin:' + * matcher: + <patternmatcher patterns='(?:[^/]+$)'> f fennel ../fennel f fenugreek ../fenugreek f fiddlehead ../fiddlehead - $ hg debugwalk -I 'rootfilesin:' - matcher: <includematcher includes='(?:[^/]+$)'> + $ hg debugwalk -v -I 'rootfilesin:' + * matcher: + <includematcher includes='(?:[^/]+$)'> f fennel ../fennel f fenugreek ../fenugreek f fiddlehead ../fiddlehead - $ hg debugwalk 'rootfilesin:.' - matcher: <patternmatcher patterns='(?:[^/]+$)'> + $ hg debugwalk -v 'rootfilesin:.' + * matcher: + <patternmatcher patterns='(?:[^/]+$)'> f fennel ../fennel f fenugreek ../fenugreek f fiddlehead ../fiddlehead - $ hg debugwalk -I 'rootfilesin:.' - matcher: <includematcher includes='(?:[^/]+$)'> + $ hg debugwalk -v -I 'rootfilesin:.' + * matcher: + <includematcher includes='(?:[^/]+$)'> f fennel ../fennel f fenugreek ../fenugreek f fiddlehead ../fiddlehead - $ hg debugwalk -X 'rootfilesin:' - matcher: <differencematcher m1=<alwaysmatcher>, m2=<includematcher includes='(?:[^/]+$)'>> + $ hg debugwalk -v -X 'rootfilesin:' + * matcher: + <differencematcher + m1=<alwaysmatcher>, + m2=<includematcher includes='(?:[^/]+$)'>> f beans/black ../beans/black f beans/borlotti ../beans/borlotti f beans/kidney ../beans/kidney @@ -157,44 +180,57 @@ f mammals/Procyonidae/coatimundi Procyonidae/coatimundi f mammals/Procyonidae/raccoon Procyonidae/raccoon f mammals/skunk skunk - $ hg debugwalk 'rootfilesin:fennel' - matcher: <patternmatcher patterns='(?:fennel/[^/]+$)'> - $ hg debugwalk -I 'rootfilesin:fennel' - matcher: <includematcher includes='(?:fennel/[^/]+$)'> - $ hg debugwalk 'rootfilesin:skunk' - matcher: <patternmatcher patterns='(?:skunk/[^/]+$)'> - $ hg debugwalk -I 'rootfilesin:skunk' - matcher: <includematcher includes='(?:skunk/[^/]+$)'> - $ hg debugwalk 'rootfilesin:beans' - matcher: <patternmatcher patterns='(?:beans/[^/]+$)'> + $ hg debugwalk -v 'rootfilesin:fennel' + * matcher: + <patternmatcher patterns='(?:fennel/[^/]+$)'> + $ hg debugwalk -v -I 'rootfilesin:fennel' + * matcher: + <includematcher includes='(?:fennel/[^/]+$)'> + $ hg debugwalk -v 'rootfilesin:skunk' + * matcher: + <patternmatcher patterns='(?:skunk/[^/]+$)'> + $ hg debugwalk -v -I 'rootfilesin:skunk' + * matcher: + <includematcher includes='(?:skunk/[^/]+$)'> + $ hg debugwalk -v 'rootfilesin:beans' + * matcher: + <patternmatcher patterns='(?:beans/[^/]+$)'> f beans/black ../beans/black f beans/borlotti ../beans/borlotti f beans/kidney ../beans/kidney f beans/navy ../beans/navy f beans/pinto ../beans/pinto f beans/turtle ../beans/turtle - $ hg debugwalk -I 'rootfilesin:beans' - matcher: <includematcher includes='(?:beans/[^/]+$)'> + $ hg debugwalk -v -I 'rootfilesin:beans' + * matcher: + <includematcher includes='(?:beans/[^/]+$)'> f beans/black ../beans/black f beans/borlotti ../beans/borlotti f beans/kidney ../beans/kidney f beans/navy ../beans/navy f beans/pinto ../beans/pinto f beans/turtle ../beans/turtle - $ hg debugwalk 'rootfilesin:mammals' - matcher: <patternmatcher patterns='(?:mammals/[^/]+$)'> + $ hg debugwalk -v 'rootfilesin:mammals' + * matcher: + <patternmatcher patterns='(?:mammals/[^/]+$)'> f mammals/skunk skunk - $ hg debugwalk -I 'rootfilesin:mammals' - matcher: <includematcher includes='(?:mammals/[^/]+$)'> + $ hg debugwalk -v -I 'rootfilesin:mammals' + * matcher: + <includematcher includes='(?:mammals/[^/]+$)'> f mammals/skunk skunk - $ hg debugwalk 'rootfilesin:mammals/' - matcher: <patternmatcher patterns='(?:mammals/[^/]+$)'> + $ hg debugwalk -v 'rootfilesin:mammals/' + * matcher: + <patternmatcher patterns='(?:mammals/[^/]+$)'> f mammals/skunk skunk - $ hg debugwalk -I 'rootfilesin:mammals/' - matcher: <includematcher includes='(?:mammals/[^/]+$)'> + $ hg debugwalk -v -I 'rootfilesin:mammals/' + * matcher: + <includematcher includes='(?:mammals/[^/]+$)'> f mammals/skunk skunk - $ hg debugwalk -X 'rootfilesin:mammals' - matcher: <differencematcher m1=<alwaysmatcher>, m2=<includematcher includes='(?:mammals/[^/]+$)'>> + $ hg debugwalk -v -X 'rootfilesin:mammals' + * matcher: + <differencematcher + m1=<alwaysmatcher>, + m2=<includematcher includes='(?:mammals/[^/]+$)'>> f beans/black ../beans/black f beans/borlotti ../beans/borlotti f beans/kidney ../beans/kidney @@ -208,169 +244,226 @@ f mammals/Procyonidae/coatimundi Procyonidae/coatimundi f mammals/Procyonidae/raccoon Procyonidae/raccoon - $ hg debugwalk . - matcher: <patternmatcher patterns='(?:mammals(?:/|$))'> + $ hg debugwalk -v . + * matcher: + <patternmatcher patterns='(?:mammals(?:/|$))'> f mammals/Procyonidae/cacomistle Procyonidae/cacomistle f mammals/Procyonidae/coatimundi Procyonidae/coatimundi f mammals/Procyonidae/raccoon Procyonidae/raccoon f mammals/skunk skunk - $ hg debugwalk -I. - matcher: <includematcher includes='(?:mammals(?:/|$))'> + $ hg debugwalk -v -I. + * matcher: + <includematcher includes='(?:mammals(?:/|$))'> f mammals/Procyonidae/cacomistle Procyonidae/cacomistle f mammals/Procyonidae/coatimundi Procyonidae/coatimundi f mammals/Procyonidae/raccoon Procyonidae/raccoon f mammals/skunk skunk - $ hg debugwalk Procyonidae - matcher: <patternmatcher patterns='(?:mammals\\/Procyonidae(?:/|$))'> + $ hg debugwalk -v Procyonidae + * matcher: + <patternmatcher patterns='(?:mammals/Procyonidae(?:/|$))'> f mammals/Procyonidae/cacomistle Procyonidae/cacomistle f mammals/Procyonidae/coatimundi Procyonidae/coatimundi f mammals/Procyonidae/raccoon Procyonidae/raccoon $ cd Procyonidae - $ hg debugwalk . - matcher: <patternmatcher patterns='(?:mammals\\/Procyonidae(?:/|$))'> + $ hg debugwalk -v . + * matcher: + <patternmatcher patterns='(?:mammals/Procyonidae(?:/|$))'> f mammals/Procyonidae/cacomistle cacomistle f mammals/Procyonidae/coatimundi coatimundi f mammals/Procyonidae/raccoon raccoon - $ hg debugwalk .. - matcher: <patternmatcher patterns='(?:mammals(?:/|$))'> + $ hg debugwalk -v .. + * matcher: + <patternmatcher patterns='(?:mammals(?:/|$))'> f mammals/Procyonidae/cacomistle cacomistle f mammals/Procyonidae/coatimundi coatimundi f mammals/Procyonidae/raccoon raccoon f mammals/skunk ../skunk $ cd .. - $ hg debugwalk ../beans - matcher: <patternmatcher patterns='(?:beans(?:/|$))'> + $ hg debugwalk -v ../beans + * matcher: + <patternmatcher patterns='(?:beans(?:/|$))'> f beans/black ../beans/black f beans/borlotti ../beans/borlotti f beans/kidney ../beans/kidney f beans/navy ../beans/navy f beans/pinto ../beans/pinto f beans/turtle ../beans/turtle - $ hg debugwalk . - matcher: <patternmatcher patterns='(?:mammals(?:/|$))'> + $ hg debugwalk -v . + * matcher: + <patternmatcher patterns='(?:mammals(?:/|$))'> f mammals/Procyonidae/cacomistle Procyonidae/cacomistle f mammals/Procyonidae/coatimundi Procyonidae/coatimundi f mammals/Procyonidae/raccoon Procyonidae/raccoon f mammals/skunk skunk - $ hg debugwalk .hg + $ hg debugwalk -v .hg abort: path 'mammals/.hg' is inside nested repo 'mammals' [255] - $ hg debugwalk ../.hg + $ hg debugwalk -v ../.hg abort: path contains illegal component: .hg [255] $ cd .. - $ hg debugwalk -Ibeans - matcher: <includematcher includes='(?:beans(?:/|$))'> + $ hg debugwalk -v -Ibeans + * matcher: + <includematcher includes='(?:beans(?:/|$))'> f beans/black beans/black f beans/borlotti beans/borlotti f beans/kidney beans/kidney f beans/navy beans/navy f beans/pinto beans/pinto f beans/turtle beans/turtle - $ hg debugwalk -I '{*,{b,m}*/*}k' - matcher: <includematcher includes='(?:(?:[^/]*|(?:b|m)[^/]*\\/[^/]*)k(?:/|$))'> + $ hg debugwalk -v -I '{*,{b,m}*/*}k' + * matcher: + <includematcher includes='(?:(?:[^/]*|(?:b|m)[^/]*/[^/]*)k(?:/|$))'> f beans/black beans/black f fenugreek fenugreek f mammals/skunk mammals/skunk - $ hg debugwalk -Ibeans mammals - matcher: <intersectionmatcher m1=<patternmatcher patterns='(?:mammals(?:/|$))'>, m2=<includematcher includes='(?:beans(?:/|$))'>> - $ hg debugwalk -Inon-existent - matcher: <includematcher includes='(?:non\\-existent(?:/|$))'> - $ hg debugwalk -Inon-existent -Ibeans/black - matcher: <includematcher includes='(?:non\\-existent(?:/|$)|beans\\/black(?:/|$))'> + $ hg debugwalk -v -Ibeans mammals + * matcher: + <intersectionmatcher + m1=<patternmatcher patterns='(?:mammals(?:/|$))'>, + m2=<includematcher includes='(?:beans(?:/|$))'>> + $ hg debugwalk -v -Inon-existent + * matcher: + <includematcher includes='(?:non\\-existent(?:/|$))'> + $ hg debugwalk -v -Inon-existent -Ibeans/black + * matcher: + <includematcher includes='(?:non\\-existent(?:/|$)|beans/black(?:/|$))'> f beans/black beans/black - $ hg debugwalk -Ibeans beans/black - matcher: <intersectionmatcher m1=<patternmatcher patterns='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans(?:/|$))'>> + $ hg debugwalk -v -Ibeans beans/black + * matcher: + <intersectionmatcher + m1=<patternmatcher patterns='(?:beans/black(?:/|$))'>, + m2=<includematcher includes='(?:beans(?:/|$))'>> f beans/black beans/black exact - $ hg debugwalk -Ibeans/black beans - matcher: <intersectionmatcher m1=<patternmatcher patterns='(?:beans(?:/|$))'>, m2=<includematcher includes='(?:beans\\/black(?:/|$))'>> + $ hg debugwalk -v -Ibeans/black beans + * matcher: + <intersectionmatcher + m1=<patternmatcher patterns='(?:beans(?:/|$))'>, + m2=<includematcher includes='(?:beans/black(?:/|$))'>> f beans/black beans/black - $ hg debugwalk -Xbeans/black beans - matcher: <differencematcher m1=<patternmatcher patterns='(?:beans(?:/|$))'>, m2=<includematcher includes='(?:beans\\/black(?:/|$))'>> + $ hg debugwalk -v -Xbeans/black beans + * matcher: + <differencematcher + m1=<patternmatcher patterns='(?:beans(?:/|$))'>, + m2=<includematcher includes='(?:beans/black(?:/|$))'>> f beans/borlotti beans/borlotti f beans/kidney beans/kidney f beans/navy beans/navy f beans/pinto beans/pinto f beans/turtle beans/turtle - $ hg debugwalk -Xbeans/black -Ibeans - matcher: <differencematcher m1=<includematcher includes='(?:beans(?:/|$))'>, m2=<includematcher includes='(?:beans\\/black(?:/|$))'>> + $ hg debugwalk -v -Xbeans/black -Ibeans + * matcher: + <differencematcher + m1=<includematcher includes='(?:beans(?:/|$))'>, + m2=<includematcher includes='(?:beans/black(?:/|$))'>> f beans/borlotti beans/borlotti f beans/kidney beans/kidney f beans/navy beans/navy f beans/pinto beans/pinto f beans/turtle beans/turtle - $ hg debugwalk -Xbeans/black beans/black - matcher: <differencematcher m1=<patternmatcher patterns='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans\\/black(?:/|$))'>> - $ hg debugwalk -Xbeans/black -Ibeans/black - matcher: <differencematcher m1=<includematcher includes='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans\\/black(?:/|$))'>> - $ hg debugwalk -Xbeans beans/black - matcher: <differencematcher m1=<patternmatcher patterns='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans(?:/|$))'>> - $ hg debugwalk -Xbeans -Ibeans/black - matcher: <differencematcher m1=<includematcher includes='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans(?:/|$))'>> - $ hg debugwalk 'glob:mammals/../beans/b*' - matcher: <patternmatcher patterns='(?:beans\\/b[^/]*$)'> + $ hg debugwalk -v -Xbeans/black beans/black + * matcher: + <differencematcher + m1=<patternmatcher patterns='(?:beans/black(?:/|$))'>, + m2=<includematcher includes='(?:beans/black(?:/|$))'>> + $ hg debugwalk -v -Xbeans/black -Ibeans/black + * matcher: + <differencematcher + m1=<includematcher includes='(?:beans/black(?:/|$))'>, + m2=<includematcher includes='(?:beans/black(?:/|$))'>> + $ hg debugwalk -v -Xbeans beans/black + * matcher: + <differencematcher + m1=<patternmatcher patterns='(?:beans/black(?:/|$))'>, + m2=<includematcher includes='(?:beans(?:/|$))'>> + $ hg debugwalk -v -Xbeans -Ibeans/black + * matcher: + <differencematcher + m1=<includematcher includes='(?:beans/black(?:/|$))'>, + m2=<includematcher includes='(?:beans(?:/|$))'>> + $ hg debugwalk -v 'glob:mammals/../beans/b*' + * matcher: + <patternmatcher patterns='(?:beans/b[^/]*$)'> f beans/black beans/black f beans/borlotti beans/borlotti - $ hg debugwalk '-X*/Procyonidae' mammals - matcher: <differencematcher m1=<patternmatcher patterns='(?:mammals(?:/|$))'>, m2=<includematcher includes='(?:[^/]*\\/Procyonidae(?:/|$))'>> + $ hg debugwalk -v '-X*/Procyonidae' mammals + * matcher: + <differencematcher + m1=<patternmatcher patterns='(?:mammals(?:/|$))'>, + m2=<includematcher includes='(?:[^/]*/Procyonidae(?:/|$))'>> f mammals/skunk mammals/skunk - $ hg debugwalk path:mammals - matcher: <patternmatcher patterns='(?:mammals(?:/|$))'> + $ hg debugwalk -v path:mammals + * matcher: + <patternmatcher patterns='(?:mammals(?:/|$))'> f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon f mammals/skunk mammals/skunk - $ hg debugwalk .. + $ hg debugwalk -v .. abort: .. not under root '$TESTTMP/t' [255] - $ hg debugwalk beans/../.. + $ hg debugwalk -v beans/../.. abort: beans/../.. not under root '$TESTTMP/t' [255] - $ hg debugwalk .hg + $ hg debugwalk -v .hg abort: path contains illegal component: .hg [255] - $ hg debugwalk beans/../.hg + $ hg debugwalk -v beans/../.hg abort: path contains illegal component: .hg [255] - $ hg debugwalk beans/../.hg/data + $ hg debugwalk -v beans/../.hg/data abort: path contains illegal component: .hg/data [255] - $ hg debugwalk beans/.hg + $ hg debugwalk -v beans/.hg abort: path 'beans/.hg' is inside nested repo 'beans' [255] Test explicit paths and excludes: - $ hg debugwalk fennel -X fennel - matcher: <differencematcher m1=<patternmatcher patterns='(?:fennel(?:/|$))'>, m2=<includematcher includes='(?:fennel(?:/|$))'>> - $ hg debugwalk fennel -X 'f*' - matcher: <differencematcher m1=<patternmatcher patterns='(?:fennel(?:/|$))'>, m2=<includematcher includes='(?:f[^/]*(?:/|$))'>> - $ hg debugwalk beans/black -X 'path:beans' - matcher: <differencematcher m1=<patternmatcher patterns='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans(?:/|$))'>> - $ hg debugwalk -I 'path:beans/black' -X 'path:beans' - matcher: <differencematcher m1=<includematcher includes='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans(?:/|$))'>> + $ hg debugwalk -v fennel -X fennel + * matcher: + <differencematcher + m1=<patternmatcher patterns='(?:fennel(?:/|$))'>, + m2=<includematcher includes='(?:fennel(?:/|$))'>> + $ hg debugwalk -v fennel -X 'f*' + * matcher: + <differencematcher + m1=<patternmatcher patterns='(?:fennel(?:/|$))'>, + m2=<includematcher includes='(?:f[^/]*(?:/|$))'>> + $ hg debugwalk -v beans/black -X 'path:beans' + * matcher: + <differencematcher + m1=<patternmatcher patterns='(?:beans/black(?:/|$))'>, + m2=<includematcher includes='(?:beans(?:/|$))'>> + $ hg debugwalk -v -I 'path:beans/black' -X 'path:beans' + * matcher: + <differencematcher + m1=<includematcher includes='(?:beans/black(?:/|$))'>, + m2=<includematcher includes='(?:beans(?:/|$))'>> Test absolute paths: - $ hg debugwalk `pwd`/beans - matcher: <patternmatcher patterns='(?:beans(?:/|$))'> + $ hg debugwalk -v `pwd`/beans + * matcher: + <patternmatcher patterns='(?:beans(?:/|$))'> f beans/black beans/black f beans/borlotti beans/borlotti f beans/kidney beans/kidney f beans/navy beans/navy f beans/pinto beans/pinto f beans/turtle beans/turtle - $ hg debugwalk `pwd`/.. + $ hg debugwalk -v `pwd`/.. abort: $TESTTMP/t/.. not under root '$TESTTMP/t' [255] Test patterns: - $ hg debugwalk glob:\* - matcher: <patternmatcher patterns='(?:[^/]*$)'> + $ hg debugwalk -v glob:\* + * matcher: + <patternmatcher patterns='(?:[^/]*$)'> f fennel fennel f fenugreek fenugreek f fiddlehead fiddlehead @@ -379,138 +472,165 @@ $ hg addremove adding glob:glob warning: filename contains ':', which is reserved on Windows: 'glob:glob' - $ hg debugwalk glob:\* - matcher: <patternmatcher patterns='(?:[^/]*$)'> + $ hg debugwalk -v glob:\* + * matcher: + <patternmatcher patterns='(?:[^/]*$)'> f fennel fennel f fenugreek fenugreek f fiddlehead fiddlehead f glob:glob glob:glob - $ hg debugwalk glob:glob - matcher: <patternmatcher patterns='(?:glob$)'> + $ hg debugwalk -v glob:glob + * matcher: + <patternmatcher patterns='(?:glob$)'> glob: $ENOENT$ - $ hg debugwalk glob:glob:glob - matcher: <patternmatcher patterns='(?:glob\\:glob$)'> + $ hg debugwalk -v glob:glob:glob + * matcher: + <patternmatcher patterns='(?:glob:glob$)'> f glob:glob glob:glob exact - $ hg debugwalk path:glob:glob - matcher: <patternmatcher patterns='(?:glob\\:glob(?:/|$))'> + $ hg debugwalk -v path:glob:glob + * matcher: + <patternmatcher patterns='(?:glob:glob(?:/|$))'> f glob:glob glob:glob exact $ rm glob:glob $ hg addremove removing glob:glob #endif - $ hg debugwalk 'glob:**e' - matcher: <patternmatcher patterns='(?:.*e$)'> + $ hg debugwalk -v 'glob:**e' + * matcher: + <patternmatcher patterns='(?:.*e$)'> f beans/turtle beans/turtle f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle - $ hg debugwalk 're:.*[kb]$' - matcher: <patternmatcher patterns='(?:.*[kb]$)'> + $ hg debugwalk -v 're:.*[kb]$' + * matcher: + <patternmatcher patterns='(?:.*[kb]$)'> f beans/black beans/black f fenugreek fenugreek f mammals/skunk mammals/skunk - $ hg debugwalk path:beans/black - matcher: <patternmatcher patterns='(?:beans\\/black(?:/|$))'> + $ hg debugwalk -v path:beans/black + * matcher: + <patternmatcher patterns='(?:beans/black(?:/|$))'> f beans/black beans/black exact - $ hg debugwalk path:beans//black - matcher: <patternmatcher patterns='(?:beans\\/black(?:/|$))'> + $ hg debugwalk -v path:beans//black + * matcher: + <patternmatcher patterns='(?:beans/black(?:/|$))'> f beans/black beans/black exact - $ hg debugwalk relglob:Procyonidae - matcher: <patternmatcher patterns='(?:(?:|.*/)Procyonidae$)'> - $ hg debugwalk 'relglob:Procyonidae/**' - matcher: <patternmatcher patterns='(?:(?:|.*/)Procyonidae\\/.*$)'> + $ hg debugwalk -v relglob:Procyonidae + * matcher: + <patternmatcher patterns='(?:(?:|.*/)Procyonidae$)'> + $ hg debugwalk -v 'relglob:Procyonidae/**' + * matcher: + <patternmatcher patterns='(?:(?:|.*/)Procyonidae/.*$)'> f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon - $ hg debugwalk 'relglob:Procyonidae/**' fennel - matcher: <patternmatcher patterns='(?:(?:|.*/)Procyonidae\\/.*$|fennel(?:/|$))'> + $ hg debugwalk -v 'relglob:Procyonidae/**' fennel + * matcher: + <patternmatcher patterns='(?:(?:|.*/)Procyonidae/.*$|fennel(?:/|$))'> f fennel fennel exact f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon - $ hg debugwalk beans 'glob:beans/*' - matcher: <patternmatcher patterns='(?:beans(?:/|$)|beans\\/[^/]*$)'> + $ hg debugwalk -v beans 'glob:beans/*' + * matcher: + <patternmatcher patterns='(?:beans(?:/|$)|beans/[^/]*$)'> f beans/black beans/black f beans/borlotti beans/borlotti f beans/kidney beans/kidney f beans/navy beans/navy f beans/pinto beans/pinto f beans/turtle beans/turtle - $ hg debugwalk 'glob:mamm**' - matcher: <patternmatcher patterns='(?:mamm.*$)'> + $ hg debugwalk -v 'glob:mamm**' + * matcher: + <patternmatcher patterns='(?:mamm.*$)'> f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon f mammals/skunk mammals/skunk - $ hg debugwalk 'glob:mamm**' fennel - matcher: <patternmatcher patterns='(?:mamm.*$|fennel(?:/|$))'> + $ hg debugwalk -v 'glob:mamm**' fennel + * matcher: + <patternmatcher patterns='(?:mamm.*$|fennel(?:/|$))'> f fennel fennel exact f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon f mammals/skunk mammals/skunk - $ hg debugwalk 'glob:j*' - matcher: <patternmatcher patterns='(?:j[^/]*$)'> - $ hg debugwalk NOEXIST - matcher: <patternmatcher patterns='(?:NOEXIST(?:/|$))'> + $ hg debugwalk -v 'glob:j*' + * matcher: + <patternmatcher patterns='(?:j[^/]*$)'> + $ hg debugwalk -v NOEXIST + * matcher: + <patternmatcher patterns='(?:NOEXIST(?:/|$))'> NOEXIST: * (glob) #if fifo $ mkfifo fifo - $ hg debugwalk fifo - matcher: <patternmatcher patterns='(?:fifo(?:/|$))'> + $ hg debugwalk -v fifo + * matcher: + <patternmatcher patterns='(?:fifo(?:/|$))'> fifo: unsupported file type (type is fifo) #endif $ rm fenugreek - $ hg debugwalk fenugreek - matcher: <patternmatcher patterns='(?:fenugreek(?:/|$))'> + $ hg debugwalk -v fenugreek + * matcher: + <patternmatcher patterns='(?:fenugreek(?:/|$))'> f fenugreek fenugreek exact $ hg rm fenugreek - $ hg debugwalk fenugreek - matcher: <patternmatcher patterns='(?:fenugreek(?:/|$))'> + $ hg debugwalk -v fenugreek + * matcher: + <patternmatcher patterns='(?:fenugreek(?:/|$))'> f fenugreek fenugreek exact $ touch new - $ hg debugwalk new - matcher: <patternmatcher patterns='(?:new(?:/|$))'> + $ hg debugwalk -v new + * matcher: + <patternmatcher patterns='(?:new(?:/|$))'> f new new exact $ mkdir ignored $ touch ignored/file $ echo '^ignored$' > .hgignore - $ hg debugwalk ignored - matcher: <patternmatcher patterns='(?:ignored(?:/|$))'> - $ hg debugwalk ignored/file - matcher: <patternmatcher patterns='(?:ignored\\/file(?:/|$))'> + $ hg debugwalk -v ignored + * matcher: + <patternmatcher patterns='(?:ignored(?:/|$))'> + $ hg debugwalk -v ignored/file + * matcher: + <patternmatcher patterns='(?:ignored/file(?:/|$))'> f ignored/file ignored/file exact Test listfile and listfile0 $ $PYTHON -c "open('listfile0', 'wb').write(b'fenugreek\0new\0')" - $ hg debugwalk -I 'listfile0:listfile0' - matcher: <includematcher includes='(?:fenugreek(?:/|$)|new(?:/|$))'> + $ hg debugwalk -v -I 'listfile0:listfile0' + * matcher: + <includematcher includes='(?:fenugreek(?:/|$)|new(?:/|$))'> f fenugreek fenugreek f new new $ $PYTHON -c "open('listfile', 'wb').write(b'fenugreek\nnew\r\nmammals/skunk\n')" - $ hg debugwalk -I 'listfile:listfile' - matcher: <includematcher includes='(?:fenugreek(?:/|$)|new(?:/|$)|mammals\\/skunk(?:/|$))'> + $ hg debugwalk -v -I 'listfile:listfile' + * matcher: + <includematcher includes='(?:fenugreek(?:/|$)|new(?:/|$)|mammals/skunk(?:/|$))'> f fenugreek fenugreek f mammals/skunk mammals/skunk f new new $ cd .. - $ hg debugwalk -R t t/mammals/skunk - matcher: <patternmatcher patterns='(?:mammals\\/skunk(?:/|$))'> + $ hg debugwalk -v -R t t/mammals/skunk + * matcher: + <patternmatcher patterns='(?:mammals/skunk(?:/|$))'> f mammals/skunk t/mammals/skunk exact $ mkdir t2 $ cd t2 - $ hg debugwalk -R ../t ../t/mammals/skunk - matcher: <patternmatcher patterns='(?:mammals\\/skunk(?:/|$))'> + $ hg debugwalk -v -R ../t ../t/mammals/skunk + * matcher: + <patternmatcher patterns='(?:mammals/skunk(?:/|$))'> f mammals/skunk ../t/mammals/skunk exact - $ hg debugwalk --cwd ../t mammals/skunk - matcher: <patternmatcher patterns='(?:mammals\\/skunk(?:/|$))'> + $ hg debugwalk -v --cwd ../t mammals/skunk + * matcher: + <patternmatcher patterns='(?:mammals/skunk(?:/|$))'> f mammals/skunk mammals/skunk exact $ cd .. @@ -526,7 +646,7 @@ > EOF $ $PYTHON printnum.py >> overflow.list $ echo fenugreek >> overflow.list - $ hg debugwalk 'listfile:overflow.list' 2>&1 | egrep -v '(^matcher: |^xxx)' + $ hg debugwalk 'listfile:overflow.list' 2>&1 | egrep -v '^xxx' f fennel fennel exact f fenugreek fenugreek exact $ cd ..
--- a/tests/test-walkrepo.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-walkrepo.py Thu Jul 19 13:55:54 2018 -0400 @@ -17,22 +17,22 @@ checklink = util.checklink u = uimod.ui.load() -sym = checklink('.') +sym = checklink(b'.') -hg.repository(u, 'top1', create=1) -mkdir('subdir') -chdir('subdir') -hg.repository(u, 'sub1', create=1) -mkdir('subsubdir') -chdir('subsubdir') -hg.repository(u, 'subsub1', create=1) +hg.repository(u, b'top1', create=1) +mkdir(b'subdir') +chdir(b'subdir') +hg.repository(u, b'sub1', create=1) +mkdir(b'subsubdir') +chdir(b'subsubdir') +hg.repository(u, b'subsub1', create=1) chdir(os.path.pardir) if sym: - os.symlink(os.path.pardir, 'circle') - os.symlink(pjoin('subsubdir', 'subsub1'), 'subsub1') + os.symlink(os.path.pardir, b'circle') + os.symlink(pjoin(b'subsubdir', b'subsub1'), b'subsub1') def runtest(): - reposet = frozenset(walkrepos('.', followsym=True)) + reposet = frozenset(walkrepos(b'.', followsym=True)) if sym and (len(reposet) != 3): print("reposet = %r" % (reposet,)) print(("Found %d repositories when I should have found 3" @@ -41,19 +41,19 @@ print("reposet = %r" % (reposet,)) print(("Found %d repositories when I should have found 2" % (len(reposet),))) - sub1set = frozenset((pjoin('.', 'sub1'), - pjoin('.', 'circle', 'subdir', 'sub1'))) + sub1set = frozenset((pjoin(b'.', b'sub1'), + pjoin(b'.', b'circle', b'subdir', b'sub1'))) if len(sub1set & reposet) != 1: print("sub1set = %r" % (sub1set,)) print("reposet = %r" % (reposet,)) print("sub1set and reposet should have exactly one path in common.") - sub2set = frozenset((pjoin('.', 'subsub1'), - pjoin('.', 'subsubdir', 'subsub1'))) + sub2set = frozenset((pjoin(b'.', b'subsub1'), + pjoin(b'.', b'subsubdir', b'subsub1'))) if len(sub2set & reposet) != 1: print("sub2set = %r" % (sub2set,)) print("reposet = %r" % (reposet,)) print("sub2set and reposet should have exactly one path in common.") - sub3 = pjoin('.', 'circle', 'top1') + sub3 = pjoin(b'.', b'circle', b'top1') if sym and sub3 not in reposet: print("reposet = %r" % (reposet,)) print("Symbolic links are supported and %s is not in reposet" % (sub3,))
--- a/tests/test-wireproto-command-capabilities.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-wireproto-command-capabilities.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,3 +1,5 @@ +#require no-chg + $ . $TESTDIR/wireprotohelpers.sh $ hg init server
--- a/tests/test-wireproto.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-wireproto.py Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,7 @@ from __future__ import absolute_import, print_function +import sys + from mercurial import ( error, pycompat, @@ -9,6 +11,9 @@ wireprotov1peer, wireprotov1server, ) +from mercurial.utils import ( + stringutil, +) stringio = util.stringio class proto(object): @@ -92,10 +97,16 @@ srv = serverrepo() clt = clientpeer(srv, uimod.ui()) -print(clt.greet(b"Foobar")) +def printb(data, end=b'\n'): + out = getattr(sys.stdout, 'buffer', sys.stdout) + out.write(data + end) + out.flush() + +printb(clt.greet(b"Foobar")) with clt.commandexecutor() as e: fgreet1 = e.callcommand(b'greet', {b'name': b'Fo, =;:<o'}) fgreet2 = e.callcommand(b'greet', {b'name': b'Bar'}) -print([f.result() for f in (fgreet1, fgreet2)]) +printb(stringutil.pprint([f.result() for f in (fgreet1, fgreet2)], + bprefix=True))
--- a/tests/test-wireproto.py.out Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-wireproto.py.out Thu Jul 19 13:55:54 2018 -0400 @@ -1,2 +1,2 @@ Hello, Foobar -['Hello, Fo, =;:<o', 'Hello, Bar'] +[b'Hello, Fo, =;:<o', b'Hello, Bar']
--- a/tests/test-wireproto.t Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-wireproto.t Thu Jul 19 13:55:54 2018 -0400 @@ -1,5 +1,3 @@ -#require killdaemons - Test wire protocol argument passing Setup repo:
--- a/tests/test-wsgirequest.py Sun Jul 01 23:36:53 2018 +0900 +++ b/tests/test-wsgirequest.py Thu Jul 19 13:55:54 2018 -0400 @@ -206,18 +206,18 @@ """repository path components get stripped from URL.""" with self.assertRaisesRegex(error.ProgrammingError, - b'reponame requires PATH_INFO'): + 'reponame requires PATH_INFO'): parse(DEFAULT_ENV, reponame=b'repo') with self.assertRaisesRegex(error.ProgrammingError, - b'PATH_INFO does not begin with repo ' - b'name'): + 'PATH_INFO does not begin with repo ' + 'name'): parse(DEFAULT_ENV, reponame=b'repo', extra={ r'PATH_INFO': r'/pathinfo', }) with self.assertRaisesRegex(error.ProgrammingError, - b'reponame prefix of PATH_INFO'): + 'reponame prefix of PATH_INFO'): parse(DEFAULT_ENV, reponame=b'repo', extra={ r'PATH_INFO': r'/repoextra/path', }) @@ -251,7 +251,7 @@ def testaltbaseurl(self): # Simple hostname remap. - r = parse(DEFAULT_ENV, altbaseurl='http://altserver') + r = parse(DEFAULT_ENV, altbaseurl=b'http://altserver') self.assertEqual(r.url, b'http://testserver') self.assertEqual(r.baseurl, b'http://testserver') @@ -264,7 +264,7 @@ self.assertIsNone(r.reponame) # With a custom port. - r = parse(DEFAULT_ENV, altbaseurl='http://altserver:8000') + r = parse(DEFAULT_ENV, altbaseurl=b'http://altserver:8000') self.assertEqual(r.url, b'http://testserver') self.assertEqual(r.baseurl, b'http://testserver') self.assertEqual(r.advertisedurl, b'http://altserver:8000') @@ -276,7 +276,7 @@ self.assertIsNone(r.reponame) # With a changed protocol. - r = parse(DEFAULT_ENV, altbaseurl='https://altserver') + r = parse(DEFAULT_ENV, altbaseurl=b'https://altserver') self.assertEqual(r.url, b'http://testserver') self.assertEqual(r.baseurl, b'http://testserver') self.assertEqual(r.advertisedurl, b'https://altserver') @@ -289,7 +289,7 @@ self.assertIsNone(r.reponame) # Need to specify explicit port number for proper https:// alt URLs. - r = parse(DEFAULT_ENV, altbaseurl='https://altserver:443') + r = parse(DEFAULT_ENV, altbaseurl=b'https://altserver:443') self.assertEqual(r.url, b'http://testserver') self.assertEqual(r.baseurl, b'http://testserver') self.assertEqual(r.advertisedurl, b'https://altserver') @@ -301,7 +301,7 @@ self.assertIsNone(r.reponame) # With only PATH_INFO defined. - r = parse(DEFAULT_ENV, altbaseurl='http://altserver', extra={ + r = parse(DEFAULT_ENV, altbaseurl=b'http://altserver', extra={ r'PATH_INFO': r'/path1/path2', }) self.assertEqual(r.url, b'http://testserver/path1/path2') @@ -315,7 +315,7 @@ self.assertIsNone(r.reponame) # Path on alt URL. - r = parse(DEFAULT_ENV, altbaseurl='http://altserver/altpath') + r = parse(DEFAULT_ENV, altbaseurl=b'http://altserver/altpath') self.assertEqual(r.url, b'http://testserver') self.assertEqual(r.baseurl, b'http://testserver') self.assertEqual(r.advertisedurl, b'http://altserver/altpath') @@ -327,7 +327,7 @@ self.assertIsNone(r.reponame) # With a trailing slash. - r = parse(DEFAULT_ENV, altbaseurl='http://altserver/altpath/') + r = parse(DEFAULT_ENV, altbaseurl=b'http://altserver/altpath/') self.assertEqual(r.url, b'http://testserver') self.assertEqual(r.baseurl, b'http://testserver') self.assertEqual(r.advertisedurl, b'http://altserver/altpath/') @@ -339,7 +339,7 @@ self.assertIsNone(r.reponame) # PATH_INFO + path on alt URL. - r = parse(DEFAULT_ENV, altbaseurl='http://altserver/altpath', extra={ + r = parse(DEFAULT_ENV, altbaseurl=b'http://altserver/altpath', extra={ r'PATH_INFO': r'/path1/path2', }) self.assertEqual(r.url, b'http://testserver/path1/path2') @@ -354,7 +354,7 @@ self.assertIsNone(r.reponame) # PATH_INFO + path on alt URL with trailing slash. - r = parse(DEFAULT_ENV, altbaseurl='http://altserver/altpath/', extra={ + r = parse(DEFAULT_ENV, altbaseurl=b'http://altserver/altpath/', extra={ r'PATH_INFO': r'/path1/path2', }) self.assertEqual(r.url, b'http://testserver/path1/path2') @@ -369,7 +369,7 @@ self.assertIsNone(r.reponame) # Local SCRIPT_NAME is ignored. - r = parse(DEFAULT_ENV, altbaseurl='http://altserver', extra={ + r = parse(DEFAULT_ENV, altbaseurl=b'http://altserver', extra={ r'SCRIPT_NAME': r'/script', r'PATH_INFO': r'/path1/path2', }) @@ -384,7 +384,7 @@ self.assertIsNone(r.reponame) # Use remote's path for script name, app path - r = parse(DEFAULT_ENV, altbaseurl='http://altserver/altroot', extra={ + r = parse(DEFAULT_ENV, altbaseurl=b'http://altserver/altroot', extra={ r'SCRIPT_NAME': r'/script', r'PATH_INFO': r'/path1/path2', }) @@ -401,7 +401,7 @@ # reponame is factored in properly. r = parse(DEFAULT_ENV, reponame=b'repo', - altbaseurl='http://altserver/altroot', + altbaseurl=b'http://altserver/altroot', extra={ r'SCRIPT_NAME': r'/script', r'PATH_INFO': r'/repo/path1/path2',