--- 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',